Codebase list i3-wm / bcaac77
Import Upstream version 4.16.1 Jakob Haufe 4 years ago
551 changed file(s) with 133023 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 Revision history for AnyEvent-I3
1
2 0.18 2017-08-19
3
4 * support the GET_CONFIG command
5
6 0.17 2017-04-09
7
8 * support the shutdown event
9 * use lib '.' for Perl 5.25.11+
10
11 0.16 2014-10-03
12
13 * support the barconfig_update and binding event
14
15 0.15 2013-02-18
16
17 * support the window event
18
19 0.14 2012-09-22
20
21 * support the mode event
22
23 0.13 2012-08-05
24
25 * support the GET_VERSION request with a fall-back to i3 --version
26
27 0.12 2012-07-11
28
29 * taint mode fix: remove relative directories from $ENV{PATH}
30
31 0.11 2012-07-10
32
33 * taint mode fix for FreeBSD
34
35 0.10 2012-07-09
36
37 * Use i3 --get-socketpath by default for determining the socket path
38 * Bugfix: Also delete callbacks which are triggered due to an error
39
40 0.09 2011-10-12
41
42 * Implement GET_BAR_CONFIG request
43
44 0.08 2011-09-26
45
46 * Implement GET_MARKS request
47 * The synopsis mentioned ->workspaces, but it’s ->get_workspaces
48
49 0.07 2010-11-21
50
51 * Implement GET_TREE request
52
53 0.06 2010-06-16
54
55 * Add check to Makefile to abort in a Windows environment (neither i3 nor
56 unix sockets available)
57
58 0.05 2010-06-09
59
60 * use getpwuid() to resolve ~ in socket paths instead of glob()
61
62 0.04 2010-03-27
63
64 * use new default ipc-socket path, glob() path, bump version
65
66 0.03 2010-03-26
67
68 * fix MANIFEST
69
70 0.02 2010-03-23
71
72 * first upload to CPAN
0 Changes
1 lib/AnyEvent/I3.pm
2 Makefile.PL
3 MANIFEST This list of files
4 MANIFEST.SKIP
5 README
6 t/00-load.t
7 t/01-workspaces.t
8 t/02-sugar.t
9 t/boilerplate.t
10 t/manifest.t
11 t/pod-coverage.t
12 t/pod.t
0 ^\.git/
1 \.bak$
2 blib/
3 ^Makefile$
4 ^Makefile.old$
5 Build
6 Build.bat
7 ^pm_to_blib
8 \.tar\.gz$
9 ^pod2htm(.*).tmp$
10 ^AnyEvent-I3-
11 ^MYMETA.*
12 ^MANIFEST\.bak
0 use strict;
1 use warnings;
2
3 use 5.006;
4 use ExtUtils::MakeMaker;
5
6 if ( $^O eq 'MSWin32' ) {
7 die "AnyEvent::I3 cannot be used on win32 (unix sockets are missing)";
8 }
9
10 my %meta = (
11 name => 'AnyEvent-I3',
12 author => 'Michael Stapelberg, C<< <michael at i3wm.org> >>',
13 license => ['perl'],
14 'meta-spec' => { version => 2 },
15 resources => {
16 repository => {
17 url => 'git://github.com/i3/i3',
18 web => 'https://github.com/i3/i3',
19 type => 'git',
20 },
21 bugtracker => {
22 web => 'https://github.com/i3/i3/issues',
23 },
24 homepage => 'https://i3wm.org/',
25 license => ['https://dev.perl.org/licenses'],
26 },
27 );
28
29 my %requirements = (
30 configure_requires => {
31 'ExtUtils::MakeMaker' => 6.36,
32 },
33 build_requires => {
34 'ExtUtils::MakeMaker' => 6.36
35 },
36 runtime_requires => {
37 'AnyEvent' => 0,
38 'AnyEvent::Handle' => 0,
39 'AnyEvent::Socket' => 0,
40 'JSON::XS' => 0,
41 },
42 test_requires => {
43 'Test::More' => 0.80,
44 },
45 );
46
47 my %merged_requirements = (
48 'ExtUtils::MakeMaker' => 0,
49 'AnyEvent' => 0,
50 'AnyEvent::Handle' => 0,
51 'AnyEvent::Socket' => 0,
52 'JSON::XS' => 0,
53 'Test::More' => 0.80,
54 );
55
56 $meta{prereqs}{configure}{requires} = $requirements{configure_requires};
57 $meta{prereqs}{build}{requires} = $requirements{build_requires};
58 $meta{prereqs}{runtime}{requires} = $requirements{runtime_requires};
59 $meta{prereqs}{test}{requires} = $requirements{test_requires};
60
61 my %MM_Args = (
62 AUTHOR => 'Michael Stapelberg',
63 NAME => 'AnyEvent::I3',
64 DISTNAME => 'AnyEvent-I3',
65 EXE_FILES => [],
66 MIN_PERL_VERSION => '5.006',
67 VERSION_FROM => 'lib/AnyEvent/I3.pm',
68 ABSTRACT_FROM => 'lib/AnyEvent/I3.pm',
69 test => {
70 TESTS => 't/*.t',
71 },
72 );
73
74 sub is_eumm {
75 eval { ExtUtils::MakeMaker->VERSION( $_[0] ) };
76 }
77
78 is_eumm(6.30) and $MM_Args{LICENSE} = $meta{license}[0];
79 is_eumm(6.47_01) or delete $MM_Args{MIN_PERL_VERSION};
80 is_eumm(6.52)
81 and $MM_Args{CONFIGURE_REQUIRES} = $requirements{configure_requires};
82
83 is_eumm(6.57_02) and !is_eumm(6.57_07) and $MM_Args{NO_MYMETA} = 1;
84
85 if ( is_eumm(6.63_03) ) {
86 %MM_Args = (
87 %MM_Args,
88 TEST_REQUIRES => $requirements{test_requires},
89 BUILD_REQUIRES => $requirements{build_requires},
90 PREREQ_PM => $requirements{runtime_requires},
91 );
92 }
93 else {
94 $MM_Args{PREREQ_PM} = \%merged_requirements;
95 }
96 unless ( -f 'META.yml' ) {
97 $MM_Args{META_ADD} = \%meta;
98 }
99 WriteMakefile(%MM_Args);
0 AnyEvent-I3
1
2 This module connects to the i3 window manager using the UNIX socket based
3 IPC interface it provides (if enabled in the configuration file). You can
4 then subscribe to events or send messages and receive their replies.
5
6 INSTALLATION
7
8 To install this module, run the following commands:
9
10 perl Makefile.PL
11 make
12 make test
13 make install
14
15 SUPPORT AND DOCUMENTATION
16
17 After installing, you can find documentation for this module with the
18 perldoc command.
19
20 perldoc AnyEvent::I3
21
22 You can also look for information at:
23
24 RT, CPAN's request tracker
25 https://rt.cpan.org/NoAuth/Bugs.html?Dist=AnyEvent-I3
26
27 The i3 window manager website
28 https://i3wm.org
29
30
31 LICENSE AND COPYRIGHT
32
33 Copyright (C) 2010 Michael Stapelberg
34
35 This program is free software; you can redistribute it and/or modify it
36 under the terms of either: the GNU General Public License as published
37 by the Free Software Foundation; or the Artistic License.
38
39 See https://dev.perl.org/licenses/ for more information.
0 package AnyEvent::I3;
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use JSON::XS;
6 use AnyEvent::Handle;
7 use AnyEvent::Socket;
8 use AnyEvent;
9 use Encode;
10 use Scalar::Util qw(tainted);
11 use Carp;
12
13 =head1 NAME
14
15 AnyEvent::I3 - communicate with the i3 window manager
16
17 =cut
18
19 our $VERSION = '0.18';
20
21 =head1 VERSION
22
23 Version 0.18
24
25 =head1 SYNOPSIS
26
27 This module connects to the i3 window manager using the UNIX socket based
28 IPC interface it provides (if enabled in the configuration file). You can
29 then subscribe to events or send messages and receive their replies.
30
31 use AnyEvent::I3 qw(:all);
32
33 my $i3 = i3();
34
35 $i3->connect->recv or die "Error connecting";
36 say "Connected to i3";
37
38 my $workspaces = $i3->message(TYPE_GET_WORKSPACES)->recv;
39 say "Currently, you use " . @{$workspaces} . " workspaces";
40
41 ...or, using the sugar methods:
42
43 use AnyEvent::I3;
44
45 my $workspaces = i3->get_workspaces->recv;
46 say "Currently, you use " . @{$workspaces} . " workspaces";
47
48 A somewhat more involved example which dumps the i3 layout tree whenever there
49 is a workspace event:
50
51 use Data::Dumper;
52 use AnyEvent;
53 use AnyEvent::I3;
54
55 my $i3 = i3();
56
57 $i3->connect->recv or die "Error connecting to i3";
58
59 $i3->subscribe({
60 workspace => sub {
61 $i3->get_tree->cb(sub {
62 my ($tree) = @_;
63 say "tree: " . Dumper($tree);
64 });
65 }
66 })->recv->{success} or die "Error subscribing to events";
67
68 AE::cv->recv
69
70 =head1 EXPORT
71
72 =head2 $i3 = i3([ $path ]);
73
74 Creates a new C<AnyEvent::I3> object and returns it.
75
76 C<path> is an optional path of the UNIX socket to connect to. It is strongly
77 advised to NOT specify this unless you're absolutely sure you need it.
78 C<AnyEvent::I3> will automatically figure it out by querying the running i3
79 instance on the current DISPLAY which is almost always what you want.
80
81 =head1 SUBROUTINES/METHODS
82
83 =cut
84
85 use Exporter qw(import);
86 use base 'Exporter';
87
88 our @EXPORT = qw(i3);
89
90 use constant TYPE_RUN_COMMAND => 0;
91 use constant TYPE_COMMAND => 0;
92 use constant TYPE_GET_WORKSPACES => 1;
93 use constant TYPE_SUBSCRIBE => 2;
94 use constant TYPE_GET_OUTPUTS => 3;
95 use constant TYPE_GET_TREE => 4;
96 use constant TYPE_GET_MARKS => 5;
97 use constant TYPE_GET_BAR_CONFIG => 6;
98 use constant TYPE_GET_VERSION => 7;
99 use constant TYPE_GET_BINDING_MODES => 8;
100 use constant TYPE_GET_CONFIG => 9;
101 use constant TYPE_SEND_TICK => 10;
102 use constant TYPE_SYNC => 11;
103
104 our %EXPORT_TAGS = ( 'all' => [
105 qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
106 TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
107 TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK TYPE_SYNC)
108 ] );
109
110 our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
111
112 my $magic = "i3-ipc";
113
114 # TODO: auto-generate this from the header file? (i3/ipc.h)
115 my $event_mask = (1 << 31);
116 my %events = (
117 workspace => ($event_mask | 0),
118 output => ($event_mask | 1),
119 mode => ($event_mask | 2),
120 window => ($event_mask | 3),
121 barconfig_update => ($event_mask | 4),
122 binding => ($event_mask | 5),
123 shutdown => ($event_mask | 6),
124 tick => ($event_mask | 7),
125 _error => 0xFFFFFFFF,
126 );
127
128 sub i3 {
129 AnyEvent::I3->new(@_)
130 }
131
132 # Calls i3, even when running in taint mode.
133 sub _call_i3 {
134 my ($args) = @_;
135
136 my $path_tainted = tainted($ENV{PATH});
137 # This effectively circumvents taint mode checking for $ENV{PATH}. We
138 # do this because users might specify PATH explicitly to call i3 in a
139 # custom location (think ~/.bin/).
140 (local $ENV{PATH}) = ($ENV{PATH} =~ /(.*)/);
141
142 # In taint mode, we also need to remove all relative directories from
143 # PATH (like . or ../bin). We only do this in taint mode and warn the
144 # user, since this might break a real-world use case for some people.
145 if ($path_tainted) {
146 my @dirs = split /:/, $ENV{PATH};
147 my @filtered = grep !/^\./, @dirs;
148 if (scalar @dirs != scalar @filtered) {
149 $ENV{PATH} = join ':', @filtered;
150 warn qq|Removed relative directories from PATH because you | .
151 qq|are running Perl with taint mode enabled. Remove -T | .
152 qq|to be able to use relative directories in PATH. | .
153 qq|New PATH is "$ENV{PATH}"|;
154 }
155 }
156 # Otherwise the qx() operator wont work:
157 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
158 chomp(my $result = qx(i3 $args));
159 # Circumventing taint mode again: the socket can be anywhere on the
160 # system and that’s okay.
161 if ($result =~ /^([^\0]+)$/) {
162 return $1;
163 }
164
165 warn "Calling i3 $args failed. Is DISPLAY set and is i3 in your PATH?";
166 return undef;
167 }
168
169 =head2 $i3 = AnyEvent::I3->new([ $path ])
170
171 Creates a new C<AnyEvent::I3> object and returns it.
172
173 C<path> is an optional path of the UNIX socket to connect to. It is strongly
174 advised to NOT specify this unless you're absolutely sure you need it.
175 C<AnyEvent::I3> will automatically figure it out by querying the running i3
176 instance on the current DISPLAY which is almost always what you want.
177
178 =cut
179 sub new {
180 my ($class, $path) = @_;
181
182 $path = _call_i3('--get-socketpath') unless $path;
183
184 # This is the old default path (v3.*). This fallback line can be removed in
185 # a year from now. -- Michael, 2012-07-09
186 $path ||= '~/.i3/ipc.sock';
187
188 # Check if we need to resolve ~
189 if ($path =~ /~/) {
190 # We use getpwuid() instead of $ENV{HOME} because the latter is tainted
191 # and thus produces warnings when running tests with perl -T
192 my $home = (getpwuid($<))[7];
193 confess "Could not get home directory" unless $home and -d $home;
194 $path =~ s/~/$home/g;
195 }
196
197 bless { path => $path } => $class;
198 }
199
200 =head2 $i3->connect
201
202 Establishes the connection to i3. Returns an C<AnyEvent::CondVar> which will
203 be triggered with a boolean (true if the connection was established) as soon as
204 the connection has been established.
205
206 if ($i3->connect->recv) {
207 say "Connected to i3";
208 }
209
210 =cut
211 sub connect {
212 my ($self) = @_;
213 my $cv = AnyEvent->condvar;
214
215 tcp_connect "unix/", $self->{path}, sub {
216 my ($fh) = @_;
217
218 return $cv->send(0) unless $fh;
219
220 $self->{ipchdl} = AnyEvent::Handle->new(
221 fh => $fh,
222 on_read => sub { my ($hdl) = @_; $self->_data_available($hdl) },
223 on_error => sub {
224 my ($hdl, $fatal, $msg) = @_;
225 delete $self->{ipchdl};
226 $hdl->destroy;
227
228 my $cb = $self->{callbacks};
229
230 # Trigger all one-time callbacks with undef
231 for my $type (keys %{$cb}) {
232 next if ($type & $event_mask) == $event_mask;
233 $cb->{$type}->();
234 delete $cb->{$type};
235 }
236
237 # Trigger _error callback, if set
238 my $type = $events{_error};
239 return unless defined($cb->{$type});
240 $cb->{$type}->($msg);
241 }
242 );
243
244 $cv->send(1)
245 };
246
247 $cv
248 }
249
250 sub _data_available {
251 my ($self, $hdl) = @_;
252
253 $hdl->unshift_read(
254 chunk => length($magic) + 4 + 4,
255 sub {
256 my $header = $_[1];
257 # Unpack message length and read the payload
258 my ($len, $type) = unpack("LL", substr($header, length($magic)));
259 $hdl->unshift_read(
260 chunk => $len,
261 sub { $self->_handle_i3_message($type, $_[1]) }
262 );
263 }
264 );
265 }
266
267 sub _handle_i3_message {
268 my ($self, $type, $payload) = @_;
269
270 return unless defined($self->{callbacks}->{$type});
271
272 my $cb = $self->{callbacks}->{$type};
273 $cb->(decode_json $payload);
274
275 return if ($type & $event_mask) == $event_mask;
276
277 # If this was a one-time callback, we delete it
278 # (when connection is lost, all one-time callbacks get triggered)
279 delete $self->{callbacks}->{$type};
280 }
281
282 =head2 $i3->subscribe(\%callbacks)
283
284 Subscribes to the given event types. This function awaits a hashref with the
285 key being the name of the event and the value being a callback.
286
287 my %callbacks = (
288 workspace => sub { say "Workspaces changed" }
289 );
290
291 if ($i3->subscribe(\%callbacks)->recv->{success}) {
292 say "Successfully subscribed";
293 }
294
295 The special callback with name C<_error> is called when the connection to i3
296 is killed (because of a crash, exit or restart of i3 most likely). You can
297 use it to print an appropriate message and exit cleanly or to try to reconnect.
298
299 my %callbacks = (
300 _error => sub {
301 my ($msg) = @_;
302 say "I am sorry. I am so sorry: $msg";
303 exit 1;
304 }
305 );
306
307 $i3->subscribe(\%callbacks)->recv;
308
309 =cut
310 sub subscribe {
311 my ($self, $callbacks) = @_;
312
313 # Register callbacks for each message type
314 for my $key (keys %{$callbacks}) {
315 my $type = $events{$key};
316 $self->{callbacks}->{$type} = $callbacks->{$key};
317 }
318
319 $self->message(TYPE_SUBSCRIBE, [ keys %{$callbacks} ])
320 }
321
322 =head2 $i3->message($type, $content)
323
324 Sends a message of the specified C<type> to i3, possibly containing the data
325 structure C<content> (or C<content>, encoded as utf8, if C<content> is a
326 scalar), if specified.
327
328 my $reply = $i3->message(TYPE_RUN_COMMAND, "reload")->recv;
329 if ($reply->{success}) {
330 say "Configuration successfully reloaded";
331 }
332
333 =cut
334 sub message {
335 my ($self, $type, $content) = @_;
336
337 confess "No message type specified" unless defined($type);
338
339 confess "No connection to i3" unless defined($self->{ipchdl});
340
341 my $payload = "";
342 if ($content) {
343 if (not ref($content)) {
344 # Convert from Perl’s internal encoding to UTF8 octets
345 $payload = encode_utf8($content);
346 } else {
347 $payload = encode_json $content;
348 }
349 }
350 my $message = $magic . pack("LL", length($payload), $type) . $payload;
351 $self->{ipchdl}->push_write($message);
352
353 my $cv = AnyEvent->condvar;
354
355 # We don’t preserve the old callback as it makes no sense to
356 # have a callback on message reply types (only on events)
357 $self->{callbacks}->{$type} =
358 sub {
359 my ($reply) = @_;
360 $cv->send($reply);
361 undef $self->{callbacks}->{$type};
362 };
363
364 $cv
365 }
366
367 =head1 SUGAR METHODS
368
369 These methods intend to make your scripts as beautiful as possible. All of
370 them automatically establish a connection to i3 blockingly (if it does not
371 already exist).
372
373 =cut
374
375 sub _ensure_connection {
376 my ($self) = @_;
377
378 return if defined($self->{ipchdl});
379
380 $self->connect->recv or confess "Unable to connect to i3 (socket path " . $self->{path} . ")";
381 }
382
383 =head2 get_workspaces
384
385 Gets the current workspaces from i3.
386
387 my $ws = i3->get_workspaces->recv;
388 say Dumper($ws);
389
390 =cut
391 sub get_workspaces {
392 my ($self) = @_;
393
394 $self->_ensure_connection;
395
396 $self->message(TYPE_GET_WORKSPACES)
397 }
398
399 =head2 get_outputs
400
401 Gets the current outputs from i3.
402
403 my $outs = i3->get_outputs->recv;
404 say Dumper($outs);
405
406 =cut
407 sub get_outputs {
408 my ($self) = @_;
409
410 $self->_ensure_connection;
411
412 $self->message(TYPE_GET_OUTPUTS)
413 }
414
415 =head2 get_tree
416
417 Gets the layout tree from i3 (>= v4.0).
418
419 my $tree = i3->get_tree->recv;
420 say Dumper($tree);
421
422 =cut
423 sub get_tree {
424 my ($self) = @_;
425
426 $self->_ensure_connection;
427
428 $self->message(TYPE_GET_TREE)
429 }
430
431 =head2 get_marks
432
433 Gets all the window identifier marks from i3 (>= v4.1).
434
435 my $marks = i3->get_marks->recv;
436 say Dumper($marks);
437
438 =cut
439 sub get_marks {
440 my ($self) = @_;
441
442 $self->_ensure_connection;
443
444 $self->message(TYPE_GET_MARKS)
445 }
446
447 =head2 get_bar_config
448
449 Gets the bar configuration for the specific bar id from i3 (>= v4.1).
450
451 my $config = i3->get_bar_config($id)->recv;
452 say Dumper($config);
453
454 =cut
455 sub get_bar_config {
456 my ($self, $id) = @_;
457
458 $self->_ensure_connection;
459
460 $self->message(TYPE_GET_BAR_CONFIG, $id)
461 }
462
463 =head2 get_version
464
465 Gets the i3 version via IPC, with a fall-back that parses the output of i3
466 --version (for i3 < v4.3).
467
468 my $version = i3->get_version()->recv;
469 say "major: " . $version->{major} . ", minor = " . $version->{minor};
470
471 =cut
472 sub get_version {
473 my ($self) = @_;
474
475 $self->_ensure_connection;
476
477 my $cv = AnyEvent->condvar;
478
479 my $version_cv = $self->message(TYPE_GET_VERSION);
480 my $timeout;
481 $timeout = AnyEvent->timer(
482 after => 1,
483 cb => sub {
484 warn "Falling back to i3 --version since the running i3 doesn’t support GET_VERSION yet.";
485 my $version = _call_i3('--version');
486 $version =~ s/^i3 version //;
487 my $patch = 0;
488 my ($major, $minor) = ($version =~ /^([0-9]+)\.([0-9]+)/);
489 if ($version =~ /^[0-9]+\.[0-9]+\.([0-9]+)/) {
490 $patch = $1;
491 }
492 # Strip everything from the © sign on.
493 $version =~ s/ ©.*$//g;
494 $cv->send({
495 major => int($major),
496 minor => int($minor),
497 patch => int($patch),
498 human_readable => $version,
499 });
500 undef $timeout;
501 },
502 );
503 $version_cv->cb(sub {
504 undef $timeout;
505 $cv->send($version_cv->recv);
506 });
507
508 return $cv;
509 }
510
511 =head2 get_config
512
513 Gets the raw last read config from i3. Requires i3 >= 4.14
514
515 =cut
516 sub get_config {
517 my ($self) = @_;
518
519 $self->_ensure_connection;
520
521 $self->message(TYPE_GET_CONFIG);
522 }
523
524 =head2 send_tick
525
526 Sends a tick event. Requires i3 >= 4.15
527
528 =cut
529 sub send_tick {
530 my ($self, $payload) = @_;
531
532 $self->_ensure_connection;
533
534 $self->message(TYPE_SEND_TICK, $payload);
535 }
536
537 =head2 sync
538
539 Sends an i3 sync event. Requires i3 >= 4.16
540
541 =cut
542 sub sync {
543 my ($self, $payload) = @_;
544
545 $self->_ensure_connection;
546
547 $self->message(TYPE_SYNC, $payload);
548 }
549
550 =head2 command($content)
551
552 Makes i3 execute the given command
553
554 my $reply = i3->command("reload")->recv;
555 die "command failed" unless $reply->{success};
556
557 =cut
558 sub command {
559 my ($self, $content) = @_;
560
561 $self->_ensure_connection;
562
563 $self->message(TYPE_RUN_COMMAND, $content)
564 }
565
566 =head1 AUTHOR
567
568 Michael Stapelberg, C<< <michael at i3wm.org> >>
569
570 =head1 BUGS
571
572 Please report any bugs or feature requests to C<bug-anyevent-i3 at
573 rt.cpan.org>, or through the web interface at
574 L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=AnyEvent-I3>. I will be
575 notified, and then you'll automatically be notified of progress on your bug as
576 I make changes.
577
578 =head1 SUPPORT
579
580 You can find documentation for this module with the perldoc command.
581
582 perldoc AnyEvent::I3
583
584 You can also look for information at:
585
586 =over 2
587
588 =item * RT: CPAN's request tracker
589
590 L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=AnyEvent-I3>
591
592 =item * The i3 window manager website
593
594 L<https://i3wm.org>
595
596 =back
597
598
599 =head1 ACKNOWLEDGEMENTS
600
601
602 =head1 LICENSE AND COPYRIGHT
603
604 Copyright 2010-2012 Michael Stapelberg.
605
606 This program is free software; you can redistribute it and/or modify it
607 under the terms of either: the GNU General Public License as published
608 by the Free Software Foundation; or the Artistic License.
609
610 See https://dev.perl.org/licenses/ for more information.
611
612
613 =cut
614
615 1; # End of AnyEvent::I3
0 #!perl -T
1
2 use Test::More tests => 1;
3
4 BEGIN {
5 use_ok( 'AnyEvent::I3' ) || print "Bail out!
6 ";
7 }
8
9 diag( "Testing AnyEvent::I3 $AnyEvent::I3::VERSION, Perl $], $^X" );
0 #!perl -T
1 # vim:ts=4:sw=4:expandtab
2
3 use Test::More tests => 3;
4 use AnyEvent::I3;
5 use AnyEvent;
6
7 my $i3 = i3();
8 my $cv = AnyEvent->condvar;
9
10 # Try to connect to i3
11 $i3->connect->cb(sub { my ($v) = @_; $cv->send($v->recv) });
12
13 # But cancel if we are not connected after 0.5 seconds
14 my $t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0) });
15 my $connected = $cv->recv;
16
17 SKIP: {
18 skip 'No connection to i3', 3 unless $connected;
19
20 my $workspaces = $i3->message(1)->recv;
21 isa_ok($workspaces, 'ARRAY');
22
23 ok(@{$workspaces} > 0, 'More than zero workspaces found');
24
25 ok(defined(@{$workspaces}[0]->{num}), 'JSON deserialized');
26 }
27
28 diag( "Testing AnyEvent::I3 $AnyEvent::I3::VERSION, Perl $], $^X" );
0 #!perl -T
1 # vim:ts=4:sw=4:expandtab
2
3 use Test::More tests => 3;
4 use AnyEvent::I3;
5 use AnyEvent;
6
7 my $i3 = i3();
8 my $cv = AnyEvent->condvar;
9
10 # Try to connect to i3
11 $i3->connect->cb(sub { my ($v) = @_; $cv->send($v->recv) });
12
13 # But cancel if we are not connected after 0.5 seconds
14 my $t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0) });
15 my $connected = $cv->recv;
16
17 SKIP: {
18 skip 'No connection to i3', 3 unless $connected;
19
20 my $workspaces = i3->get_workspaces->recv;
21 isa_ok($workspaces, 'ARRAY');
22
23 ok(@{$workspaces} > 0, 'More than zero workspaces found');
24
25 ok(defined(@{$workspaces}[0]->{num}), 'JSON deserialized');
26 }
27
28 diag( "Testing AnyEvent::I3 $AnyEvent::I3::VERSION, Perl $], $^X" );
0 #!perl -T
1
2 use strict;
3 use warnings;
4 use Test::More tests => 3;
5
6 sub not_in_file_ok {
7 my ($filename, %regex) = @_;
8 open( my $fh, '<', $filename )
9 or die "couldn't open $filename for reading: $!";
10
11 my %violated;
12
13 while (my $line = <$fh>) {
14 while (my ($desc, $regex) = each %regex) {
15 if ($line =~ $regex) {
16 push @{$violated{$desc}||=[]}, $.;
17 }
18 }
19 }
20
21 if (%violated) {
22 fail("$filename contains boilerplate text");
23 diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
24 } else {
25 pass("$filename contains no boilerplate text");
26 }
27 }
28
29 sub module_boilerplate_ok {
30 my ($module) = @_;
31 not_in_file_ok($module =>
32 'the great new $MODULENAME' => qr/ - The great new /,
33 'boilerplate description' => qr/Quick summary of what the module/,
34 'stub function definition' => qr/function[12]/,
35 );
36 }
37
38 TODO: {
39 local $TODO = "Need to replace the boilerplate text";
40
41 not_in_file_ok(README =>
42 "The README is used..." => qr/The README is used/,
43 "'version information here'" => qr/to provide version information/,
44 );
45
46 not_in_file_ok(Changes =>
47 "placeholder date/time" => qr(Date/time)
48 );
49
50 module_boilerplate_ok('lib/AnyEvent/I3.pm');
51
52
53 }
54
0 #!perl -T
1
2 use strict;
3 use warnings;
4 use Test::More;
5
6 unless ( $ENV{RELEASE_TESTING} ) {
7 plan( skip_all => "Author tests not required for installation" );
8 }
9
10 eval "use Test::CheckManifest 0.9";
11 plan skip_all => "Test::CheckManifest 0.9 required" if $@;
12 ok_manifest();
0 use strict;
1 use warnings;
2 use Test::More;
3
4 # Ensure a recent version of Test::Pod::Coverage
5 my $min_tpc = 1.08;
6 eval "use Test::Pod::Coverage $min_tpc";
7 plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
8 if $@;
9
10 # Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
11 # but older versions don't recognize some common documentation styles
12 my $min_pc = 0.18;
13 eval "use Pod::Coverage $min_pc";
14 plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
15 if $@;
16
17 all_pod_coverage_ok();
0 #!perl -T
1
2 use strict;
3 use warnings;
4 use Test::More;
5
6 # Ensure a recent version of Test::Pod
7 my $min_tp = 1.22;
8 eval "use Test::Pod $min_tp";
9 plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
10
11 all_pod_files_ok();
0
1 i3 has the following dependencies:
2
3 "min" means minimum required version
4 "lkgv" means last known good version
5
6 ┌──────────────┬────────┬────────┬─────────────────────────────────────────────────────────────┐
7 │ dependency │ min. │ lkgv │ URL │
8 ├──────────────┼────────┼────────┼─────────────────────────────────────────────────────────────┤
9 │ pkg-config │ 0.25 │ 0.29 │ https://pkgconfig.freedesktop.org/ │
10 │ libxcb │ 1.1.93 │ 1.12 │ https://xcb.freedesktop.org/dist/ │
11 │ xcb-util │ 0.3.3 │ 0.4.1 │ https://xcb.freedesktop.org/dist/ │
12 │ xkbcommon │ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/ │
13 │ xkbcommon-x11│ 0.4.0 │ 0.6.1 │ https://xkbcommon.org/ │
14 │ util-cursor³⁴│ 0.0.99 │ 0.1.3 │ https://xcb.freedesktop.org/dist/ │
15 │ util-wm⁴ │ 0.3.8 │ 0.3.8 │ https://xcb.freedesktop.org/dist/ │
16 │ util-keysyms⁴│ 0.3.8 │ 0.4.0 │ https://xcb.freedesktop.org/dist/ │
17 │ util-xrm⁴ │ 1.0.0 │ 1.0.0 │ https://github.com/Airblader/xcb-util-xrm/ │
18 │ libev │ 4.0 │ 4.19 │ http://libev.schmorp.de/ │
19 │ yajl │ 2.0.1 │ 2.1.0 │ https://lloyd.github.com/yajl/ │
20 │ asciidoc │ 8.3.0 │ 8.6.9 │ http://www.methods.co.nz/asciidoc/ │
21 │ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
22 │ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/dist/Pod-Simple/ │
23 │ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
24 │ PCRE │ 8.12 │ 8.38 │ https://www.pcre.org/ │
25 │ libsn¹ │ 0.10 │ 0.12 │ https://freedesktop.org/wiki/Software/startup-notification/ │
26 │ pango │ 1.30.0 │ 1.40.1 │ http://www.pango.org/ │
27 │ cairo │ 1.14.4 │ 1.14.6 │ https://cairographics.org/ │
28 └──────────────┴────────┴────────┴─────────────────────────────────────────────────────────────┘
29 ¹ libsn = libstartup-notification
30 ² Pod::Simple is a Perl module required for converting the testsuite
31 documentation to HTML. See https://michael.stapelberg.de/cpan/#Pod::Simple
32 ³ xcb-util-cursor, to be precise.
33 ⁴ Depending on your distribution, this might be considered part of xcb-util.
34
35 i3bar, i3-msg, i3-input, i3-nagbar and i3-config-wizard do not introduce any
36 new dependencies.
37
38 i3-migrate-config-to-v4 and i3-dmenu-desktop are implemented in Perl, but have
39 no dependencies besides Perl 5.10.
40
41 i3-save-tree is also implemented in Perl and needs AnyEvent::I3 (>= 0.12) and
42 JSON::XS. While i3-save-tree is not required for running i3 itself, it is
43 strongly recommended to provide it in distribution packages.
0 4.16.1 (2019-01-27)
0 Copyright © 2009, Michael Stapelberg and contributors
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12
13 * Neither the name of Michael Stapelberg nor the
14 names of contributors may be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY Michael Stapelberg ''AS IS'' AND ANY
18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL Michael Stapelberg BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 @CODE_COVERAGE_RULES@
1
2 echo-version:
3 @echo "@I3_VERSION@"
4
5 bin_PROGRAMS = \
6 i3 \
7 i3bar/i3bar \
8 i3-config-wizard/i3-config-wizard \
9 i3-dump-log/i3-dump-log \
10 i3-input/i3-input \
11 i3-msg/i3-msg \
12 i3-nagbar/i3-nagbar
13
14 install-exec-hook:
15 $(LN_S) -f i3 $(DESTDIR)$(bindir)/i3-with-shmlog
16
17 uninstall-hook:
18 rm -f $(DESTDIR)$(bindir)/i3-with-shmlog
19
20 i3includedir=$(includedir)/i3
21 i3include_HEADERS = \
22 include/i3/ipc.h
23
24 dist_bin_SCRIPTS = \
25 i3-dmenu-desktop \
26 i3-migrate-config-to-v4 \
27 i3-save-tree \
28 i3-sensible-editor \
29 i3-sensible-pager \
30 i3-sensible-terminal
31
32 i3confdir = $(sysconfdir)/i3
33 dist_i3conf_DATA = \
34 etc/config \
35 etc/config.keycodes
36
37 applicationsdir = $(datarootdir)/applications
38 xsessionsdir = $(datarootdir)/xsessions
39 dist_applications_DATA = \
40 share/applications/i3.desktop
41 dist_xsessions_DATA = \
42 share/xsessions/i3.desktop \
43 share/xsessions/i3-with-shmlog.desktop
44
45 noinst_LIBRARIES = libi3.a
46
47 check_PROGRAMS = \
48 test.commands_parser \
49 test.config_parser \
50 test.inject_randr15
51
52 check_SCRIPTS = \
53 testcases/complete-run.pl
54
55 check_DATA = \
56 anyevent-i3.stamp
57
58 clean-check:
59 rm -rf testsuite-* latest i3-cfg-for-* _Inline
60 clean-local: clean-check
61
62 TESTS = testcases/complete-run.pl
63
64 EXTRA_DIST = \
65 $(dist_docs_toc_DATA:.html=) \
66 $(dist_docs_notoc_DATA:.html=) \
67 AnyEvent-I3/Changes \
68 AnyEvent-I3/MANIFEST \
69 AnyEvent-I3/MANIFEST.SKIP \
70 AnyEvent-I3/Makefile.PL \
71 AnyEvent-I3/README \
72 AnyEvent-I3/lib/AnyEvent/I3.pm \
73 AnyEvent-I3/t/00-load.t \
74 AnyEvent-I3/t/01-workspaces.t \
75 AnyEvent-I3/t/02-sugar.t \
76 AnyEvent-I3/t/boilerplate.t \
77 AnyEvent-I3/t/manifest.t \
78 AnyEvent-I3/t/pod-coverage.t \
79 AnyEvent-I3/t/pod.t \
80 contrib/dump-asy.pl \
81 contrib/gtk-tree-watch.pl \
82 contrib/i3-wsbar \
83 contrib/per-workspace-layout.pl \
84 contrib/trivial-bar-script.sh \
85 docs/asciidoc-git.conf \
86 docs/bigpicture.png \
87 docs/i3-pod2html \
88 docs/i3-sync.dia \
89 docs/i3-sync.png \
90 docs/i3-sync-working.dia \
91 docs/i3-sync-working.png \
92 docs/keyboard-layer1.png \
93 docs/keyboard-layer2.png \
94 docs/layout-saving-1.png \
95 docs/logo-30.png \
96 docs/modes.png \
97 docs/refcard.html \
98 docs/refcard_style.css \
99 docs/single_terminal.png \
100 docs/snapping.png \
101 docs/tree-layout1.png \
102 docs/tree-layout2.png \
103 docs/tree-shot1.png \
104 docs/tree-shot2.png \
105 docs/tree-shot3.png \
106 docs/tree-shot4.png \
107 docs/two_columns.png \
108 docs/two_terminals.png \
109 docs/wsbar.dia \
110 docs/wsbar.png \
111 i3bar/LICENSE \
112 libi3/README \
113 $(asciidoc_MANS:.1=.man) \
114 $(asciidoc_MANS:.1=.man) \
115 man/asciidoc.conf.in \
116 DEPENDS \
117 I3_VERSION \
118 LICENSE \
119 PACKAGE-MAINTAINER \
120 RELEASE-NOTES-4.16.1 \
121 generate-command-parser.pl \
122 parser-specs/commands.spec \
123 parser-specs/config.spec \
124 parser-specs/highlighting.vim \
125 pseudo-doc.doxygen \
126 testcases/complete-run.pl.in \
127 testcases/i3-test.config \
128 testcases/lib/i3test/Test.pm \
129 testcases/lib/i3test/Util.pm \
130 testcases/lib/i3test/XTEST.pm \
131 testcases/lib/i3test.pm.in \
132 testcases/lib/SocketActivation.pm \
133 testcases/lib/StartXServer.pm \
134 testcases/lib/StatusLine.pm \
135 testcases/lib/TestWorker.pm \
136 testcases/Makefile.PL \
137 testcases/new-test \
138 testcases/restart-state.golden \
139 testcases/t \
140 testcases/valgrind.supp
141
142 # dirstamps contains directories which we want to be created in $(top_builddir)
143 # so that our custom rules can store files in them.
144 dirstamp = .dirstamp
145 dirstamps = \
146 docs/$(dirstamp) \
147 man/$(dirstamp) \
148 parser/$(dirstamp)
149 DISTCLEANFILES = $(dirstamps)
150
151 $(dirstamps):
152 @stamp='$@'; $(MKDIR_P) "$${stamp%/*}"
153 @: > $@
154
155 ################################################################################
156 # docs generation
157 ################################################################################
158
159 docs_tocdir = ${docdir}
160 docs_notocdir = ${docdir}
161 docs_poddir = ${docdir}
162 if BUILD_DOCS
163 dist_docs_toc_DATA = \
164 docs/hacking-howto.html \
165 docs/userguide.html \
166 docs/ipc.html \
167 docs/multi-monitor.html \
168 docs/wsbar.html \
169 docs/testsuite.html \
170 docs/i3bar-protocol.html \
171 docs/layout-saving.html
172
173 dist_docs_notoc_DATA = \
174 docs/debugging.html
175
176 dist_docs_pod_DATA = \
177 docs/lib-i3test.html \
178 docs/lib-i3test-test.html
179
180 $(dist_docs_toc_DATA): docs/%.html: docs/% docs/$(dirstamp)
181 $(AM_V_GEN) @PATH_ASCIIDOC@ -a toc -n -o $@ $<
182
183 $(dist_docs_notoc_DATA): docs/%.html: docs/% docs/$(dirstamp)
184 $(AM_V_GEN) @PATH_ASCIIDOC@ -n -o $@ $<
185
186 docs/lib-i3test.html: testcases/lib/i3test.pm docs/$(dirstamp)
187 $(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@
188
189 docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm docs/$(dirstamp)
190 $(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@
191
192 else
193 dist_docs_toc_DATA =
194 dist_docs_notoc_DATA =
195 dist_docs_pod_DATA =
196 endif
197
198 ################################################################################
199 # manpage generation
200 ################################################################################
201
202 if BUILD_MANS
203 dist_man1_MANS = \
204 $(asciidoc_MANS) \
205 $(pod_MANS)
206
207 asciidoc_MANS = \
208 man/i3.1 \
209 man/i3bar.1 \
210 man/i3-msg.1 \
211 man/i3-input.1 \
212 man/i3-nagbar.1 \
213 man/i3-config-wizard.1 \
214 man/i3-migrate-config-to-v4.1 \
215 man/i3-sensible-editor.1 \
216 man/i3-sensible-pager.1 \
217 man/i3-sensible-terminal.1 \
218 man/i3-dump-log.1
219
220 pod_MANS = \
221 man/i3-dmenu-desktop.1 \
222 man/i3-save-tree.1
223
224 $(asciidoc_MANS): man/%.1: man/%.xml man/$(dirstamp)
225 $(AM_V_GEN) out='$@'; @PATH_XMLTO@ man -o "$${out%/*}" $<
226 @stamp='$@'; $(MKDIR_P) "$${stamp%/*}"
227
228 man/%.xml: man/%.man man/asciidoc.conf man/$(dirstamp)
229 $(AM_V_GEN) @PATH_ASCIIDOC@ -d manpage -b docbook -f $(top_builddir)/man/asciidoc.conf -o $@ $<
230
231 $(pod_MANS): man/%.1: % man/$(dirstamp)
232 $(AM_V_GEN) @PATH_POD2MAN@ --utf8 $< > $@
233 else
234 asciidoc_MANS =
235 endif
236
237 AM_CPPFLAGS = \
238 -DSYSCONFDIR="\"$(sysconfdir)\"" \
239 -I$(top_builddir)/parser \
240 -I$(top_srcdir)/include \
241 @AX_EXTEND_SRCDIR_CPPFLAGS@
242
243 i3_CFLAGS = \
244 $(AM_CFLAGS) \
245 $(libi3_CFLAGS) \
246 $(LIBSN_CFLAGS) \
247 $(XCB_CFLAGS) \
248 $(XCB_UTIL_CURSOR_CFLAGS) \
249 $(XCB_UTIL_KEYSYM_CFLAGS) \
250 $(XCB_UTIL_WM_CFLAGS) \
251 $(XCB_UTIL_XRM_CFLAGS) \
252 $(XKBCOMMON_CFLAGS) \
253 $(YAJL_CFLAGS) \
254 $(LIBPCRE_CFLAGS) \
255 $(PTHREAD_CFLAGS) \
256 $(CODE_COVERAGE_CFLAGS)
257
258 i3_CPPFLAGS = \
259 $(AM_CPPFLAGS) \
260 $(CODE_COVERAGE_CPPFLAGS)
261
262 i3_LDADD = \
263 $(libi3_LIBS) \
264 $(LIBSN_LIBS) \
265 $(XCB_LIBS) \
266 $(XCB_UTIL_CURSOR_LIBS) \
267 $(XCB_UTIL_KEYSYMS_LIBS) \
268 $(XCB_UTIL_WM_LIBS) \
269 $(XCB_UTIL_XRM_LIBS) \
270 $(XKBCOMMON_LIBS) \
271 $(YAJL_LIBS) \
272 $(LIBPCRE_LIBS) \
273 $(PANGOCAIRO_LIBS) \
274 $(PTHREAD_LIBS) \
275 $(CODE_COVERAGE_LDFLAGS)
276
277 libi3_CFLAGS = \
278 $(AM_CFLAGS) \
279 $(XCB_CFLAGS) \
280 $(XCB_UTIL_CFLAGS) \
281 $(XCB_UTIL_XRM_CFLAGS) \
282 $(YAJL_CFLAGS) \
283 $(PANGOCAIRO_CFLAGS)
284
285 libi3_LIBS = \
286 $(top_builddir)/libi3.a \
287 $(XCB_LIBS) \
288 $(XCB_UTIL_LIBS) \
289 $(XCB_UTIL_XRM_LIBS) \
290 $(YAJL_LIBS) \
291 $(PANGOCAIRO_LIBS)
292
293 libi3_a_CFLAGS = \
294 $(libi3_CFLAGS)
295
296 libi3_a_SOURCES = \
297 include/libi3.h \
298 libi3/dpi.c \
299 libi3/draw_util.c \
300 libi3/fake_configure_notify.c \
301 libi3/font.c \
302 libi3/format_placeholders.c \
303 libi3/g_utf8_make_valid.c \
304 libi3/get_colorpixel.c \
305 libi3/get_config_path.c \
306 libi3/get_exe_path.c \
307 libi3/get_mod_mask.c \
308 libi3/get_process_filename.c \
309 libi3/get_visualtype.c \
310 libi3/ipc_connect.c \
311 libi3/ipc_recv_message.c \
312 libi3/ipc_send_message.c \
313 libi3/is_debug_build.c \
314 libi3/mkdirp.c \
315 libi3/resolve_tilde.c \
316 libi3/root_atom_contents.c \
317 libi3/safewrappers.c \
318 libi3/string.c \
319 libi3/strndup.c \
320 libi3/ucs2_conversion.c
321
322 i3_dump_log_i3_dump_log_CFLAGS = \
323 $(AM_CFLAGS) \
324 $(PTHREAD_CFLAGS) \
325 $(libi3_CFLAGS)
326
327 i3_dump_log_i3_dump_log_LDADD = \
328 $(PTHREAD_LIBS) \
329 $(libi3_LIBS)
330
331 i3_dump_log_i3_dump_log_SOURCES = \
332 i3-dump-log/main.c
333
334 i3_input_i3_input_CFLAGS = \
335 $(AM_CFLAGS) \
336 $(libi3_CFLAGS)
337
338 i3_input_i3_input_LDADD = \
339 $(libi3_LIBS) \
340 $(XCB_UTIL_KEYSYMS_LIBS)
341
342 i3_input_i3_input_SOURCES = \
343 i3-input/i3-input.h \
344 i3-input/keysym2ucs.c \
345 i3-input/keysym2ucs.h \
346 i3-input/main.c
347
348 i3_msg_i3_msg_CFLAGS = \
349 $(AM_CFLAGS) \
350 $(libi3_CFLAGS)
351
352 i3_msg_i3_msg_LDADD = \
353 $(libi3_LIBS)
354
355 i3_msg_i3_msg_SOURCES = \
356 i3-msg/main.c
357
358 i3_nagbar_i3_nagbar_CFLAGS = \
359 $(AM_CFLAGS) \
360 $(LIBSN_CFLAGS) \
361 $(libi3_CFLAGS)
362
363 i3_nagbar_i3_nagbar_LDADD = \
364 $(libi3_LIBS) \
365 $(LIBSN_LIBS) \
366 $(XCB_UTIL_CURSOR_LIBS)
367
368 i3_nagbar_i3_nagbar_SOURCES = \
369 i3-nagbar/atoms.xmacro \
370 i3-nagbar/i3-nagbar.h \
371 i3-nagbar/main.c
372
373 i3bar_i3bar_CPPFLAGS = \
374 $(AM_CPPFLAGS) \
375 -I$(top_srcdir)/i3bar/include
376
377 i3bar_i3bar_CFLAGS = \
378 $(AM_CFLAGS) \
379 $(libi3_CFLAGS) \
380 $(XCB_CFLAGS) \
381 $(XKBCOMMON_CFLAGS) \
382 $(PANGOCAIRO_CFLAGS) \
383 $(YAJL_CFLAGS)
384
385 i3bar_i3bar_LDADD = \
386 $(libi3_LIBS) \
387 $(XCB_LIBS) \
388 $(XCB_UTIL_CURSOR_LIBS) \
389 $(XKBCOMMON_LIBS) \
390 $(PANGOCAIRO_LIBS) \
391 $(YAJL_LIBS)
392
393 i3bar_i3bar_SOURCES = \
394 i3bar/include/child.h \
395 i3bar/include/common.h \
396 i3bar/include/configuration.h \
397 i3bar/include/ipc.h \
398 i3bar/include/mode.h \
399 i3bar/include/outputs.h \
400 i3bar/include/parse_json_header.h \
401 i3bar/include/trayclients.h \
402 i3bar/include/util.h \
403 i3bar/include/workspaces.h \
404 i3bar/include/xcb_atoms.def \
405 i3bar/include/xcb.h \
406 i3bar/src/child.c \
407 i3bar/src/config.c \
408 i3bar/src/ipc.c \
409 i3bar/src/main.c \
410 i3bar/src/mode.c \
411 i3bar/src/outputs.c \
412 i3bar/src/parse_json_header.c \
413 i3bar/src/workspaces.c \
414 i3bar/src/xcb.c
415
416 i3_config_wizard_i3_config_wizard_CFLAGS = \
417 $(AM_CFLAGS) \
418 $(libi3_CFLAGS) \
419 $(LIBSN_CFLAGS) \
420 $(XKBCOMMON_CFLAGS)
421
422 i3_config_wizard_i3_config_wizard_LDADD = \
423 $(libi3_LIBS) \
424 $(LIBSN_LIBS) \
425 $(XCB_UTIL_KEYSYMS_LIBS) \
426 $(XKBCOMMON_LIBS)
427
428 i3_config_wizard_i3_config_wizard_SOURCES = \
429 i3-config-wizard/atoms.xmacro \
430 i3-config-wizard/main.c \
431 i3-config-wizard/xcb.h
432
433 test_inject_randr15_CPPFLAGS = \
434 $(AM_CPPFLAGS)
435
436 test_inject_randr15_CFLAGS = \
437 $(AM_CFLAGS) \
438 $(i3_CFLAGS)
439
440 test_inject_randr15_SOURCES = \
441 testcases/inject_randr1.5.c
442
443 test_inject_randr15_LDADD = \
444 $(i3_LDADD)
445
446 test_commands_parser_CPPFLAGS = \
447 $(AM_CPPFLAGS) \
448 -DTEST_PARSER
449
450 test_commands_parser_CFLAGS = \
451 $(AM_CFLAGS) \
452 $(i3_CFLAGS)
453
454 test_commands_parser_SOURCES = \
455 src/commands_parser.c
456
457 test_commands_parser_LDADD = \
458 $(i3_LDADD)
459
460 test_config_parser_CPPFLAGS = \
461 $(AM_CPPFLAGS) \
462 -DTEST_PARSER
463
464 test_config_parser_CFLAGS = \
465 $(AM_CFLAGS) \
466 $(i3_CFLAGS)
467
468 test_config_parser_SOURCES = \
469 src/config_parser.c
470
471 test_config_parser_LDADD = \
472 $(i3_LDADD)
473
474 command_parser_SOURCES = \
475 parser/GENERATED_command_enums.h \
476 parser/GENERATED_command_tokens.h \
477 parser/GENERATED_command_call.h
478
479 config_parser_SOURCES = \
480 parser/GENERATED_config_enums.h \
481 parser/GENERATED_config_tokens.h \
482 parser/GENERATED_config_call.h
483
484 i3_SOURCES = \
485 $(command_parser_SOURCES) \
486 $(config_parser_SOURCES) \
487 include/all.h \
488 include/assignments.h \
489 include/atoms_NET_SUPPORTED.xmacro \
490 include/atoms_rest.xmacro \
491 include/atoms.xmacro \
492 include/bindings.h \
493 include/click.h \
494 include/cmdparse.h \
495 include/commands.h \
496 include/commands_parser.h \
497 include/config_directives.h \
498 include/configuration.h \
499 include/config_parser.h \
500 include/con.h \
501 include/data.h \
502 include/display_version.h \
503 include/ewmh.h \
504 include/fake_outputs.h \
505 include/floating.h \
506 include/handlers.h \
507 include/i3.h \
508 include/ipc.h \
509 include/key_press.h \
510 include/load_layout.h \
511 include/log.h \
512 include/main.h \
513 include/manage.h \
514 include/match.h \
515 include/move.h \
516 include/output.h \
517 include/queue.h \
518 include/randr.h \
519 include/regex.h \
520 include/render.h \
521 include/resize.h \
522 include/restore_layout.h \
523 include/scratchpad.h \
524 include/sd-daemon.h \
525 include/shmlog.h \
526 include/sighandler.h \
527 include/startup.h \
528 include/sync.h \
529 include/tree.h \
530 include/util.h \
531 include/window.h \
532 include/workspace.h \
533 include/xcb.h \
534 include/xcursor.h \
535 include/x.h \
536 include/xinerama.h \
537 include/yajl_utils.h \
538 src/assignments.c \
539 src/bindings.c \
540 src/click.c \
541 src/commands.c \
542 src/commands_parser.c \
543 src/con.c \
544 src/config.c \
545 src/config_directives.c \
546 src/config_parser.c \
547 src/display_version.c \
548 src/ewmh.c \
549 src/fake_outputs.c \
550 src/floating.c \
551 src/handlers.c \
552 src/ipc.c \
553 src/key_press.c \
554 src/load_layout.c \
555 src/log.c \
556 src/main.c \
557 src/manage.c \
558 src/match.c \
559 src/move.c \
560 src/output.c \
561 src/randr.c \
562 src/regex.c \
563 src/render.c \
564 src/resize.c \
565 src/restore_layout.c \
566 src/scratchpad.c \
567 src/sd-daemon.c \
568 src/sighandler.c \
569 src/startup.c \
570 src/sync.c \
571 src/tree.c \
572 src/util.c \
573 src/version.c \
574 src/window.c \
575 src/workspace.c \
576 src/x.c \
577 src/xcb.c \
578 src/xcursor.c \
579 src/xinerama.c
580
581 ################################################################################
582 # parser generation
583 ################################################################################
584
585 $(command_parser_SOURCES): %.h: i3-command-parser.stamp
586
587 $(config_parser_SOURCES): %.h: i3-config-parser.stamp
588
589 src/i3-commands_parser.$(OBJEXT): i3-command-parser.stamp
590
591 src/i3-config_parser.$(OBJEXT): i3-config-parser.stamp
592
593 i3-command-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/commands.spec
594 $(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/commands.spec --prefix=command
595 $(AM_V_at) mv GENERATED_command_* $(top_builddir)/parser
596 $(AM_V_at) touch $@
597
598 i3-config-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/config.spec
599 $(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/config.spec --prefix=config
600 $(AM_V_at) mv GENERATED_config_* $(top_builddir)/parser
601 $(AM_V_at) touch $@
602
603 ################################################################################
604 # AnyEvent-I3 build process
605 ################################################################################
606
607 anyevent-i3.stamp: AnyEvent-I3/lib/AnyEvent/I3.pm
608 $(AM_V_BUILD) (cd $(top_srcdir)/AnyEvent-I3 && perl Makefile.PL && make)
609 $(AM_V_at) touch $@
610
611 CLEANFILES = \
612 i3-command-parser.stamp \
613 i3-config-parser.stamp \
614 anyevent-i3.stamp
0 # Makefile.in generated by automake 1.16.1 from Makefile.am.
1 # @configure_input@
2
3 # Copyright (C) 1994-2018 Free Software Foundation, Inc.
4
5 # This Makefile.in is free software; the Free Software Foundation
6 # gives unlimited permission to copy and/or distribute it,
7 # with or without modifications, as long as this notice is preserved.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
11 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 # PARTICULAR PURPOSE.
13
14 @SET_MAKE@
15
16
17
18
19
20 VPATH = @srcdir@
21 am__is_gnu_make = { \
22 if test -z '$(MAKELEVEL)'; then \
23 false; \
24 elif test -n '$(MAKE_HOST)'; then \
25 true; \
26 elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
27 true; \
28 else \
29 false; \
30 fi; \
31 }
32 am__make_running_with_option = \
33 case $${target_option-} in \
34 ?) ;; \
35 *) echo "am__make_running_with_option: internal error: invalid" \
36 "target option '$${target_option-}' specified" >&2; \
37 exit 1;; \
38 esac; \
39 has_opt=no; \
40 sane_makeflags=$$MAKEFLAGS; \
41 if $(am__is_gnu_make); then \
42 sane_makeflags=$$MFLAGS; \
43 else \
44 case $$MAKEFLAGS in \
45 *\\[\ \ ]*) \
46 bs=\\; \
47 sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
48 | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
49 esac; \
50 fi; \
51 skip_next=no; \
52 strip_trailopt () \
53 { \
54 flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
55 }; \
56 for flg in $$sane_makeflags; do \
57 test $$skip_next = yes && { skip_next=no; continue; }; \
58 case $$flg in \
59 *=*|--*) continue;; \
60 -*I) strip_trailopt 'I'; skip_next=yes;; \
61 -*I?*) strip_trailopt 'I';; \
62 -*O) strip_trailopt 'O'; skip_next=yes;; \
63 -*O?*) strip_trailopt 'O';; \
64 -*l) strip_trailopt 'l'; skip_next=yes;; \
65 -*l?*) strip_trailopt 'l';; \
66 -[dEDm]) skip_next=yes;; \
67 -[JT]) skip_next=yes;; \
68 esac; \
69 case $$flg in \
70 *$$target_option*) has_opt=yes; break;; \
71 esac; \
72 done; \
73 test $$has_opt = yes
74 am__make_dryrun = (target_option=n; $(am__make_running_with_option))
75 am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
76 pkgdatadir = $(datadir)/@PACKAGE@
77 pkgincludedir = $(includedir)/@PACKAGE@
78 pkglibdir = $(libdir)/@PACKAGE@
79 pkglibexecdir = $(libexecdir)/@PACKAGE@
80 am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
81 install_sh_DATA = $(install_sh) -c -m 644
82 install_sh_PROGRAM = $(install_sh) -c
83 install_sh_SCRIPT = $(install_sh) -c
84 INSTALL_HEADER = $(INSTALL_DATA)
85 transform = $(program_transform_name)
86 NORMAL_INSTALL = :
87 PRE_INSTALL = :
88 POST_INSTALL = :
89 NORMAL_UNINSTALL = :
90 PRE_UNINSTALL = :
91 POST_UNINSTALL = :
92 build_triplet = @build@
93 host_triplet = @host@
94 target_triplet = @target@
95 bin_PROGRAMS = i3$(EXEEXT) i3bar/i3bar$(EXEEXT) \
96 i3-config-wizard/i3-config-wizard$(EXEEXT) \
97 i3-dump-log/i3-dump-log$(EXEEXT) i3-input/i3-input$(EXEEXT) \
98 i3-msg/i3-msg$(EXEEXT) i3-nagbar/i3-nagbar$(EXEEXT)
99 check_PROGRAMS = test.commands_parser$(EXEEXT) \
100 test.config_parser$(EXEEXT) test.inject_randr15$(EXEEXT)
101 subdir = .
102 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
103 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_flag.m4 \
104 $(top_srcdir)/m4/ax_cflags_warn_all.m4 \
105 $(top_srcdir)/m4/ax_check_compile_flag.m4 \
106 $(top_srcdir)/m4/ax_check_enable_debug.m4 \
107 $(top_srcdir)/m4/ax_check_gnu_make.m4 \
108 $(top_srcdir)/m4/ax_check_link_flag.m4 \
109 $(top_srcdir)/m4/ax_code_coverage.m4 \
110 $(top_srcdir)/m4/ax_configure_args.m4 \
111 $(top_srcdir)/m4/ax_enable_builddir.m4 \
112 $(top_srcdir)/m4/ax_extend_srcdir.m4 \
113 $(top_srcdir)/m4/ax_pthread.m4 \
114 $(top_srcdir)/m4/ax_require_defined.m4 \
115 $(top_srcdir)/m4/ax_sanitizers.m4 $(top_srcdir)/configure.ac
116 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
117 $(ACLOCAL_M4)
118 DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
119 $(am__configure_deps) $(dist_bin_SCRIPTS) \
120 $(dist_applications_DATA) $(am__dist_docs_notoc_DATA_DIST) \
121 $(am__dist_docs_pod_DATA_DIST) $(am__dist_docs_toc_DATA_DIST) \
122 $(dist_i3conf_DATA) $(dist_xsessions_DATA) \
123 $(i3include_HEADERS) $(am__DIST_COMMON)
124 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
125 configure.lineno config.status.lineno
126 mkinstalldirs = $(install_sh) -d
127 CONFIG_HEADER = config.h
128 CONFIG_CLEAN_FILES = testcases/lib/i3test.pm man/asciidoc.conf \
129 testcases/complete-run.pl
130 CONFIG_CLEAN_VPATH_FILES =
131 am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \
132 "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(applicationsdir)" \
133 "$(DESTDIR)$(docs_notocdir)" "$(DESTDIR)$(docs_poddir)" \
134 "$(DESTDIR)$(docs_tocdir)" "$(DESTDIR)$(i3confdir)" \
135 "$(DESTDIR)$(xsessionsdir)" "$(DESTDIR)$(i3includedir)"
136 PROGRAMS = $(bin_PROGRAMS)
137 LIBRARIES = $(noinst_LIBRARIES)
138 ARFLAGS = cru
139 AM_V_AR = $(am__v_AR_@AM_V@)
140 am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
141 am__v_AR_0 = @echo " AR " $@;
142 am__v_AR_1 =
143 libi3_a_AR = $(AR) $(ARFLAGS)
144 libi3_a_LIBADD =
145 am__dirstamp = $(am__leading_dot)dirstamp
146 am_libi3_a_OBJECTS = libi3/a-dpi.$(OBJEXT) libi3/a-draw_util.$(OBJEXT) \
147 libi3/a-fake_configure_notify.$(OBJEXT) libi3/a-font.$(OBJEXT) \
148 libi3/a-format_placeholders.$(OBJEXT) \
149 libi3/a-g_utf8_make_valid.$(OBJEXT) \
150 libi3/a-get_colorpixel.$(OBJEXT) \
151 libi3/a-get_config_path.$(OBJEXT) \
152 libi3/a-get_exe_path.$(OBJEXT) libi3/a-get_mod_mask.$(OBJEXT) \
153 libi3/a-get_process_filename.$(OBJEXT) \
154 libi3/a-get_visualtype.$(OBJEXT) libi3/a-ipc_connect.$(OBJEXT) \
155 libi3/a-ipc_recv_message.$(OBJEXT) \
156 libi3/a-ipc_send_message.$(OBJEXT) \
157 libi3/a-is_debug_build.$(OBJEXT) libi3/a-mkdirp.$(OBJEXT) \
158 libi3/a-resolve_tilde.$(OBJEXT) \
159 libi3/a-root_atom_contents.$(OBJEXT) \
160 libi3/a-safewrappers.$(OBJEXT) libi3/a-string.$(OBJEXT) \
161 libi3/a-strndup.$(OBJEXT) libi3/a-ucs2_conversion.$(OBJEXT)
162 libi3_a_OBJECTS = $(am_libi3_a_OBJECTS)
163 am__objects_1 =
164 am_i3_OBJECTS = $(am__objects_1) $(am__objects_1) \
165 src/i3-assignments.$(OBJEXT) src/i3-bindings.$(OBJEXT) \
166 src/i3-click.$(OBJEXT) src/i3-commands.$(OBJEXT) \
167 src/i3-commands_parser.$(OBJEXT) src/i3-con.$(OBJEXT) \
168 src/i3-config.$(OBJEXT) src/i3-config_directives.$(OBJEXT) \
169 src/i3-config_parser.$(OBJEXT) \
170 src/i3-display_version.$(OBJEXT) src/i3-ewmh.$(OBJEXT) \
171 src/i3-fake_outputs.$(OBJEXT) src/i3-floating.$(OBJEXT) \
172 src/i3-handlers.$(OBJEXT) src/i3-ipc.$(OBJEXT) \
173 src/i3-key_press.$(OBJEXT) src/i3-load_layout.$(OBJEXT) \
174 src/i3-log.$(OBJEXT) src/i3-main.$(OBJEXT) \
175 src/i3-manage.$(OBJEXT) src/i3-match.$(OBJEXT) \
176 src/i3-move.$(OBJEXT) src/i3-output.$(OBJEXT) \
177 src/i3-randr.$(OBJEXT) src/i3-regex.$(OBJEXT) \
178 src/i3-render.$(OBJEXT) src/i3-resize.$(OBJEXT) \
179 src/i3-restore_layout.$(OBJEXT) src/i3-scratchpad.$(OBJEXT) \
180 src/i3-sd-daemon.$(OBJEXT) src/i3-sighandler.$(OBJEXT) \
181 src/i3-startup.$(OBJEXT) src/i3-sync.$(OBJEXT) \
182 src/i3-tree.$(OBJEXT) src/i3-util.$(OBJEXT) \
183 src/i3-version.$(OBJEXT) src/i3-window.$(OBJEXT) \
184 src/i3-workspace.$(OBJEXT) src/i3-x.$(OBJEXT) \
185 src/i3-xcb.$(OBJEXT) src/i3-xcursor.$(OBJEXT) \
186 src/i3-xinerama.$(OBJEXT)
187 i3_OBJECTS = $(am_i3_OBJECTS)
188 am__DEPENDENCIES_1 =
189 am__DEPENDENCIES_2 = $(top_builddir)/libi3.a $(am__DEPENDENCIES_1) \
190 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
191 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
192 i3_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
193 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
194 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
195 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
196 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
197 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
198 $(am__DEPENDENCIES_1)
199 i3_LINK = $(CCLD) $(i3_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \
200 $@
201 am_i3_config_wizard_i3_config_wizard_OBJECTS = \
202 i3-config-wizard/i3_config_wizard-main.$(OBJEXT)
203 i3_config_wizard_i3_config_wizard_OBJECTS = \
204 $(am_i3_config_wizard_i3_config_wizard_OBJECTS)
205 i3_config_wizard_i3_config_wizard_DEPENDENCIES = \
206 $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
207 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
208 i3_config_wizard_i3_config_wizard_LINK = $(CCLD) \
209 $(i3_config_wizard_i3_config_wizard_CFLAGS) $(CFLAGS) \
210 $(AM_LDFLAGS) $(LDFLAGS) -o $@
211 am_i3_dump_log_i3_dump_log_OBJECTS = \
212 i3-dump-log/i3_dump_log-main.$(OBJEXT)
213 i3_dump_log_i3_dump_log_OBJECTS = \
214 $(am_i3_dump_log_i3_dump_log_OBJECTS)
215 i3_dump_log_i3_dump_log_DEPENDENCIES = $(am__DEPENDENCIES_1) \
216 $(am__DEPENDENCIES_2)
217 i3_dump_log_i3_dump_log_LINK = $(CCLD) \
218 $(i3_dump_log_i3_dump_log_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
219 $(LDFLAGS) -o $@
220 am_i3_input_i3_input_OBJECTS = i3-input/i3_input-keysym2ucs.$(OBJEXT) \
221 i3-input/i3_input-main.$(OBJEXT)
222 i3_input_i3_input_OBJECTS = $(am_i3_input_i3_input_OBJECTS)
223 i3_input_i3_input_DEPENDENCIES = $(am__DEPENDENCIES_2) \
224 $(am__DEPENDENCIES_1)
225 i3_input_i3_input_LINK = $(CCLD) $(i3_input_i3_input_CFLAGS) $(CFLAGS) \
226 $(AM_LDFLAGS) $(LDFLAGS) -o $@
227 am_i3_msg_i3_msg_OBJECTS = i3-msg/i3_msg-main.$(OBJEXT)
228 i3_msg_i3_msg_OBJECTS = $(am_i3_msg_i3_msg_OBJECTS)
229 i3_msg_i3_msg_DEPENDENCIES = $(am__DEPENDENCIES_2)
230 i3_msg_i3_msg_LINK = $(CCLD) $(i3_msg_i3_msg_CFLAGS) $(CFLAGS) \
231 $(AM_LDFLAGS) $(LDFLAGS) -o $@
232 am_i3_nagbar_i3_nagbar_OBJECTS = i3-nagbar/i3_nagbar-main.$(OBJEXT)
233 i3_nagbar_i3_nagbar_OBJECTS = $(am_i3_nagbar_i3_nagbar_OBJECTS)
234 i3_nagbar_i3_nagbar_DEPENDENCIES = $(am__DEPENDENCIES_2) \
235 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
236 i3_nagbar_i3_nagbar_LINK = $(CCLD) $(i3_nagbar_i3_nagbar_CFLAGS) \
237 $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
238 am_i3bar_i3bar_OBJECTS = i3bar/src/i3bar-child.$(OBJEXT) \
239 i3bar/src/i3bar-config.$(OBJEXT) i3bar/src/i3bar-ipc.$(OBJEXT) \
240 i3bar/src/i3bar-main.$(OBJEXT) i3bar/src/i3bar-mode.$(OBJEXT) \
241 i3bar/src/i3bar-outputs.$(OBJEXT) \
242 i3bar/src/i3bar-parse_json_header.$(OBJEXT) \
243 i3bar/src/i3bar-workspaces.$(OBJEXT) \
244 i3bar/src/i3bar-xcb.$(OBJEXT)
245 i3bar_i3bar_OBJECTS = $(am_i3bar_i3bar_OBJECTS)
246 i3bar_i3bar_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
247 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
248 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
249 i3bar_i3bar_LINK = $(CCLD) $(i3bar_i3bar_CFLAGS) $(CFLAGS) \
250 $(AM_LDFLAGS) $(LDFLAGS) -o $@
251 am_test_commands_parser_OBJECTS = \
252 src/test_commands_parser-commands_parser.$(OBJEXT)
253 test_commands_parser_OBJECTS = $(am_test_commands_parser_OBJECTS)
254 am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
255 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
256 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
257 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
258 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
259 $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
260 $(am__DEPENDENCIES_1)
261 test_commands_parser_DEPENDENCIES = $(am__DEPENDENCIES_3)
262 test_commands_parser_LINK = $(CCLD) $(test_commands_parser_CFLAGS) \
263 $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
264 am_test_config_parser_OBJECTS = \
265 src/test_config_parser-config_parser.$(OBJEXT)
266 test_config_parser_OBJECTS = $(am_test_config_parser_OBJECTS)
267 test_config_parser_DEPENDENCIES = $(am__DEPENDENCIES_3)
268 test_config_parser_LINK = $(CCLD) $(test_config_parser_CFLAGS) \
269 $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
270 am_test_inject_randr15_OBJECTS = \
271 testcases/test_inject_randr15-inject_randr1.5.$(OBJEXT)
272 test_inject_randr15_OBJECTS = $(am_test_inject_randr15_OBJECTS)
273 test_inject_randr15_DEPENDENCIES = $(am__DEPENDENCIES_3)
274 test_inject_randr15_LINK = $(CCLD) $(test_inject_randr15_CFLAGS) \
275 $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
276 am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
277 am__vpath_adj = case $$p in \
278 $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
279 *) f=$$p;; \
280 esac;
281 am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
282 am__install_max = 40
283 am__nobase_strip_setup = \
284 srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
285 am__nobase_strip = \
286 for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
287 am__nobase_list = $(am__nobase_strip_setup); \
288 for p in $$list; do echo "$$p $$p"; done | \
289 sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
290 $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
291 if (++n[$$2] == $(am__install_max)) \
292 { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
293 END { for (dir in files) print dir, files[dir] }'
294 am__base_list = \
295 sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
296 sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
297 am__uninstall_files_from_dir = { \
298 test -z "$$files" \
299 || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
300 || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
301 $(am__cd) "$$dir" && rm -f $$files; }; \
302 }
303 SCRIPTS = $(dist_bin_SCRIPTS)
304 AM_V_P = $(am__v_P_@AM_V@)
305 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
306 am__v_P_0 = false
307 am__v_P_1 = :
308 AM_V_GEN = $(am__v_GEN_@AM_V@)
309 am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
310 am__v_GEN_0 = @echo " GEN " $@;
311 am__v_GEN_1 =
312 AM_V_at = $(am__v_at_@AM_V@)
313 am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
314 am__v_at_0 = @
315 am__v_at_1 =
316 DEFAULT_INCLUDES = -I.@am__isrc@
317 depcomp = $(SHELL) $(top_srcdir)/depcomp
318 am__maybe_remake_depfiles = depfiles
319 am__depfiles_remade = \
320 i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Po \
321 i3-dump-log/$(DEPDIR)/i3_dump_log-main.Po \
322 i3-input/$(DEPDIR)/i3_input-keysym2ucs.Po \
323 i3-input/$(DEPDIR)/i3_input-main.Po \
324 i3-msg/$(DEPDIR)/i3_msg-main.Po \
325 i3-nagbar/$(DEPDIR)/i3_nagbar-main.Po \
326 i3bar/src/$(DEPDIR)/i3bar-child.Po \
327 i3bar/src/$(DEPDIR)/i3bar-config.Po \
328 i3bar/src/$(DEPDIR)/i3bar-ipc.Po \
329 i3bar/src/$(DEPDIR)/i3bar-main.Po \
330 i3bar/src/$(DEPDIR)/i3bar-mode.Po \
331 i3bar/src/$(DEPDIR)/i3bar-outputs.Po \
332 i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Po \
333 i3bar/src/$(DEPDIR)/i3bar-workspaces.Po \
334 i3bar/src/$(DEPDIR)/i3bar-xcb.Po libi3/$(DEPDIR)/a-dpi.Po \
335 libi3/$(DEPDIR)/a-draw_util.Po \
336 libi3/$(DEPDIR)/a-fake_configure_notify.Po \
337 libi3/$(DEPDIR)/a-font.Po \
338 libi3/$(DEPDIR)/a-format_placeholders.Po \
339 libi3/$(DEPDIR)/a-g_utf8_make_valid.Po \
340 libi3/$(DEPDIR)/a-get_colorpixel.Po \
341 libi3/$(DEPDIR)/a-get_config_path.Po \
342 libi3/$(DEPDIR)/a-get_exe_path.Po \
343 libi3/$(DEPDIR)/a-get_mod_mask.Po \
344 libi3/$(DEPDIR)/a-get_process_filename.Po \
345 libi3/$(DEPDIR)/a-get_visualtype.Po \
346 libi3/$(DEPDIR)/a-ipc_connect.Po \
347 libi3/$(DEPDIR)/a-ipc_recv_message.Po \
348 libi3/$(DEPDIR)/a-ipc_send_message.Po \
349 libi3/$(DEPDIR)/a-is_debug_build.Po \
350 libi3/$(DEPDIR)/a-mkdirp.Po libi3/$(DEPDIR)/a-resolve_tilde.Po \
351 libi3/$(DEPDIR)/a-root_atom_contents.Po \
352 libi3/$(DEPDIR)/a-safewrappers.Po libi3/$(DEPDIR)/a-string.Po \
353 libi3/$(DEPDIR)/a-strndup.Po \
354 libi3/$(DEPDIR)/a-ucs2_conversion.Po \
355 src/$(DEPDIR)/i3-assignments.Po src/$(DEPDIR)/i3-bindings.Po \
356 src/$(DEPDIR)/i3-click.Po src/$(DEPDIR)/i3-commands.Po \
357 src/$(DEPDIR)/i3-commands_parser.Po src/$(DEPDIR)/i3-con.Po \
358 src/$(DEPDIR)/i3-config.Po \
359 src/$(DEPDIR)/i3-config_directives.Po \
360 src/$(DEPDIR)/i3-config_parser.Po \
361 src/$(DEPDIR)/i3-display_version.Po src/$(DEPDIR)/i3-ewmh.Po \
362 src/$(DEPDIR)/i3-fake_outputs.Po src/$(DEPDIR)/i3-floating.Po \
363 src/$(DEPDIR)/i3-handlers.Po src/$(DEPDIR)/i3-ipc.Po \
364 src/$(DEPDIR)/i3-key_press.Po src/$(DEPDIR)/i3-load_layout.Po \
365 src/$(DEPDIR)/i3-log.Po src/$(DEPDIR)/i3-main.Po \
366 src/$(DEPDIR)/i3-manage.Po src/$(DEPDIR)/i3-match.Po \
367 src/$(DEPDIR)/i3-move.Po src/$(DEPDIR)/i3-output.Po \
368 src/$(DEPDIR)/i3-randr.Po src/$(DEPDIR)/i3-regex.Po \
369 src/$(DEPDIR)/i3-render.Po src/$(DEPDIR)/i3-resize.Po \
370 src/$(DEPDIR)/i3-restore_layout.Po \
371 src/$(DEPDIR)/i3-scratchpad.Po src/$(DEPDIR)/i3-sd-daemon.Po \
372 src/$(DEPDIR)/i3-sighandler.Po src/$(DEPDIR)/i3-startup.Po \
373 src/$(DEPDIR)/i3-sync.Po src/$(DEPDIR)/i3-tree.Po \
374 src/$(DEPDIR)/i3-util.Po src/$(DEPDIR)/i3-version.Po \
375 src/$(DEPDIR)/i3-window.Po src/$(DEPDIR)/i3-workspace.Po \
376 src/$(DEPDIR)/i3-x.Po src/$(DEPDIR)/i3-xcb.Po \
377 src/$(DEPDIR)/i3-xcursor.Po src/$(DEPDIR)/i3-xinerama.Po \
378 src/$(DEPDIR)/test_commands_parser-commands_parser.Po \
379 src/$(DEPDIR)/test_config_parser-config_parser.Po \
380 testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Po
381 am__mv = mv -f
382 AM_V_lt = $(am__v_lt_@AM_V@)
383 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
384 am__v_lt_0 = --silent
385 am__v_lt_1 =
386 COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
387 $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
388 AM_V_CC = $(am__v_CC_@AM_V@)
389 am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
390 am__v_CC_0 = @echo " CC " $@;
391 am__v_CC_1 =
392 CCLD = $(CC)
393 LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
394 AM_V_CCLD = $(am__v_CCLD_@AM_V@)
395 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
396 am__v_CCLD_0 = @echo " CCLD " $@;
397 am__v_CCLD_1 =
398 SOURCES = $(libi3_a_SOURCES) $(i3_SOURCES) \
399 $(i3_config_wizard_i3_config_wizard_SOURCES) \
400 $(i3_dump_log_i3_dump_log_SOURCES) \
401 $(i3_input_i3_input_SOURCES) $(i3_msg_i3_msg_SOURCES) \
402 $(i3_nagbar_i3_nagbar_SOURCES) $(i3bar_i3bar_SOURCES) \
403 $(test_commands_parser_SOURCES) $(test_config_parser_SOURCES) \
404 $(test_inject_randr15_SOURCES)
405 DIST_SOURCES = $(libi3_a_SOURCES) $(i3_SOURCES) \
406 $(i3_config_wizard_i3_config_wizard_SOURCES) \
407 $(i3_dump_log_i3_dump_log_SOURCES) \
408 $(i3_input_i3_input_SOURCES) $(i3_msg_i3_msg_SOURCES) \
409 $(i3_nagbar_i3_nagbar_SOURCES) $(i3bar_i3bar_SOURCES) \
410 $(test_commands_parser_SOURCES) $(test_config_parser_SOURCES) \
411 $(test_inject_randr15_SOURCES)
412 am__can_run_installinfo = \
413 case $$AM_UPDATE_INFO_DIR in \
414 n|no|NO) false;; \
415 *) (install-info --version) >/dev/null 2>&1;; \
416 esac
417 man1dir = $(mandir)/man1
418 NROFF = nroff
419 MANS = $(dist_man1_MANS)
420 am__dist_docs_notoc_DATA_DIST = docs/debugging.html
421 am__dist_docs_pod_DATA_DIST = docs/lib-i3test.html \
422 docs/lib-i3test-test.html
423 am__dist_docs_toc_DATA_DIST = docs/hacking-howto.html \
424 docs/userguide.html docs/ipc.html docs/multi-monitor.html \
425 docs/wsbar.html docs/testsuite.html docs/i3bar-protocol.html \
426 docs/layout-saving.html
427 DATA = $(dist_applications_DATA) $(dist_docs_notoc_DATA) \
428 $(dist_docs_pod_DATA) $(dist_docs_toc_DATA) \
429 $(dist_i3conf_DATA) $(dist_xsessions_DATA)
430 HEADERS = $(i3include_HEADERS)
431 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
432 $(LISP)config.h.in
433 # Read a list of newline-separated strings from the standard input,
434 # and print each of them once, without duplicates. Input order is
435 # *not* preserved.
436 am__uniquify_input = $(AWK) '\
437 BEGIN { nonempty = 0; } \
438 { items[$$0] = 1; nonempty = 1; } \
439 END { if (nonempty) { for (i in items) print i; }; } \
440 '
441 # Make sure the list of sources is unique. This is necessary because,
442 # e.g., the same source file might be shared among _SOURCES variables
443 # for different programs/libraries.
444 am__define_uniq_tagged_files = \
445 list='$(am__tagged_files)'; \
446 unique=`for i in $$list; do \
447 if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
448 done | $(am__uniquify_input)`
449 ETAGS = etags
450 CTAGS = ctags
451 CSCOPE = cscope
452 AM_RECURSIVE_TARGETS = cscope check recheck
453 am__tty_colors_dummy = \
454 mgn= red= grn= lgn= blu= brg= std=; \
455 am__color_tests=no
456 am__tty_colors = { \
457 $(am__tty_colors_dummy); \
458 if test "X$(AM_COLOR_TESTS)" = Xno; then \
459 am__color_tests=no; \
460 elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
461 am__color_tests=yes; \
462 elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
463 am__color_tests=yes; \
464 fi; \
465 if test $$am__color_tests = yes; then \
466 red=''; \
467 grn=''; \
468 lgn=''; \
469 blu=''; \
470 mgn=''; \
471 brg=''; \
472 std=''; \
473 fi; \
474 }
475 am__recheck_rx = ^[ ]*:recheck:[ ]*
476 am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
477 am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
478 # A command that, given a newline-separated list of test names on the
479 # standard input, print the name of the tests that are to be re-run
480 # upon "make recheck".
481 am__list_recheck_tests = $(AWK) '{ \
482 recheck = 1; \
483 while ((rc = (getline line < ($$0 ".trs"))) != 0) \
484 { \
485 if (rc < 0) \
486 { \
487 if ((getline line2 < ($$0 ".log")) < 0) \
488 recheck = 0; \
489 break; \
490 } \
491 else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
492 { \
493 recheck = 0; \
494 break; \
495 } \
496 else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
497 { \
498 break; \
499 } \
500 }; \
501 if (recheck) \
502 print $$0; \
503 close ($$0 ".trs"); \
504 close ($$0 ".log"); \
505 }'
506 # A command that, given a newline-separated list of test names on the
507 # standard input, create the global log from their .trs and .log files.
508 am__create_global_log = $(AWK) ' \
509 function fatal(msg) \
510 { \
511 print "fatal: making $@: " msg | "cat >&2"; \
512 exit 1; \
513 } \
514 function rst_section(header) \
515 { \
516 print header; \
517 len = length(header); \
518 for (i = 1; i <= len; i = i + 1) \
519 printf "="; \
520 printf "\n\n"; \
521 } \
522 { \
523 copy_in_global_log = 1; \
524 global_test_result = "RUN"; \
525 while ((rc = (getline line < ($$0 ".trs"))) != 0) \
526 { \
527 if (rc < 0) \
528 fatal("failed to read from " $$0 ".trs"); \
529 if (line ~ /$(am__global_test_result_rx)/) \
530 { \
531 sub("$(am__global_test_result_rx)", "", line); \
532 sub("[ ]*$$", "", line); \
533 global_test_result = line; \
534 } \
535 else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
536 copy_in_global_log = 0; \
537 }; \
538 if (copy_in_global_log) \
539 { \
540 rst_section(global_test_result ": " $$0); \
541 while ((rc = (getline line < ($$0 ".log"))) != 0) \
542 { \
543 if (rc < 0) \
544 fatal("failed to read from " $$0 ".log"); \
545 print line; \
546 }; \
547 printf "\n"; \
548 }; \
549 close ($$0 ".trs"); \
550 close ($$0 ".log"); \
551 }'
552 # Restructured Text title.
553 am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
554 # Solaris 10 'make', and several other traditional 'make' implementations,
555 # pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
556 # by disabling -e (using the XSI extension "set +e") if it's set.
557 am__sh_e_setup = case $$- in *e*) set +e;; esac
558 # Default flags passed to test drivers.
559 am__common_driver_flags = \
560 --color-tests "$$am__color_tests" \
561 --enable-hard-errors "$$am__enable_hard_errors" \
562 --expect-failure "$$am__expect_failure"
563 # To be inserted before the command running the test. Creates the
564 # directory for the log if needed. Stores in $dir the directory
565 # containing $f, in $tst the test, in $log the log. Executes the
566 # developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
567 # passes TESTS_ENVIRONMENT. Set up options for the wrapper that
568 # will run the test scripts (or their associated LOG_COMPILER, if
569 # thy have one).
570 am__check_pre = \
571 $(am__sh_e_setup); \
572 $(am__vpath_adj_setup) $(am__vpath_adj) \
573 $(am__tty_colors); \
574 srcdir=$(srcdir); export srcdir; \
575 case "$@" in \
576 */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
577 *) am__odir=.;; \
578 esac; \
579 test "x$$am__odir" = x"." || test -d "$$am__odir" \
580 || $(MKDIR_P) "$$am__odir" || exit $$?; \
581 if test -f "./$$f"; then dir=./; \
582 elif test -f "$$f"; then dir=; \
583 else dir="$(srcdir)/"; fi; \
584 tst=$$dir$$f; log='$@'; \
585 if test -n '$(DISABLE_HARD_ERRORS)'; then \
586 am__enable_hard_errors=no; \
587 else \
588 am__enable_hard_errors=yes; \
589 fi; \
590 case " $(XFAIL_TESTS) " in \
591 *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
592 am__expect_failure=yes;; \
593 *) \
594 am__expect_failure=no;; \
595 esac; \
596 $(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
597 # A shell command to get the names of the tests scripts with any registered
598 # extension removed (i.e., equivalently, the names of the test logs, with
599 # the '.log' extension removed). The result is saved in the shell variable
600 # '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
601 # we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
602 # since that might cause problem with VPATH rewrites for suffix-less tests.
603 # See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
604 am__set_TESTS_bases = \
605 bases='$(TEST_LOGS)'; \
606 bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
607 bases=`echo $$bases`
608 RECHECK_LOGS = $(TEST_LOGS)
609 TEST_SUITE_LOG = test-suite.log
610 TEST_EXTENSIONS = @EXEEXT@ .test
611 LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
612 LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
613 am__set_b = \
614 case '$@' in \
615 */*) \
616 case '$*' in \
617 */*) b='$*';; \
618 *) b=`echo '$@' | sed 's/\.log$$//'`; \
619 esac;; \
620 *) \
621 b='$*';; \
622 esac
623 am__test_logs1 = $(TESTS:=.log)
624 am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
625 TEST_LOGS = $(am__test_logs2:.test.log=.log)
626 TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
627 TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
628 $(TEST_LOG_FLAGS)
629 am__DIST_COMMON = $(dist_man1_MANS) $(srcdir)/Makefile.in \
630 $(srcdir)/config.h.in $(top_srcdir)/man/asciidoc.conf.in \
631 $(top_srcdir)/testcases/complete-run.pl.in \
632 $(top_srcdir)/testcases/lib/i3test.pm.in ar-lib compile \
633 config.guess config.sub depcomp install-sh missing test-driver
634 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
635 distdir = $(PACKAGE)-$(VERSION)
636 top_distdir = $(distdir)
637 am__remove_distdir = \
638 if test -d "$(distdir)"; then \
639 find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
640 && rm -rf "$(distdir)" \
641 || { sleep 5 && rm -rf "$(distdir)"; }; \
642 else :; fi
643 am__post_remove_distdir = $(am__remove_distdir)
644 GZIP_ENV = --best
645 DIST_ARCHIVES = $(distdir).tar.bz2
646 DIST_TARGETS = dist-bzip2
647 distuninstallcheck_listfiles = find . -type f -print
648 am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
649 | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
650 distcleancheck_listfiles = find . -type f -print
651 ACLOCAL = @ACLOCAL@
652 AMTAR = @AMTAR@
653 AM_CFLAGS = @AM_CFLAGS@
654 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
655 AR = @AR@
656 AUTOCONF = @AUTOCONF@
657 AUTOHEADER = @AUTOHEADER@
658 AUTOMAKE = @AUTOMAKE@
659 AWK = @AWK@
660 AX_EXTEND_SRCDIR_CPPFLAGS = @AX_EXTEND_SRCDIR_CPPFLAGS@
661 CC = @CC@
662 CCDEPMODE = @CCDEPMODE@
663 CFLAGS = @CFLAGS@
664 CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
665 CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@
666 CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@
667 CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
668 CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
669 CPP = @CPP@
670 CPPFLAGS = @CPPFLAGS@
671 CYGPATH_W = @CYGPATH_W@
672 DEFS = @DEFS@
673 DEPDIR = @DEPDIR@
674 ECHO_C = @ECHO_C@
675 ECHO_N = @ECHO_N@
676 ECHO_T = @ECHO_T@
677 EGREP = @EGREP@
678 EXEEXT = @EXEEXT@
679 GCOV = @GCOV@
680 GENHTML = @GENHTML@
681 GREP = @GREP@
682 I3_VERSION = @I3_VERSION@
683 INSTALL = @INSTALL@
684 INSTALL_DATA = @INSTALL_DATA@
685 INSTALL_PROGRAM = @INSTALL_PROGRAM@
686 INSTALL_SCRIPT = @INSTALL_SCRIPT@
687 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
688 LCOV = @LCOV@
689 LDFLAGS = @LDFLAGS@
690 LIBOBJS = @LIBOBJS@
691 LIBPCRE_CFLAGS = @LIBPCRE_CFLAGS@
692 LIBPCRE_LIBS = @LIBPCRE_LIBS@
693 LIBS = @LIBS@
694 LIBSN_CFLAGS = @LIBSN_CFLAGS@
695 LIBSN_LIBS = @LIBSN_LIBS@
696 LN_S = @LN_S@
697 LTLIBOBJS = @LTLIBOBJS@
698 MAINT = @MAINT@
699 MAKEINFO = @MAKEINFO@
700 MKDIR_P = @MKDIR_P@
701 OBJEXT = @OBJEXT@
702 PACKAGE = @PACKAGE@
703 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
704 PACKAGE_NAME = @PACKAGE_NAME@
705 PACKAGE_STRING = @PACKAGE_STRING@
706 PACKAGE_TARNAME = @PACKAGE_TARNAME@
707 PACKAGE_URL = @PACKAGE_URL@
708 PACKAGE_VERSION = @PACKAGE_VERSION@
709 PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
710 PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
711 PATH_ASCIIDOC = @PATH_ASCIIDOC@
712 PATH_POD2MAN = @PATH_POD2MAN@
713 PATH_SEPARATOR = @PATH_SEPARATOR@
714 PATH_XMLTO = @PATH_XMLTO@
715 PKG_CONFIG = @PKG_CONFIG@
716 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
717 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
718 PTHREAD_CC = @PTHREAD_CC@
719 PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
720 PTHREAD_LIBS = @PTHREAD_LIBS@
721 RANLIB = @RANLIB@
722 SED = @SED@
723 SET_MAKE = @SET_MAKE@
724 SHELL = @SHELL@
725 STRIP = @STRIP@
726 VERSION = @VERSION@
727 XCB_CFLAGS = @XCB_CFLAGS@
728 XCB_LIBS = @XCB_LIBS@
729 XCB_UTIL_CFLAGS = @XCB_UTIL_CFLAGS@
730 XCB_UTIL_CURSOR_CFLAGS = @XCB_UTIL_CURSOR_CFLAGS@
731 XCB_UTIL_CURSOR_LIBS = @XCB_UTIL_CURSOR_LIBS@
732 XCB_UTIL_KEYSYMS_CFLAGS = @XCB_UTIL_KEYSYMS_CFLAGS@
733 XCB_UTIL_KEYSYMS_LIBS = @XCB_UTIL_KEYSYMS_LIBS@
734 XCB_UTIL_LIBS = @XCB_UTIL_LIBS@
735 XCB_UTIL_WM_CFLAGS = @XCB_UTIL_WM_CFLAGS@
736 XCB_UTIL_WM_LIBS = @XCB_UTIL_WM_LIBS@
737 XCB_UTIL_XRM_CFLAGS = @XCB_UTIL_XRM_CFLAGS@
738 XCB_UTIL_XRM_LIBS = @XCB_UTIL_XRM_LIBS@
739 XKBCOMMON_CFLAGS = @XKBCOMMON_CFLAGS@
740 XKBCOMMON_LIBS = @XKBCOMMON_LIBS@
741 YAJL_CFLAGS = @YAJL_CFLAGS@
742 YAJL_LIBS = @YAJL_LIBS@
743 abs_builddir = @abs_builddir@
744 abs_srcdir = @abs_srcdir@
745 abs_top_builddir = @abs_top_builddir@
746 abs_top_srcdir = @abs_top_srcdir@
747 ac_ct_AR = @ac_ct_AR@
748 ac_ct_CC = @ac_ct_CC@
749 am__include = @am__include@
750 am__leading_dot = @am__leading_dot@
751 am__quote = @am__quote@
752 am__tar = @am__tar@
753 am__untar = @am__untar@
754 ax_enable_builddir_sed = @ax_enable_builddir_sed@
755 ax_pthread_config = @ax_pthread_config@
756 bindir = @bindir@
757 build = @build@
758 build_alias = @build_alias@
759 build_cpu = @build_cpu@
760 build_os = @build_os@
761 build_vendor = @build_vendor@
762 builddir = @builddir@
763 datadir = @datadir@
764 datarootdir = @datarootdir@
765 docdir = @docdir@
766 dvidir = @dvidir@
767 exec_prefix = @exec_prefix@
768 host = @host@
769 host_alias = @host_alias@
770 host_cpu = @host_cpu@
771 host_os = @host_os@
772 host_vendor = @host_vendor@
773 htmldir = @htmldir@
774 ifGNUmake = @ifGNUmake@
775 includedir = @includedir@
776 infodir = @infodir@
777 install_sh = @install_sh@
778 libdir = @libdir@
779 libexecdir = @libexecdir@
780 localedir = @localedir@
781 localstatedir = @localstatedir@
782 mandir = @mandir@
783 mkdir_p = @mkdir_p@
784 oldincludedir = @oldincludedir@
785 pdfdir = @pdfdir@
786 prefix = @prefix@
787 program_transform_name = @program_transform_name@
788 psdir = @psdir@
789 runstatedir = @runstatedir@
790 sbindir = @sbindir@
791 sharedstatedir = @sharedstatedir@
792 srcdir = @srcdir@
793 sysconfdir = @sysconfdir@
794 target = @target@
795 target_alias = @target_alias@
796 target_cpu = @target_cpu@
797 target_os = @target_os@
798 target_vendor = @target_vendor@
799 top_build_prefix = @top_build_prefix@
800 top_builddir = @top_builddir@
801 top_srcdir = @top_srcdir@
802 i3includedir = $(includedir)/i3
803 i3include_HEADERS = \
804 include/i3/ipc.h
805
806 dist_bin_SCRIPTS = \
807 i3-dmenu-desktop \
808 i3-migrate-config-to-v4 \
809 i3-save-tree \
810 i3-sensible-editor \
811 i3-sensible-pager \
812 i3-sensible-terminal
813
814 i3confdir = $(sysconfdir)/i3
815 dist_i3conf_DATA = \
816 etc/config \
817 etc/config.keycodes
818
819 applicationsdir = $(datarootdir)/applications
820 xsessionsdir = $(datarootdir)/xsessions
821 dist_applications_DATA = \
822 share/applications/i3.desktop
823
824 dist_xsessions_DATA = \
825 share/xsessions/i3.desktop \
826 share/xsessions/i3-with-shmlog.desktop
827
828 noinst_LIBRARIES = libi3.a
829 check_SCRIPTS = \
830 testcases/complete-run.pl
831
832 check_DATA = \
833 anyevent-i3.stamp
834
835 TESTS = testcases/complete-run.pl
836 EXTRA_DIST = \
837 $(dist_docs_toc_DATA:.html=) \
838 $(dist_docs_notoc_DATA:.html=) \
839 AnyEvent-I3/Changes \
840 AnyEvent-I3/MANIFEST \
841 AnyEvent-I3/MANIFEST.SKIP \
842 AnyEvent-I3/Makefile.PL \
843 AnyEvent-I3/README \
844 AnyEvent-I3/lib/AnyEvent/I3.pm \
845 AnyEvent-I3/t/00-load.t \
846 AnyEvent-I3/t/01-workspaces.t \
847 AnyEvent-I3/t/02-sugar.t \
848 AnyEvent-I3/t/boilerplate.t \
849 AnyEvent-I3/t/manifest.t \
850 AnyEvent-I3/t/pod-coverage.t \
851 AnyEvent-I3/t/pod.t \
852 contrib/dump-asy.pl \
853 contrib/gtk-tree-watch.pl \
854 contrib/i3-wsbar \
855 contrib/per-workspace-layout.pl \
856 contrib/trivial-bar-script.sh \
857 docs/asciidoc-git.conf \
858 docs/bigpicture.png \
859 docs/i3-pod2html \
860 docs/i3-sync.dia \
861 docs/i3-sync.png \
862 docs/i3-sync-working.dia \
863 docs/i3-sync-working.png \
864 docs/keyboard-layer1.png \
865 docs/keyboard-layer2.png \
866 docs/layout-saving-1.png \
867 docs/logo-30.png \
868 docs/modes.png \
869 docs/refcard.html \
870 docs/refcard_style.css \
871 docs/single_terminal.png \
872 docs/snapping.png \
873 docs/tree-layout1.png \
874 docs/tree-layout2.png \
875 docs/tree-shot1.png \
876 docs/tree-shot2.png \
877 docs/tree-shot3.png \
878 docs/tree-shot4.png \
879 docs/two_columns.png \
880 docs/two_terminals.png \
881 docs/wsbar.dia \
882 docs/wsbar.png \
883 i3bar/LICENSE \
884 libi3/README \
885 $(asciidoc_MANS:.1=.man) \
886 $(asciidoc_MANS:.1=.man) \
887 man/asciidoc.conf.in \
888 DEPENDS \
889 I3_VERSION \
890 LICENSE \
891 PACKAGE-MAINTAINER \
892 RELEASE-NOTES-4.16.1 \
893 generate-command-parser.pl \
894 parser-specs/commands.spec \
895 parser-specs/config.spec \
896 parser-specs/highlighting.vim \
897 pseudo-doc.doxygen \
898 testcases/complete-run.pl.in \
899 testcases/i3-test.config \
900 testcases/lib/i3test/Test.pm \
901 testcases/lib/i3test/Util.pm \
902 testcases/lib/i3test/XTEST.pm \
903 testcases/lib/i3test.pm.in \
904 testcases/lib/SocketActivation.pm \
905 testcases/lib/StartXServer.pm \
906 testcases/lib/StatusLine.pm \
907 testcases/lib/TestWorker.pm \
908 testcases/Makefile.PL \
909 testcases/new-test \
910 testcases/restart-state.golden \
911 testcases/t \
912 testcases/valgrind.supp
913
914
915 # dirstamps contains directories which we want to be created in $(top_builddir)
916 # so that our custom rules can store files in them.
917 dirstamp = .dirstamp
918 dirstamps = \
919 docs/$(dirstamp) \
920 man/$(dirstamp) \
921 parser/$(dirstamp)
922
923 DISTCLEANFILES = $(dirstamps)
924
925 ################################################################################
926 # docs generation
927 ################################################################################
928 docs_tocdir = ${docdir}
929 docs_notocdir = ${docdir}
930 docs_poddir = ${docdir}
931 @BUILD_DOCS_FALSE@dist_docs_toc_DATA =
932 @BUILD_DOCS_TRUE@dist_docs_toc_DATA = \
933 @BUILD_DOCS_TRUE@ docs/hacking-howto.html \
934 @BUILD_DOCS_TRUE@ docs/userguide.html \
935 @BUILD_DOCS_TRUE@ docs/ipc.html \
936 @BUILD_DOCS_TRUE@ docs/multi-monitor.html \
937 @BUILD_DOCS_TRUE@ docs/wsbar.html \
938 @BUILD_DOCS_TRUE@ docs/testsuite.html \
939 @BUILD_DOCS_TRUE@ docs/i3bar-protocol.html \
940 @BUILD_DOCS_TRUE@ docs/layout-saving.html
941
942 @BUILD_DOCS_FALSE@dist_docs_notoc_DATA =
943 @BUILD_DOCS_TRUE@dist_docs_notoc_DATA = \
944 @BUILD_DOCS_TRUE@ docs/debugging.html
945
946 @BUILD_DOCS_FALSE@dist_docs_pod_DATA =
947 @BUILD_DOCS_TRUE@dist_docs_pod_DATA = \
948 @BUILD_DOCS_TRUE@ docs/lib-i3test.html \
949 @BUILD_DOCS_TRUE@ docs/lib-i3test-test.html
950
951
952 ################################################################################
953 # manpage generation
954 ################################################################################
955 @BUILD_MANS_TRUE@dist_man1_MANS = \
956 @BUILD_MANS_TRUE@ $(asciidoc_MANS) \
957 @BUILD_MANS_TRUE@ $(pod_MANS)
958
959 @BUILD_MANS_FALSE@asciidoc_MANS =
960 @BUILD_MANS_TRUE@asciidoc_MANS = \
961 @BUILD_MANS_TRUE@ man/i3.1 \
962 @BUILD_MANS_TRUE@ man/i3bar.1 \
963 @BUILD_MANS_TRUE@ man/i3-msg.1 \
964 @BUILD_MANS_TRUE@ man/i3-input.1 \
965 @BUILD_MANS_TRUE@ man/i3-nagbar.1 \
966 @BUILD_MANS_TRUE@ man/i3-config-wizard.1 \
967 @BUILD_MANS_TRUE@ man/i3-migrate-config-to-v4.1 \
968 @BUILD_MANS_TRUE@ man/i3-sensible-editor.1 \
969 @BUILD_MANS_TRUE@ man/i3-sensible-pager.1 \
970 @BUILD_MANS_TRUE@ man/i3-sensible-terminal.1 \
971 @BUILD_MANS_TRUE@ man/i3-dump-log.1
972
973 @BUILD_MANS_TRUE@pod_MANS = \
974 @BUILD_MANS_TRUE@ man/i3-dmenu-desktop.1 \
975 @BUILD_MANS_TRUE@ man/i3-save-tree.1
976
977 AM_CPPFLAGS = \
978 -DSYSCONFDIR="\"$(sysconfdir)\"" \
979 -I$(top_builddir)/parser \
980 -I$(top_srcdir)/include \
981 @AX_EXTEND_SRCDIR_CPPFLAGS@
982
983 i3_CFLAGS = \
984 $(AM_CFLAGS) \
985 $(libi3_CFLAGS) \
986 $(LIBSN_CFLAGS) \
987 $(XCB_CFLAGS) \
988 $(XCB_UTIL_CURSOR_CFLAGS) \
989 $(XCB_UTIL_KEYSYM_CFLAGS) \
990 $(XCB_UTIL_WM_CFLAGS) \
991 $(XCB_UTIL_XRM_CFLAGS) \
992 $(XKBCOMMON_CFLAGS) \
993 $(YAJL_CFLAGS) \
994 $(LIBPCRE_CFLAGS) \
995 $(PTHREAD_CFLAGS) \
996 $(CODE_COVERAGE_CFLAGS)
997
998 i3_CPPFLAGS = \
999 $(AM_CPPFLAGS) \
1000 $(CODE_COVERAGE_CPPFLAGS)
1001
1002 i3_LDADD = \
1003 $(libi3_LIBS) \
1004 $(LIBSN_LIBS) \
1005 $(XCB_LIBS) \
1006 $(XCB_UTIL_CURSOR_LIBS) \
1007 $(XCB_UTIL_KEYSYMS_LIBS) \
1008 $(XCB_UTIL_WM_LIBS) \
1009 $(XCB_UTIL_XRM_LIBS) \
1010 $(XKBCOMMON_LIBS) \
1011 $(YAJL_LIBS) \
1012 $(LIBPCRE_LIBS) \
1013 $(PANGOCAIRO_LIBS) \
1014 $(PTHREAD_LIBS) \
1015 $(CODE_COVERAGE_LDFLAGS)
1016
1017 libi3_CFLAGS = \
1018 $(AM_CFLAGS) \
1019 $(XCB_CFLAGS) \
1020 $(XCB_UTIL_CFLAGS) \
1021 $(XCB_UTIL_XRM_CFLAGS) \
1022 $(YAJL_CFLAGS) \
1023 $(PANGOCAIRO_CFLAGS)
1024
1025 libi3_LIBS = \
1026 $(top_builddir)/libi3.a \
1027 $(XCB_LIBS) \
1028 $(XCB_UTIL_LIBS) \
1029 $(XCB_UTIL_XRM_LIBS) \
1030 $(YAJL_LIBS) \
1031 $(PANGOCAIRO_LIBS)
1032
1033 libi3_a_CFLAGS = \
1034 $(libi3_CFLAGS)
1035
1036 libi3_a_SOURCES = \
1037 include/libi3.h \
1038 libi3/dpi.c \
1039 libi3/draw_util.c \
1040 libi3/fake_configure_notify.c \
1041 libi3/font.c \
1042 libi3/format_placeholders.c \
1043 libi3/g_utf8_make_valid.c \
1044 libi3/get_colorpixel.c \
1045 libi3/get_config_path.c \
1046 libi3/get_exe_path.c \
1047 libi3/get_mod_mask.c \
1048 libi3/get_process_filename.c \
1049 libi3/get_visualtype.c \
1050 libi3/ipc_connect.c \
1051 libi3/ipc_recv_message.c \
1052 libi3/ipc_send_message.c \
1053 libi3/is_debug_build.c \
1054 libi3/mkdirp.c \
1055 libi3/resolve_tilde.c \
1056 libi3/root_atom_contents.c \
1057 libi3/safewrappers.c \
1058 libi3/string.c \
1059 libi3/strndup.c \
1060 libi3/ucs2_conversion.c
1061
1062 i3_dump_log_i3_dump_log_CFLAGS = \
1063 $(AM_CFLAGS) \
1064 $(PTHREAD_CFLAGS) \
1065 $(libi3_CFLAGS)
1066
1067 i3_dump_log_i3_dump_log_LDADD = \
1068 $(PTHREAD_LIBS) \
1069 $(libi3_LIBS)
1070
1071 i3_dump_log_i3_dump_log_SOURCES = \
1072 i3-dump-log/main.c
1073
1074 i3_input_i3_input_CFLAGS = \
1075 $(AM_CFLAGS) \
1076 $(libi3_CFLAGS)
1077
1078 i3_input_i3_input_LDADD = \
1079 $(libi3_LIBS) \
1080 $(XCB_UTIL_KEYSYMS_LIBS)
1081
1082 i3_input_i3_input_SOURCES = \
1083 i3-input/i3-input.h \
1084 i3-input/keysym2ucs.c \
1085 i3-input/keysym2ucs.h \
1086 i3-input/main.c
1087
1088 i3_msg_i3_msg_CFLAGS = \
1089 $(AM_CFLAGS) \
1090 $(libi3_CFLAGS)
1091
1092 i3_msg_i3_msg_LDADD = \
1093 $(libi3_LIBS)
1094
1095 i3_msg_i3_msg_SOURCES = \
1096 i3-msg/main.c
1097
1098 i3_nagbar_i3_nagbar_CFLAGS = \
1099 $(AM_CFLAGS) \
1100 $(LIBSN_CFLAGS) \
1101 $(libi3_CFLAGS)
1102
1103 i3_nagbar_i3_nagbar_LDADD = \
1104 $(libi3_LIBS) \
1105 $(LIBSN_LIBS) \
1106 $(XCB_UTIL_CURSOR_LIBS)
1107
1108 i3_nagbar_i3_nagbar_SOURCES = \
1109 i3-nagbar/atoms.xmacro \
1110 i3-nagbar/i3-nagbar.h \
1111 i3-nagbar/main.c
1112
1113 i3bar_i3bar_CPPFLAGS = \
1114 $(AM_CPPFLAGS) \
1115 -I$(top_srcdir)/i3bar/include
1116
1117 i3bar_i3bar_CFLAGS = \
1118 $(AM_CFLAGS) \
1119 $(libi3_CFLAGS) \
1120 $(XCB_CFLAGS) \
1121 $(XKBCOMMON_CFLAGS) \
1122 $(PANGOCAIRO_CFLAGS) \
1123 $(YAJL_CFLAGS)
1124
1125 i3bar_i3bar_LDADD = \
1126 $(libi3_LIBS) \
1127 $(XCB_LIBS) \
1128 $(XCB_UTIL_CURSOR_LIBS) \
1129 $(XKBCOMMON_LIBS) \
1130 $(PANGOCAIRO_LIBS) \
1131 $(YAJL_LIBS)
1132
1133 i3bar_i3bar_SOURCES = \
1134 i3bar/include/child.h \
1135 i3bar/include/common.h \
1136 i3bar/include/configuration.h \
1137 i3bar/include/ipc.h \
1138 i3bar/include/mode.h \
1139 i3bar/include/outputs.h \
1140 i3bar/include/parse_json_header.h \
1141 i3bar/include/trayclients.h \
1142 i3bar/include/util.h \
1143 i3bar/include/workspaces.h \
1144 i3bar/include/xcb_atoms.def \
1145 i3bar/include/xcb.h \
1146 i3bar/src/child.c \
1147 i3bar/src/config.c \
1148 i3bar/src/ipc.c \
1149 i3bar/src/main.c \
1150 i3bar/src/mode.c \
1151 i3bar/src/outputs.c \
1152 i3bar/src/parse_json_header.c \
1153 i3bar/src/workspaces.c \
1154 i3bar/src/xcb.c
1155
1156 i3_config_wizard_i3_config_wizard_CFLAGS = \
1157 $(AM_CFLAGS) \
1158 $(libi3_CFLAGS) \
1159 $(LIBSN_CFLAGS) \
1160 $(XKBCOMMON_CFLAGS)
1161
1162 i3_config_wizard_i3_config_wizard_LDADD = \
1163 $(libi3_LIBS) \
1164 $(LIBSN_LIBS) \
1165 $(XCB_UTIL_KEYSYMS_LIBS) \
1166 $(XKBCOMMON_LIBS)
1167
1168 i3_config_wizard_i3_config_wizard_SOURCES = \
1169 i3-config-wizard/atoms.xmacro \
1170 i3-config-wizard/main.c \
1171 i3-config-wizard/xcb.h
1172
1173 test_inject_randr15_CPPFLAGS = \
1174 $(AM_CPPFLAGS)
1175
1176 test_inject_randr15_CFLAGS = \
1177 $(AM_CFLAGS) \
1178 $(i3_CFLAGS)
1179
1180 test_inject_randr15_SOURCES = \
1181 testcases/inject_randr1.5.c
1182
1183 test_inject_randr15_LDADD = \
1184 $(i3_LDADD)
1185
1186 test_commands_parser_CPPFLAGS = \
1187 $(AM_CPPFLAGS) \
1188 -DTEST_PARSER
1189
1190 test_commands_parser_CFLAGS = \
1191 $(AM_CFLAGS) \
1192 $(i3_CFLAGS)
1193
1194 test_commands_parser_SOURCES = \
1195 src/commands_parser.c
1196
1197 test_commands_parser_LDADD = \
1198 $(i3_LDADD)
1199
1200 test_config_parser_CPPFLAGS = \
1201 $(AM_CPPFLAGS) \
1202 -DTEST_PARSER
1203
1204 test_config_parser_CFLAGS = \
1205 $(AM_CFLAGS) \
1206 $(i3_CFLAGS)
1207
1208 test_config_parser_SOURCES = \
1209 src/config_parser.c
1210
1211 test_config_parser_LDADD = \
1212 $(i3_LDADD)
1213
1214 command_parser_SOURCES = \
1215 parser/GENERATED_command_enums.h \
1216 parser/GENERATED_command_tokens.h \
1217 parser/GENERATED_command_call.h
1218
1219 config_parser_SOURCES = \
1220 parser/GENERATED_config_enums.h \
1221 parser/GENERATED_config_tokens.h \
1222 parser/GENERATED_config_call.h
1223
1224 i3_SOURCES = \
1225 $(command_parser_SOURCES) \
1226 $(config_parser_SOURCES) \
1227 include/all.h \
1228 include/assignments.h \
1229 include/atoms_NET_SUPPORTED.xmacro \
1230 include/atoms_rest.xmacro \
1231 include/atoms.xmacro \
1232 include/bindings.h \
1233 include/click.h \
1234 include/cmdparse.h \
1235 include/commands.h \
1236 include/commands_parser.h \
1237 include/config_directives.h \
1238 include/configuration.h \
1239 include/config_parser.h \
1240 include/con.h \
1241 include/data.h \
1242 include/display_version.h \
1243 include/ewmh.h \
1244 include/fake_outputs.h \
1245 include/floating.h \
1246 include/handlers.h \
1247 include/i3.h \
1248 include/ipc.h \
1249 include/key_press.h \
1250 include/load_layout.h \
1251 include/log.h \
1252 include/main.h \
1253 include/manage.h \
1254 include/match.h \
1255 include/move.h \
1256 include/output.h \
1257 include/queue.h \
1258 include/randr.h \
1259 include/regex.h \
1260 include/render.h \
1261 include/resize.h \
1262 include/restore_layout.h \
1263 include/scratchpad.h \
1264 include/sd-daemon.h \
1265 include/shmlog.h \
1266 include/sighandler.h \
1267 include/startup.h \
1268 include/sync.h \
1269 include/tree.h \
1270 include/util.h \
1271 include/window.h \
1272 include/workspace.h \
1273 include/xcb.h \
1274 include/xcursor.h \
1275 include/x.h \
1276 include/xinerama.h \
1277 include/yajl_utils.h \
1278 src/assignments.c \
1279 src/bindings.c \
1280 src/click.c \
1281 src/commands.c \
1282 src/commands_parser.c \
1283 src/con.c \
1284 src/config.c \
1285 src/config_directives.c \
1286 src/config_parser.c \
1287 src/display_version.c \
1288 src/ewmh.c \
1289 src/fake_outputs.c \
1290 src/floating.c \
1291 src/handlers.c \
1292 src/ipc.c \
1293 src/key_press.c \
1294 src/load_layout.c \
1295 src/log.c \
1296 src/main.c \
1297 src/manage.c \
1298 src/match.c \
1299 src/move.c \
1300 src/output.c \
1301 src/randr.c \
1302 src/regex.c \
1303 src/render.c \
1304 src/resize.c \
1305 src/restore_layout.c \
1306 src/scratchpad.c \
1307 src/sd-daemon.c \
1308 src/sighandler.c \
1309 src/startup.c \
1310 src/sync.c \
1311 src/tree.c \
1312 src/util.c \
1313 src/version.c \
1314 src/window.c \
1315 src/workspace.c \
1316 src/x.c \
1317 src/xcb.c \
1318 src/xcursor.c \
1319 src/xinerama.c
1320
1321 CLEANFILES = \
1322 i3-command-parser.stamp \
1323 i3-config-parser.stamp \
1324 anyevent-i3.stamp
1325
1326 all: config.h
1327 $(MAKE) $(AM_MAKEFLAGS) all-am
1328
1329 .SUFFIXES:
1330 .SUFFIXES: .c .log .o .obj .test .test$(EXEEXT) .trs
1331 am--refresh: Makefile
1332 @:
1333 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
1334 @for dep in $?; do \
1335 case '$(am__configure_deps)' in \
1336 *$$dep*) \
1337 echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
1338 $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
1339 && exit 0; \
1340 exit 1;; \
1341 esac; \
1342 done; \
1343 echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
1344 $(am__cd) $(top_srcdir) && \
1345 $(AUTOMAKE) --foreign Makefile
1346 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
1347 @case '$?' in \
1348 *config.status*) \
1349 echo ' $(SHELL) ./config.status'; \
1350 $(SHELL) ./config.status;; \
1351 *) \
1352 echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
1353 cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
1354 esac;
1355
1356 $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
1357 $(SHELL) ./config.status --recheck
1358
1359 $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
1360 $(am__cd) $(srcdir) && $(AUTOCONF)
1361 $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
1362 $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
1363 $(am__aclocal_m4_deps):
1364
1365 config.h: stamp-h1
1366 @test -f $@ || rm -f stamp-h1
1367 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
1368
1369 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
1370 @rm -f stamp-h1
1371 cd $(top_builddir) && $(SHELL) ./config.status config.h
1372 $(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
1373 ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
1374 rm -f stamp-h1
1375 touch $@
1376
1377 distclean-hdr:
1378 -rm -f config.h stamp-h1
1379 testcases/lib/i3test.pm: $(top_builddir)/config.status $(top_srcdir)/testcases/lib/i3test.pm.in
1380 cd $(top_builddir) && $(SHELL) ./config.status $@
1381 man/asciidoc.conf: $(top_builddir)/config.status $(top_srcdir)/man/asciidoc.conf.in
1382 cd $(top_builddir) && $(SHELL) ./config.status $@
1383 testcases/complete-run.pl: $(top_builddir)/config.status $(top_srcdir)/testcases/complete-run.pl.in
1384 cd $(top_builddir) && $(SHELL) ./config.status $@
1385 install-binPROGRAMS: $(bin_PROGRAMS)
1386 @$(NORMAL_INSTALL)
1387 @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
1388 if test -n "$$list"; then \
1389 echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
1390 $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
1391 fi; \
1392 for p in $$list; do echo "$$p $$p"; done | \
1393 sed 's/$(EXEEXT)$$//' | \
1394 while read p p1; do if test -f $$p \
1395 ; then echo "$$p"; echo "$$p"; else :; fi; \
1396 done | \
1397 sed -e 'p;s,.*/,,;n;h' \
1398 -e 's|.*|.|' \
1399 -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
1400 sed 'N;N;N;s,\n, ,g' | \
1401 $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
1402 { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
1403 if ($$2 == $$4) files[d] = files[d] " " $$1; \
1404 else { print "f", $$3 "/" $$4, $$1; } } \
1405 END { for (d in files) print "f", d, files[d] }' | \
1406 while read type dir files; do \
1407 if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
1408 test -z "$$files" || { \
1409 echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
1410 $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
1411 } \
1412 ; done
1413
1414 uninstall-binPROGRAMS:
1415 @$(NORMAL_UNINSTALL)
1416 @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
1417 files=`for p in $$list; do echo "$$p"; done | \
1418 sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
1419 -e 's/$$/$(EXEEXT)/' \
1420 `; \
1421 test -n "$$list" || exit 0; \
1422 echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
1423 cd "$(DESTDIR)$(bindir)" && rm -f $$files
1424
1425 clean-binPROGRAMS:
1426 -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
1427
1428 clean-checkPROGRAMS:
1429 -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS)
1430
1431 clean-noinstLIBRARIES:
1432 -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
1433 libi3/$(am__dirstamp):
1434 @$(MKDIR_P) libi3
1435 @: > libi3/$(am__dirstamp)
1436 libi3/$(DEPDIR)/$(am__dirstamp):
1437 @$(MKDIR_P) libi3/$(DEPDIR)
1438 @: > libi3/$(DEPDIR)/$(am__dirstamp)
1439 libi3/a-dpi.$(OBJEXT): libi3/$(am__dirstamp) \
1440 libi3/$(DEPDIR)/$(am__dirstamp)
1441 libi3/a-draw_util.$(OBJEXT): libi3/$(am__dirstamp) \
1442 libi3/$(DEPDIR)/$(am__dirstamp)
1443 libi3/a-fake_configure_notify.$(OBJEXT): libi3/$(am__dirstamp) \
1444 libi3/$(DEPDIR)/$(am__dirstamp)
1445 libi3/a-font.$(OBJEXT): libi3/$(am__dirstamp) \
1446 libi3/$(DEPDIR)/$(am__dirstamp)
1447 libi3/a-format_placeholders.$(OBJEXT): libi3/$(am__dirstamp) \
1448 libi3/$(DEPDIR)/$(am__dirstamp)
1449 libi3/a-g_utf8_make_valid.$(OBJEXT): libi3/$(am__dirstamp) \
1450 libi3/$(DEPDIR)/$(am__dirstamp)
1451 libi3/a-get_colorpixel.$(OBJEXT): libi3/$(am__dirstamp) \
1452 libi3/$(DEPDIR)/$(am__dirstamp)
1453 libi3/a-get_config_path.$(OBJEXT): libi3/$(am__dirstamp) \
1454 libi3/$(DEPDIR)/$(am__dirstamp)
1455 libi3/a-get_exe_path.$(OBJEXT): libi3/$(am__dirstamp) \
1456 libi3/$(DEPDIR)/$(am__dirstamp)
1457 libi3/a-get_mod_mask.$(OBJEXT): libi3/$(am__dirstamp) \
1458 libi3/$(DEPDIR)/$(am__dirstamp)
1459 libi3/a-get_process_filename.$(OBJEXT): libi3/$(am__dirstamp) \
1460 libi3/$(DEPDIR)/$(am__dirstamp)
1461 libi3/a-get_visualtype.$(OBJEXT): libi3/$(am__dirstamp) \
1462 libi3/$(DEPDIR)/$(am__dirstamp)
1463 libi3/a-ipc_connect.$(OBJEXT): libi3/$(am__dirstamp) \
1464 libi3/$(DEPDIR)/$(am__dirstamp)
1465 libi3/a-ipc_recv_message.$(OBJEXT): libi3/$(am__dirstamp) \
1466 libi3/$(DEPDIR)/$(am__dirstamp)
1467 libi3/a-ipc_send_message.$(OBJEXT): libi3/$(am__dirstamp) \
1468 libi3/$(DEPDIR)/$(am__dirstamp)
1469 libi3/a-is_debug_build.$(OBJEXT): libi3/$(am__dirstamp) \
1470 libi3/$(DEPDIR)/$(am__dirstamp)
1471 libi3/a-mkdirp.$(OBJEXT): libi3/$(am__dirstamp) \
1472 libi3/$(DEPDIR)/$(am__dirstamp)
1473 libi3/a-resolve_tilde.$(OBJEXT): libi3/$(am__dirstamp) \
1474 libi3/$(DEPDIR)/$(am__dirstamp)
1475 libi3/a-root_atom_contents.$(OBJEXT): libi3/$(am__dirstamp) \
1476 libi3/$(DEPDIR)/$(am__dirstamp)
1477 libi3/a-safewrappers.$(OBJEXT): libi3/$(am__dirstamp) \
1478 libi3/$(DEPDIR)/$(am__dirstamp)
1479 libi3/a-string.$(OBJEXT): libi3/$(am__dirstamp) \
1480 libi3/$(DEPDIR)/$(am__dirstamp)
1481 libi3/a-strndup.$(OBJEXT): libi3/$(am__dirstamp) \
1482 libi3/$(DEPDIR)/$(am__dirstamp)
1483 libi3/a-ucs2_conversion.$(OBJEXT): libi3/$(am__dirstamp) \
1484 libi3/$(DEPDIR)/$(am__dirstamp)
1485
1486 libi3.a: $(libi3_a_OBJECTS) $(libi3_a_DEPENDENCIES) $(EXTRA_libi3_a_DEPENDENCIES)
1487 $(AM_V_at)-rm -f libi3.a
1488 $(AM_V_AR)$(libi3_a_AR) libi3.a $(libi3_a_OBJECTS) $(libi3_a_LIBADD)
1489 $(AM_V_at)$(RANLIB) libi3.a
1490 src/$(am__dirstamp):
1491 @$(MKDIR_P) src
1492 @: > src/$(am__dirstamp)
1493 src/$(DEPDIR)/$(am__dirstamp):
1494 @$(MKDIR_P) src/$(DEPDIR)
1495 @: > src/$(DEPDIR)/$(am__dirstamp)
1496 src/i3-assignments.$(OBJEXT): src/$(am__dirstamp) \
1497 src/$(DEPDIR)/$(am__dirstamp)
1498 src/i3-bindings.$(OBJEXT): src/$(am__dirstamp) \
1499 src/$(DEPDIR)/$(am__dirstamp)
1500 src/i3-click.$(OBJEXT): src/$(am__dirstamp) \
1501 src/$(DEPDIR)/$(am__dirstamp)
1502 src/i3-commands.$(OBJEXT): src/$(am__dirstamp) \
1503 src/$(DEPDIR)/$(am__dirstamp)
1504 src/i3-commands_parser.$(OBJEXT): src/$(am__dirstamp) \
1505 src/$(DEPDIR)/$(am__dirstamp)
1506 src/i3-con.$(OBJEXT): src/$(am__dirstamp) \
1507 src/$(DEPDIR)/$(am__dirstamp)
1508 src/i3-config.$(OBJEXT): src/$(am__dirstamp) \
1509 src/$(DEPDIR)/$(am__dirstamp)
1510 src/i3-config_directives.$(OBJEXT): src/$(am__dirstamp) \
1511 src/$(DEPDIR)/$(am__dirstamp)
1512 src/i3-config_parser.$(OBJEXT): src/$(am__dirstamp) \
1513 src/$(DEPDIR)/$(am__dirstamp)
1514 src/i3-display_version.$(OBJEXT): src/$(am__dirstamp) \
1515 src/$(DEPDIR)/$(am__dirstamp)
1516 src/i3-ewmh.$(OBJEXT): src/$(am__dirstamp) \
1517 src/$(DEPDIR)/$(am__dirstamp)
1518 src/i3-fake_outputs.$(OBJEXT): src/$(am__dirstamp) \
1519 src/$(DEPDIR)/$(am__dirstamp)
1520 src/i3-floating.$(OBJEXT): src/$(am__dirstamp) \
1521 src/$(DEPDIR)/$(am__dirstamp)
1522 src/i3-handlers.$(OBJEXT): src/$(am__dirstamp) \
1523 src/$(DEPDIR)/$(am__dirstamp)
1524 src/i3-ipc.$(OBJEXT): src/$(am__dirstamp) \
1525 src/$(DEPDIR)/$(am__dirstamp)
1526 src/i3-key_press.$(OBJEXT): src/$(am__dirstamp) \
1527 src/$(DEPDIR)/$(am__dirstamp)
1528 src/i3-load_layout.$(OBJEXT): src/$(am__dirstamp) \
1529 src/$(DEPDIR)/$(am__dirstamp)
1530 src/i3-log.$(OBJEXT): src/$(am__dirstamp) \
1531 src/$(DEPDIR)/$(am__dirstamp)
1532 src/i3-main.$(OBJEXT): src/$(am__dirstamp) \
1533 src/$(DEPDIR)/$(am__dirstamp)
1534 src/i3-manage.$(OBJEXT): src/$(am__dirstamp) \
1535 src/$(DEPDIR)/$(am__dirstamp)
1536 src/i3-match.$(OBJEXT): src/$(am__dirstamp) \
1537 src/$(DEPDIR)/$(am__dirstamp)
1538 src/i3-move.$(OBJEXT): src/$(am__dirstamp) \
1539 src/$(DEPDIR)/$(am__dirstamp)
1540 src/i3-output.$(OBJEXT): src/$(am__dirstamp) \
1541 src/$(DEPDIR)/$(am__dirstamp)
1542 src/i3-randr.$(OBJEXT): src/$(am__dirstamp) \
1543 src/$(DEPDIR)/$(am__dirstamp)
1544 src/i3-regex.$(OBJEXT): src/$(am__dirstamp) \
1545 src/$(DEPDIR)/$(am__dirstamp)
1546 src/i3-render.$(OBJEXT): src/$(am__dirstamp) \
1547 src/$(DEPDIR)/$(am__dirstamp)
1548 src/i3-resize.$(OBJEXT): src/$(am__dirstamp) \
1549 src/$(DEPDIR)/$(am__dirstamp)
1550 src/i3-restore_layout.$(OBJEXT): src/$(am__dirstamp) \
1551 src/$(DEPDIR)/$(am__dirstamp)
1552 src/i3-scratchpad.$(OBJEXT): src/$(am__dirstamp) \
1553 src/$(DEPDIR)/$(am__dirstamp)
1554 src/i3-sd-daemon.$(OBJEXT): src/$(am__dirstamp) \
1555 src/$(DEPDIR)/$(am__dirstamp)
1556 src/i3-sighandler.$(OBJEXT): src/$(am__dirstamp) \
1557 src/$(DEPDIR)/$(am__dirstamp)
1558 src/i3-startup.$(OBJEXT): src/$(am__dirstamp) \
1559 src/$(DEPDIR)/$(am__dirstamp)
1560 src/i3-sync.$(OBJEXT): src/$(am__dirstamp) \
1561 src/$(DEPDIR)/$(am__dirstamp)
1562 src/i3-tree.$(OBJEXT): src/$(am__dirstamp) \
1563 src/$(DEPDIR)/$(am__dirstamp)
1564 src/i3-util.$(OBJEXT): src/$(am__dirstamp) \
1565 src/$(DEPDIR)/$(am__dirstamp)
1566 src/i3-version.$(OBJEXT): src/$(am__dirstamp) \
1567 src/$(DEPDIR)/$(am__dirstamp)
1568 src/i3-window.$(OBJEXT): src/$(am__dirstamp) \
1569 src/$(DEPDIR)/$(am__dirstamp)
1570 src/i3-workspace.$(OBJEXT): src/$(am__dirstamp) \
1571 src/$(DEPDIR)/$(am__dirstamp)
1572 src/i3-x.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
1573 src/i3-xcb.$(OBJEXT): src/$(am__dirstamp) \
1574 src/$(DEPDIR)/$(am__dirstamp)
1575 src/i3-xcursor.$(OBJEXT): src/$(am__dirstamp) \
1576 src/$(DEPDIR)/$(am__dirstamp)
1577 src/i3-xinerama.$(OBJEXT): src/$(am__dirstamp) \
1578 src/$(DEPDIR)/$(am__dirstamp)
1579
1580 i3$(EXEEXT): $(i3_OBJECTS) $(i3_DEPENDENCIES) $(EXTRA_i3_DEPENDENCIES)
1581 @rm -f i3$(EXEEXT)
1582 $(AM_V_CCLD)$(i3_LINK) $(i3_OBJECTS) $(i3_LDADD) $(LIBS)
1583 i3-config-wizard/$(am__dirstamp):
1584 @$(MKDIR_P) i3-config-wizard
1585 @: > i3-config-wizard/$(am__dirstamp)
1586 i3-config-wizard/$(DEPDIR)/$(am__dirstamp):
1587 @$(MKDIR_P) i3-config-wizard/$(DEPDIR)
1588 @: > i3-config-wizard/$(DEPDIR)/$(am__dirstamp)
1589 i3-config-wizard/i3_config_wizard-main.$(OBJEXT): \
1590 i3-config-wizard/$(am__dirstamp) \
1591 i3-config-wizard/$(DEPDIR)/$(am__dirstamp)
1592
1593 i3-config-wizard/i3-config-wizard$(EXEEXT): $(i3_config_wizard_i3_config_wizard_OBJECTS) $(i3_config_wizard_i3_config_wizard_DEPENDENCIES) $(EXTRA_i3_config_wizard_i3_config_wizard_DEPENDENCIES) i3-config-wizard/$(am__dirstamp)
1594 @rm -f i3-config-wizard/i3-config-wizard$(EXEEXT)
1595 $(AM_V_CCLD)$(i3_config_wizard_i3_config_wizard_LINK) $(i3_config_wizard_i3_config_wizard_OBJECTS) $(i3_config_wizard_i3_config_wizard_LDADD) $(LIBS)
1596 i3-dump-log/$(am__dirstamp):
1597 @$(MKDIR_P) i3-dump-log
1598 @: > i3-dump-log/$(am__dirstamp)
1599 i3-dump-log/$(DEPDIR)/$(am__dirstamp):
1600 @$(MKDIR_P) i3-dump-log/$(DEPDIR)
1601 @: > i3-dump-log/$(DEPDIR)/$(am__dirstamp)
1602 i3-dump-log/i3_dump_log-main.$(OBJEXT): i3-dump-log/$(am__dirstamp) \
1603 i3-dump-log/$(DEPDIR)/$(am__dirstamp)
1604
1605 i3-dump-log/i3-dump-log$(EXEEXT): $(i3_dump_log_i3_dump_log_OBJECTS) $(i3_dump_log_i3_dump_log_DEPENDENCIES) $(EXTRA_i3_dump_log_i3_dump_log_DEPENDENCIES) i3-dump-log/$(am__dirstamp)
1606 @rm -f i3-dump-log/i3-dump-log$(EXEEXT)
1607 $(AM_V_CCLD)$(i3_dump_log_i3_dump_log_LINK) $(i3_dump_log_i3_dump_log_OBJECTS) $(i3_dump_log_i3_dump_log_LDADD) $(LIBS)
1608 i3-input/$(am__dirstamp):
1609 @$(MKDIR_P) i3-input
1610 @: > i3-input/$(am__dirstamp)
1611 i3-input/$(DEPDIR)/$(am__dirstamp):
1612 @$(MKDIR_P) i3-input/$(DEPDIR)
1613 @: > i3-input/$(DEPDIR)/$(am__dirstamp)
1614 i3-input/i3_input-keysym2ucs.$(OBJEXT): i3-input/$(am__dirstamp) \
1615 i3-input/$(DEPDIR)/$(am__dirstamp)
1616 i3-input/i3_input-main.$(OBJEXT): i3-input/$(am__dirstamp) \
1617 i3-input/$(DEPDIR)/$(am__dirstamp)
1618
1619 i3-input/i3-input$(EXEEXT): $(i3_input_i3_input_OBJECTS) $(i3_input_i3_input_DEPENDENCIES) $(EXTRA_i3_input_i3_input_DEPENDENCIES) i3-input/$(am__dirstamp)
1620 @rm -f i3-input/i3-input$(EXEEXT)
1621 $(AM_V_CCLD)$(i3_input_i3_input_LINK) $(i3_input_i3_input_OBJECTS) $(i3_input_i3_input_LDADD) $(LIBS)
1622 i3-msg/$(am__dirstamp):
1623 @$(MKDIR_P) i3-msg
1624 @: > i3-msg/$(am__dirstamp)
1625 i3-msg/$(DEPDIR)/$(am__dirstamp):
1626 @$(MKDIR_P) i3-msg/$(DEPDIR)
1627 @: > i3-msg/$(DEPDIR)/$(am__dirstamp)
1628 i3-msg/i3_msg-main.$(OBJEXT): i3-msg/$(am__dirstamp) \
1629 i3-msg/$(DEPDIR)/$(am__dirstamp)
1630
1631 i3-msg/i3-msg$(EXEEXT): $(i3_msg_i3_msg_OBJECTS) $(i3_msg_i3_msg_DEPENDENCIES) $(EXTRA_i3_msg_i3_msg_DEPENDENCIES) i3-msg/$(am__dirstamp)
1632 @rm -f i3-msg/i3-msg$(EXEEXT)
1633 $(AM_V_CCLD)$(i3_msg_i3_msg_LINK) $(i3_msg_i3_msg_OBJECTS) $(i3_msg_i3_msg_LDADD) $(LIBS)
1634 i3-nagbar/$(am__dirstamp):
1635 @$(MKDIR_P) i3-nagbar
1636 @: > i3-nagbar/$(am__dirstamp)
1637 i3-nagbar/$(DEPDIR)/$(am__dirstamp):
1638 @$(MKDIR_P) i3-nagbar/$(DEPDIR)
1639 @: > i3-nagbar/$(DEPDIR)/$(am__dirstamp)
1640 i3-nagbar/i3_nagbar-main.$(OBJEXT): i3-nagbar/$(am__dirstamp) \
1641 i3-nagbar/$(DEPDIR)/$(am__dirstamp)
1642
1643 i3-nagbar/i3-nagbar$(EXEEXT): $(i3_nagbar_i3_nagbar_OBJECTS) $(i3_nagbar_i3_nagbar_DEPENDENCIES) $(EXTRA_i3_nagbar_i3_nagbar_DEPENDENCIES) i3-nagbar/$(am__dirstamp)
1644 @rm -f i3-nagbar/i3-nagbar$(EXEEXT)
1645 $(AM_V_CCLD)$(i3_nagbar_i3_nagbar_LINK) $(i3_nagbar_i3_nagbar_OBJECTS) $(i3_nagbar_i3_nagbar_LDADD) $(LIBS)
1646 i3bar/src/$(am__dirstamp):
1647 @$(MKDIR_P) i3bar/src
1648 @: > i3bar/src/$(am__dirstamp)
1649 i3bar/src/$(DEPDIR)/$(am__dirstamp):
1650 @$(MKDIR_P) i3bar/src/$(DEPDIR)
1651 @: > i3bar/src/$(DEPDIR)/$(am__dirstamp)
1652 i3bar/src/i3bar-child.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1653 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1654 i3bar/src/i3bar-config.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1655 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1656 i3bar/src/i3bar-ipc.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1657 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1658 i3bar/src/i3bar-main.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1659 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1660 i3bar/src/i3bar-mode.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1661 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1662 i3bar/src/i3bar-outputs.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1663 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1664 i3bar/src/i3bar-parse_json_header.$(OBJEXT): \
1665 i3bar/src/$(am__dirstamp) i3bar/src/$(DEPDIR)/$(am__dirstamp)
1666 i3bar/src/i3bar-workspaces.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1667 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1668 i3bar/src/i3bar-xcb.$(OBJEXT): i3bar/src/$(am__dirstamp) \
1669 i3bar/src/$(DEPDIR)/$(am__dirstamp)
1670 i3bar/$(am__dirstamp):
1671 @$(MKDIR_P) i3bar
1672 @: > i3bar/$(am__dirstamp)
1673
1674 i3bar/i3bar$(EXEEXT): $(i3bar_i3bar_OBJECTS) $(i3bar_i3bar_DEPENDENCIES) $(EXTRA_i3bar_i3bar_DEPENDENCIES) i3bar/$(am__dirstamp)
1675 @rm -f i3bar/i3bar$(EXEEXT)
1676 $(AM_V_CCLD)$(i3bar_i3bar_LINK) $(i3bar_i3bar_OBJECTS) $(i3bar_i3bar_LDADD) $(LIBS)
1677 src/test_commands_parser-commands_parser.$(OBJEXT): \
1678 src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
1679
1680 test.commands_parser$(EXEEXT): $(test_commands_parser_OBJECTS) $(test_commands_parser_DEPENDENCIES) $(EXTRA_test_commands_parser_DEPENDENCIES)
1681 @rm -f test.commands_parser$(EXEEXT)
1682 $(AM_V_CCLD)$(test_commands_parser_LINK) $(test_commands_parser_OBJECTS) $(test_commands_parser_LDADD) $(LIBS)
1683 src/test_config_parser-config_parser.$(OBJEXT): src/$(am__dirstamp) \
1684 src/$(DEPDIR)/$(am__dirstamp)
1685
1686 test.config_parser$(EXEEXT): $(test_config_parser_OBJECTS) $(test_config_parser_DEPENDENCIES) $(EXTRA_test_config_parser_DEPENDENCIES)
1687 @rm -f test.config_parser$(EXEEXT)
1688 $(AM_V_CCLD)$(test_config_parser_LINK) $(test_config_parser_OBJECTS) $(test_config_parser_LDADD) $(LIBS)
1689 testcases/$(am__dirstamp):
1690 @$(MKDIR_P) testcases
1691 @: > testcases/$(am__dirstamp)
1692 testcases/$(DEPDIR)/$(am__dirstamp):
1693 @$(MKDIR_P) testcases/$(DEPDIR)
1694 @: > testcases/$(DEPDIR)/$(am__dirstamp)
1695 testcases/test_inject_randr15-inject_randr1.5.$(OBJEXT): \
1696 testcases/$(am__dirstamp) testcases/$(DEPDIR)/$(am__dirstamp)
1697
1698 test.inject_randr15$(EXEEXT): $(test_inject_randr15_OBJECTS) $(test_inject_randr15_DEPENDENCIES) $(EXTRA_test_inject_randr15_DEPENDENCIES)
1699 @rm -f test.inject_randr15$(EXEEXT)
1700 $(AM_V_CCLD)$(test_inject_randr15_LINK) $(test_inject_randr15_OBJECTS) $(test_inject_randr15_LDADD) $(LIBS)
1701 install-dist_binSCRIPTS: $(dist_bin_SCRIPTS)
1702 @$(NORMAL_INSTALL)
1703 @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || list=; \
1704 if test -n "$$list"; then \
1705 echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
1706 $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
1707 fi; \
1708 for p in $$list; do \
1709 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
1710 if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
1711 done | \
1712 sed -e 'p;s,.*/,,;n' \
1713 -e 'h;s|.*|.|' \
1714 -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
1715 $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
1716 { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
1717 if ($$2 == $$4) { files[d] = files[d] " " $$1; \
1718 if (++n[d] == $(am__install_max)) { \
1719 print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
1720 else { print "f", d "/" $$4, $$1 } } \
1721 END { for (d in files) print "f", d, files[d] }' | \
1722 while read type dir files; do \
1723 if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
1724 test -z "$$files" || { \
1725 echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \
1726 $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
1727 } \
1728 ; done
1729
1730 uninstall-dist_binSCRIPTS:
1731 @$(NORMAL_UNINSTALL)
1732 @list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \
1733 files=`for p in $$list; do echo "$$p"; done | \
1734 sed -e 's,.*/,,;$(transform)'`; \
1735 dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir)
1736
1737 mostlyclean-compile:
1738 -rm -f *.$(OBJEXT)
1739 -rm -f i3-config-wizard/*.$(OBJEXT)
1740 -rm -f i3-dump-log/*.$(OBJEXT)
1741 -rm -f i3-input/*.$(OBJEXT)
1742 -rm -f i3-msg/*.$(OBJEXT)
1743 -rm -f i3-nagbar/*.$(OBJEXT)
1744 -rm -f i3bar/src/*.$(OBJEXT)
1745 -rm -f libi3/*.$(OBJEXT)
1746 -rm -f src/*.$(OBJEXT)
1747 -rm -f testcases/*.$(OBJEXT)
1748
1749 distclean-compile:
1750 -rm -f *.tab.c
1751
1752 @AMDEP_TRUE@@am__include@ @am__quote@i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Po@am__quote@ # am--include-marker
1753 @AMDEP_TRUE@@am__include@ @am__quote@i3-dump-log/$(DEPDIR)/i3_dump_log-main.Po@am__quote@ # am--include-marker
1754 @AMDEP_TRUE@@am__include@ @am__quote@i3-input/$(DEPDIR)/i3_input-keysym2ucs.Po@am__quote@ # am--include-marker
1755 @AMDEP_TRUE@@am__include@ @am__quote@i3-input/$(DEPDIR)/i3_input-main.Po@am__quote@ # am--include-marker
1756 @AMDEP_TRUE@@am__include@ @am__quote@i3-msg/$(DEPDIR)/i3_msg-main.Po@am__quote@ # am--include-marker
1757 @AMDEP_TRUE@@am__include@ @am__quote@i3-nagbar/$(DEPDIR)/i3_nagbar-main.Po@am__quote@ # am--include-marker
1758 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-child.Po@am__quote@ # am--include-marker
1759 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-config.Po@am__quote@ # am--include-marker
1760 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-ipc.Po@am__quote@ # am--include-marker
1761 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-main.Po@am__quote@ # am--include-marker
1762 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-mode.Po@am__quote@ # am--include-marker
1763 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-outputs.Po@am__quote@ # am--include-marker
1764 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Po@am__quote@ # am--include-marker
1765 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-workspaces.Po@am__quote@ # am--include-marker
1766 @AMDEP_TRUE@@am__include@ @am__quote@i3bar/src/$(DEPDIR)/i3bar-xcb.Po@am__quote@ # am--include-marker
1767 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-dpi.Po@am__quote@ # am--include-marker
1768 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-draw_util.Po@am__quote@ # am--include-marker
1769 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-fake_configure_notify.Po@am__quote@ # am--include-marker
1770 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-font.Po@am__quote@ # am--include-marker
1771 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-format_placeholders.Po@am__quote@ # am--include-marker
1772 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-g_utf8_make_valid.Po@am__quote@ # am--include-marker
1773 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-get_colorpixel.Po@am__quote@ # am--include-marker
1774 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-get_config_path.Po@am__quote@ # am--include-marker
1775 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-get_exe_path.Po@am__quote@ # am--include-marker
1776 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-get_mod_mask.Po@am__quote@ # am--include-marker
1777 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-get_process_filename.Po@am__quote@ # am--include-marker
1778 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-get_visualtype.Po@am__quote@ # am--include-marker
1779 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-ipc_connect.Po@am__quote@ # am--include-marker
1780 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-ipc_recv_message.Po@am__quote@ # am--include-marker
1781 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-ipc_send_message.Po@am__quote@ # am--include-marker
1782 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-is_debug_build.Po@am__quote@ # am--include-marker
1783 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-mkdirp.Po@am__quote@ # am--include-marker
1784 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-resolve_tilde.Po@am__quote@ # am--include-marker
1785 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-root_atom_contents.Po@am__quote@ # am--include-marker
1786 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-safewrappers.Po@am__quote@ # am--include-marker
1787 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-string.Po@am__quote@ # am--include-marker
1788 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-strndup.Po@am__quote@ # am--include-marker
1789 @AMDEP_TRUE@@am__include@ @am__quote@libi3/$(DEPDIR)/a-ucs2_conversion.Po@am__quote@ # am--include-marker
1790 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-assignments.Po@am__quote@ # am--include-marker
1791 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-bindings.Po@am__quote@ # am--include-marker
1792 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-click.Po@am__quote@ # am--include-marker
1793 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-commands.Po@am__quote@ # am--include-marker
1794 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-commands_parser.Po@am__quote@ # am--include-marker
1795 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-con.Po@am__quote@ # am--include-marker
1796 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-config.Po@am__quote@ # am--include-marker
1797 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-config_directives.Po@am__quote@ # am--include-marker
1798 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-config_parser.Po@am__quote@ # am--include-marker
1799 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-display_version.Po@am__quote@ # am--include-marker
1800 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-ewmh.Po@am__quote@ # am--include-marker
1801 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-fake_outputs.Po@am__quote@ # am--include-marker
1802 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-floating.Po@am__quote@ # am--include-marker
1803 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-handlers.Po@am__quote@ # am--include-marker
1804 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-ipc.Po@am__quote@ # am--include-marker
1805 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-key_press.Po@am__quote@ # am--include-marker
1806 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-load_layout.Po@am__quote@ # am--include-marker
1807 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-log.Po@am__quote@ # am--include-marker
1808 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-main.Po@am__quote@ # am--include-marker
1809 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-manage.Po@am__quote@ # am--include-marker
1810 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-match.Po@am__quote@ # am--include-marker
1811 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-move.Po@am__quote@ # am--include-marker
1812 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-output.Po@am__quote@ # am--include-marker
1813 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-randr.Po@am__quote@ # am--include-marker
1814 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-regex.Po@am__quote@ # am--include-marker
1815 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-render.Po@am__quote@ # am--include-marker
1816 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-resize.Po@am__quote@ # am--include-marker
1817 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-restore_layout.Po@am__quote@ # am--include-marker
1818 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-scratchpad.Po@am__quote@ # am--include-marker
1819 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-sd-daemon.Po@am__quote@ # am--include-marker
1820 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-sighandler.Po@am__quote@ # am--include-marker
1821 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-startup.Po@am__quote@ # am--include-marker
1822 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-sync.Po@am__quote@ # am--include-marker
1823 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-tree.Po@am__quote@ # am--include-marker
1824 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-util.Po@am__quote@ # am--include-marker
1825 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-version.Po@am__quote@ # am--include-marker
1826 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-window.Po@am__quote@ # am--include-marker
1827 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-workspace.Po@am__quote@ # am--include-marker
1828 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-x.Po@am__quote@ # am--include-marker
1829 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-xcb.Po@am__quote@ # am--include-marker
1830 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-xcursor.Po@am__quote@ # am--include-marker
1831 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/i3-xinerama.Po@am__quote@ # am--include-marker
1832 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/test_commands_parser-commands_parser.Po@am__quote@ # am--include-marker
1833 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/test_config_parser-config_parser.Po@am__quote@ # am--include-marker
1834 @AMDEP_TRUE@@am__include@ @am__quote@testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Po@am__quote@ # am--include-marker
1835
1836 $(am__depfiles_remade):
1837 @$(MKDIR_P) $(@D)
1838 @echo '# dummy' >$@-t && $(am__mv) $@-t $@
1839
1840 am--depfiles: $(am__depfiles_remade)
1841
1842 .c.o:
1843 @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
1844 @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
1845 @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
1846 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
1847 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1848 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
1849
1850 .c.obj:
1851 @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
1852 @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
1853 @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
1854 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
1855 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1856 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
1857
1858 libi3/a-dpi.o: libi3/dpi.c
1859 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-dpi.o -MD -MP -MF libi3/$(DEPDIR)/a-dpi.Tpo -c -o libi3/a-dpi.o `test -f 'libi3/dpi.c' || echo '$(srcdir)/'`libi3/dpi.c
1860 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-dpi.Tpo libi3/$(DEPDIR)/a-dpi.Po
1861 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/dpi.c' object='libi3/a-dpi.o' libtool=no @AMDEPBACKSLASH@
1862 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1863 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-dpi.o `test -f 'libi3/dpi.c' || echo '$(srcdir)/'`libi3/dpi.c
1864
1865 libi3/a-dpi.obj: libi3/dpi.c
1866 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-dpi.obj -MD -MP -MF libi3/$(DEPDIR)/a-dpi.Tpo -c -o libi3/a-dpi.obj `if test -f 'libi3/dpi.c'; then $(CYGPATH_W) 'libi3/dpi.c'; else $(CYGPATH_W) '$(srcdir)/libi3/dpi.c'; fi`
1867 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-dpi.Tpo libi3/$(DEPDIR)/a-dpi.Po
1868 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/dpi.c' object='libi3/a-dpi.obj' libtool=no @AMDEPBACKSLASH@
1869 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1870 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-dpi.obj `if test -f 'libi3/dpi.c'; then $(CYGPATH_W) 'libi3/dpi.c'; else $(CYGPATH_W) '$(srcdir)/libi3/dpi.c'; fi`
1871
1872 libi3/a-draw_util.o: libi3/draw_util.c
1873 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-draw_util.o -MD -MP -MF libi3/$(DEPDIR)/a-draw_util.Tpo -c -o libi3/a-draw_util.o `test -f 'libi3/draw_util.c' || echo '$(srcdir)/'`libi3/draw_util.c
1874 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-draw_util.Tpo libi3/$(DEPDIR)/a-draw_util.Po
1875 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/draw_util.c' object='libi3/a-draw_util.o' libtool=no @AMDEPBACKSLASH@
1876 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1877 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-draw_util.o `test -f 'libi3/draw_util.c' || echo '$(srcdir)/'`libi3/draw_util.c
1878
1879 libi3/a-draw_util.obj: libi3/draw_util.c
1880 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-draw_util.obj -MD -MP -MF libi3/$(DEPDIR)/a-draw_util.Tpo -c -o libi3/a-draw_util.obj `if test -f 'libi3/draw_util.c'; then $(CYGPATH_W) 'libi3/draw_util.c'; else $(CYGPATH_W) '$(srcdir)/libi3/draw_util.c'; fi`
1881 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-draw_util.Tpo libi3/$(DEPDIR)/a-draw_util.Po
1882 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/draw_util.c' object='libi3/a-draw_util.obj' libtool=no @AMDEPBACKSLASH@
1883 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1884 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-draw_util.obj `if test -f 'libi3/draw_util.c'; then $(CYGPATH_W) 'libi3/draw_util.c'; else $(CYGPATH_W) '$(srcdir)/libi3/draw_util.c'; fi`
1885
1886 libi3/a-fake_configure_notify.o: libi3/fake_configure_notify.c
1887 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-fake_configure_notify.o -MD -MP -MF libi3/$(DEPDIR)/a-fake_configure_notify.Tpo -c -o libi3/a-fake_configure_notify.o `test -f 'libi3/fake_configure_notify.c' || echo '$(srcdir)/'`libi3/fake_configure_notify.c
1888 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-fake_configure_notify.Tpo libi3/$(DEPDIR)/a-fake_configure_notify.Po
1889 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/fake_configure_notify.c' object='libi3/a-fake_configure_notify.o' libtool=no @AMDEPBACKSLASH@
1890 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1891 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-fake_configure_notify.o `test -f 'libi3/fake_configure_notify.c' || echo '$(srcdir)/'`libi3/fake_configure_notify.c
1892
1893 libi3/a-fake_configure_notify.obj: libi3/fake_configure_notify.c
1894 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-fake_configure_notify.obj -MD -MP -MF libi3/$(DEPDIR)/a-fake_configure_notify.Tpo -c -o libi3/a-fake_configure_notify.obj `if test -f 'libi3/fake_configure_notify.c'; then $(CYGPATH_W) 'libi3/fake_configure_notify.c'; else $(CYGPATH_W) '$(srcdir)/libi3/fake_configure_notify.c'; fi`
1895 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-fake_configure_notify.Tpo libi3/$(DEPDIR)/a-fake_configure_notify.Po
1896 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/fake_configure_notify.c' object='libi3/a-fake_configure_notify.obj' libtool=no @AMDEPBACKSLASH@
1897 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1898 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-fake_configure_notify.obj `if test -f 'libi3/fake_configure_notify.c'; then $(CYGPATH_W) 'libi3/fake_configure_notify.c'; else $(CYGPATH_W) '$(srcdir)/libi3/fake_configure_notify.c'; fi`
1899
1900 libi3/a-font.o: libi3/font.c
1901 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-font.o -MD -MP -MF libi3/$(DEPDIR)/a-font.Tpo -c -o libi3/a-font.o `test -f 'libi3/font.c' || echo '$(srcdir)/'`libi3/font.c
1902 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-font.Tpo libi3/$(DEPDIR)/a-font.Po
1903 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/font.c' object='libi3/a-font.o' libtool=no @AMDEPBACKSLASH@
1904 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1905 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-font.o `test -f 'libi3/font.c' || echo '$(srcdir)/'`libi3/font.c
1906
1907 libi3/a-font.obj: libi3/font.c
1908 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-font.obj -MD -MP -MF libi3/$(DEPDIR)/a-font.Tpo -c -o libi3/a-font.obj `if test -f 'libi3/font.c'; then $(CYGPATH_W) 'libi3/font.c'; else $(CYGPATH_W) '$(srcdir)/libi3/font.c'; fi`
1909 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-font.Tpo libi3/$(DEPDIR)/a-font.Po
1910 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/font.c' object='libi3/a-font.obj' libtool=no @AMDEPBACKSLASH@
1911 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1912 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-font.obj `if test -f 'libi3/font.c'; then $(CYGPATH_W) 'libi3/font.c'; else $(CYGPATH_W) '$(srcdir)/libi3/font.c'; fi`
1913
1914 libi3/a-format_placeholders.o: libi3/format_placeholders.c
1915 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-format_placeholders.o -MD -MP -MF libi3/$(DEPDIR)/a-format_placeholders.Tpo -c -o libi3/a-format_placeholders.o `test -f 'libi3/format_placeholders.c' || echo '$(srcdir)/'`libi3/format_placeholders.c
1916 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-format_placeholders.Tpo libi3/$(DEPDIR)/a-format_placeholders.Po
1917 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/format_placeholders.c' object='libi3/a-format_placeholders.o' libtool=no @AMDEPBACKSLASH@
1918 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1919 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-format_placeholders.o `test -f 'libi3/format_placeholders.c' || echo '$(srcdir)/'`libi3/format_placeholders.c
1920
1921 libi3/a-format_placeholders.obj: libi3/format_placeholders.c
1922 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-format_placeholders.obj -MD -MP -MF libi3/$(DEPDIR)/a-format_placeholders.Tpo -c -o libi3/a-format_placeholders.obj `if test -f 'libi3/format_placeholders.c'; then $(CYGPATH_W) 'libi3/format_placeholders.c'; else $(CYGPATH_W) '$(srcdir)/libi3/format_placeholders.c'; fi`
1923 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-format_placeholders.Tpo libi3/$(DEPDIR)/a-format_placeholders.Po
1924 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/format_placeholders.c' object='libi3/a-format_placeholders.obj' libtool=no @AMDEPBACKSLASH@
1925 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1926 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-format_placeholders.obj `if test -f 'libi3/format_placeholders.c'; then $(CYGPATH_W) 'libi3/format_placeholders.c'; else $(CYGPATH_W) '$(srcdir)/libi3/format_placeholders.c'; fi`
1927
1928 libi3/a-g_utf8_make_valid.o: libi3/g_utf8_make_valid.c
1929 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-g_utf8_make_valid.o -MD -MP -MF libi3/$(DEPDIR)/a-g_utf8_make_valid.Tpo -c -o libi3/a-g_utf8_make_valid.o `test -f 'libi3/g_utf8_make_valid.c' || echo '$(srcdir)/'`libi3/g_utf8_make_valid.c
1930 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-g_utf8_make_valid.Tpo libi3/$(DEPDIR)/a-g_utf8_make_valid.Po
1931 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/g_utf8_make_valid.c' object='libi3/a-g_utf8_make_valid.o' libtool=no @AMDEPBACKSLASH@
1932 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1933 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-g_utf8_make_valid.o `test -f 'libi3/g_utf8_make_valid.c' || echo '$(srcdir)/'`libi3/g_utf8_make_valid.c
1934
1935 libi3/a-g_utf8_make_valid.obj: libi3/g_utf8_make_valid.c
1936 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-g_utf8_make_valid.obj -MD -MP -MF libi3/$(DEPDIR)/a-g_utf8_make_valid.Tpo -c -o libi3/a-g_utf8_make_valid.obj `if test -f 'libi3/g_utf8_make_valid.c'; then $(CYGPATH_W) 'libi3/g_utf8_make_valid.c'; else $(CYGPATH_W) '$(srcdir)/libi3/g_utf8_make_valid.c'; fi`
1937 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-g_utf8_make_valid.Tpo libi3/$(DEPDIR)/a-g_utf8_make_valid.Po
1938 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/g_utf8_make_valid.c' object='libi3/a-g_utf8_make_valid.obj' libtool=no @AMDEPBACKSLASH@
1939 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1940 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-g_utf8_make_valid.obj `if test -f 'libi3/g_utf8_make_valid.c'; then $(CYGPATH_W) 'libi3/g_utf8_make_valid.c'; else $(CYGPATH_W) '$(srcdir)/libi3/g_utf8_make_valid.c'; fi`
1941
1942 libi3/a-get_colorpixel.o: libi3/get_colorpixel.c
1943 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_colorpixel.o -MD -MP -MF libi3/$(DEPDIR)/a-get_colorpixel.Tpo -c -o libi3/a-get_colorpixel.o `test -f 'libi3/get_colorpixel.c' || echo '$(srcdir)/'`libi3/get_colorpixel.c
1944 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_colorpixel.Tpo libi3/$(DEPDIR)/a-get_colorpixel.Po
1945 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_colorpixel.c' object='libi3/a-get_colorpixel.o' libtool=no @AMDEPBACKSLASH@
1946 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1947 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_colorpixel.o `test -f 'libi3/get_colorpixel.c' || echo '$(srcdir)/'`libi3/get_colorpixel.c
1948
1949 libi3/a-get_colorpixel.obj: libi3/get_colorpixel.c
1950 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_colorpixel.obj -MD -MP -MF libi3/$(DEPDIR)/a-get_colorpixel.Tpo -c -o libi3/a-get_colorpixel.obj `if test -f 'libi3/get_colorpixel.c'; then $(CYGPATH_W) 'libi3/get_colorpixel.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_colorpixel.c'; fi`
1951 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_colorpixel.Tpo libi3/$(DEPDIR)/a-get_colorpixel.Po
1952 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_colorpixel.c' object='libi3/a-get_colorpixel.obj' libtool=no @AMDEPBACKSLASH@
1953 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1954 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_colorpixel.obj `if test -f 'libi3/get_colorpixel.c'; then $(CYGPATH_W) 'libi3/get_colorpixel.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_colorpixel.c'; fi`
1955
1956 libi3/a-get_config_path.o: libi3/get_config_path.c
1957 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_config_path.o -MD -MP -MF libi3/$(DEPDIR)/a-get_config_path.Tpo -c -o libi3/a-get_config_path.o `test -f 'libi3/get_config_path.c' || echo '$(srcdir)/'`libi3/get_config_path.c
1958 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_config_path.Tpo libi3/$(DEPDIR)/a-get_config_path.Po
1959 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_config_path.c' object='libi3/a-get_config_path.o' libtool=no @AMDEPBACKSLASH@
1960 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1961 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_config_path.o `test -f 'libi3/get_config_path.c' || echo '$(srcdir)/'`libi3/get_config_path.c
1962
1963 libi3/a-get_config_path.obj: libi3/get_config_path.c
1964 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_config_path.obj -MD -MP -MF libi3/$(DEPDIR)/a-get_config_path.Tpo -c -o libi3/a-get_config_path.obj `if test -f 'libi3/get_config_path.c'; then $(CYGPATH_W) 'libi3/get_config_path.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_config_path.c'; fi`
1965 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_config_path.Tpo libi3/$(DEPDIR)/a-get_config_path.Po
1966 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_config_path.c' object='libi3/a-get_config_path.obj' libtool=no @AMDEPBACKSLASH@
1967 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1968 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_config_path.obj `if test -f 'libi3/get_config_path.c'; then $(CYGPATH_W) 'libi3/get_config_path.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_config_path.c'; fi`
1969
1970 libi3/a-get_exe_path.o: libi3/get_exe_path.c
1971 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_exe_path.o -MD -MP -MF libi3/$(DEPDIR)/a-get_exe_path.Tpo -c -o libi3/a-get_exe_path.o `test -f 'libi3/get_exe_path.c' || echo '$(srcdir)/'`libi3/get_exe_path.c
1972 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_exe_path.Tpo libi3/$(DEPDIR)/a-get_exe_path.Po
1973 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_exe_path.c' object='libi3/a-get_exe_path.o' libtool=no @AMDEPBACKSLASH@
1974 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1975 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_exe_path.o `test -f 'libi3/get_exe_path.c' || echo '$(srcdir)/'`libi3/get_exe_path.c
1976
1977 libi3/a-get_exe_path.obj: libi3/get_exe_path.c
1978 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_exe_path.obj -MD -MP -MF libi3/$(DEPDIR)/a-get_exe_path.Tpo -c -o libi3/a-get_exe_path.obj `if test -f 'libi3/get_exe_path.c'; then $(CYGPATH_W) 'libi3/get_exe_path.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_exe_path.c'; fi`
1979 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_exe_path.Tpo libi3/$(DEPDIR)/a-get_exe_path.Po
1980 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_exe_path.c' object='libi3/a-get_exe_path.obj' libtool=no @AMDEPBACKSLASH@
1981 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1982 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_exe_path.obj `if test -f 'libi3/get_exe_path.c'; then $(CYGPATH_W) 'libi3/get_exe_path.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_exe_path.c'; fi`
1983
1984 libi3/a-get_mod_mask.o: libi3/get_mod_mask.c
1985 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_mod_mask.o -MD -MP -MF libi3/$(DEPDIR)/a-get_mod_mask.Tpo -c -o libi3/a-get_mod_mask.o `test -f 'libi3/get_mod_mask.c' || echo '$(srcdir)/'`libi3/get_mod_mask.c
1986 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_mod_mask.Tpo libi3/$(DEPDIR)/a-get_mod_mask.Po
1987 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_mod_mask.c' object='libi3/a-get_mod_mask.o' libtool=no @AMDEPBACKSLASH@
1988 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1989 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_mod_mask.o `test -f 'libi3/get_mod_mask.c' || echo '$(srcdir)/'`libi3/get_mod_mask.c
1990
1991 libi3/a-get_mod_mask.obj: libi3/get_mod_mask.c
1992 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_mod_mask.obj -MD -MP -MF libi3/$(DEPDIR)/a-get_mod_mask.Tpo -c -o libi3/a-get_mod_mask.obj `if test -f 'libi3/get_mod_mask.c'; then $(CYGPATH_W) 'libi3/get_mod_mask.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_mod_mask.c'; fi`
1993 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_mod_mask.Tpo libi3/$(DEPDIR)/a-get_mod_mask.Po
1994 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_mod_mask.c' object='libi3/a-get_mod_mask.obj' libtool=no @AMDEPBACKSLASH@
1995 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1996 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_mod_mask.obj `if test -f 'libi3/get_mod_mask.c'; then $(CYGPATH_W) 'libi3/get_mod_mask.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_mod_mask.c'; fi`
1997
1998 libi3/a-get_process_filename.o: libi3/get_process_filename.c
1999 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_process_filename.o -MD -MP -MF libi3/$(DEPDIR)/a-get_process_filename.Tpo -c -o libi3/a-get_process_filename.o `test -f 'libi3/get_process_filename.c' || echo '$(srcdir)/'`libi3/get_process_filename.c
2000 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_process_filename.Tpo libi3/$(DEPDIR)/a-get_process_filename.Po
2001 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_process_filename.c' object='libi3/a-get_process_filename.o' libtool=no @AMDEPBACKSLASH@
2002 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2003 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_process_filename.o `test -f 'libi3/get_process_filename.c' || echo '$(srcdir)/'`libi3/get_process_filename.c
2004
2005 libi3/a-get_process_filename.obj: libi3/get_process_filename.c
2006 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_process_filename.obj -MD -MP -MF libi3/$(DEPDIR)/a-get_process_filename.Tpo -c -o libi3/a-get_process_filename.obj `if test -f 'libi3/get_process_filename.c'; then $(CYGPATH_W) 'libi3/get_process_filename.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_process_filename.c'; fi`
2007 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_process_filename.Tpo libi3/$(DEPDIR)/a-get_process_filename.Po
2008 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_process_filename.c' object='libi3/a-get_process_filename.obj' libtool=no @AMDEPBACKSLASH@
2009 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2010 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_process_filename.obj `if test -f 'libi3/get_process_filename.c'; then $(CYGPATH_W) 'libi3/get_process_filename.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_process_filename.c'; fi`
2011
2012 libi3/a-get_visualtype.o: libi3/get_visualtype.c
2013 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_visualtype.o -MD -MP -MF libi3/$(DEPDIR)/a-get_visualtype.Tpo -c -o libi3/a-get_visualtype.o `test -f 'libi3/get_visualtype.c' || echo '$(srcdir)/'`libi3/get_visualtype.c
2014 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_visualtype.Tpo libi3/$(DEPDIR)/a-get_visualtype.Po
2015 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_visualtype.c' object='libi3/a-get_visualtype.o' libtool=no @AMDEPBACKSLASH@
2016 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2017 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_visualtype.o `test -f 'libi3/get_visualtype.c' || echo '$(srcdir)/'`libi3/get_visualtype.c
2018
2019 libi3/a-get_visualtype.obj: libi3/get_visualtype.c
2020 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-get_visualtype.obj -MD -MP -MF libi3/$(DEPDIR)/a-get_visualtype.Tpo -c -o libi3/a-get_visualtype.obj `if test -f 'libi3/get_visualtype.c'; then $(CYGPATH_W) 'libi3/get_visualtype.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_visualtype.c'; fi`
2021 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-get_visualtype.Tpo libi3/$(DEPDIR)/a-get_visualtype.Po
2022 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/get_visualtype.c' object='libi3/a-get_visualtype.obj' libtool=no @AMDEPBACKSLASH@
2023 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2024 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-get_visualtype.obj `if test -f 'libi3/get_visualtype.c'; then $(CYGPATH_W) 'libi3/get_visualtype.c'; else $(CYGPATH_W) '$(srcdir)/libi3/get_visualtype.c'; fi`
2025
2026 libi3/a-ipc_connect.o: libi3/ipc_connect.c
2027 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ipc_connect.o -MD -MP -MF libi3/$(DEPDIR)/a-ipc_connect.Tpo -c -o libi3/a-ipc_connect.o `test -f 'libi3/ipc_connect.c' || echo '$(srcdir)/'`libi3/ipc_connect.c
2028 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ipc_connect.Tpo libi3/$(DEPDIR)/a-ipc_connect.Po
2029 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ipc_connect.c' object='libi3/a-ipc_connect.o' libtool=no @AMDEPBACKSLASH@
2030 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2031 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ipc_connect.o `test -f 'libi3/ipc_connect.c' || echo '$(srcdir)/'`libi3/ipc_connect.c
2032
2033 libi3/a-ipc_connect.obj: libi3/ipc_connect.c
2034 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ipc_connect.obj -MD -MP -MF libi3/$(DEPDIR)/a-ipc_connect.Tpo -c -o libi3/a-ipc_connect.obj `if test -f 'libi3/ipc_connect.c'; then $(CYGPATH_W) 'libi3/ipc_connect.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ipc_connect.c'; fi`
2035 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ipc_connect.Tpo libi3/$(DEPDIR)/a-ipc_connect.Po
2036 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ipc_connect.c' object='libi3/a-ipc_connect.obj' libtool=no @AMDEPBACKSLASH@
2037 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2038 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ipc_connect.obj `if test -f 'libi3/ipc_connect.c'; then $(CYGPATH_W) 'libi3/ipc_connect.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ipc_connect.c'; fi`
2039
2040 libi3/a-ipc_recv_message.o: libi3/ipc_recv_message.c
2041 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ipc_recv_message.o -MD -MP -MF libi3/$(DEPDIR)/a-ipc_recv_message.Tpo -c -o libi3/a-ipc_recv_message.o `test -f 'libi3/ipc_recv_message.c' || echo '$(srcdir)/'`libi3/ipc_recv_message.c
2042 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ipc_recv_message.Tpo libi3/$(DEPDIR)/a-ipc_recv_message.Po
2043 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ipc_recv_message.c' object='libi3/a-ipc_recv_message.o' libtool=no @AMDEPBACKSLASH@
2044 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2045 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ipc_recv_message.o `test -f 'libi3/ipc_recv_message.c' || echo '$(srcdir)/'`libi3/ipc_recv_message.c
2046
2047 libi3/a-ipc_recv_message.obj: libi3/ipc_recv_message.c
2048 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ipc_recv_message.obj -MD -MP -MF libi3/$(DEPDIR)/a-ipc_recv_message.Tpo -c -o libi3/a-ipc_recv_message.obj `if test -f 'libi3/ipc_recv_message.c'; then $(CYGPATH_W) 'libi3/ipc_recv_message.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ipc_recv_message.c'; fi`
2049 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ipc_recv_message.Tpo libi3/$(DEPDIR)/a-ipc_recv_message.Po
2050 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ipc_recv_message.c' object='libi3/a-ipc_recv_message.obj' libtool=no @AMDEPBACKSLASH@
2051 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2052 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ipc_recv_message.obj `if test -f 'libi3/ipc_recv_message.c'; then $(CYGPATH_W) 'libi3/ipc_recv_message.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ipc_recv_message.c'; fi`
2053
2054 libi3/a-ipc_send_message.o: libi3/ipc_send_message.c
2055 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ipc_send_message.o -MD -MP -MF libi3/$(DEPDIR)/a-ipc_send_message.Tpo -c -o libi3/a-ipc_send_message.o `test -f 'libi3/ipc_send_message.c' || echo '$(srcdir)/'`libi3/ipc_send_message.c
2056 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ipc_send_message.Tpo libi3/$(DEPDIR)/a-ipc_send_message.Po
2057 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ipc_send_message.c' object='libi3/a-ipc_send_message.o' libtool=no @AMDEPBACKSLASH@
2058 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2059 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ipc_send_message.o `test -f 'libi3/ipc_send_message.c' || echo '$(srcdir)/'`libi3/ipc_send_message.c
2060
2061 libi3/a-ipc_send_message.obj: libi3/ipc_send_message.c
2062 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ipc_send_message.obj -MD -MP -MF libi3/$(DEPDIR)/a-ipc_send_message.Tpo -c -o libi3/a-ipc_send_message.obj `if test -f 'libi3/ipc_send_message.c'; then $(CYGPATH_W) 'libi3/ipc_send_message.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ipc_send_message.c'; fi`
2063 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ipc_send_message.Tpo libi3/$(DEPDIR)/a-ipc_send_message.Po
2064 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ipc_send_message.c' object='libi3/a-ipc_send_message.obj' libtool=no @AMDEPBACKSLASH@
2065 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2066 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ipc_send_message.obj `if test -f 'libi3/ipc_send_message.c'; then $(CYGPATH_W) 'libi3/ipc_send_message.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ipc_send_message.c'; fi`
2067
2068 libi3/a-is_debug_build.o: libi3/is_debug_build.c
2069 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-is_debug_build.o -MD -MP -MF libi3/$(DEPDIR)/a-is_debug_build.Tpo -c -o libi3/a-is_debug_build.o `test -f 'libi3/is_debug_build.c' || echo '$(srcdir)/'`libi3/is_debug_build.c
2070 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-is_debug_build.Tpo libi3/$(DEPDIR)/a-is_debug_build.Po
2071 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/is_debug_build.c' object='libi3/a-is_debug_build.o' libtool=no @AMDEPBACKSLASH@
2072 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2073 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-is_debug_build.o `test -f 'libi3/is_debug_build.c' || echo '$(srcdir)/'`libi3/is_debug_build.c
2074
2075 libi3/a-is_debug_build.obj: libi3/is_debug_build.c
2076 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-is_debug_build.obj -MD -MP -MF libi3/$(DEPDIR)/a-is_debug_build.Tpo -c -o libi3/a-is_debug_build.obj `if test -f 'libi3/is_debug_build.c'; then $(CYGPATH_W) 'libi3/is_debug_build.c'; else $(CYGPATH_W) '$(srcdir)/libi3/is_debug_build.c'; fi`
2077 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-is_debug_build.Tpo libi3/$(DEPDIR)/a-is_debug_build.Po
2078 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/is_debug_build.c' object='libi3/a-is_debug_build.obj' libtool=no @AMDEPBACKSLASH@
2079 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2080 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-is_debug_build.obj `if test -f 'libi3/is_debug_build.c'; then $(CYGPATH_W) 'libi3/is_debug_build.c'; else $(CYGPATH_W) '$(srcdir)/libi3/is_debug_build.c'; fi`
2081
2082 libi3/a-mkdirp.o: libi3/mkdirp.c
2083 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-mkdirp.o -MD -MP -MF libi3/$(DEPDIR)/a-mkdirp.Tpo -c -o libi3/a-mkdirp.o `test -f 'libi3/mkdirp.c' || echo '$(srcdir)/'`libi3/mkdirp.c
2084 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-mkdirp.Tpo libi3/$(DEPDIR)/a-mkdirp.Po
2085 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/mkdirp.c' object='libi3/a-mkdirp.o' libtool=no @AMDEPBACKSLASH@
2086 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2087 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-mkdirp.o `test -f 'libi3/mkdirp.c' || echo '$(srcdir)/'`libi3/mkdirp.c
2088
2089 libi3/a-mkdirp.obj: libi3/mkdirp.c
2090 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-mkdirp.obj -MD -MP -MF libi3/$(DEPDIR)/a-mkdirp.Tpo -c -o libi3/a-mkdirp.obj `if test -f 'libi3/mkdirp.c'; then $(CYGPATH_W) 'libi3/mkdirp.c'; else $(CYGPATH_W) '$(srcdir)/libi3/mkdirp.c'; fi`
2091 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-mkdirp.Tpo libi3/$(DEPDIR)/a-mkdirp.Po
2092 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/mkdirp.c' object='libi3/a-mkdirp.obj' libtool=no @AMDEPBACKSLASH@
2093 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2094 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-mkdirp.obj `if test -f 'libi3/mkdirp.c'; then $(CYGPATH_W) 'libi3/mkdirp.c'; else $(CYGPATH_W) '$(srcdir)/libi3/mkdirp.c'; fi`
2095
2096 libi3/a-resolve_tilde.o: libi3/resolve_tilde.c
2097 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-resolve_tilde.o -MD -MP -MF libi3/$(DEPDIR)/a-resolve_tilde.Tpo -c -o libi3/a-resolve_tilde.o `test -f 'libi3/resolve_tilde.c' || echo '$(srcdir)/'`libi3/resolve_tilde.c
2098 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-resolve_tilde.Tpo libi3/$(DEPDIR)/a-resolve_tilde.Po
2099 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/resolve_tilde.c' object='libi3/a-resolve_tilde.o' libtool=no @AMDEPBACKSLASH@
2100 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2101 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-resolve_tilde.o `test -f 'libi3/resolve_tilde.c' || echo '$(srcdir)/'`libi3/resolve_tilde.c
2102
2103 libi3/a-resolve_tilde.obj: libi3/resolve_tilde.c
2104 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-resolve_tilde.obj -MD -MP -MF libi3/$(DEPDIR)/a-resolve_tilde.Tpo -c -o libi3/a-resolve_tilde.obj `if test -f 'libi3/resolve_tilde.c'; then $(CYGPATH_W) 'libi3/resolve_tilde.c'; else $(CYGPATH_W) '$(srcdir)/libi3/resolve_tilde.c'; fi`
2105 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-resolve_tilde.Tpo libi3/$(DEPDIR)/a-resolve_tilde.Po
2106 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/resolve_tilde.c' object='libi3/a-resolve_tilde.obj' libtool=no @AMDEPBACKSLASH@
2107 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2108 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-resolve_tilde.obj `if test -f 'libi3/resolve_tilde.c'; then $(CYGPATH_W) 'libi3/resolve_tilde.c'; else $(CYGPATH_W) '$(srcdir)/libi3/resolve_tilde.c'; fi`
2109
2110 libi3/a-root_atom_contents.o: libi3/root_atom_contents.c
2111 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-root_atom_contents.o -MD -MP -MF libi3/$(DEPDIR)/a-root_atom_contents.Tpo -c -o libi3/a-root_atom_contents.o `test -f 'libi3/root_atom_contents.c' || echo '$(srcdir)/'`libi3/root_atom_contents.c
2112 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-root_atom_contents.Tpo libi3/$(DEPDIR)/a-root_atom_contents.Po
2113 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/root_atom_contents.c' object='libi3/a-root_atom_contents.o' libtool=no @AMDEPBACKSLASH@
2114 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2115 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-root_atom_contents.o `test -f 'libi3/root_atom_contents.c' || echo '$(srcdir)/'`libi3/root_atom_contents.c
2116
2117 libi3/a-root_atom_contents.obj: libi3/root_atom_contents.c
2118 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-root_atom_contents.obj -MD -MP -MF libi3/$(DEPDIR)/a-root_atom_contents.Tpo -c -o libi3/a-root_atom_contents.obj `if test -f 'libi3/root_atom_contents.c'; then $(CYGPATH_W) 'libi3/root_atom_contents.c'; else $(CYGPATH_W) '$(srcdir)/libi3/root_atom_contents.c'; fi`
2119 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-root_atom_contents.Tpo libi3/$(DEPDIR)/a-root_atom_contents.Po
2120 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/root_atom_contents.c' object='libi3/a-root_atom_contents.obj' libtool=no @AMDEPBACKSLASH@
2121 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2122 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-root_atom_contents.obj `if test -f 'libi3/root_atom_contents.c'; then $(CYGPATH_W) 'libi3/root_atom_contents.c'; else $(CYGPATH_W) '$(srcdir)/libi3/root_atom_contents.c'; fi`
2123
2124 libi3/a-safewrappers.o: libi3/safewrappers.c
2125 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-safewrappers.o -MD -MP -MF libi3/$(DEPDIR)/a-safewrappers.Tpo -c -o libi3/a-safewrappers.o `test -f 'libi3/safewrappers.c' || echo '$(srcdir)/'`libi3/safewrappers.c
2126 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-safewrappers.Tpo libi3/$(DEPDIR)/a-safewrappers.Po
2127 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/safewrappers.c' object='libi3/a-safewrappers.o' libtool=no @AMDEPBACKSLASH@
2128 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2129 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-safewrappers.o `test -f 'libi3/safewrappers.c' || echo '$(srcdir)/'`libi3/safewrappers.c
2130
2131 libi3/a-safewrappers.obj: libi3/safewrappers.c
2132 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-safewrappers.obj -MD -MP -MF libi3/$(DEPDIR)/a-safewrappers.Tpo -c -o libi3/a-safewrappers.obj `if test -f 'libi3/safewrappers.c'; then $(CYGPATH_W) 'libi3/safewrappers.c'; else $(CYGPATH_W) '$(srcdir)/libi3/safewrappers.c'; fi`
2133 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-safewrappers.Tpo libi3/$(DEPDIR)/a-safewrappers.Po
2134 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/safewrappers.c' object='libi3/a-safewrappers.obj' libtool=no @AMDEPBACKSLASH@
2135 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2136 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-safewrappers.obj `if test -f 'libi3/safewrappers.c'; then $(CYGPATH_W) 'libi3/safewrappers.c'; else $(CYGPATH_W) '$(srcdir)/libi3/safewrappers.c'; fi`
2137
2138 libi3/a-string.o: libi3/string.c
2139 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-string.o -MD -MP -MF libi3/$(DEPDIR)/a-string.Tpo -c -o libi3/a-string.o `test -f 'libi3/string.c' || echo '$(srcdir)/'`libi3/string.c
2140 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-string.Tpo libi3/$(DEPDIR)/a-string.Po
2141 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/string.c' object='libi3/a-string.o' libtool=no @AMDEPBACKSLASH@
2142 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2143 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-string.o `test -f 'libi3/string.c' || echo '$(srcdir)/'`libi3/string.c
2144
2145 libi3/a-string.obj: libi3/string.c
2146 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-string.obj -MD -MP -MF libi3/$(DEPDIR)/a-string.Tpo -c -o libi3/a-string.obj `if test -f 'libi3/string.c'; then $(CYGPATH_W) 'libi3/string.c'; else $(CYGPATH_W) '$(srcdir)/libi3/string.c'; fi`
2147 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-string.Tpo libi3/$(DEPDIR)/a-string.Po
2148 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/string.c' object='libi3/a-string.obj' libtool=no @AMDEPBACKSLASH@
2149 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2150 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-string.obj `if test -f 'libi3/string.c'; then $(CYGPATH_W) 'libi3/string.c'; else $(CYGPATH_W) '$(srcdir)/libi3/string.c'; fi`
2151
2152 libi3/a-strndup.o: libi3/strndup.c
2153 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-strndup.o -MD -MP -MF libi3/$(DEPDIR)/a-strndup.Tpo -c -o libi3/a-strndup.o `test -f 'libi3/strndup.c' || echo '$(srcdir)/'`libi3/strndup.c
2154 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-strndup.Tpo libi3/$(DEPDIR)/a-strndup.Po
2155 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/strndup.c' object='libi3/a-strndup.o' libtool=no @AMDEPBACKSLASH@
2156 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2157 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-strndup.o `test -f 'libi3/strndup.c' || echo '$(srcdir)/'`libi3/strndup.c
2158
2159 libi3/a-strndup.obj: libi3/strndup.c
2160 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-strndup.obj -MD -MP -MF libi3/$(DEPDIR)/a-strndup.Tpo -c -o libi3/a-strndup.obj `if test -f 'libi3/strndup.c'; then $(CYGPATH_W) 'libi3/strndup.c'; else $(CYGPATH_W) '$(srcdir)/libi3/strndup.c'; fi`
2161 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-strndup.Tpo libi3/$(DEPDIR)/a-strndup.Po
2162 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/strndup.c' object='libi3/a-strndup.obj' libtool=no @AMDEPBACKSLASH@
2163 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2164 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-strndup.obj `if test -f 'libi3/strndup.c'; then $(CYGPATH_W) 'libi3/strndup.c'; else $(CYGPATH_W) '$(srcdir)/libi3/strndup.c'; fi`
2165
2166 libi3/a-ucs2_conversion.o: libi3/ucs2_conversion.c
2167 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ucs2_conversion.o -MD -MP -MF libi3/$(DEPDIR)/a-ucs2_conversion.Tpo -c -o libi3/a-ucs2_conversion.o `test -f 'libi3/ucs2_conversion.c' || echo '$(srcdir)/'`libi3/ucs2_conversion.c
2168 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ucs2_conversion.Tpo libi3/$(DEPDIR)/a-ucs2_conversion.Po
2169 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ucs2_conversion.c' object='libi3/a-ucs2_conversion.o' libtool=no @AMDEPBACKSLASH@
2170 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2171 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ucs2_conversion.o `test -f 'libi3/ucs2_conversion.c' || echo '$(srcdir)/'`libi3/ucs2_conversion.c
2172
2173 libi3/a-ucs2_conversion.obj: libi3/ucs2_conversion.c
2174 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -MT libi3/a-ucs2_conversion.obj -MD -MP -MF libi3/$(DEPDIR)/a-ucs2_conversion.Tpo -c -o libi3/a-ucs2_conversion.obj `if test -f 'libi3/ucs2_conversion.c'; then $(CYGPATH_W) 'libi3/ucs2_conversion.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ucs2_conversion.c'; fi`
2175 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libi3/$(DEPDIR)/a-ucs2_conversion.Tpo libi3/$(DEPDIR)/a-ucs2_conversion.Po
2176 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libi3/ucs2_conversion.c' object='libi3/a-ucs2_conversion.obj' libtool=no @AMDEPBACKSLASH@
2177 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2178 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libi3_a_CFLAGS) $(CFLAGS) -c -o libi3/a-ucs2_conversion.obj `if test -f 'libi3/ucs2_conversion.c'; then $(CYGPATH_W) 'libi3/ucs2_conversion.c'; else $(CYGPATH_W) '$(srcdir)/libi3/ucs2_conversion.c'; fi`
2179
2180 src/i3-assignments.o: src/assignments.c
2181 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-assignments.o -MD -MP -MF src/$(DEPDIR)/i3-assignments.Tpo -c -o src/i3-assignments.o `test -f 'src/assignments.c' || echo '$(srcdir)/'`src/assignments.c
2182 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-assignments.Tpo src/$(DEPDIR)/i3-assignments.Po
2183 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/assignments.c' object='src/i3-assignments.o' libtool=no @AMDEPBACKSLASH@
2184 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2185 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-assignments.o `test -f 'src/assignments.c' || echo '$(srcdir)/'`src/assignments.c
2186
2187 src/i3-assignments.obj: src/assignments.c
2188 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-assignments.obj -MD -MP -MF src/$(DEPDIR)/i3-assignments.Tpo -c -o src/i3-assignments.obj `if test -f 'src/assignments.c'; then $(CYGPATH_W) 'src/assignments.c'; else $(CYGPATH_W) '$(srcdir)/src/assignments.c'; fi`
2189 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-assignments.Tpo src/$(DEPDIR)/i3-assignments.Po
2190 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/assignments.c' object='src/i3-assignments.obj' libtool=no @AMDEPBACKSLASH@
2191 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2192 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-assignments.obj `if test -f 'src/assignments.c'; then $(CYGPATH_W) 'src/assignments.c'; else $(CYGPATH_W) '$(srcdir)/src/assignments.c'; fi`
2193
2194 src/i3-bindings.o: src/bindings.c
2195 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-bindings.o -MD -MP -MF src/$(DEPDIR)/i3-bindings.Tpo -c -o src/i3-bindings.o `test -f 'src/bindings.c' || echo '$(srcdir)/'`src/bindings.c
2196 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-bindings.Tpo src/$(DEPDIR)/i3-bindings.Po
2197 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/bindings.c' object='src/i3-bindings.o' libtool=no @AMDEPBACKSLASH@
2198 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2199 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-bindings.o `test -f 'src/bindings.c' || echo '$(srcdir)/'`src/bindings.c
2200
2201 src/i3-bindings.obj: src/bindings.c
2202 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-bindings.obj -MD -MP -MF src/$(DEPDIR)/i3-bindings.Tpo -c -o src/i3-bindings.obj `if test -f 'src/bindings.c'; then $(CYGPATH_W) 'src/bindings.c'; else $(CYGPATH_W) '$(srcdir)/src/bindings.c'; fi`
2203 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-bindings.Tpo src/$(DEPDIR)/i3-bindings.Po
2204 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/bindings.c' object='src/i3-bindings.obj' libtool=no @AMDEPBACKSLASH@
2205 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2206 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-bindings.obj `if test -f 'src/bindings.c'; then $(CYGPATH_W) 'src/bindings.c'; else $(CYGPATH_W) '$(srcdir)/src/bindings.c'; fi`
2207
2208 src/i3-click.o: src/click.c
2209 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-click.o -MD -MP -MF src/$(DEPDIR)/i3-click.Tpo -c -o src/i3-click.o `test -f 'src/click.c' || echo '$(srcdir)/'`src/click.c
2210 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-click.Tpo src/$(DEPDIR)/i3-click.Po
2211 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/click.c' object='src/i3-click.o' libtool=no @AMDEPBACKSLASH@
2212 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2213 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-click.o `test -f 'src/click.c' || echo '$(srcdir)/'`src/click.c
2214
2215 src/i3-click.obj: src/click.c
2216 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-click.obj -MD -MP -MF src/$(DEPDIR)/i3-click.Tpo -c -o src/i3-click.obj `if test -f 'src/click.c'; then $(CYGPATH_W) 'src/click.c'; else $(CYGPATH_W) '$(srcdir)/src/click.c'; fi`
2217 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-click.Tpo src/$(DEPDIR)/i3-click.Po
2218 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/click.c' object='src/i3-click.obj' libtool=no @AMDEPBACKSLASH@
2219 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2220 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-click.obj `if test -f 'src/click.c'; then $(CYGPATH_W) 'src/click.c'; else $(CYGPATH_W) '$(srcdir)/src/click.c'; fi`
2221
2222 src/i3-commands.o: src/commands.c
2223 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-commands.o -MD -MP -MF src/$(DEPDIR)/i3-commands.Tpo -c -o src/i3-commands.o `test -f 'src/commands.c' || echo '$(srcdir)/'`src/commands.c
2224 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-commands.Tpo src/$(DEPDIR)/i3-commands.Po
2225 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/commands.c' object='src/i3-commands.o' libtool=no @AMDEPBACKSLASH@
2226 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2227 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-commands.o `test -f 'src/commands.c' || echo '$(srcdir)/'`src/commands.c
2228
2229 src/i3-commands.obj: src/commands.c
2230 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-commands.obj -MD -MP -MF src/$(DEPDIR)/i3-commands.Tpo -c -o src/i3-commands.obj `if test -f 'src/commands.c'; then $(CYGPATH_W) 'src/commands.c'; else $(CYGPATH_W) '$(srcdir)/src/commands.c'; fi`
2231 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-commands.Tpo src/$(DEPDIR)/i3-commands.Po
2232 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/commands.c' object='src/i3-commands.obj' libtool=no @AMDEPBACKSLASH@
2233 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2234 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-commands.obj `if test -f 'src/commands.c'; then $(CYGPATH_W) 'src/commands.c'; else $(CYGPATH_W) '$(srcdir)/src/commands.c'; fi`
2235
2236 src/i3-commands_parser.o: src/commands_parser.c
2237 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-commands_parser.o -MD -MP -MF src/$(DEPDIR)/i3-commands_parser.Tpo -c -o src/i3-commands_parser.o `test -f 'src/commands_parser.c' || echo '$(srcdir)/'`src/commands_parser.c
2238 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-commands_parser.Tpo src/$(DEPDIR)/i3-commands_parser.Po
2239 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/commands_parser.c' object='src/i3-commands_parser.o' libtool=no @AMDEPBACKSLASH@
2240 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2241 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-commands_parser.o `test -f 'src/commands_parser.c' || echo '$(srcdir)/'`src/commands_parser.c
2242
2243 src/i3-commands_parser.obj: src/commands_parser.c
2244 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-commands_parser.obj -MD -MP -MF src/$(DEPDIR)/i3-commands_parser.Tpo -c -o src/i3-commands_parser.obj `if test -f 'src/commands_parser.c'; then $(CYGPATH_W) 'src/commands_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/commands_parser.c'; fi`
2245 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-commands_parser.Tpo src/$(DEPDIR)/i3-commands_parser.Po
2246 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/commands_parser.c' object='src/i3-commands_parser.obj' libtool=no @AMDEPBACKSLASH@
2247 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2248 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-commands_parser.obj `if test -f 'src/commands_parser.c'; then $(CYGPATH_W) 'src/commands_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/commands_parser.c'; fi`
2249
2250 src/i3-con.o: src/con.c
2251 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-con.o -MD -MP -MF src/$(DEPDIR)/i3-con.Tpo -c -o src/i3-con.o `test -f 'src/con.c' || echo '$(srcdir)/'`src/con.c
2252 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-con.Tpo src/$(DEPDIR)/i3-con.Po
2253 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/con.c' object='src/i3-con.o' libtool=no @AMDEPBACKSLASH@
2254 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2255 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-con.o `test -f 'src/con.c' || echo '$(srcdir)/'`src/con.c
2256
2257 src/i3-con.obj: src/con.c
2258 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-con.obj -MD -MP -MF src/$(DEPDIR)/i3-con.Tpo -c -o src/i3-con.obj `if test -f 'src/con.c'; then $(CYGPATH_W) 'src/con.c'; else $(CYGPATH_W) '$(srcdir)/src/con.c'; fi`
2259 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-con.Tpo src/$(DEPDIR)/i3-con.Po
2260 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/con.c' object='src/i3-con.obj' libtool=no @AMDEPBACKSLASH@
2261 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2262 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-con.obj `if test -f 'src/con.c'; then $(CYGPATH_W) 'src/con.c'; else $(CYGPATH_W) '$(srcdir)/src/con.c'; fi`
2263
2264 src/i3-config.o: src/config.c
2265 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-config.o -MD -MP -MF src/$(DEPDIR)/i3-config.Tpo -c -o src/i3-config.o `test -f 'src/config.c' || echo '$(srcdir)/'`src/config.c
2266 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-config.Tpo src/$(DEPDIR)/i3-config.Po
2267 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config.c' object='src/i3-config.o' libtool=no @AMDEPBACKSLASH@
2268 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2269 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-config.o `test -f 'src/config.c' || echo '$(srcdir)/'`src/config.c
2270
2271 src/i3-config.obj: src/config.c
2272 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-config.obj -MD -MP -MF src/$(DEPDIR)/i3-config.Tpo -c -o src/i3-config.obj `if test -f 'src/config.c'; then $(CYGPATH_W) 'src/config.c'; else $(CYGPATH_W) '$(srcdir)/src/config.c'; fi`
2273 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-config.Tpo src/$(DEPDIR)/i3-config.Po
2274 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config.c' object='src/i3-config.obj' libtool=no @AMDEPBACKSLASH@
2275 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2276 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-config.obj `if test -f 'src/config.c'; then $(CYGPATH_W) 'src/config.c'; else $(CYGPATH_W) '$(srcdir)/src/config.c'; fi`
2277
2278 src/i3-config_directives.o: src/config_directives.c
2279 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-config_directives.o -MD -MP -MF src/$(DEPDIR)/i3-config_directives.Tpo -c -o src/i3-config_directives.o `test -f 'src/config_directives.c' || echo '$(srcdir)/'`src/config_directives.c
2280 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-config_directives.Tpo src/$(DEPDIR)/i3-config_directives.Po
2281 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config_directives.c' object='src/i3-config_directives.o' libtool=no @AMDEPBACKSLASH@
2282 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2283 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-config_directives.o `test -f 'src/config_directives.c' || echo '$(srcdir)/'`src/config_directives.c
2284
2285 src/i3-config_directives.obj: src/config_directives.c
2286 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-config_directives.obj -MD -MP -MF src/$(DEPDIR)/i3-config_directives.Tpo -c -o src/i3-config_directives.obj `if test -f 'src/config_directives.c'; then $(CYGPATH_W) 'src/config_directives.c'; else $(CYGPATH_W) '$(srcdir)/src/config_directives.c'; fi`
2287 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-config_directives.Tpo src/$(DEPDIR)/i3-config_directives.Po
2288 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config_directives.c' object='src/i3-config_directives.obj' libtool=no @AMDEPBACKSLASH@
2289 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2290 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-config_directives.obj `if test -f 'src/config_directives.c'; then $(CYGPATH_W) 'src/config_directives.c'; else $(CYGPATH_W) '$(srcdir)/src/config_directives.c'; fi`
2291
2292 src/i3-config_parser.o: src/config_parser.c
2293 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-config_parser.o -MD -MP -MF src/$(DEPDIR)/i3-config_parser.Tpo -c -o src/i3-config_parser.o `test -f 'src/config_parser.c' || echo '$(srcdir)/'`src/config_parser.c
2294 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-config_parser.Tpo src/$(DEPDIR)/i3-config_parser.Po
2295 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config_parser.c' object='src/i3-config_parser.o' libtool=no @AMDEPBACKSLASH@
2296 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2297 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-config_parser.o `test -f 'src/config_parser.c' || echo '$(srcdir)/'`src/config_parser.c
2298
2299 src/i3-config_parser.obj: src/config_parser.c
2300 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-config_parser.obj -MD -MP -MF src/$(DEPDIR)/i3-config_parser.Tpo -c -o src/i3-config_parser.obj `if test -f 'src/config_parser.c'; then $(CYGPATH_W) 'src/config_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/config_parser.c'; fi`
2301 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-config_parser.Tpo src/$(DEPDIR)/i3-config_parser.Po
2302 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config_parser.c' object='src/i3-config_parser.obj' libtool=no @AMDEPBACKSLASH@
2303 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2304 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-config_parser.obj `if test -f 'src/config_parser.c'; then $(CYGPATH_W) 'src/config_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/config_parser.c'; fi`
2305
2306 src/i3-display_version.o: src/display_version.c
2307 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-display_version.o -MD -MP -MF src/$(DEPDIR)/i3-display_version.Tpo -c -o src/i3-display_version.o `test -f 'src/display_version.c' || echo '$(srcdir)/'`src/display_version.c
2308 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-display_version.Tpo src/$(DEPDIR)/i3-display_version.Po
2309 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/display_version.c' object='src/i3-display_version.o' libtool=no @AMDEPBACKSLASH@
2310 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2311 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-display_version.o `test -f 'src/display_version.c' || echo '$(srcdir)/'`src/display_version.c
2312
2313 src/i3-display_version.obj: src/display_version.c
2314 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-display_version.obj -MD -MP -MF src/$(DEPDIR)/i3-display_version.Tpo -c -o src/i3-display_version.obj `if test -f 'src/display_version.c'; then $(CYGPATH_W) 'src/display_version.c'; else $(CYGPATH_W) '$(srcdir)/src/display_version.c'; fi`
2315 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-display_version.Tpo src/$(DEPDIR)/i3-display_version.Po
2316 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/display_version.c' object='src/i3-display_version.obj' libtool=no @AMDEPBACKSLASH@
2317 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2318 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-display_version.obj `if test -f 'src/display_version.c'; then $(CYGPATH_W) 'src/display_version.c'; else $(CYGPATH_W) '$(srcdir)/src/display_version.c'; fi`
2319
2320 src/i3-ewmh.o: src/ewmh.c
2321 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-ewmh.o -MD -MP -MF src/$(DEPDIR)/i3-ewmh.Tpo -c -o src/i3-ewmh.o `test -f 'src/ewmh.c' || echo '$(srcdir)/'`src/ewmh.c
2322 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-ewmh.Tpo src/$(DEPDIR)/i3-ewmh.Po
2323 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ewmh.c' object='src/i3-ewmh.o' libtool=no @AMDEPBACKSLASH@
2324 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2325 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-ewmh.o `test -f 'src/ewmh.c' || echo '$(srcdir)/'`src/ewmh.c
2326
2327 src/i3-ewmh.obj: src/ewmh.c
2328 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-ewmh.obj -MD -MP -MF src/$(DEPDIR)/i3-ewmh.Tpo -c -o src/i3-ewmh.obj `if test -f 'src/ewmh.c'; then $(CYGPATH_W) 'src/ewmh.c'; else $(CYGPATH_W) '$(srcdir)/src/ewmh.c'; fi`
2329 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-ewmh.Tpo src/$(DEPDIR)/i3-ewmh.Po
2330 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ewmh.c' object='src/i3-ewmh.obj' libtool=no @AMDEPBACKSLASH@
2331 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2332 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-ewmh.obj `if test -f 'src/ewmh.c'; then $(CYGPATH_W) 'src/ewmh.c'; else $(CYGPATH_W) '$(srcdir)/src/ewmh.c'; fi`
2333
2334 src/i3-fake_outputs.o: src/fake_outputs.c
2335 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-fake_outputs.o -MD -MP -MF src/$(DEPDIR)/i3-fake_outputs.Tpo -c -o src/i3-fake_outputs.o `test -f 'src/fake_outputs.c' || echo '$(srcdir)/'`src/fake_outputs.c
2336 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-fake_outputs.Tpo src/$(DEPDIR)/i3-fake_outputs.Po
2337 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/fake_outputs.c' object='src/i3-fake_outputs.o' libtool=no @AMDEPBACKSLASH@
2338 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2339 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-fake_outputs.o `test -f 'src/fake_outputs.c' || echo '$(srcdir)/'`src/fake_outputs.c
2340
2341 src/i3-fake_outputs.obj: src/fake_outputs.c
2342 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-fake_outputs.obj -MD -MP -MF src/$(DEPDIR)/i3-fake_outputs.Tpo -c -o src/i3-fake_outputs.obj `if test -f 'src/fake_outputs.c'; then $(CYGPATH_W) 'src/fake_outputs.c'; else $(CYGPATH_W) '$(srcdir)/src/fake_outputs.c'; fi`
2343 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-fake_outputs.Tpo src/$(DEPDIR)/i3-fake_outputs.Po
2344 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/fake_outputs.c' object='src/i3-fake_outputs.obj' libtool=no @AMDEPBACKSLASH@
2345 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2346 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-fake_outputs.obj `if test -f 'src/fake_outputs.c'; then $(CYGPATH_W) 'src/fake_outputs.c'; else $(CYGPATH_W) '$(srcdir)/src/fake_outputs.c'; fi`
2347
2348 src/i3-floating.o: src/floating.c
2349 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-floating.o -MD -MP -MF src/$(DEPDIR)/i3-floating.Tpo -c -o src/i3-floating.o `test -f 'src/floating.c' || echo '$(srcdir)/'`src/floating.c
2350 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-floating.Tpo src/$(DEPDIR)/i3-floating.Po
2351 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/floating.c' object='src/i3-floating.o' libtool=no @AMDEPBACKSLASH@
2352 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2353 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-floating.o `test -f 'src/floating.c' || echo '$(srcdir)/'`src/floating.c
2354
2355 src/i3-floating.obj: src/floating.c
2356 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-floating.obj -MD -MP -MF src/$(DEPDIR)/i3-floating.Tpo -c -o src/i3-floating.obj `if test -f 'src/floating.c'; then $(CYGPATH_W) 'src/floating.c'; else $(CYGPATH_W) '$(srcdir)/src/floating.c'; fi`
2357 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-floating.Tpo src/$(DEPDIR)/i3-floating.Po
2358 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/floating.c' object='src/i3-floating.obj' libtool=no @AMDEPBACKSLASH@
2359 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2360 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-floating.obj `if test -f 'src/floating.c'; then $(CYGPATH_W) 'src/floating.c'; else $(CYGPATH_W) '$(srcdir)/src/floating.c'; fi`
2361
2362 src/i3-handlers.o: src/handlers.c
2363 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-handlers.o -MD -MP -MF src/$(DEPDIR)/i3-handlers.Tpo -c -o src/i3-handlers.o `test -f 'src/handlers.c' || echo '$(srcdir)/'`src/handlers.c
2364 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-handlers.Tpo src/$(DEPDIR)/i3-handlers.Po
2365 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/handlers.c' object='src/i3-handlers.o' libtool=no @AMDEPBACKSLASH@
2366 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2367 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-handlers.o `test -f 'src/handlers.c' || echo '$(srcdir)/'`src/handlers.c
2368
2369 src/i3-handlers.obj: src/handlers.c
2370 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-handlers.obj -MD -MP -MF src/$(DEPDIR)/i3-handlers.Tpo -c -o src/i3-handlers.obj `if test -f 'src/handlers.c'; then $(CYGPATH_W) 'src/handlers.c'; else $(CYGPATH_W) '$(srcdir)/src/handlers.c'; fi`
2371 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-handlers.Tpo src/$(DEPDIR)/i3-handlers.Po
2372 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/handlers.c' object='src/i3-handlers.obj' libtool=no @AMDEPBACKSLASH@
2373 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2374 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-handlers.obj `if test -f 'src/handlers.c'; then $(CYGPATH_W) 'src/handlers.c'; else $(CYGPATH_W) '$(srcdir)/src/handlers.c'; fi`
2375
2376 src/i3-ipc.o: src/ipc.c
2377 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-ipc.o -MD -MP -MF src/$(DEPDIR)/i3-ipc.Tpo -c -o src/i3-ipc.o `test -f 'src/ipc.c' || echo '$(srcdir)/'`src/ipc.c
2378 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-ipc.Tpo src/$(DEPDIR)/i3-ipc.Po
2379 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ipc.c' object='src/i3-ipc.o' libtool=no @AMDEPBACKSLASH@
2380 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2381 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-ipc.o `test -f 'src/ipc.c' || echo '$(srcdir)/'`src/ipc.c
2382
2383 src/i3-ipc.obj: src/ipc.c
2384 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-ipc.obj -MD -MP -MF src/$(DEPDIR)/i3-ipc.Tpo -c -o src/i3-ipc.obj `if test -f 'src/ipc.c'; then $(CYGPATH_W) 'src/ipc.c'; else $(CYGPATH_W) '$(srcdir)/src/ipc.c'; fi`
2385 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-ipc.Tpo src/$(DEPDIR)/i3-ipc.Po
2386 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ipc.c' object='src/i3-ipc.obj' libtool=no @AMDEPBACKSLASH@
2387 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2388 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-ipc.obj `if test -f 'src/ipc.c'; then $(CYGPATH_W) 'src/ipc.c'; else $(CYGPATH_W) '$(srcdir)/src/ipc.c'; fi`
2389
2390 src/i3-key_press.o: src/key_press.c
2391 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-key_press.o -MD -MP -MF src/$(DEPDIR)/i3-key_press.Tpo -c -o src/i3-key_press.o `test -f 'src/key_press.c' || echo '$(srcdir)/'`src/key_press.c
2392 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-key_press.Tpo src/$(DEPDIR)/i3-key_press.Po
2393 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/key_press.c' object='src/i3-key_press.o' libtool=no @AMDEPBACKSLASH@
2394 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2395 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-key_press.o `test -f 'src/key_press.c' || echo '$(srcdir)/'`src/key_press.c
2396
2397 src/i3-key_press.obj: src/key_press.c
2398 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-key_press.obj -MD -MP -MF src/$(DEPDIR)/i3-key_press.Tpo -c -o src/i3-key_press.obj `if test -f 'src/key_press.c'; then $(CYGPATH_W) 'src/key_press.c'; else $(CYGPATH_W) '$(srcdir)/src/key_press.c'; fi`
2399 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-key_press.Tpo src/$(DEPDIR)/i3-key_press.Po
2400 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/key_press.c' object='src/i3-key_press.obj' libtool=no @AMDEPBACKSLASH@
2401 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2402 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-key_press.obj `if test -f 'src/key_press.c'; then $(CYGPATH_W) 'src/key_press.c'; else $(CYGPATH_W) '$(srcdir)/src/key_press.c'; fi`
2403
2404 src/i3-load_layout.o: src/load_layout.c
2405 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-load_layout.o -MD -MP -MF src/$(DEPDIR)/i3-load_layout.Tpo -c -o src/i3-load_layout.o `test -f 'src/load_layout.c' || echo '$(srcdir)/'`src/load_layout.c
2406 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-load_layout.Tpo src/$(DEPDIR)/i3-load_layout.Po
2407 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/load_layout.c' object='src/i3-load_layout.o' libtool=no @AMDEPBACKSLASH@
2408 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2409 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-load_layout.o `test -f 'src/load_layout.c' || echo '$(srcdir)/'`src/load_layout.c
2410
2411 src/i3-load_layout.obj: src/load_layout.c
2412 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-load_layout.obj -MD -MP -MF src/$(DEPDIR)/i3-load_layout.Tpo -c -o src/i3-load_layout.obj `if test -f 'src/load_layout.c'; then $(CYGPATH_W) 'src/load_layout.c'; else $(CYGPATH_W) '$(srcdir)/src/load_layout.c'; fi`
2413 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-load_layout.Tpo src/$(DEPDIR)/i3-load_layout.Po
2414 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/load_layout.c' object='src/i3-load_layout.obj' libtool=no @AMDEPBACKSLASH@
2415 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2416 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-load_layout.obj `if test -f 'src/load_layout.c'; then $(CYGPATH_W) 'src/load_layout.c'; else $(CYGPATH_W) '$(srcdir)/src/load_layout.c'; fi`
2417
2418 src/i3-log.o: src/log.c
2419 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-log.o -MD -MP -MF src/$(DEPDIR)/i3-log.Tpo -c -o src/i3-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
2420 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-log.Tpo src/$(DEPDIR)/i3-log.Po
2421 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/log.c' object='src/i3-log.o' libtool=no @AMDEPBACKSLASH@
2422 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2423 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
2424
2425 src/i3-log.obj: src/log.c
2426 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-log.obj -MD -MP -MF src/$(DEPDIR)/i3-log.Tpo -c -o src/i3-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi`
2427 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-log.Tpo src/$(DEPDIR)/i3-log.Po
2428 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/log.c' object='src/i3-log.obj' libtool=no @AMDEPBACKSLASH@
2429 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2430 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi`
2431
2432 src/i3-main.o: src/main.c
2433 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-main.o -MD -MP -MF src/$(DEPDIR)/i3-main.Tpo -c -o src/i3-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c
2434 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-main.Tpo src/$(DEPDIR)/i3-main.Po
2435 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/main.c' object='src/i3-main.o' libtool=no @AMDEPBACKSLASH@
2436 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2437 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c
2438
2439 src/i3-main.obj: src/main.c
2440 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-main.obj -MD -MP -MF src/$(DEPDIR)/i3-main.Tpo -c -o src/i3-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi`
2441 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-main.Tpo src/$(DEPDIR)/i3-main.Po
2442 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/main.c' object='src/i3-main.obj' libtool=no @AMDEPBACKSLASH@
2443 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2444 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi`
2445
2446 src/i3-manage.o: src/manage.c
2447 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-manage.o -MD -MP -MF src/$(DEPDIR)/i3-manage.Tpo -c -o src/i3-manage.o `test -f 'src/manage.c' || echo '$(srcdir)/'`src/manage.c
2448 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-manage.Tpo src/$(DEPDIR)/i3-manage.Po
2449 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/manage.c' object='src/i3-manage.o' libtool=no @AMDEPBACKSLASH@
2450 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2451 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-manage.o `test -f 'src/manage.c' || echo '$(srcdir)/'`src/manage.c
2452
2453 src/i3-manage.obj: src/manage.c
2454 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-manage.obj -MD -MP -MF src/$(DEPDIR)/i3-manage.Tpo -c -o src/i3-manage.obj `if test -f 'src/manage.c'; then $(CYGPATH_W) 'src/manage.c'; else $(CYGPATH_W) '$(srcdir)/src/manage.c'; fi`
2455 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-manage.Tpo src/$(DEPDIR)/i3-manage.Po
2456 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/manage.c' object='src/i3-manage.obj' libtool=no @AMDEPBACKSLASH@
2457 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2458 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-manage.obj `if test -f 'src/manage.c'; then $(CYGPATH_W) 'src/manage.c'; else $(CYGPATH_W) '$(srcdir)/src/manage.c'; fi`
2459
2460 src/i3-match.o: src/match.c
2461 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-match.o -MD -MP -MF src/$(DEPDIR)/i3-match.Tpo -c -o src/i3-match.o `test -f 'src/match.c' || echo '$(srcdir)/'`src/match.c
2462 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-match.Tpo src/$(DEPDIR)/i3-match.Po
2463 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/match.c' object='src/i3-match.o' libtool=no @AMDEPBACKSLASH@
2464 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2465 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-match.o `test -f 'src/match.c' || echo '$(srcdir)/'`src/match.c
2466
2467 src/i3-match.obj: src/match.c
2468 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-match.obj -MD -MP -MF src/$(DEPDIR)/i3-match.Tpo -c -o src/i3-match.obj `if test -f 'src/match.c'; then $(CYGPATH_W) 'src/match.c'; else $(CYGPATH_W) '$(srcdir)/src/match.c'; fi`
2469 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-match.Tpo src/$(DEPDIR)/i3-match.Po
2470 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/match.c' object='src/i3-match.obj' libtool=no @AMDEPBACKSLASH@
2471 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2472 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-match.obj `if test -f 'src/match.c'; then $(CYGPATH_W) 'src/match.c'; else $(CYGPATH_W) '$(srcdir)/src/match.c'; fi`
2473
2474 src/i3-move.o: src/move.c
2475 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-move.o -MD -MP -MF src/$(DEPDIR)/i3-move.Tpo -c -o src/i3-move.o `test -f 'src/move.c' || echo '$(srcdir)/'`src/move.c
2476 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-move.Tpo src/$(DEPDIR)/i3-move.Po
2477 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/move.c' object='src/i3-move.o' libtool=no @AMDEPBACKSLASH@
2478 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2479 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-move.o `test -f 'src/move.c' || echo '$(srcdir)/'`src/move.c
2480
2481 src/i3-move.obj: src/move.c
2482 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-move.obj -MD -MP -MF src/$(DEPDIR)/i3-move.Tpo -c -o src/i3-move.obj `if test -f 'src/move.c'; then $(CYGPATH_W) 'src/move.c'; else $(CYGPATH_W) '$(srcdir)/src/move.c'; fi`
2483 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-move.Tpo src/$(DEPDIR)/i3-move.Po
2484 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/move.c' object='src/i3-move.obj' libtool=no @AMDEPBACKSLASH@
2485 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2486 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-move.obj `if test -f 'src/move.c'; then $(CYGPATH_W) 'src/move.c'; else $(CYGPATH_W) '$(srcdir)/src/move.c'; fi`
2487
2488 src/i3-output.o: src/output.c
2489 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-output.o -MD -MP -MF src/$(DEPDIR)/i3-output.Tpo -c -o src/i3-output.o `test -f 'src/output.c' || echo '$(srcdir)/'`src/output.c
2490 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-output.Tpo src/$(DEPDIR)/i3-output.Po
2491 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/output.c' object='src/i3-output.o' libtool=no @AMDEPBACKSLASH@
2492 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2493 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-output.o `test -f 'src/output.c' || echo '$(srcdir)/'`src/output.c
2494
2495 src/i3-output.obj: src/output.c
2496 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-output.obj -MD -MP -MF src/$(DEPDIR)/i3-output.Tpo -c -o src/i3-output.obj `if test -f 'src/output.c'; then $(CYGPATH_W) 'src/output.c'; else $(CYGPATH_W) '$(srcdir)/src/output.c'; fi`
2497 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-output.Tpo src/$(DEPDIR)/i3-output.Po
2498 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/output.c' object='src/i3-output.obj' libtool=no @AMDEPBACKSLASH@
2499 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2500 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-output.obj `if test -f 'src/output.c'; then $(CYGPATH_W) 'src/output.c'; else $(CYGPATH_W) '$(srcdir)/src/output.c'; fi`
2501
2502 src/i3-randr.o: src/randr.c
2503 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-randr.o -MD -MP -MF src/$(DEPDIR)/i3-randr.Tpo -c -o src/i3-randr.o `test -f 'src/randr.c' || echo '$(srcdir)/'`src/randr.c
2504 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-randr.Tpo src/$(DEPDIR)/i3-randr.Po
2505 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/randr.c' object='src/i3-randr.o' libtool=no @AMDEPBACKSLASH@
2506 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2507 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-randr.o `test -f 'src/randr.c' || echo '$(srcdir)/'`src/randr.c
2508
2509 src/i3-randr.obj: src/randr.c
2510 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-randr.obj -MD -MP -MF src/$(DEPDIR)/i3-randr.Tpo -c -o src/i3-randr.obj `if test -f 'src/randr.c'; then $(CYGPATH_W) 'src/randr.c'; else $(CYGPATH_W) '$(srcdir)/src/randr.c'; fi`
2511 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-randr.Tpo src/$(DEPDIR)/i3-randr.Po
2512 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/randr.c' object='src/i3-randr.obj' libtool=no @AMDEPBACKSLASH@
2513 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2514 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-randr.obj `if test -f 'src/randr.c'; then $(CYGPATH_W) 'src/randr.c'; else $(CYGPATH_W) '$(srcdir)/src/randr.c'; fi`
2515
2516 src/i3-regex.o: src/regex.c
2517 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-regex.o -MD -MP -MF src/$(DEPDIR)/i3-regex.Tpo -c -o src/i3-regex.o `test -f 'src/regex.c' || echo '$(srcdir)/'`src/regex.c
2518 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-regex.Tpo src/$(DEPDIR)/i3-regex.Po
2519 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/regex.c' object='src/i3-regex.o' libtool=no @AMDEPBACKSLASH@
2520 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2521 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-regex.o `test -f 'src/regex.c' || echo '$(srcdir)/'`src/regex.c
2522
2523 src/i3-regex.obj: src/regex.c
2524 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-regex.obj -MD -MP -MF src/$(DEPDIR)/i3-regex.Tpo -c -o src/i3-regex.obj `if test -f 'src/regex.c'; then $(CYGPATH_W) 'src/regex.c'; else $(CYGPATH_W) '$(srcdir)/src/regex.c'; fi`
2525 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-regex.Tpo src/$(DEPDIR)/i3-regex.Po
2526 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/regex.c' object='src/i3-regex.obj' libtool=no @AMDEPBACKSLASH@
2527 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2528 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-regex.obj `if test -f 'src/regex.c'; then $(CYGPATH_W) 'src/regex.c'; else $(CYGPATH_W) '$(srcdir)/src/regex.c'; fi`
2529
2530 src/i3-render.o: src/render.c
2531 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-render.o -MD -MP -MF src/$(DEPDIR)/i3-render.Tpo -c -o src/i3-render.o `test -f 'src/render.c' || echo '$(srcdir)/'`src/render.c
2532 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-render.Tpo src/$(DEPDIR)/i3-render.Po
2533 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/render.c' object='src/i3-render.o' libtool=no @AMDEPBACKSLASH@
2534 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2535 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-render.o `test -f 'src/render.c' || echo '$(srcdir)/'`src/render.c
2536
2537 src/i3-render.obj: src/render.c
2538 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-render.obj -MD -MP -MF src/$(DEPDIR)/i3-render.Tpo -c -o src/i3-render.obj `if test -f 'src/render.c'; then $(CYGPATH_W) 'src/render.c'; else $(CYGPATH_W) '$(srcdir)/src/render.c'; fi`
2539 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-render.Tpo src/$(DEPDIR)/i3-render.Po
2540 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/render.c' object='src/i3-render.obj' libtool=no @AMDEPBACKSLASH@
2541 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2542 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-render.obj `if test -f 'src/render.c'; then $(CYGPATH_W) 'src/render.c'; else $(CYGPATH_W) '$(srcdir)/src/render.c'; fi`
2543
2544 src/i3-resize.o: src/resize.c
2545 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-resize.o -MD -MP -MF src/$(DEPDIR)/i3-resize.Tpo -c -o src/i3-resize.o `test -f 'src/resize.c' || echo '$(srcdir)/'`src/resize.c
2546 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-resize.Tpo src/$(DEPDIR)/i3-resize.Po
2547 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/resize.c' object='src/i3-resize.o' libtool=no @AMDEPBACKSLASH@
2548 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2549 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-resize.o `test -f 'src/resize.c' || echo '$(srcdir)/'`src/resize.c
2550
2551 src/i3-resize.obj: src/resize.c
2552 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-resize.obj -MD -MP -MF src/$(DEPDIR)/i3-resize.Tpo -c -o src/i3-resize.obj `if test -f 'src/resize.c'; then $(CYGPATH_W) 'src/resize.c'; else $(CYGPATH_W) '$(srcdir)/src/resize.c'; fi`
2553 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-resize.Tpo src/$(DEPDIR)/i3-resize.Po
2554 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/resize.c' object='src/i3-resize.obj' libtool=no @AMDEPBACKSLASH@
2555 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2556 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-resize.obj `if test -f 'src/resize.c'; then $(CYGPATH_W) 'src/resize.c'; else $(CYGPATH_W) '$(srcdir)/src/resize.c'; fi`
2557
2558 src/i3-restore_layout.o: src/restore_layout.c
2559 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-restore_layout.o -MD -MP -MF src/$(DEPDIR)/i3-restore_layout.Tpo -c -o src/i3-restore_layout.o `test -f 'src/restore_layout.c' || echo '$(srcdir)/'`src/restore_layout.c
2560 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-restore_layout.Tpo src/$(DEPDIR)/i3-restore_layout.Po
2561 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/restore_layout.c' object='src/i3-restore_layout.o' libtool=no @AMDEPBACKSLASH@
2562 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2563 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-restore_layout.o `test -f 'src/restore_layout.c' || echo '$(srcdir)/'`src/restore_layout.c
2564
2565 src/i3-restore_layout.obj: src/restore_layout.c
2566 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-restore_layout.obj -MD -MP -MF src/$(DEPDIR)/i3-restore_layout.Tpo -c -o src/i3-restore_layout.obj `if test -f 'src/restore_layout.c'; then $(CYGPATH_W) 'src/restore_layout.c'; else $(CYGPATH_W) '$(srcdir)/src/restore_layout.c'; fi`
2567 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-restore_layout.Tpo src/$(DEPDIR)/i3-restore_layout.Po
2568 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/restore_layout.c' object='src/i3-restore_layout.obj' libtool=no @AMDEPBACKSLASH@
2569 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2570 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-restore_layout.obj `if test -f 'src/restore_layout.c'; then $(CYGPATH_W) 'src/restore_layout.c'; else $(CYGPATH_W) '$(srcdir)/src/restore_layout.c'; fi`
2571
2572 src/i3-scratchpad.o: src/scratchpad.c
2573 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-scratchpad.o -MD -MP -MF src/$(DEPDIR)/i3-scratchpad.Tpo -c -o src/i3-scratchpad.o `test -f 'src/scratchpad.c' || echo '$(srcdir)/'`src/scratchpad.c
2574 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-scratchpad.Tpo src/$(DEPDIR)/i3-scratchpad.Po
2575 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/scratchpad.c' object='src/i3-scratchpad.o' libtool=no @AMDEPBACKSLASH@
2576 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2577 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-scratchpad.o `test -f 'src/scratchpad.c' || echo '$(srcdir)/'`src/scratchpad.c
2578
2579 src/i3-scratchpad.obj: src/scratchpad.c
2580 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-scratchpad.obj -MD -MP -MF src/$(DEPDIR)/i3-scratchpad.Tpo -c -o src/i3-scratchpad.obj `if test -f 'src/scratchpad.c'; then $(CYGPATH_W) 'src/scratchpad.c'; else $(CYGPATH_W) '$(srcdir)/src/scratchpad.c'; fi`
2581 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-scratchpad.Tpo src/$(DEPDIR)/i3-scratchpad.Po
2582 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/scratchpad.c' object='src/i3-scratchpad.obj' libtool=no @AMDEPBACKSLASH@
2583 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2584 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-scratchpad.obj `if test -f 'src/scratchpad.c'; then $(CYGPATH_W) 'src/scratchpad.c'; else $(CYGPATH_W) '$(srcdir)/src/scratchpad.c'; fi`
2585
2586 src/i3-sd-daemon.o: src/sd-daemon.c
2587 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-sd-daemon.o -MD -MP -MF src/$(DEPDIR)/i3-sd-daemon.Tpo -c -o src/i3-sd-daemon.o `test -f 'src/sd-daemon.c' || echo '$(srcdir)/'`src/sd-daemon.c
2588 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-sd-daemon.Tpo src/$(DEPDIR)/i3-sd-daemon.Po
2589 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sd-daemon.c' object='src/i3-sd-daemon.o' libtool=no @AMDEPBACKSLASH@
2590 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2591 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-sd-daemon.o `test -f 'src/sd-daemon.c' || echo '$(srcdir)/'`src/sd-daemon.c
2592
2593 src/i3-sd-daemon.obj: src/sd-daemon.c
2594 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-sd-daemon.obj -MD -MP -MF src/$(DEPDIR)/i3-sd-daemon.Tpo -c -o src/i3-sd-daemon.obj `if test -f 'src/sd-daemon.c'; then $(CYGPATH_W) 'src/sd-daemon.c'; else $(CYGPATH_W) '$(srcdir)/src/sd-daemon.c'; fi`
2595 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-sd-daemon.Tpo src/$(DEPDIR)/i3-sd-daemon.Po
2596 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sd-daemon.c' object='src/i3-sd-daemon.obj' libtool=no @AMDEPBACKSLASH@
2597 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2598 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-sd-daemon.obj `if test -f 'src/sd-daemon.c'; then $(CYGPATH_W) 'src/sd-daemon.c'; else $(CYGPATH_W) '$(srcdir)/src/sd-daemon.c'; fi`
2599
2600 src/i3-sighandler.o: src/sighandler.c
2601 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-sighandler.o -MD -MP -MF src/$(DEPDIR)/i3-sighandler.Tpo -c -o src/i3-sighandler.o `test -f 'src/sighandler.c' || echo '$(srcdir)/'`src/sighandler.c
2602 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-sighandler.Tpo src/$(DEPDIR)/i3-sighandler.Po
2603 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sighandler.c' object='src/i3-sighandler.o' libtool=no @AMDEPBACKSLASH@
2604 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2605 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-sighandler.o `test -f 'src/sighandler.c' || echo '$(srcdir)/'`src/sighandler.c
2606
2607 src/i3-sighandler.obj: src/sighandler.c
2608 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-sighandler.obj -MD -MP -MF src/$(DEPDIR)/i3-sighandler.Tpo -c -o src/i3-sighandler.obj `if test -f 'src/sighandler.c'; then $(CYGPATH_W) 'src/sighandler.c'; else $(CYGPATH_W) '$(srcdir)/src/sighandler.c'; fi`
2609 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-sighandler.Tpo src/$(DEPDIR)/i3-sighandler.Po
2610 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sighandler.c' object='src/i3-sighandler.obj' libtool=no @AMDEPBACKSLASH@
2611 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2612 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-sighandler.obj `if test -f 'src/sighandler.c'; then $(CYGPATH_W) 'src/sighandler.c'; else $(CYGPATH_W) '$(srcdir)/src/sighandler.c'; fi`
2613
2614 src/i3-startup.o: src/startup.c
2615 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-startup.o -MD -MP -MF src/$(DEPDIR)/i3-startup.Tpo -c -o src/i3-startup.o `test -f 'src/startup.c' || echo '$(srcdir)/'`src/startup.c
2616 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-startup.Tpo src/$(DEPDIR)/i3-startup.Po
2617 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/startup.c' object='src/i3-startup.o' libtool=no @AMDEPBACKSLASH@
2618 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2619 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-startup.o `test -f 'src/startup.c' || echo '$(srcdir)/'`src/startup.c
2620
2621 src/i3-startup.obj: src/startup.c
2622 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-startup.obj -MD -MP -MF src/$(DEPDIR)/i3-startup.Tpo -c -o src/i3-startup.obj `if test -f 'src/startup.c'; then $(CYGPATH_W) 'src/startup.c'; else $(CYGPATH_W) '$(srcdir)/src/startup.c'; fi`
2623 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-startup.Tpo src/$(DEPDIR)/i3-startup.Po
2624 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/startup.c' object='src/i3-startup.obj' libtool=no @AMDEPBACKSLASH@
2625 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2626 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-startup.obj `if test -f 'src/startup.c'; then $(CYGPATH_W) 'src/startup.c'; else $(CYGPATH_W) '$(srcdir)/src/startup.c'; fi`
2627
2628 src/i3-sync.o: src/sync.c
2629 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-sync.o -MD -MP -MF src/$(DEPDIR)/i3-sync.Tpo -c -o src/i3-sync.o `test -f 'src/sync.c' || echo '$(srcdir)/'`src/sync.c
2630 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-sync.Tpo src/$(DEPDIR)/i3-sync.Po
2631 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sync.c' object='src/i3-sync.o' libtool=no @AMDEPBACKSLASH@
2632 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2633 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-sync.o `test -f 'src/sync.c' || echo '$(srcdir)/'`src/sync.c
2634
2635 src/i3-sync.obj: src/sync.c
2636 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-sync.obj -MD -MP -MF src/$(DEPDIR)/i3-sync.Tpo -c -o src/i3-sync.obj `if test -f 'src/sync.c'; then $(CYGPATH_W) 'src/sync.c'; else $(CYGPATH_W) '$(srcdir)/src/sync.c'; fi`
2637 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-sync.Tpo src/$(DEPDIR)/i3-sync.Po
2638 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/sync.c' object='src/i3-sync.obj' libtool=no @AMDEPBACKSLASH@
2639 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2640 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-sync.obj `if test -f 'src/sync.c'; then $(CYGPATH_W) 'src/sync.c'; else $(CYGPATH_W) '$(srcdir)/src/sync.c'; fi`
2641
2642 src/i3-tree.o: src/tree.c
2643 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-tree.o -MD -MP -MF src/$(DEPDIR)/i3-tree.Tpo -c -o src/i3-tree.o `test -f 'src/tree.c' || echo '$(srcdir)/'`src/tree.c
2644 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-tree.Tpo src/$(DEPDIR)/i3-tree.Po
2645 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/tree.c' object='src/i3-tree.o' libtool=no @AMDEPBACKSLASH@
2646 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2647 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-tree.o `test -f 'src/tree.c' || echo '$(srcdir)/'`src/tree.c
2648
2649 src/i3-tree.obj: src/tree.c
2650 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-tree.obj -MD -MP -MF src/$(DEPDIR)/i3-tree.Tpo -c -o src/i3-tree.obj `if test -f 'src/tree.c'; then $(CYGPATH_W) 'src/tree.c'; else $(CYGPATH_W) '$(srcdir)/src/tree.c'; fi`
2651 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-tree.Tpo src/$(DEPDIR)/i3-tree.Po
2652 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/tree.c' object='src/i3-tree.obj' libtool=no @AMDEPBACKSLASH@
2653 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2654 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-tree.obj `if test -f 'src/tree.c'; then $(CYGPATH_W) 'src/tree.c'; else $(CYGPATH_W) '$(srcdir)/src/tree.c'; fi`
2655
2656 src/i3-util.o: src/util.c
2657 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-util.o -MD -MP -MF src/$(DEPDIR)/i3-util.Tpo -c -o src/i3-util.o `test -f 'src/util.c' || echo '$(srcdir)/'`src/util.c
2658 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-util.Tpo src/$(DEPDIR)/i3-util.Po
2659 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/util.c' object='src/i3-util.o' libtool=no @AMDEPBACKSLASH@
2660 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2661 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-util.o `test -f 'src/util.c' || echo '$(srcdir)/'`src/util.c
2662
2663 src/i3-util.obj: src/util.c
2664 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-util.obj -MD -MP -MF src/$(DEPDIR)/i3-util.Tpo -c -o src/i3-util.obj `if test -f 'src/util.c'; then $(CYGPATH_W) 'src/util.c'; else $(CYGPATH_W) '$(srcdir)/src/util.c'; fi`
2665 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-util.Tpo src/$(DEPDIR)/i3-util.Po
2666 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/util.c' object='src/i3-util.obj' libtool=no @AMDEPBACKSLASH@
2667 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2668 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-util.obj `if test -f 'src/util.c'; then $(CYGPATH_W) 'src/util.c'; else $(CYGPATH_W) '$(srcdir)/src/util.c'; fi`
2669
2670 src/i3-version.o: src/version.c
2671 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-version.o -MD -MP -MF src/$(DEPDIR)/i3-version.Tpo -c -o src/i3-version.o `test -f 'src/version.c' || echo '$(srcdir)/'`src/version.c
2672 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-version.Tpo src/$(DEPDIR)/i3-version.Po
2673 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/version.c' object='src/i3-version.o' libtool=no @AMDEPBACKSLASH@
2674 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2675 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-version.o `test -f 'src/version.c' || echo '$(srcdir)/'`src/version.c
2676
2677 src/i3-version.obj: src/version.c
2678 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-version.obj -MD -MP -MF src/$(DEPDIR)/i3-version.Tpo -c -o src/i3-version.obj `if test -f 'src/version.c'; then $(CYGPATH_W) 'src/version.c'; else $(CYGPATH_W) '$(srcdir)/src/version.c'; fi`
2679 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-version.Tpo src/$(DEPDIR)/i3-version.Po
2680 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/version.c' object='src/i3-version.obj' libtool=no @AMDEPBACKSLASH@
2681 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2682 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-version.obj `if test -f 'src/version.c'; then $(CYGPATH_W) 'src/version.c'; else $(CYGPATH_W) '$(srcdir)/src/version.c'; fi`
2683
2684 src/i3-window.o: src/window.c
2685 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-window.o -MD -MP -MF src/$(DEPDIR)/i3-window.Tpo -c -o src/i3-window.o `test -f 'src/window.c' || echo '$(srcdir)/'`src/window.c
2686 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-window.Tpo src/$(DEPDIR)/i3-window.Po
2687 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/window.c' object='src/i3-window.o' libtool=no @AMDEPBACKSLASH@
2688 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2689 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-window.o `test -f 'src/window.c' || echo '$(srcdir)/'`src/window.c
2690
2691 src/i3-window.obj: src/window.c
2692 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-window.obj -MD -MP -MF src/$(DEPDIR)/i3-window.Tpo -c -o src/i3-window.obj `if test -f 'src/window.c'; then $(CYGPATH_W) 'src/window.c'; else $(CYGPATH_W) '$(srcdir)/src/window.c'; fi`
2693 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-window.Tpo src/$(DEPDIR)/i3-window.Po
2694 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/window.c' object='src/i3-window.obj' libtool=no @AMDEPBACKSLASH@
2695 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2696 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-window.obj `if test -f 'src/window.c'; then $(CYGPATH_W) 'src/window.c'; else $(CYGPATH_W) '$(srcdir)/src/window.c'; fi`
2697
2698 src/i3-workspace.o: src/workspace.c
2699 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-workspace.o -MD -MP -MF src/$(DEPDIR)/i3-workspace.Tpo -c -o src/i3-workspace.o `test -f 'src/workspace.c' || echo '$(srcdir)/'`src/workspace.c
2700 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-workspace.Tpo src/$(DEPDIR)/i3-workspace.Po
2701 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/workspace.c' object='src/i3-workspace.o' libtool=no @AMDEPBACKSLASH@
2702 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2703 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-workspace.o `test -f 'src/workspace.c' || echo '$(srcdir)/'`src/workspace.c
2704
2705 src/i3-workspace.obj: src/workspace.c
2706 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-workspace.obj -MD -MP -MF src/$(DEPDIR)/i3-workspace.Tpo -c -o src/i3-workspace.obj `if test -f 'src/workspace.c'; then $(CYGPATH_W) 'src/workspace.c'; else $(CYGPATH_W) '$(srcdir)/src/workspace.c'; fi`
2707 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-workspace.Tpo src/$(DEPDIR)/i3-workspace.Po
2708 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/workspace.c' object='src/i3-workspace.obj' libtool=no @AMDEPBACKSLASH@
2709 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2710 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-workspace.obj `if test -f 'src/workspace.c'; then $(CYGPATH_W) 'src/workspace.c'; else $(CYGPATH_W) '$(srcdir)/src/workspace.c'; fi`
2711
2712 src/i3-x.o: src/x.c
2713 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-x.o -MD -MP -MF src/$(DEPDIR)/i3-x.Tpo -c -o src/i3-x.o `test -f 'src/x.c' || echo '$(srcdir)/'`src/x.c
2714 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-x.Tpo src/$(DEPDIR)/i3-x.Po
2715 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/x.c' object='src/i3-x.o' libtool=no @AMDEPBACKSLASH@
2716 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2717 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-x.o `test -f 'src/x.c' || echo '$(srcdir)/'`src/x.c
2718
2719 src/i3-x.obj: src/x.c
2720 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-x.obj -MD -MP -MF src/$(DEPDIR)/i3-x.Tpo -c -o src/i3-x.obj `if test -f 'src/x.c'; then $(CYGPATH_W) 'src/x.c'; else $(CYGPATH_W) '$(srcdir)/src/x.c'; fi`
2721 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-x.Tpo src/$(DEPDIR)/i3-x.Po
2722 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/x.c' object='src/i3-x.obj' libtool=no @AMDEPBACKSLASH@
2723 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2724 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-x.obj `if test -f 'src/x.c'; then $(CYGPATH_W) 'src/x.c'; else $(CYGPATH_W) '$(srcdir)/src/x.c'; fi`
2725
2726 src/i3-xcb.o: src/xcb.c
2727 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-xcb.o -MD -MP -MF src/$(DEPDIR)/i3-xcb.Tpo -c -o src/i3-xcb.o `test -f 'src/xcb.c' || echo '$(srcdir)/'`src/xcb.c
2728 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-xcb.Tpo src/$(DEPDIR)/i3-xcb.Po
2729 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xcb.c' object='src/i3-xcb.o' libtool=no @AMDEPBACKSLASH@
2730 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2731 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-xcb.o `test -f 'src/xcb.c' || echo '$(srcdir)/'`src/xcb.c
2732
2733 src/i3-xcb.obj: src/xcb.c
2734 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-xcb.obj -MD -MP -MF src/$(DEPDIR)/i3-xcb.Tpo -c -o src/i3-xcb.obj `if test -f 'src/xcb.c'; then $(CYGPATH_W) 'src/xcb.c'; else $(CYGPATH_W) '$(srcdir)/src/xcb.c'; fi`
2735 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-xcb.Tpo src/$(DEPDIR)/i3-xcb.Po
2736 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xcb.c' object='src/i3-xcb.obj' libtool=no @AMDEPBACKSLASH@
2737 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2738 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-xcb.obj `if test -f 'src/xcb.c'; then $(CYGPATH_W) 'src/xcb.c'; else $(CYGPATH_W) '$(srcdir)/src/xcb.c'; fi`
2739
2740 src/i3-xcursor.o: src/xcursor.c
2741 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-xcursor.o -MD -MP -MF src/$(DEPDIR)/i3-xcursor.Tpo -c -o src/i3-xcursor.o `test -f 'src/xcursor.c' || echo '$(srcdir)/'`src/xcursor.c
2742 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-xcursor.Tpo src/$(DEPDIR)/i3-xcursor.Po
2743 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xcursor.c' object='src/i3-xcursor.o' libtool=no @AMDEPBACKSLASH@
2744 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2745 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-xcursor.o `test -f 'src/xcursor.c' || echo '$(srcdir)/'`src/xcursor.c
2746
2747 src/i3-xcursor.obj: src/xcursor.c
2748 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-xcursor.obj -MD -MP -MF src/$(DEPDIR)/i3-xcursor.Tpo -c -o src/i3-xcursor.obj `if test -f 'src/xcursor.c'; then $(CYGPATH_W) 'src/xcursor.c'; else $(CYGPATH_W) '$(srcdir)/src/xcursor.c'; fi`
2749 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-xcursor.Tpo src/$(DEPDIR)/i3-xcursor.Po
2750 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xcursor.c' object='src/i3-xcursor.obj' libtool=no @AMDEPBACKSLASH@
2751 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2752 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-xcursor.obj `if test -f 'src/xcursor.c'; then $(CYGPATH_W) 'src/xcursor.c'; else $(CYGPATH_W) '$(srcdir)/src/xcursor.c'; fi`
2753
2754 src/i3-xinerama.o: src/xinerama.c
2755 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-xinerama.o -MD -MP -MF src/$(DEPDIR)/i3-xinerama.Tpo -c -o src/i3-xinerama.o `test -f 'src/xinerama.c' || echo '$(srcdir)/'`src/xinerama.c
2756 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-xinerama.Tpo src/$(DEPDIR)/i3-xinerama.Po
2757 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xinerama.c' object='src/i3-xinerama.o' libtool=no @AMDEPBACKSLASH@
2758 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2759 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-xinerama.o `test -f 'src/xinerama.c' || echo '$(srcdir)/'`src/xinerama.c
2760
2761 src/i3-xinerama.obj: src/xinerama.c
2762 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -MT src/i3-xinerama.obj -MD -MP -MF src/$(DEPDIR)/i3-xinerama.Tpo -c -o src/i3-xinerama.obj `if test -f 'src/xinerama.c'; then $(CYGPATH_W) 'src/xinerama.c'; else $(CYGPATH_W) '$(srcdir)/src/xinerama.c'; fi`
2763 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/i3-xinerama.Tpo src/$(DEPDIR)/i3-xinerama.Po
2764 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/xinerama.c' object='src/i3-xinerama.obj' libtool=no @AMDEPBACKSLASH@
2765 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2766 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(CFLAGS) -c -o src/i3-xinerama.obj `if test -f 'src/xinerama.c'; then $(CYGPATH_W) 'src/xinerama.c'; else $(CYGPATH_W) '$(srcdir)/src/xinerama.c'; fi`
2767
2768 i3-config-wizard/i3_config_wizard-main.o: i3-config-wizard/main.c
2769 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_i3_config_wizard_CFLAGS) $(CFLAGS) -MT i3-config-wizard/i3_config_wizard-main.o -MD -MP -MF i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Tpo -c -o i3-config-wizard/i3_config_wizard-main.o `test -f 'i3-config-wizard/main.c' || echo '$(srcdir)/'`i3-config-wizard/main.c
2770 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Tpo i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Po
2771 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-config-wizard/main.c' object='i3-config-wizard/i3_config_wizard-main.o' libtool=no @AMDEPBACKSLASH@
2772 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2773 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_i3_config_wizard_CFLAGS) $(CFLAGS) -c -o i3-config-wizard/i3_config_wizard-main.o `test -f 'i3-config-wizard/main.c' || echo '$(srcdir)/'`i3-config-wizard/main.c
2774
2775 i3-config-wizard/i3_config_wizard-main.obj: i3-config-wizard/main.c
2776 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_i3_config_wizard_CFLAGS) $(CFLAGS) -MT i3-config-wizard/i3_config_wizard-main.obj -MD -MP -MF i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Tpo -c -o i3-config-wizard/i3_config_wizard-main.obj `if test -f 'i3-config-wizard/main.c'; then $(CYGPATH_W) 'i3-config-wizard/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-config-wizard/main.c'; fi`
2777 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Tpo i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Po
2778 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-config-wizard/main.c' object='i3-config-wizard/i3_config_wizard-main.obj' libtool=no @AMDEPBACKSLASH@
2779 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2780 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_config_wizard_i3_config_wizard_CFLAGS) $(CFLAGS) -c -o i3-config-wizard/i3_config_wizard-main.obj `if test -f 'i3-config-wizard/main.c'; then $(CYGPATH_W) 'i3-config-wizard/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-config-wizard/main.c'; fi`
2781
2782 i3-dump-log/i3_dump_log-main.o: i3-dump-log/main.c
2783 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_i3_dump_log_CFLAGS) $(CFLAGS) -MT i3-dump-log/i3_dump_log-main.o -MD -MP -MF i3-dump-log/$(DEPDIR)/i3_dump_log-main.Tpo -c -o i3-dump-log/i3_dump_log-main.o `test -f 'i3-dump-log/main.c' || echo '$(srcdir)/'`i3-dump-log/main.c
2784 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-dump-log/$(DEPDIR)/i3_dump_log-main.Tpo i3-dump-log/$(DEPDIR)/i3_dump_log-main.Po
2785 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-dump-log/main.c' object='i3-dump-log/i3_dump_log-main.o' libtool=no @AMDEPBACKSLASH@
2786 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2787 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_i3_dump_log_CFLAGS) $(CFLAGS) -c -o i3-dump-log/i3_dump_log-main.o `test -f 'i3-dump-log/main.c' || echo '$(srcdir)/'`i3-dump-log/main.c
2788
2789 i3-dump-log/i3_dump_log-main.obj: i3-dump-log/main.c
2790 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_i3_dump_log_CFLAGS) $(CFLAGS) -MT i3-dump-log/i3_dump_log-main.obj -MD -MP -MF i3-dump-log/$(DEPDIR)/i3_dump_log-main.Tpo -c -o i3-dump-log/i3_dump_log-main.obj `if test -f 'i3-dump-log/main.c'; then $(CYGPATH_W) 'i3-dump-log/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-dump-log/main.c'; fi`
2791 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-dump-log/$(DEPDIR)/i3_dump_log-main.Tpo i3-dump-log/$(DEPDIR)/i3_dump_log-main.Po
2792 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-dump-log/main.c' object='i3-dump-log/i3_dump_log-main.obj' libtool=no @AMDEPBACKSLASH@
2793 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2794 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_dump_log_i3_dump_log_CFLAGS) $(CFLAGS) -c -o i3-dump-log/i3_dump_log-main.obj `if test -f 'i3-dump-log/main.c'; then $(CYGPATH_W) 'i3-dump-log/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-dump-log/main.c'; fi`
2795
2796 i3-input/i3_input-keysym2ucs.o: i3-input/keysym2ucs.c
2797 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -MT i3-input/i3_input-keysym2ucs.o -MD -MP -MF i3-input/$(DEPDIR)/i3_input-keysym2ucs.Tpo -c -o i3-input/i3_input-keysym2ucs.o `test -f 'i3-input/keysym2ucs.c' || echo '$(srcdir)/'`i3-input/keysym2ucs.c
2798 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-input/$(DEPDIR)/i3_input-keysym2ucs.Tpo i3-input/$(DEPDIR)/i3_input-keysym2ucs.Po
2799 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-input/keysym2ucs.c' object='i3-input/i3_input-keysym2ucs.o' libtool=no @AMDEPBACKSLASH@
2800 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2801 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -c -o i3-input/i3_input-keysym2ucs.o `test -f 'i3-input/keysym2ucs.c' || echo '$(srcdir)/'`i3-input/keysym2ucs.c
2802
2803 i3-input/i3_input-keysym2ucs.obj: i3-input/keysym2ucs.c
2804 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -MT i3-input/i3_input-keysym2ucs.obj -MD -MP -MF i3-input/$(DEPDIR)/i3_input-keysym2ucs.Tpo -c -o i3-input/i3_input-keysym2ucs.obj `if test -f 'i3-input/keysym2ucs.c'; then $(CYGPATH_W) 'i3-input/keysym2ucs.c'; else $(CYGPATH_W) '$(srcdir)/i3-input/keysym2ucs.c'; fi`
2805 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-input/$(DEPDIR)/i3_input-keysym2ucs.Tpo i3-input/$(DEPDIR)/i3_input-keysym2ucs.Po
2806 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-input/keysym2ucs.c' object='i3-input/i3_input-keysym2ucs.obj' libtool=no @AMDEPBACKSLASH@
2807 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2808 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -c -o i3-input/i3_input-keysym2ucs.obj `if test -f 'i3-input/keysym2ucs.c'; then $(CYGPATH_W) 'i3-input/keysym2ucs.c'; else $(CYGPATH_W) '$(srcdir)/i3-input/keysym2ucs.c'; fi`
2809
2810 i3-input/i3_input-main.o: i3-input/main.c
2811 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -MT i3-input/i3_input-main.o -MD -MP -MF i3-input/$(DEPDIR)/i3_input-main.Tpo -c -o i3-input/i3_input-main.o `test -f 'i3-input/main.c' || echo '$(srcdir)/'`i3-input/main.c
2812 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-input/$(DEPDIR)/i3_input-main.Tpo i3-input/$(DEPDIR)/i3_input-main.Po
2813 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-input/main.c' object='i3-input/i3_input-main.o' libtool=no @AMDEPBACKSLASH@
2814 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2815 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -c -o i3-input/i3_input-main.o `test -f 'i3-input/main.c' || echo '$(srcdir)/'`i3-input/main.c
2816
2817 i3-input/i3_input-main.obj: i3-input/main.c
2818 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -MT i3-input/i3_input-main.obj -MD -MP -MF i3-input/$(DEPDIR)/i3_input-main.Tpo -c -o i3-input/i3_input-main.obj `if test -f 'i3-input/main.c'; then $(CYGPATH_W) 'i3-input/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-input/main.c'; fi`
2819 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-input/$(DEPDIR)/i3_input-main.Tpo i3-input/$(DEPDIR)/i3_input-main.Po
2820 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-input/main.c' object='i3-input/i3_input-main.obj' libtool=no @AMDEPBACKSLASH@
2821 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2822 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_input_i3_input_CFLAGS) $(CFLAGS) -c -o i3-input/i3_input-main.obj `if test -f 'i3-input/main.c'; then $(CYGPATH_W) 'i3-input/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-input/main.c'; fi`
2823
2824 i3-msg/i3_msg-main.o: i3-msg/main.c
2825 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_msg_i3_msg_CFLAGS) $(CFLAGS) -MT i3-msg/i3_msg-main.o -MD -MP -MF i3-msg/$(DEPDIR)/i3_msg-main.Tpo -c -o i3-msg/i3_msg-main.o `test -f 'i3-msg/main.c' || echo '$(srcdir)/'`i3-msg/main.c
2826 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-msg/$(DEPDIR)/i3_msg-main.Tpo i3-msg/$(DEPDIR)/i3_msg-main.Po
2827 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-msg/main.c' object='i3-msg/i3_msg-main.o' libtool=no @AMDEPBACKSLASH@
2828 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2829 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_msg_i3_msg_CFLAGS) $(CFLAGS) -c -o i3-msg/i3_msg-main.o `test -f 'i3-msg/main.c' || echo '$(srcdir)/'`i3-msg/main.c
2830
2831 i3-msg/i3_msg-main.obj: i3-msg/main.c
2832 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_msg_i3_msg_CFLAGS) $(CFLAGS) -MT i3-msg/i3_msg-main.obj -MD -MP -MF i3-msg/$(DEPDIR)/i3_msg-main.Tpo -c -o i3-msg/i3_msg-main.obj `if test -f 'i3-msg/main.c'; then $(CYGPATH_W) 'i3-msg/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-msg/main.c'; fi`
2833 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-msg/$(DEPDIR)/i3_msg-main.Tpo i3-msg/$(DEPDIR)/i3_msg-main.Po
2834 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-msg/main.c' object='i3-msg/i3_msg-main.obj' libtool=no @AMDEPBACKSLASH@
2835 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2836 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_msg_i3_msg_CFLAGS) $(CFLAGS) -c -o i3-msg/i3_msg-main.obj `if test -f 'i3-msg/main.c'; then $(CYGPATH_W) 'i3-msg/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-msg/main.c'; fi`
2837
2838 i3-nagbar/i3_nagbar-main.o: i3-nagbar/main.c
2839 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_i3_nagbar_CFLAGS) $(CFLAGS) -MT i3-nagbar/i3_nagbar-main.o -MD -MP -MF i3-nagbar/$(DEPDIR)/i3_nagbar-main.Tpo -c -o i3-nagbar/i3_nagbar-main.o `test -f 'i3-nagbar/main.c' || echo '$(srcdir)/'`i3-nagbar/main.c
2840 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-nagbar/$(DEPDIR)/i3_nagbar-main.Tpo i3-nagbar/$(DEPDIR)/i3_nagbar-main.Po
2841 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-nagbar/main.c' object='i3-nagbar/i3_nagbar-main.o' libtool=no @AMDEPBACKSLASH@
2842 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2843 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_i3_nagbar_CFLAGS) $(CFLAGS) -c -o i3-nagbar/i3_nagbar-main.o `test -f 'i3-nagbar/main.c' || echo '$(srcdir)/'`i3-nagbar/main.c
2844
2845 i3-nagbar/i3_nagbar-main.obj: i3-nagbar/main.c
2846 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_i3_nagbar_CFLAGS) $(CFLAGS) -MT i3-nagbar/i3_nagbar-main.obj -MD -MP -MF i3-nagbar/$(DEPDIR)/i3_nagbar-main.Tpo -c -o i3-nagbar/i3_nagbar-main.obj `if test -f 'i3-nagbar/main.c'; then $(CYGPATH_W) 'i3-nagbar/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-nagbar/main.c'; fi`
2847 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3-nagbar/$(DEPDIR)/i3_nagbar-main.Tpo i3-nagbar/$(DEPDIR)/i3_nagbar-main.Po
2848 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3-nagbar/main.c' object='i3-nagbar/i3_nagbar-main.obj' libtool=no @AMDEPBACKSLASH@
2849 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2850 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(i3_nagbar_i3_nagbar_CFLAGS) $(CFLAGS) -c -o i3-nagbar/i3_nagbar-main.obj `if test -f 'i3-nagbar/main.c'; then $(CYGPATH_W) 'i3-nagbar/main.c'; else $(CYGPATH_W) '$(srcdir)/i3-nagbar/main.c'; fi`
2851
2852 i3bar/src/i3bar-child.o: i3bar/src/child.c
2853 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-child.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-child.Tpo -c -o i3bar/src/i3bar-child.o `test -f 'i3bar/src/child.c' || echo '$(srcdir)/'`i3bar/src/child.c
2854 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-child.Tpo i3bar/src/$(DEPDIR)/i3bar-child.Po
2855 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/child.c' object='i3bar/src/i3bar-child.o' libtool=no @AMDEPBACKSLASH@
2856 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2857 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-child.o `test -f 'i3bar/src/child.c' || echo '$(srcdir)/'`i3bar/src/child.c
2858
2859 i3bar/src/i3bar-child.obj: i3bar/src/child.c
2860 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-child.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-child.Tpo -c -o i3bar/src/i3bar-child.obj `if test -f 'i3bar/src/child.c'; then $(CYGPATH_W) 'i3bar/src/child.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/child.c'; fi`
2861 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-child.Tpo i3bar/src/$(DEPDIR)/i3bar-child.Po
2862 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/child.c' object='i3bar/src/i3bar-child.obj' libtool=no @AMDEPBACKSLASH@
2863 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2864 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-child.obj `if test -f 'i3bar/src/child.c'; then $(CYGPATH_W) 'i3bar/src/child.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/child.c'; fi`
2865
2866 i3bar/src/i3bar-config.o: i3bar/src/config.c
2867 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-config.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-config.Tpo -c -o i3bar/src/i3bar-config.o `test -f 'i3bar/src/config.c' || echo '$(srcdir)/'`i3bar/src/config.c
2868 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-config.Tpo i3bar/src/$(DEPDIR)/i3bar-config.Po
2869 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/config.c' object='i3bar/src/i3bar-config.o' libtool=no @AMDEPBACKSLASH@
2870 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2871 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-config.o `test -f 'i3bar/src/config.c' || echo '$(srcdir)/'`i3bar/src/config.c
2872
2873 i3bar/src/i3bar-config.obj: i3bar/src/config.c
2874 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-config.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-config.Tpo -c -o i3bar/src/i3bar-config.obj `if test -f 'i3bar/src/config.c'; then $(CYGPATH_W) 'i3bar/src/config.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/config.c'; fi`
2875 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-config.Tpo i3bar/src/$(DEPDIR)/i3bar-config.Po
2876 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/config.c' object='i3bar/src/i3bar-config.obj' libtool=no @AMDEPBACKSLASH@
2877 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2878 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-config.obj `if test -f 'i3bar/src/config.c'; then $(CYGPATH_W) 'i3bar/src/config.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/config.c'; fi`
2879
2880 i3bar/src/i3bar-ipc.o: i3bar/src/ipc.c
2881 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-ipc.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-ipc.Tpo -c -o i3bar/src/i3bar-ipc.o `test -f 'i3bar/src/ipc.c' || echo '$(srcdir)/'`i3bar/src/ipc.c
2882 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-ipc.Tpo i3bar/src/$(DEPDIR)/i3bar-ipc.Po
2883 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/ipc.c' object='i3bar/src/i3bar-ipc.o' libtool=no @AMDEPBACKSLASH@
2884 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2885 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-ipc.o `test -f 'i3bar/src/ipc.c' || echo '$(srcdir)/'`i3bar/src/ipc.c
2886
2887 i3bar/src/i3bar-ipc.obj: i3bar/src/ipc.c
2888 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-ipc.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-ipc.Tpo -c -o i3bar/src/i3bar-ipc.obj `if test -f 'i3bar/src/ipc.c'; then $(CYGPATH_W) 'i3bar/src/ipc.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/ipc.c'; fi`
2889 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-ipc.Tpo i3bar/src/$(DEPDIR)/i3bar-ipc.Po
2890 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/ipc.c' object='i3bar/src/i3bar-ipc.obj' libtool=no @AMDEPBACKSLASH@
2891 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2892 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-ipc.obj `if test -f 'i3bar/src/ipc.c'; then $(CYGPATH_W) 'i3bar/src/ipc.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/ipc.c'; fi`
2893
2894 i3bar/src/i3bar-main.o: i3bar/src/main.c
2895 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-main.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-main.Tpo -c -o i3bar/src/i3bar-main.o `test -f 'i3bar/src/main.c' || echo '$(srcdir)/'`i3bar/src/main.c
2896 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-main.Tpo i3bar/src/$(DEPDIR)/i3bar-main.Po
2897 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/main.c' object='i3bar/src/i3bar-main.o' libtool=no @AMDEPBACKSLASH@
2898 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2899 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-main.o `test -f 'i3bar/src/main.c' || echo '$(srcdir)/'`i3bar/src/main.c
2900
2901 i3bar/src/i3bar-main.obj: i3bar/src/main.c
2902 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-main.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-main.Tpo -c -o i3bar/src/i3bar-main.obj `if test -f 'i3bar/src/main.c'; then $(CYGPATH_W) 'i3bar/src/main.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/main.c'; fi`
2903 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-main.Tpo i3bar/src/$(DEPDIR)/i3bar-main.Po
2904 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/main.c' object='i3bar/src/i3bar-main.obj' libtool=no @AMDEPBACKSLASH@
2905 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2906 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-main.obj `if test -f 'i3bar/src/main.c'; then $(CYGPATH_W) 'i3bar/src/main.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/main.c'; fi`
2907
2908 i3bar/src/i3bar-mode.o: i3bar/src/mode.c
2909 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-mode.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-mode.Tpo -c -o i3bar/src/i3bar-mode.o `test -f 'i3bar/src/mode.c' || echo '$(srcdir)/'`i3bar/src/mode.c
2910 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-mode.Tpo i3bar/src/$(DEPDIR)/i3bar-mode.Po
2911 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/mode.c' object='i3bar/src/i3bar-mode.o' libtool=no @AMDEPBACKSLASH@
2912 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2913 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-mode.o `test -f 'i3bar/src/mode.c' || echo '$(srcdir)/'`i3bar/src/mode.c
2914
2915 i3bar/src/i3bar-mode.obj: i3bar/src/mode.c
2916 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-mode.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-mode.Tpo -c -o i3bar/src/i3bar-mode.obj `if test -f 'i3bar/src/mode.c'; then $(CYGPATH_W) 'i3bar/src/mode.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/mode.c'; fi`
2917 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-mode.Tpo i3bar/src/$(DEPDIR)/i3bar-mode.Po
2918 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/mode.c' object='i3bar/src/i3bar-mode.obj' libtool=no @AMDEPBACKSLASH@
2919 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2920 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-mode.obj `if test -f 'i3bar/src/mode.c'; then $(CYGPATH_W) 'i3bar/src/mode.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/mode.c'; fi`
2921
2922 i3bar/src/i3bar-outputs.o: i3bar/src/outputs.c
2923 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-outputs.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-outputs.Tpo -c -o i3bar/src/i3bar-outputs.o `test -f 'i3bar/src/outputs.c' || echo '$(srcdir)/'`i3bar/src/outputs.c
2924 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-outputs.Tpo i3bar/src/$(DEPDIR)/i3bar-outputs.Po
2925 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/outputs.c' object='i3bar/src/i3bar-outputs.o' libtool=no @AMDEPBACKSLASH@
2926 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2927 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-outputs.o `test -f 'i3bar/src/outputs.c' || echo '$(srcdir)/'`i3bar/src/outputs.c
2928
2929 i3bar/src/i3bar-outputs.obj: i3bar/src/outputs.c
2930 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-outputs.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-outputs.Tpo -c -o i3bar/src/i3bar-outputs.obj `if test -f 'i3bar/src/outputs.c'; then $(CYGPATH_W) 'i3bar/src/outputs.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/outputs.c'; fi`
2931 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-outputs.Tpo i3bar/src/$(DEPDIR)/i3bar-outputs.Po
2932 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/outputs.c' object='i3bar/src/i3bar-outputs.obj' libtool=no @AMDEPBACKSLASH@
2933 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2934 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-outputs.obj `if test -f 'i3bar/src/outputs.c'; then $(CYGPATH_W) 'i3bar/src/outputs.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/outputs.c'; fi`
2935
2936 i3bar/src/i3bar-parse_json_header.o: i3bar/src/parse_json_header.c
2937 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-parse_json_header.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Tpo -c -o i3bar/src/i3bar-parse_json_header.o `test -f 'i3bar/src/parse_json_header.c' || echo '$(srcdir)/'`i3bar/src/parse_json_header.c
2938 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Tpo i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Po
2939 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/parse_json_header.c' object='i3bar/src/i3bar-parse_json_header.o' libtool=no @AMDEPBACKSLASH@
2940 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2941 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-parse_json_header.o `test -f 'i3bar/src/parse_json_header.c' || echo '$(srcdir)/'`i3bar/src/parse_json_header.c
2942
2943 i3bar/src/i3bar-parse_json_header.obj: i3bar/src/parse_json_header.c
2944 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-parse_json_header.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Tpo -c -o i3bar/src/i3bar-parse_json_header.obj `if test -f 'i3bar/src/parse_json_header.c'; then $(CYGPATH_W) 'i3bar/src/parse_json_header.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/parse_json_header.c'; fi`
2945 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Tpo i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Po
2946 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/parse_json_header.c' object='i3bar/src/i3bar-parse_json_header.obj' libtool=no @AMDEPBACKSLASH@
2947 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2948 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-parse_json_header.obj `if test -f 'i3bar/src/parse_json_header.c'; then $(CYGPATH_W) 'i3bar/src/parse_json_header.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/parse_json_header.c'; fi`
2949
2950 i3bar/src/i3bar-workspaces.o: i3bar/src/workspaces.c
2951 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-workspaces.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-workspaces.Tpo -c -o i3bar/src/i3bar-workspaces.o `test -f 'i3bar/src/workspaces.c' || echo '$(srcdir)/'`i3bar/src/workspaces.c
2952 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-workspaces.Tpo i3bar/src/$(DEPDIR)/i3bar-workspaces.Po
2953 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/workspaces.c' object='i3bar/src/i3bar-workspaces.o' libtool=no @AMDEPBACKSLASH@
2954 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2955 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-workspaces.o `test -f 'i3bar/src/workspaces.c' || echo '$(srcdir)/'`i3bar/src/workspaces.c
2956
2957 i3bar/src/i3bar-workspaces.obj: i3bar/src/workspaces.c
2958 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-workspaces.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-workspaces.Tpo -c -o i3bar/src/i3bar-workspaces.obj `if test -f 'i3bar/src/workspaces.c'; then $(CYGPATH_W) 'i3bar/src/workspaces.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/workspaces.c'; fi`
2959 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-workspaces.Tpo i3bar/src/$(DEPDIR)/i3bar-workspaces.Po
2960 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/workspaces.c' object='i3bar/src/i3bar-workspaces.obj' libtool=no @AMDEPBACKSLASH@
2961 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2962 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-workspaces.obj `if test -f 'i3bar/src/workspaces.c'; then $(CYGPATH_W) 'i3bar/src/workspaces.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/workspaces.c'; fi`
2963
2964 i3bar/src/i3bar-xcb.o: i3bar/src/xcb.c
2965 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-xcb.o -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-xcb.Tpo -c -o i3bar/src/i3bar-xcb.o `test -f 'i3bar/src/xcb.c' || echo '$(srcdir)/'`i3bar/src/xcb.c
2966 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-xcb.Tpo i3bar/src/$(DEPDIR)/i3bar-xcb.Po
2967 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/xcb.c' object='i3bar/src/i3bar-xcb.o' libtool=no @AMDEPBACKSLASH@
2968 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2969 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-xcb.o `test -f 'i3bar/src/xcb.c' || echo '$(srcdir)/'`i3bar/src/xcb.c
2970
2971 i3bar/src/i3bar-xcb.obj: i3bar/src/xcb.c
2972 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -MT i3bar/src/i3bar-xcb.obj -MD -MP -MF i3bar/src/$(DEPDIR)/i3bar-xcb.Tpo -c -o i3bar/src/i3bar-xcb.obj `if test -f 'i3bar/src/xcb.c'; then $(CYGPATH_W) 'i3bar/src/xcb.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/xcb.c'; fi`
2973 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) i3bar/src/$(DEPDIR)/i3bar-xcb.Tpo i3bar/src/$(DEPDIR)/i3bar-xcb.Po
2974 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i3bar/src/xcb.c' object='i3bar/src/i3bar-xcb.obj' libtool=no @AMDEPBACKSLASH@
2975 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2976 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(i3bar_i3bar_CPPFLAGS) $(CPPFLAGS) $(i3bar_i3bar_CFLAGS) $(CFLAGS) -c -o i3bar/src/i3bar-xcb.obj `if test -f 'i3bar/src/xcb.c'; then $(CYGPATH_W) 'i3bar/src/xcb.c'; else $(CYGPATH_W) '$(srcdir)/i3bar/src/xcb.c'; fi`
2977
2978 src/test_commands_parser-commands_parser.o: src/commands_parser.c
2979 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_commands_parser_CPPFLAGS) $(CPPFLAGS) $(test_commands_parser_CFLAGS) $(CFLAGS) -MT src/test_commands_parser-commands_parser.o -MD -MP -MF src/$(DEPDIR)/test_commands_parser-commands_parser.Tpo -c -o src/test_commands_parser-commands_parser.o `test -f 'src/commands_parser.c' || echo '$(srcdir)/'`src/commands_parser.c
2980 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/test_commands_parser-commands_parser.Tpo src/$(DEPDIR)/test_commands_parser-commands_parser.Po
2981 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/commands_parser.c' object='src/test_commands_parser-commands_parser.o' libtool=no @AMDEPBACKSLASH@
2982 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2983 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_commands_parser_CPPFLAGS) $(CPPFLAGS) $(test_commands_parser_CFLAGS) $(CFLAGS) -c -o src/test_commands_parser-commands_parser.o `test -f 'src/commands_parser.c' || echo '$(srcdir)/'`src/commands_parser.c
2984
2985 src/test_commands_parser-commands_parser.obj: src/commands_parser.c
2986 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_commands_parser_CPPFLAGS) $(CPPFLAGS) $(test_commands_parser_CFLAGS) $(CFLAGS) -MT src/test_commands_parser-commands_parser.obj -MD -MP -MF src/$(DEPDIR)/test_commands_parser-commands_parser.Tpo -c -o src/test_commands_parser-commands_parser.obj `if test -f 'src/commands_parser.c'; then $(CYGPATH_W) 'src/commands_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/commands_parser.c'; fi`
2987 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/test_commands_parser-commands_parser.Tpo src/$(DEPDIR)/test_commands_parser-commands_parser.Po
2988 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/commands_parser.c' object='src/test_commands_parser-commands_parser.obj' libtool=no @AMDEPBACKSLASH@
2989 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2990 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_commands_parser_CPPFLAGS) $(CPPFLAGS) $(test_commands_parser_CFLAGS) $(CFLAGS) -c -o src/test_commands_parser-commands_parser.obj `if test -f 'src/commands_parser.c'; then $(CYGPATH_W) 'src/commands_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/commands_parser.c'; fi`
2991
2992 src/test_config_parser-config_parser.o: src/config_parser.c
2993 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_config_parser_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -MT src/test_config_parser-config_parser.o -MD -MP -MF src/$(DEPDIR)/test_config_parser-config_parser.Tpo -c -o src/test_config_parser-config_parser.o `test -f 'src/config_parser.c' || echo '$(srcdir)/'`src/config_parser.c
2994 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/test_config_parser-config_parser.Tpo src/$(DEPDIR)/test_config_parser-config_parser.Po
2995 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config_parser.c' object='src/test_config_parser-config_parser.o' libtool=no @AMDEPBACKSLASH@
2996 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
2997 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_config_parser_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -c -o src/test_config_parser-config_parser.o `test -f 'src/config_parser.c' || echo '$(srcdir)/'`src/config_parser.c
2998
2999 src/test_config_parser-config_parser.obj: src/config_parser.c
3000 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_config_parser_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -MT src/test_config_parser-config_parser.obj -MD -MP -MF src/$(DEPDIR)/test_config_parser-config_parser.Tpo -c -o src/test_config_parser-config_parser.obj `if test -f 'src/config_parser.c'; then $(CYGPATH_W) 'src/config_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/config_parser.c'; fi`
3001 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/test_config_parser-config_parser.Tpo src/$(DEPDIR)/test_config_parser-config_parser.Po
3002 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/config_parser.c' object='src/test_config_parser-config_parser.obj' libtool=no @AMDEPBACKSLASH@
3003 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
3004 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_config_parser_CPPFLAGS) $(CPPFLAGS) $(test_config_parser_CFLAGS) $(CFLAGS) -c -o src/test_config_parser-config_parser.obj `if test -f 'src/config_parser.c'; then $(CYGPATH_W) 'src/config_parser.c'; else $(CYGPATH_W) '$(srcdir)/src/config_parser.c'; fi`
3005
3006 testcases/test_inject_randr15-inject_randr1.5.o: testcases/inject_randr1.5.c
3007 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_inject_randr15_CPPFLAGS) $(CPPFLAGS) $(test_inject_randr15_CFLAGS) $(CFLAGS) -MT testcases/test_inject_randr15-inject_randr1.5.o -MD -MP -MF testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Tpo -c -o testcases/test_inject_randr15-inject_randr1.5.o `test -f 'testcases/inject_randr1.5.c' || echo '$(srcdir)/'`testcases/inject_randr1.5.c
3008 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Tpo testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Po
3009 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='testcases/inject_randr1.5.c' object='testcases/test_inject_randr15-inject_randr1.5.o' libtool=no @AMDEPBACKSLASH@
3010 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
3011 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_inject_randr15_CPPFLAGS) $(CPPFLAGS) $(test_inject_randr15_CFLAGS) $(CFLAGS) -c -o testcases/test_inject_randr15-inject_randr1.5.o `test -f 'testcases/inject_randr1.5.c' || echo '$(srcdir)/'`testcases/inject_randr1.5.c
3012
3013 testcases/test_inject_randr15-inject_randr1.5.obj: testcases/inject_randr1.5.c
3014 @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_inject_randr15_CPPFLAGS) $(CPPFLAGS) $(test_inject_randr15_CFLAGS) $(CFLAGS) -MT testcases/test_inject_randr15-inject_randr1.5.obj -MD -MP -MF testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Tpo -c -o testcases/test_inject_randr15-inject_randr1.5.obj `if test -f 'testcases/inject_randr1.5.c'; then $(CYGPATH_W) 'testcases/inject_randr1.5.c'; else $(CYGPATH_W) '$(srcdir)/testcases/inject_randr1.5.c'; fi`
3015 @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Tpo testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Po
3016 @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='testcases/inject_randr1.5.c' object='testcases/test_inject_randr15-inject_randr1.5.obj' libtool=no @AMDEPBACKSLASH@
3017 @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
3018 @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_inject_randr15_CPPFLAGS) $(CPPFLAGS) $(test_inject_randr15_CFLAGS) $(CFLAGS) -c -o testcases/test_inject_randr15-inject_randr1.5.obj `if test -f 'testcases/inject_randr1.5.c'; then $(CYGPATH_W) 'testcases/inject_randr1.5.c'; else $(CYGPATH_W) '$(srcdir)/testcases/inject_randr1.5.c'; fi`
3019 install-man1: $(dist_man1_MANS)
3020 @$(NORMAL_INSTALL)
3021 @list1='$(dist_man1_MANS)'; \
3022 list2=''; \
3023 test -n "$(man1dir)" \
3024 && test -n "`echo $$list1$$list2`" \
3025 || exit 0; \
3026 echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
3027 $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
3028 { for i in $$list1; do echo "$$i"; done; \
3029 if test -n "$$list2"; then \
3030 for i in $$list2; do echo "$$i"; done \
3031 | sed -n '/\.1[a-z]*$$/p'; \
3032 fi; \
3033 } | while read p; do \
3034 if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
3035 echo "$$d$$p"; echo "$$p"; \
3036 done | \
3037 sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
3038 -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
3039 sed 'N;N;s,\n, ,g' | { \
3040 list=; while read file base inst; do \
3041 if test "$$base" = "$$inst"; then list="$$list $$file"; else \
3042 echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
3043 $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
3044 fi; \
3045 done; \
3046 for i in $$list; do echo "$$i"; done | $(am__base_list) | \
3047 while read files; do \
3048 test -z "$$files" || { \
3049 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
3050 $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
3051 done; }
3052
3053 uninstall-man1:
3054 @$(NORMAL_UNINSTALL)
3055 @list='$(dist_man1_MANS)'; test -n "$(man1dir)" || exit 0; \
3056 files=`{ for i in $$list; do echo "$$i"; done; \
3057 } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
3058 -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
3059 dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
3060 install-dist_applicationsDATA: $(dist_applications_DATA)
3061 @$(NORMAL_INSTALL)
3062 @list='$(dist_applications_DATA)'; test -n "$(applicationsdir)" || list=; \
3063 if test -n "$$list"; then \
3064 echo " $(MKDIR_P) '$(DESTDIR)$(applicationsdir)'"; \
3065 $(MKDIR_P) "$(DESTDIR)$(applicationsdir)" || exit 1; \
3066 fi; \
3067 for p in $$list; do \
3068 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3069 echo "$$d$$p"; \
3070 done | $(am__base_list) | \
3071 while read files; do \
3072 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(applicationsdir)'"; \
3073 $(INSTALL_DATA) $$files "$(DESTDIR)$(applicationsdir)" || exit $$?; \
3074 done
3075
3076 uninstall-dist_applicationsDATA:
3077 @$(NORMAL_UNINSTALL)
3078 @list='$(dist_applications_DATA)'; test -n "$(applicationsdir)" || list=; \
3079 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3080 dir='$(DESTDIR)$(applicationsdir)'; $(am__uninstall_files_from_dir)
3081 install-dist_docs_notocDATA: $(dist_docs_notoc_DATA)
3082 @$(NORMAL_INSTALL)
3083 @list='$(dist_docs_notoc_DATA)'; test -n "$(docs_notocdir)" || list=; \
3084 if test -n "$$list"; then \
3085 echo " $(MKDIR_P) '$(DESTDIR)$(docs_notocdir)'"; \
3086 $(MKDIR_P) "$(DESTDIR)$(docs_notocdir)" || exit 1; \
3087 fi; \
3088 for p in $$list; do \
3089 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3090 echo "$$d$$p"; \
3091 done | $(am__base_list) | \
3092 while read files; do \
3093 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docs_notocdir)'"; \
3094 $(INSTALL_DATA) $$files "$(DESTDIR)$(docs_notocdir)" || exit $$?; \
3095 done
3096
3097 uninstall-dist_docs_notocDATA:
3098 @$(NORMAL_UNINSTALL)
3099 @list='$(dist_docs_notoc_DATA)'; test -n "$(docs_notocdir)" || list=; \
3100 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3101 dir='$(DESTDIR)$(docs_notocdir)'; $(am__uninstall_files_from_dir)
3102 install-dist_docs_podDATA: $(dist_docs_pod_DATA)
3103 @$(NORMAL_INSTALL)
3104 @list='$(dist_docs_pod_DATA)'; test -n "$(docs_poddir)" || list=; \
3105 if test -n "$$list"; then \
3106 echo " $(MKDIR_P) '$(DESTDIR)$(docs_poddir)'"; \
3107 $(MKDIR_P) "$(DESTDIR)$(docs_poddir)" || exit 1; \
3108 fi; \
3109 for p in $$list; do \
3110 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3111 echo "$$d$$p"; \
3112 done | $(am__base_list) | \
3113 while read files; do \
3114 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docs_poddir)'"; \
3115 $(INSTALL_DATA) $$files "$(DESTDIR)$(docs_poddir)" || exit $$?; \
3116 done
3117
3118 uninstall-dist_docs_podDATA:
3119 @$(NORMAL_UNINSTALL)
3120 @list='$(dist_docs_pod_DATA)'; test -n "$(docs_poddir)" || list=; \
3121 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3122 dir='$(DESTDIR)$(docs_poddir)'; $(am__uninstall_files_from_dir)
3123 install-dist_docs_tocDATA: $(dist_docs_toc_DATA)
3124 @$(NORMAL_INSTALL)
3125 @list='$(dist_docs_toc_DATA)'; test -n "$(docs_tocdir)" || list=; \
3126 if test -n "$$list"; then \
3127 echo " $(MKDIR_P) '$(DESTDIR)$(docs_tocdir)'"; \
3128 $(MKDIR_P) "$(DESTDIR)$(docs_tocdir)" || exit 1; \
3129 fi; \
3130 for p in $$list; do \
3131 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3132 echo "$$d$$p"; \
3133 done | $(am__base_list) | \
3134 while read files; do \
3135 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docs_tocdir)'"; \
3136 $(INSTALL_DATA) $$files "$(DESTDIR)$(docs_tocdir)" || exit $$?; \
3137 done
3138
3139 uninstall-dist_docs_tocDATA:
3140 @$(NORMAL_UNINSTALL)
3141 @list='$(dist_docs_toc_DATA)'; test -n "$(docs_tocdir)" || list=; \
3142 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3143 dir='$(DESTDIR)$(docs_tocdir)'; $(am__uninstall_files_from_dir)
3144 install-dist_i3confDATA: $(dist_i3conf_DATA)
3145 @$(NORMAL_INSTALL)
3146 @list='$(dist_i3conf_DATA)'; test -n "$(i3confdir)" || list=; \
3147 if test -n "$$list"; then \
3148 echo " $(MKDIR_P) '$(DESTDIR)$(i3confdir)'"; \
3149 $(MKDIR_P) "$(DESTDIR)$(i3confdir)" || exit 1; \
3150 fi; \
3151 for p in $$list; do \
3152 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3153 echo "$$d$$p"; \
3154 done | $(am__base_list) | \
3155 while read files; do \
3156 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(i3confdir)'"; \
3157 $(INSTALL_DATA) $$files "$(DESTDIR)$(i3confdir)" || exit $$?; \
3158 done
3159
3160 uninstall-dist_i3confDATA:
3161 @$(NORMAL_UNINSTALL)
3162 @list='$(dist_i3conf_DATA)'; test -n "$(i3confdir)" || list=; \
3163 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3164 dir='$(DESTDIR)$(i3confdir)'; $(am__uninstall_files_from_dir)
3165 install-dist_xsessionsDATA: $(dist_xsessions_DATA)
3166 @$(NORMAL_INSTALL)
3167 @list='$(dist_xsessions_DATA)'; test -n "$(xsessionsdir)" || list=; \
3168 if test -n "$$list"; then \
3169 echo " $(MKDIR_P) '$(DESTDIR)$(xsessionsdir)'"; \
3170 $(MKDIR_P) "$(DESTDIR)$(xsessionsdir)" || exit 1; \
3171 fi; \
3172 for p in $$list; do \
3173 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3174 echo "$$d$$p"; \
3175 done | $(am__base_list) | \
3176 while read files; do \
3177 echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(xsessionsdir)'"; \
3178 $(INSTALL_DATA) $$files "$(DESTDIR)$(xsessionsdir)" || exit $$?; \
3179 done
3180
3181 uninstall-dist_xsessionsDATA:
3182 @$(NORMAL_UNINSTALL)
3183 @list='$(dist_xsessions_DATA)'; test -n "$(xsessionsdir)" || list=; \
3184 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3185 dir='$(DESTDIR)$(xsessionsdir)'; $(am__uninstall_files_from_dir)
3186 install-i3includeHEADERS: $(i3include_HEADERS)
3187 @$(NORMAL_INSTALL)
3188 @list='$(i3include_HEADERS)'; test -n "$(i3includedir)" || list=; \
3189 if test -n "$$list"; then \
3190 echo " $(MKDIR_P) '$(DESTDIR)$(i3includedir)'"; \
3191 $(MKDIR_P) "$(DESTDIR)$(i3includedir)" || exit 1; \
3192 fi; \
3193 for p in $$list; do \
3194 if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
3195 echo "$$d$$p"; \
3196 done | $(am__base_list) | \
3197 while read files; do \
3198 echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(i3includedir)'"; \
3199 $(INSTALL_HEADER) $$files "$(DESTDIR)$(i3includedir)" || exit $$?; \
3200 done
3201
3202 uninstall-i3includeHEADERS:
3203 @$(NORMAL_UNINSTALL)
3204 @list='$(i3include_HEADERS)'; test -n "$(i3includedir)" || list=; \
3205 files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
3206 dir='$(DESTDIR)$(i3includedir)'; $(am__uninstall_files_from_dir)
3207
3208 ID: $(am__tagged_files)
3209 $(am__define_uniq_tagged_files); mkid -fID $$unique
3210 tags: tags-am
3211 TAGS: tags
3212
3213 tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
3214 set x; \
3215 here=`pwd`; \
3216 $(am__define_uniq_tagged_files); \
3217 shift; \
3218 if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
3219 test -n "$$unique" || unique=$$empty_fix; \
3220 if test $$# -gt 0; then \
3221 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
3222 "$$@" $$unique; \
3223 else \
3224 $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
3225 $$unique; \
3226 fi; \
3227 fi
3228 ctags: ctags-am
3229
3230 CTAGS: ctags
3231 ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
3232 $(am__define_uniq_tagged_files); \
3233 test -z "$(CTAGS_ARGS)$$unique" \
3234 || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
3235 $$unique
3236
3237 GTAGS:
3238 here=`$(am__cd) $(top_builddir) && pwd` \
3239 && $(am__cd) $(top_srcdir) \
3240 && gtags -i $(GTAGS_ARGS) "$$here"
3241 cscope: cscope.files
3242 test ! -s cscope.files \
3243 || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
3244 clean-cscope:
3245 -rm -f cscope.files
3246 cscope.files: clean-cscope cscopelist
3247 cscopelist: cscopelist-am
3248
3249 cscopelist-am: $(am__tagged_files)
3250 list='$(am__tagged_files)'; \
3251 case "$(srcdir)" in \
3252 [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
3253 *) sdir=$(subdir)/$(srcdir) ;; \
3254 esac; \
3255 for i in $$list; do \
3256 if test -f "$$i"; then \
3257 echo "$(subdir)/$$i"; \
3258 else \
3259 echo "$$sdir/$$i"; \
3260 fi; \
3261 done >> $(top_builddir)/cscope.files
3262
3263 distclean-tags:
3264 -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
3265 -rm -f cscope.out cscope.in.out cscope.po.out cscope.files
3266
3267 # Recover from deleted '.trs' file; this should ensure that
3268 # "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
3269 # both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
3270 # to avoid problems with "make -n".
3271 .log.trs:
3272 rm -f $< $@
3273 $(MAKE) $(AM_MAKEFLAGS) $<
3274
3275 # Leading 'am--fnord' is there to ensure the list of targets does not
3276 # expand to empty, as could happen e.g. with make check TESTS=''.
3277 am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
3278 am--force-recheck:
3279 @:
3280
3281 $(TEST_SUITE_LOG): $(TEST_LOGS)
3282 @$(am__set_TESTS_bases); \
3283 am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
3284 redo_bases=`for i in $$bases; do \
3285 am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
3286 done`; \
3287 if test -n "$$redo_bases"; then \
3288 redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
3289 redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
3290 if $(am__make_dryrun); then :; else \
3291 rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
3292 fi; \
3293 fi; \
3294 if test -n "$$am__remaking_logs"; then \
3295 echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
3296 "recursion detected" >&2; \
3297 elif test -n "$$redo_logs"; then \
3298 am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
3299 fi; \
3300 if $(am__make_dryrun); then :; else \
3301 st=0; \
3302 errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
3303 for i in $$redo_bases; do \
3304 test -f $$i.trs && test -r $$i.trs \
3305 || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
3306 test -f $$i.log && test -r $$i.log \
3307 || { echo "$$errmsg $$i.log" >&2; st=1; }; \
3308 done; \
3309 test $$st -eq 0 || exit 1; \
3310 fi
3311 @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
3312 ws='[ ]'; \
3313 results=`for b in $$bases; do echo $$b.trs; done`; \
3314 test -n "$$results" || results=/dev/null; \
3315 all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
3316 pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
3317 fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
3318 skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
3319 xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
3320 xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
3321 error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
3322 if test `expr $$fail + $$xpass + $$error` -eq 0; then \
3323 success=true; \
3324 else \
3325 success=false; \
3326 fi; \
3327 br='==================='; br=$$br$$br$$br$$br; \
3328 result_count () \
3329 { \
3330 if test x"$$1" = x"--maybe-color"; then \
3331 maybe_colorize=yes; \
3332 elif test x"$$1" = x"--no-color"; then \
3333 maybe_colorize=no; \
3334 else \
3335 echo "$@: invalid 'result_count' usage" >&2; exit 4; \
3336 fi; \
3337 shift; \
3338 desc=$$1 count=$$2; \
3339 if test $$maybe_colorize = yes && test $$count -gt 0; then \
3340 color_start=$$3 color_end=$$std; \
3341 else \
3342 color_start= color_end=; \
3343 fi; \
3344 echo "$${color_start}# $$desc $$count$${color_end}"; \
3345 }; \
3346 create_testsuite_report () \
3347 { \
3348 result_count $$1 "TOTAL:" $$all "$$brg"; \
3349 result_count $$1 "PASS: " $$pass "$$grn"; \
3350 result_count $$1 "SKIP: " $$skip "$$blu"; \
3351 result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
3352 result_count $$1 "FAIL: " $$fail "$$red"; \
3353 result_count $$1 "XPASS:" $$xpass "$$red"; \
3354 result_count $$1 "ERROR:" $$error "$$mgn"; \
3355 }; \
3356 { \
3357 echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
3358 $(am__rst_title); \
3359 create_testsuite_report --no-color; \
3360 echo; \
3361 echo ".. contents:: :depth: 2"; \
3362 echo; \
3363 for b in $$bases; do echo $$b; done \
3364 | $(am__create_global_log); \
3365 } >$(TEST_SUITE_LOG).tmp || exit 1; \
3366 mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
3367 if $$success; then \
3368 col="$$grn"; \
3369 else \
3370 col="$$red"; \
3371 test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
3372 fi; \
3373 echo "$${col}$$br$${std}"; \
3374 echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
3375 echo "$${col}$$br$${std}"; \
3376 create_testsuite_report --maybe-color; \
3377 echo "$$col$$br$$std"; \
3378 if $$success; then :; else \
3379 echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
3380 if test -n "$(PACKAGE_BUGREPORT)"; then \
3381 echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
3382 fi; \
3383 echo "$$col$$br$$std"; \
3384 fi; \
3385 $$success || exit 1
3386
3387 check-TESTS: $(check_PROGRAMS) $(check_SCRIPTS) $(check_DATA)
3388 @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
3389 @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
3390 @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
3391 @set +e; $(am__set_TESTS_bases); \
3392 log_list=`for i in $$bases; do echo $$i.log; done`; \
3393 trs_list=`for i in $$bases; do echo $$i.trs; done`; \
3394 log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
3395 $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
3396 exit $$?;
3397 recheck: all $(check_PROGRAMS) $(check_SCRIPTS) $(check_DATA)
3398 @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
3399 @set +e; $(am__set_TESTS_bases); \
3400 bases=`for i in $$bases; do echo $$i; done \
3401 | $(am__list_recheck_tests)` || exit 1; \
3402 log_list=`for i in $$bases; do echo $$i.log; done`; \
3403 log_list=`echo $$log_list`; \
3404 $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
3405 am__force_recheck=am--force-recheck \
3406 TEST_LOGS="$$log_list"; \
3407 exit $$?
3408 testcases/complete-run.pl.log: testcases/complete-run.pl
3409 @p='testcases/complete-run.pl'; \
3410 b='testcases/complete-run.pl'; \
3411 $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
3412 --log-file $$b.log --trs-file $$b.trs \
3413 $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
3414 "$$tst" $(AM_TESTS_FD_REDIRECT)
3415 .test.log:
3416 @p='$<'; \
3417 $(am__set_b); \
3418 $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
3419 --log-file $$b.log --trs-file $$b.trs \
3420 $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
3421 "$$tst" $(AM_TESTS_FD_REDIRECT)
3422 @am__EXEEXT_TRUE@.test$(EXEEXT).log:
3423 @am__EXEEXT_TRUE@ @p='$<'; \
3424 @am__EXEEXT_TRUE@ $(am__set_b); \
3425 @am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
3426 @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
3427 @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
3428 @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
3429
3430 distdir: $(BUILT_SOURCES)
3431 $(MAKE) $(AM_MAKEFLAGS) distdir-am
3432
3433 distdir-am: $(DISTFILES)
3434 $(am__remove_distdir)
3435 test -d "$(distdir)" || mkdir "$(distdir)"
3436 @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
3437 topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
3438 list='$(DISTFILES)'; \
3439 dist_files=`for file in $$list; do echo $$file; done | \
3440 sed -e "s|^$$srcdirstrip/||;t" \
3441 -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
3442 case $$dist_files in \
3443 */*) $(MKDIR_P) `echo "$$dist_files" | \
3444 sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
3445 sort -u` ;; \
3446 esac; \
3447 for file in $$dist_files; do \
3448 if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
3449 if test -d $$d/$$file; then \
3450 dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
3451 if test -d "$(distdir)/$$file"; then \
3452 find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
3453 fi; \
3454 if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
3455 cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
3456 find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
3457 fi; \
3458 cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
3459 else \
3460 test -f "$(distdir)/$$file" \
3461 || cp -p $$d/$$file "$(distdir)/$$file" \
3462 || exit 1; \
3463 fi; \
3464 done
3465 -test -n "$(am__skip_mode_fix)" \
3466 || find "$(distdir)" -type d ! -perm -755 \
3467 -exec chmod u+rwx,go+rx {} \; -o \
3468 ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
3469 ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
3470 ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
3471 || chmod -R a+r "$(distdir)"
3472 dist-gzip: distdir
3473 tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
3474 $(am__post_remove_distdir)
3475 dist-bzip2: distdir
3476 tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
3477 $(am__post_remove_distdir)
3478
3479 dist-lzip: distdir
3480 tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
3481 $(am__post_remove_distdir)
3482
3483 dist-xz: distdir
3484 tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
3485 $(am__post_remove_distdir)
3486
3487 dist-tarZ: distdir
3488 @echo WARNING: "Support for distribution archives compressed with" \
3489 "legacy program 'compress' is deprecated." >&2
3490 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
3491 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
3492 $(am__post_remove_distdir)
3493
3494 dist-shar: distdir
3495 @echo WARNING: "Support for shar distribution archives is" \
3496 "deprecated." >&2
3497 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
3498 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
3499 $(am__post_remove_distdir)
3500
3501 dist-zip: distdir
3502 -rm -f $(distdir).zip
3503 zip -rq $(distdir).zip $(distdir)
3504 $(am__post_remove_distdir)
3505
3506 dist dist-all:
3507 $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
3508 $(am__post_remove_distdir)
3509
3510 # This target untars the dist file and tries a VPATH configuration. Then
3511 # it guarantees that the distribution is self-contained by making another
3512 # tarfile.
3513 distcheck: dist
3514 case '$(DIST_ARCHIVES)' in \
3515 *.tar.gz*) \
3516 eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
3517 *.tar.bz2*) \
3518 bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
3519 *.tar.lz*) \
3520 lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
3521 *.tar.xz*) \
3522 xz -dc $(distdir).tar.xz | $(am__untar) ;;\
3523 *.tar.Z*) \
3524 uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
3525 *.shar.gz*) \
3526 eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
3527 *.zip*) \
3528 unzip $(distdir).zip ;;\
3529 esac
3530 chmod -R a-w $(distdir)
3531 chmod u+w $(distdir)
3532 mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
3533 chmod a-w $(distdir)
3534 test -d $(distdir)/_build || exit 0; \
3535 dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
3536 && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
3537 && am__cwd=`pwd` \
3538 && $(am__cd) $(distdir)/_build/sub \
3539 && ../../configure \
3540 $(AM_DISTCHECK_CONFIGURE_FLAGS) \
3541 $(DISTCHECK_CONFIGURE_FLAGS) \
3542 --srcdir=../.. --prefix="$$dc_install_base" \
3543 && $(MAKE) $(AM_MAKEFLAGS) \
3544 && $(MAKE) $(AM_MAKEFLAGS) dvi \
3545 && $(MAKE) $(AM_MAKEFLAGS) check \
3546 && $(MAKE) $(AM_MAKEFLAGS) install \
3547 && $(MAKE) $(AM_MAKEFLAGS) installcheck \
3548 && $(MAKE) $(AM_MAKEFLAGS) uninstall \
3549 && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
3550 distuninstallcheck \
3551 && chmod -R a-w "$$dc_install_base" \
3552 && ({ \
3553 (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
3554 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
3555 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
3556 && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
3557 distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
3558 } || { rm -rf "$$dc_destdir"; exit 1; }) \
3559 && rm -rf "$$dc_destdir" \
3560 && $(MAKE) $(AM_MAKEFLAGS) dist \
3561 && rm -rf $(DIST_ARCHIVES) \
3562 && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
3563 && cd "$$am__cwd" \
3564 || exit 1
3565 $(am__post_remove_distdir)
3566 @(echo "$(distdir) archives ready for distribution: "; \
3567 list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
3568 sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
3569 distuninstallcheck:
3570 @test -n '$(distuninstallcheck_dir)' || { \
3571 echo 'ERROR: trying to run $@ with an empty' \
3572 '$$(distuninstallcheck_dir)' >&2; \
3573 exit 1; \
3574 }; \
3575 $(am__cd) '$(distuninstallcheck_dir)' || { \
3576 echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
3577 exit 1; \
3578 }; \
3579 test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
3580 || { echo "ERROR: files left after uninstall:" ; \
3581 if test -n "$(DESTDIR)"; then \
3582 echo " (check DESTDIR support)"; \
3583 fi ; \
3584 $(distuninstallcheck_listfiles) ; \
3585 exit 1; } >&2
3586 distcleancheck: distclean
3587 @if test '$(srcdir)' = . ; then \
3588 echo "ERROR: distcleancheck can only run from a VPATH build" ; \
3589 exit 1 ; \
3590 fi
3591 @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
3592 || { echo "ERROR: files left in build directory after distclean:" ; \
3593 $(distcleancheck_listfiles) ; \
3594 exit 1; } >&2
3595 check-am: all-am
3596 $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(check_SCRIPTS) \
3597 $(check_DATA)
3598 $(MAKE) $(AM_MAKEFLAGS) check-TESTS
3599 check: check-am
3600 all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(SCRIPTS) $(MANS) $(DATA) \
3601 $(HEADERS) config.h
3602 installdirs:
3603 for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(applicationsdir)" "$(DESTDIR)$(docs_notocdir)" "$(DESTDIR)$(docs_poddir)" "$(DESTDIR)$(docs_tocdir)" "$(DESTDIR)$(i3confdir)" "$(DESTDIR)$(xsessionsdir)" "$(DESTDIR)$(i3includedir)"; do \
3604 test -z "$$dir" || $(MKDIR_P) "$$dir"; \
3605 done
3606 install: install-am
3607 install-exec: install-exec-am
3608 install-data: install-data-am
3609 uninstall: uninstall-am
3610
3611 install-am: all-am
3612 @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
3613
3614 installcheck: installcheck-am
3615 install-strip:
3616 if test -z '$(STRIP)'; then \
3617 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
3618 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
3619 install; \
3620 else \
3621 $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
3622 install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
3623 "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
3624 fi
3625 mostlyclean-generic:
3626 -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
3627 -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
3628 -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
3629
3630 clean-generic:
3631 -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
3632
3633 distclean-generic:
3634 -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
3635 -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
3636 -rm -f i3-config-wizard/$(DEPDIR)/$(am__dirstamp)
3637 -rm -f i3-config-wizard/$(am__dirstamp)
3638 -rm -f i3-dump-log/$(DEPDIR)/$(am__dirstamp)
3639 -rm -f i3-dump-log/$(am__dirstamp)
3640 -rm -f i3-input/$(DEPDIR)/$(am__dirstamp)
3641 -rm -f i3-input/$(am__dirstamp)
3642 -rm -f i3-msg/$(DEPDIR)/$(am__dirstamp)
3643 -rm -f i3-msg/$(am__dirstamp)
3644 -rm -f i3-nagbar/$(DEPDIR)/$(am__dirstamp)
3645 -rm -f i3-nagbar/$(am__dirstamp)
3646 -rm -f i3bar/$(am__dirstamp)
3647 -rm -f i3bar/src/$(DEPDIR)/$(am__dirstamp)
3648 -rm -f i3bar/src/$(am__dirstamp)
3649 -rm -f libi3/$(DEPDIR)/$(am__dirstamp)
3650 -rm -f libi3/$(am__dirstamp)
3651 -rm -f src/$(DEPDIR)/$(am__dirstamp)
3652 -rm -f src/$(am__dirstamp)
3653 -rm -f testcases/$(DEPDIR)/$(am__dirstamp)
3654 -rm -f testcases/$(am__dirstamp)
3655 -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
3656
3657 maintainer-clean-generic:
3658 @echo "This command is intended for maintainers to use"
3659 @echo "it deletes files that may require special tools to rebuild."
3660 clean: clean-am
3661
3662 clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
3663 clean-local clean-noinstLIBRARIES mostlyclean-am
3664
3665 distclean: distclean-am
3666 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
3667 -rm -f i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Po
3668 -rm -f i3-dump-log/$(DEPDIR)/i3_dump_log-main.Po
3669 -rm -f i3-input/$(DEPDIR)/i3_input-keysym2ucs.Po
3670 -rm -f i3-input/$(DEPDIR)/i3_input-main.Po
3671 -rm -f i3-msg/$(DEPDIR)/i3_msg-main.Po
3672 -rm -f i3-nagbar/$(DEPDIR)/i3_nagbar-main.Po
3673 -rm -f i3bar/src/$(DEPDIR)/i3bar-child.Po
3674 -rm -f i3bar/src/$(DEPDIR)/i3bar-config.Po
3675 -rm -f i3bar/src/$(DEPDIR)/i3bar-ipc.Po
3676 -rm -f i3bar/src/$(DEPDIR)/i3bar-main.Po
3677 -rm -f i3bar/src/$(DEPDIR)/i3bar-mode.Po
3678 -rm -f i3bar/src/$(DEPDIR)/i3bar-outputs.Po
3679 -rm -f i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Po
3680 -rm -f i3bar/src/$(DEPDIR)/i3bar-workspaces.Po
3681 -rm -f i3bar/src/$(DEPDIR)/i3bar-xcb.Po
3682 -rm -f libi3/$(DEPDIR)/a-dpi.Po
3683 -rm -f libi3/$(DEPDIR)/a-draw_util.Po
3684 -rm -f libi3/$(DEPDIR)/a-fake_configure_notify.Po
3685 -rm -f libi3/$(DEPDIR)/a-font.Po
3686 -rm -f libi3/$(DEPDIR)/a-format_placeholders.Po
3687 -rm -f libi3/$(DEPDIR)/a-g_utf8_make_valid.Po
3688 -rm -f libi3/$(DEPDIR)/a-get_colorpixel.Po
3689 -rm -f libi3/$(DEPDIR)/a-get_config_path.Po
3690 -rm -f libi3/$(DEPDIR)/a-get_exe_path.Po
3691 -rm -f libi3/$(DEPDIR)/a-get_mod_mask.Po
3692 -rm -f libi3/$(DEPDIR)/a-get_process_filename.Po
3693 -rm -f libi3/$(DEPDIR)/a-get_visualtype.Po
3694 -rm -f libi3/$(DEPDIR)/a-ipc_connect.Po
3695 -rm -f libi3/$(DEPDIR)/a-ipc_recv_message.Po
3696 -rm -f libi3/$(DEPDIR)/a-ipc_send_message.Po
3697 -rm -f libi3/$(DEPDIR)/a-is_debug_build.Po
3698 -rm -f libi3/$(DEPDIR)/a-mkdirp.Po
3699 -rm -f libi3/$(DEPDIR)/a-resolve_tilde.Po
3700 -rm -f libi3/$(DEPDIR)/a-root_atom_contents.Po
3701 -rm -f libi3/$(DEPDIR)/a-safewrappers.Po
3702 -rm -f libi3/$(DEPDIR)/a-string.Po
3703 -rm -f libi3/$(DEPDIR)/a-strndup.Po
3704 -rm -f libi3/$(DEPDIR)/a-ucs2_conversion.Po
3705 -rm -f src/$(DEPDIR)/i3-assignments.Po
3706 -rm -f src/$(DEPDIR)/i3-bindings.Po
3707 -rm -f src/$(DEPDIR)/i3-click.Po
3708 -rm -f src/$(DEPDIR)/i3-commands.Po
3709 -rm -f src/$(DEPDIR)/i3-commands_parser.Po
3710 -rm -f src/$(DEPDIR)/i3-con.Po
3711 -rm -f src/$(DEPDIR)/i3-config.Po
3712 -rm -f src/$(DEPDIR)/i3-config_directives.Po
3713 -rm -f src/$(DEPDIR)/i3-config_parser.Po
3714 -rm -f src/$(DEPDIR)/i3-display_version.Po
3715 -rm -f src/$(DEPDIR)/i3-ewmh.Po
3716 -rm -f src/$(DEPDIR)/i3-fake_outputs.Po
3717 -rm -f src/$(DEPDIR)/i3-floating.Po
3718 -rm -f src/$(DEPDIR)/i3-handlers.Po
3719 -rm -f src/$(DEPDIR)/i3-ipc.Po
3720 -rm -f src/$(DEPDIR)/i3-key_press.Po
3721 -rm -f src/$(DEPDIR)/i3-load_layout.Po
3722 -rm -f src/$(DEPDIR)/i3-log.Po
3723 -rm -f src/$(DEPDIR)/i3-main.Po
3724 -rm -f src/$(DEPDIR)/i3-manage.Po
3725 -rm -f src/$(DEPDIR)/i3-match.Po
3726 -rm -f src/$(DEPDIR)/i3-move.Po
3727 -rm -f src/$(DEPDIR)/i3-output.Po
3728 -rm -f src/$(DEPDIR)/i3-randr.Po
3729 -rm -f src/$(DEPDIR)/i3-regex.Po
3730 -rm -f src/$(DEPDIR)/i3-render.Po
3731 -rm -f src/$(DEPDIR)/i3-resize.Po
3732 -rm -f src/$(DEPDIR)/i3-restore_layout.Po
3733 -rm -f src/$(DEPDIR)/i3-scratchpad.Po
3734 -rm -f src/$(DEPDIR)/i3-sd-daemon.Po
3735 -rm -f src/$(DEPDIR)/i3-sighandler.Po
3736 -rm -f src/$(DEPDIR)/i3-startup.Po
3737 -rm -f src/$(DEPDIR)/i3-sync.Po
3738 -rm -f src/$(DEPDIR)/i3-tree.Po
3739 -rm -f src/$(DEPDIR)/i3-util.Po
3740 -rm -f src/$(DEPDIR)/i3-version.Po
3741 -rm -f src/$(DEPDIR)/i3-window.Po
3742 -rm -f src/$(DEPDIR)/i3-workspace.Po
3743 -rm -f src/$(DEPDIR)/i3-x.Po
3744 -rm -f src/$(DEPDIR)/i3-xcb.Po
3745 -rm -f src/$(DEPDIR)/i3-xcursor.Po
3746 -rm -f src/$(DEPDIR)/i3-xinerama.Po
3747 -rm -f src/$(DEPDIR)/test_commands_parser-commands_parser.Po
3748 -rm -f src/$(DEPDIR)/test_config_parser-config_parser.Po
3749 -rm -f testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Po
3750 -rm -f Makefile
3751 distclean-am: clean-am distclean-compile distclean-generic \
3752 distclean-hdr distclean-tags
3753
3754 dvi: dvi-am
3755
3756 dvi-am:
3757
3758 html: html-am
3759
3760 html-am:
3761
3762 info: info-am
3763
3764 info-am:
3765
3766 install-data-am: install-dist_applicationsDATA \
3767 install-dist_docs_notocDATA install-dist_docs_podDATA \
3768 install-dist_docs_tocDATA install-dist_i3confDATA \
3769 install-dist_xsessionsDATA install-i3includeHEADERS \
3770 install-man
3771
3772 install-dvi: install-dvi-am
3773
3774 install-dvi-am:
3775
3776 install-exec-am: install-binPROGRAMS install-dist_binSCRIPTS
3777 @$(NORMAL_INSTALL)
3778 $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
3779 install-html: install-html-am
3780
3781 install-html-am:
3782
3783 install-info: install-info-am
3784
3785 install-info-am:
3786
3787 install-man: install-man1
3788
3789 install-pdf: install-pdf-am
3790
3791 install-pdf-am:
3792
3793 install-ps: install-ps-am
3794
3795 install-ps-am:
3796
3797 installcheck-am:
3798
3799 maintainer-clean: maintainer-clean-am
3800 -rm -f $(am__CONFIG_DISTCLEAN_FILES)
3801 -rm -rf $(top_srcdir)/autom4te.cache
3802 -rm -f i3-config-wizard/$(DEPDIR)/i3_config_wizard-main.Po
3803 -rm -f i3-dump-log/$(DEPDIR)/i3_dump_log-main.Po
3804 -rm -f i3-input/$(DEPDIR)/i3_input-keysym2ucs.Po
3805 -rm -f i3-input/$(DEPDIR)/i3_input-main.Po
3806 -rm -f i3-msg/$(DEPDIR)/i3_msg-main.Po
3807 -rm -f i3-nagbar/$(DEPDIR)/i3_nagbar-main.Po
3808 -rm -f i3bar/src/$(DEPDIR)/i3bar-child.Po
3809 -rm -f i3bar/src/$(DEPDIR)/i3bar-config.Po
3810 -rm -f i3bar/src/$(DEPDIR)/i3bar-ipc.Po
3811 -rm -f i3bar/src/$(DEPDIR)/i3bar-main.Po
3812 -rm -f i3bar/src/$(DEPDIR)/i3bar-mode.Po
3813 -rm -f i3bar/src/$(DEPDIR)/i3bar-outputs.Po
3814 -rm -f i3bar/src/$(DEPDIR)/i3bar-parse_json_header.Po
3815 -rm -f i3bar/src/$(DEPDIR)/i3bar-workspaces.Po
3816 -rm -f i3bar/src/$(DEPDIR)/i3bar-xcb.Po
3817 -rm -f libi3/$(DEPDIR)/a-dpi.Po
3818 -rm -f libi3/$(DEPDIR)/a-draw_util.Po
3819 -rm -f libi3/$(DEPDIR)/a-fake_configure_notify.Po
3820 -rm -f libi3/$(DEPDIR)/a-font.Po
3821 -rm -f libi3/$(DEPDIR)/a-format_placeholders.Po
3822 -rm -f libi3/$(DEPDIR)/a-g_utf8_make_valid.Po
3823 -rm -f libi3/$(DEPDIR)/a-get_colorpixel.Po
3824 -rm -f libi3/$(DEPDIR)/a-get_config_path.Po
3825 -rm -f libi3/$(DEPDIR)/a-get_exe_path.Po
3826 -rm -f libi3/$(DEPDIR)/a-get_mod_mask.Po
3827 -rm -f libi3/$(DEPDIR)/a-get_process_filename.Po
3828 -rm -f libi3/$(DEPDIR)/a-get_visualtype.Po
3829 -rm -f libi3/$(DEPDIR)/a-ipc_connect.Po
3830 -rm -f libi3/$(DEPDIR)/a-ipc_recv_message.Po
3831 -rm -f libi3/$(DEPDIR)/a-ipc_send_message.Po
3832 -rm -f libi3/$(DEPDIR)/a-is_debug_build.Po
3833 -rm -f libi3/$(DEPDIR)/a-mkdirp.Po
3834 -rm -f libi3/$(DEPDIR)/a-resolve_tilde.Po
3835 -rm -f libi3/$(DEPDIR)/a-root_atom_contents.Po
3836 -rm -f libi3/$(DEPDIR)/a-safewrappers.Po
3837 -rm -f libi3/$(DEPDIR)/a-string.Po
3838 -rm -f libi3/$(DEPDIR)/a-strndup.Po
3839 -rm -f libi3/$(DEPDIR)/a-ucs2_conversion.Po
3840 -rm -f src/$(DEPDIR)/i3-assignments.Po
3841 -rm -f src/$(DEPDIR)/i3-bindings.Po
3842 -rm -f src/$(DEPDIR)/i3-click.Po
3843 -rm -f src/$(DEPDIR)/i3-commands.Po
3844 -rm -f src/$(DEPDIR)/i3-commands_parser.Po
3845 -rm -f src/$(DEPDIR)/i3-con.Po
3846 -rm -f src/$(DEPDIR)/i3-config.Po
3847 -rm -f src/$(DEPDIR)/i3-config_directives.Po
3848 -rm -f src/$(DEPDIR)/i3-config_parser.Po
3849 -rm -f src/$(DEPDIR)/i3-display_version.Po
3850 -rm -f src/$(DEPDIR)/i3-ewmh.Po
3851 -rm -f src/$(DEPDIR)/i3-fake_outputs.Po
3852 -rm -f src/$(DEPDIR)/i3-floating.Po
3853 -rm -f src/$(DEPDIR)/i3-handlers.Po
3854 -rm -f src/$(DEPDIR)/i3-ipc.Po
3855 -rm -f src/$(DEPDIR)/i3-key_press.Po
3856 -rm -f src/$(DEPDIR)/i3-load_layout.Po
3857 -rm -f src/$(DEPDIR)/i3-log.Po
3858 -rm -f src/$(DEPDIR)/i3-main.Po
3859 -rm -f src/$(DEPDIR)/i3-manage.Po
3860 -rm -f src/$(DEPDIR)/i3-match.Po
3861 -rm -f src/$(DEPDIR)/i3-move.Po
3862 -rm -f src/$(DEPDIR)/i3-output.Po
3863 -rm -f src/$(DEPDIR)/i3-randr.Po
3864 -rm -f src/$(DEPDIR)/i3-regex.Po
3865 -rm -f src/$(DEPDIR)/i3-render.Po
3866 -rm -f src/$(DEPDIR)/i3-resize.Po
3867 -rm -f src/$(DEPDIR)/i3-restore_layout.Po
3868 -rm -f src/$(DEPDIR)/i3-scratchpad.Po
3869 -rm -f src/$(DEPDIR)/i3-sd-daemon.Po
3870 -rm -f src/$(DEPDIR)/i3-sighandler.Po
3871 -rm -f src/$(DEPDIR)/i3-startup.Po
3872 -rm -f src/$(DEPDIR)/i3-sync.Po
3873 -rm -f src/$(DEPDIR)/i3-tree.Po
3874 -rm -f src/$(DEPDIR)/i3-util.Po
3875 -rm -f src/$(DEPDIR)/i3-version.Po
3876 -rm -f src/$(DEPDIR)/i3-window.Po
3877 -rm -f src/$(DEPDIR)/i3-workspace.Po
3878 -rm -f src/$(DEPDIR)/i3-x.Po
3879 -rm -f src/$(DEPDIR)/i3-xcb.Po
3880 -rm -f src/$(DEPDIR)/i3-xcursor.Po
3881 -rm -f src/$(DEPDIR)/i3-xinerama.Po
3882 -rm -f src/$(DEPDIR)/test_commands_parser-commands_parser.Po
3883 -rm -f src/$(DEPDIR)/test_config_parser-config_parser.Po
3884 -rm -f testcases/$(DEPDIR)/test_inject_randr15-inject_randr1.5.Po
3885 -rm -f Makefile
3886 maintainer-clean-am: distclean-am maintainer-clean-generic
3887
3888 mostlyclean: mostlyclean-am
3889
3890 mostlyclean-am: mostlyclean-compile mostlyclean-generic
3891
3892 pdf: pdf-am
3893
3894 pdf-am:
3895
3896 ps: ps-am
3897
3898 ps-am:
3899
3900 uninstall-am: uninstall-binPROGRAMS uninstall-dist_applicationsDATA \
3901 uninstall-dist_binSCRIPTS uninstall-dist_docs_notocDATA \
3902 uninstall-dist_docs_podDATA uninstall-dist_docs_tocDATA \
3903 uninstall-dist_i3confDATA uninstall-dist_xsessionsDATA \
3904 uninstall-i3includeHEADERS uninstall-man
3905 @$(NORMAL_INSTALL)
3906 $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
3907 uninstall-man: uninstall-man1
3908
3909 .MAKE: all check-am install-am install-exec-am install-strip \
3910 uninstall-am
3911
3912 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \
3913 check-TESTS check-am clean clean-binPROGRAMS \
3914 clean-checkPROGRAMS clean-cscope clean-generic clean-local \
3915 clean-noinstLIBRARIES cscope cscopelist-am ctags ctags-am dist \
3916 dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
3917 dist-xz dist-zip distcheck distclean distclean-compile \
3918 distclean-generic distclean-hdr distclean-tags distcleancheck \
3919 distdir distuninstallcheck dvi dvi-am html html-am info \
3920 info-am install install-am install-binPROGRAMS install-data \
3921 install-data-am install-dist_applicationsDATA \
3922 install-dist_binSCRIPTS install-dist_docs_notocDATA \
3923 install-dist_docs_podDATA install-dist_docs_tocDATA \
3924 install-dist_i3confDATA install-dist_xsessionsDATA install-dvi \
3925 install-dvi-am install-exec install-exec-am install-exec-hook \
3926 install-html install-html-am install-i3includeHEADERS \
3927 install-info install-info-am install-man install-man1 \
3928 install-pdf install-pdf-am install-ps install-ps-am \
3929 install-strip installcheck installcheck-am installdirs \
3930 maintainer-clean maintainer-clean-generic mostlyclean \
3931 mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
3932 recheck tags tags-am uninstall uninstall-am \
3933 uninstall-binPROGRAMS uninstall-dist_applicationsDATA \
3934 uninstall-dist_binSCRIPTS uninstall-dist_docs_notocDATA \
3935 uninstall-dist_docs_podDATA uninstall-dist_docs_tocDATA \
3936 uninstall-dist_i3confDATA uninstall-dist_xsessionsDATA \
3937 uninstall-hook uninstall-i3includeHEADERS uninstall-man \
3938 uninstall-man1
3939
3940 .PRECIOUS: Makefile
3941
3942 @CODE_COVERAGE_RULES@
3943
3944 echo-version:
3945 @echo "@I3_VERSION@"
3946
3947 install-exec-hook:
3948 $(LN_S) -f i3 $(DESTDIR)$(bindir)/i3-with-shmlog
3949
3950 uninstall-hook:
3951 rm -f $(DESTDIR)$(bindir)/i3-with-shmlog
3952
3953 clean-check:
3954 rm -rf testsuite-* latest i3-cfg-for-* _Inline
3955 clean-local: clean-check
3956
3957 $(dirstamps):
3958 @stamp='$@'; $(MKDIR_P) "$${stamp%/*}"
3959 @: > $@
3960
3961 @BUILD_DOCS_TRUE@$(dist_docs_toc_DATA): docs/%.html: docs/% docs/$(dirstamp)
3962 @BUILD_DOCS_TRUE@ $(AM_V_GEN) @PATH_ASCIIDOC@ -a toc -n -o $@ $<
3963
3964 @BUILD_DOCS_TRUE@$(dist_docs_notoc_DATA): docs/%.html: docs/% docs/$(dirstamp)
3965 @BUILD_DOCS_TRUE@ $(AM_V_GEN) @PATH_ASCIIDOC@ -n -o $@ $<
3966
3967 @BUILD_DOCS_TRUE@docs/lib-i3test.html: testcases/lib/i3test.pm docs/$(dirstamp)
3968 @BUILD_DOCS_TRUE@ $(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@
3969
3970 @BUILD_DOCS_TRUE@docs/lib-i3test-test.html: testcases/lib/i3test/Test.pm docs/$(dirstamp)
3971 @BUILD_DOCS_TRUE@ $(AM_V_GEN) $(top_srcdir)/docs/i3-pod2html $< $@
3972
3973 @BUILD_MANS_TRUE@$(asciidoc_MANS): man/%.1: man/%.xml man/$(dirstamp)
3974 @BUILD_MANS_TRUE@ $(AM_V_GEN) out='$@'; @PATH_XMLTO@ man -o "$${out%/*}" $<
3975 @BUILD_MANS_TRUE@ @stamp='$@'; $(MKDIR_P) "$${stamp%/*}"
3976
3977 @BUILD_MANS_TRUE@man/%.xml: man/%.man man/asciidoc.conf man/$(dirstamp)
3978 @BUILD_MANS_TRUE@ $(AM_V_GEN) @PATH_ASCIIDOC@ -d manpage -b docbook -f $(top_builddir)/man/asciidoc.conf -o $@ $<
3979
3980 @BUILD_MANS_TRUE@$(pod_MANS): man/%.1: % man/$(dirstamp)
3981 @BUILD_MANS_TRUE@ $(AM_V_GEN) @PATH_POD2MAN@ --utf8 $< > $@
3982
3983 ################################################################################
3984 # parser generation
3985 ################################################################################
3986
3987 $(command_parser_SOURCES): %.h: i3-command-parser.stamp
3988
3989 $(config_parser_SOURCES): %.h: i3-config-parser.stamp
3990
3991 src/i3-commands_parser.$(OBJEXT): i3-command-parser.stamp
3992
3993 src/i3-config_parser.$(OBJEXT): i3-config-parser.stamp
3994
3995 i3-command-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/commands.spec
3996 $(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/commands.spec --prefix=command
3997 $(AM_V_at) mv GENERATED_command_* $(top_builddir)/parser
3998 $(AM_V_at) touch $@
3999
4000 i3-config-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-specs/config.spec
4001 $(AM_V_GEN) $(top_srcdir)/generate-command-parser.pl --input=$(top_srcdir)/parser-specs/config.spec --prefix=config
4002 $(AM_V_at) mv GENERATED_config_* $(top_builddir)/parser
4003 $(AM_V_at) touch $@
4004
4005 ################################################################################
4006 # AnyEvent-I3 build process
4007 ################################################################################
4008
4009 anyevent-i3.stamp: AnyEvent-I3/lib/AnyEvent/I3.pm
4010 $(AM_V_BUILD) (cd $(top_srcdir)/AnyEvent-I3 && perl Makefile.PL && make)
4011 $(AM_V_at) touch $@
4012
4013 # Tell versions [3.59,3.63) of GNU make to not export all variables.
4014 # Otherwise a system limit (for SysV at least) may be exceeded.
4015 .NOEXPORT:
0 Dear package maintainer,
1
2 thanks for packaging i3. By doing so, you are improving your distribution
3 and i3 in general.
4
5 Please read the file DEPENDS now, so you know which libraries are necessary
6 and where to get them from if your distribution does not already have
7 packages for them.
8
9 Please make sure the manpage for i3 will be properly created and installed
10 in your package.
11
12 Please check the i3-sensible-{editor,pager,terminal} scripts and modify them if
13 necessary. The former two provide fallbacks in case $PAGER or $EDITOR is not
14 set (which might be more common than you think, because they have to be set in
15 ~/.xsession, not in the shell configuration!) while the latter tries to launch
16 a terminal emulator. The scripts are most prominently used in i3-nagbar, which
17 alerts the user when the configuration is broken for some reason. Also,
18 i3-sensible-terminal is used in the default configuration.
19
20 If your distribution has a mechanism to get the preferred terminal, such as the
21 x-terminal-emulator symlink in Debian, please use it in i3-sensible-terminal.
22
23 On debian, compilation and installing the manpages looks like this:
24
25 autoreconf -fi
26 mkdir -p build && cd build
27 ../configure
28 make -j8 install
29
30 Please make sure that i3-migrate-config-to-v4 and i3-config-wizard are
31 installed with i3. The Perl script is necessary to (automatically) convert v3
32 configs to v4. The wizard provides the possibility to create a keysym-based
33 config with the user’s preferred modifier and should be started on the first
34 start of i3 (it will automatically exit if it finds a config file).
35
36 If you have any questions, ideas, hints, problems or whatever, please do not
37 hesitate to contact me. I will help you out. Just drop me an E-Mail (find the
38 address at https://michael.stapelberg.de/Impressum/, scroll down to bottom),
39 contact me using the same address in jabber or ask on our IRC channel:
40 (#i3 on irc.freenode.net).
41
42 Thanks again for your efforts,
43 Michael
0
1 ┌──────────────────────────────┐
2 │ Release notes for i3 v4.16.1 │
3 └──────────────────────────────┘
4
5 This is i3 v4.16.1. This version is considered stable. All users of i3 are
6 strongly encouraged to upgrade.
7
8 This is a bugfix release for v4.16.
9
10 ┌────────────────────────────┐
11 │ Bugfixes │
12 └────────────────────────────┘
13
14 • Truncate wm_name utf8 strings to first zero byte
15 (fixes window title corruption)
16 • Apply title_align to non-leaf containers
17 Additionally, marks will now display for non-leaf containers.
18 • attach_to_workspace: set new parent before tree_render
19 (fixes a heap-use-after-free)
20 • Use ipc queue for all messages
21 (fixes an i3bar crash)
22 • Fix crash with popups when fullscreen is non-leaf
23 • Fix: render_con shows floating containers on wrong workspace
24
25
26 ┌────────────────────────────┐
27 │ Thanks! │
28 └────────────────────────────┘
29
30 Thanks for testing, bugfixes, discussions and everything I forgot go out to:
31
32 Orestis Floros
33
34 -- Michael Stapelberg, 2019-01-27
0 # generated automatically by aclocal 1.16.1 -*- Autoconf -*-
1
2 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
3
4 # This file is free software; the Free Software Foundation
5 # gives unlimited permission to copy and/or distribute it,
6 # with or without modifications, as long as this notice is preserved.
7
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY, to the extent permitted by law; without
10 # even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 # PARTICULAR PURPOSE.
12
13 m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
14 m4_ifndef([AC_AUTOCONF_VERSION],
15 [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
16 m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
17 [m4_warning([this file was generated for autoconf 2.69.
18 You have another version of autoconf. It may work, but is not guaranteed to.
19 If you have problems, you may need to regenerate the build system entirely.
20 To do so, use the procedure documented by the package, typically 'autoreconf'.])])
21
22 dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
23 dnl serial 11 (pkg-config-0.29)
24 dnl
25 dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
26 dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
27 dnl
28 dnl This program is free software; you can redistribute it and/or modify
29 dnl it under the terms of the GNU General Public License as published by
30 dnl the Free Software Foundation; either version 2 of the License, or
31 dnl (at your option) any later version.
32 dnl
33 dnl This program is distributed in the hope that it will be useful, but
34 dnl WITHOUT ANY WARRANTY; without even the implied warranty of
35 dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 dnl General Public License for more details.
37 dnl
38 dnl You should have received a copy of the GNU General Public License
39 dnl along with this program; if not, write to the Free Software
40 dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
41 dnl 02111-1307, USA.
42 dnl
43 dnl As a special exception to the GNU General Public License, if you
44 dnl distribute this file as part of a program that contains a
45 dnl configuration script generated by Autoconf, you may include it under
46 dnl the same distribution terms that you use for the rest of that
47 dnl program.
48
49 dnl PKG_PREREQ(MIN-VERSION)
50 dnl -----------------------
51 dnl Since: 0.29
52 dnl
53 dnl Verify that the version of the pkg-config macros are at least
54 dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
55 dnl installed version of pkg-config, this checks the developer's version
56 dnl of pkg.m4 when generating configure.
57 dnl
58 dnl To ensure that this macro is defined, also add:
59 dnl m4_ifndef([PKG_PREREQ],
60 dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
61 dnl
62 dnl See the "Since" comment for each macro you use to see what version
63 dnl of the macros you require.
64 m4_defun([PKG_PREREQ],
65 [m4_define([PKG_MACROS_VERSION], [0.29])
66 m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
67 [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
68 ])dnl PKG_PREREQ
69
70 dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
71 dnl ----------------------------------
72 dnl Since: 0.16
73 dnl
74 dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
75 dnl first found in the path. Checks that the version of pkg-config found
76 dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
77 dnl used since that's the first version where most current features of
78 dnl pkg-config existed.
79 AC_DEFUN([PKG_PROG_PKG_CONFIG],
80 [m4_pattern_forbid([^_?PKG_[A-Z_]+$])
81 m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
82 m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
83 AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
84 AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
85 AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
86
87 if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
88 AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
89 fi
90 if test -n "$PKG_CONFIG"; then
91 _pkg_min_version=m4_default([$1], [0.9.0])
92 AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
93 if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
94 AC_MSG_RESULT([yes])
95 else
96 AC_MSG_RESULT([no])
97 PKG_CONFIG=""
98 fi
99 fi[]dnl
100 ])dnl PKG_PROG_PKG_CONFIG
101
102 dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
103 dnl -------------------------------------------------------------------
104 dnl Since: 0.18
105 dnl
106 dnl Check to see whether a particular set of modules exists. Similar to
107 dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
108 dnl
109 dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
110 dnl only at the first occurence in configure.ac, so if the first place
111 dnl it's called might be skipped (such as if it is within an "if", you
112 dnl have to call PKG_CHECK_EXISTS manually
113 AC_DEFUN([PKG_CHECK_EXISTS],
114 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
115 if test -n "$PKG_CONFIG" && \
116 AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
117 m4_default([$2], [:])
118 m4_ifvaln([$3], [else
119 $3])dnl
120 fi])
121
122 dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
123 dnl ---------------------------------------------
124 dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
125 dnl pkg_failed based on the result.
126 m4_define([_PKG_CONFIG],
127 [if test -n "$$1"; then
128 pkg_cv_[]$1="$$1"
129 elif test -n "$PKG_CONFIG"; then
130 PKG_CHECK_EXISTS([$3],
131 [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
132 test "x$?" != "x0" && pkg_failed=yes ],
133 [pkg_failed=yes])
134 else
135 pkg_failed=untried
136 fi[]dnl
137 ])dnl _PKG_CONFIG
138
139 dnl _PKG_SHORT_ERRORS_SUPPORTED
140 dnl ---------------------------
141 dnl Internal check to see if pkg-config supports short errors.
142 AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
143 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])
144 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
145 _pkg_short_errors_supported=yes
146 else
147 _pkg_short_errors_supported=no
148 fi[]dnl
149 ])dnl _PKG_SHORT_ERRORS_SUPPORTED
150
151
152 dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
153 dnl [ACTION-IF-NOT-FOUND])
154 dnl --------------------------------------------------------------
155 dnl Since: 0.4.0
156 dnl
157 dnl Note that if there is a possibility the first call to
158 dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
159 dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
160 AC_DEFUN([PKG_CHECK_MODULES],
161 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
162 AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
163 AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
164
165 pkg_failed=no
166 AC_MSG_CHECKING([for $1])
167
168 _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
169 _PKG_CONFIG([$1][_LIBS], [libs], [$2])
170
171 m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
172 and $1[]_LIBS to avoid the need to call pkg-config.
173 See the pkg-config man page for more details.])
174
175 if test $pkg_failed = yes; then
176 AC_MSG_RESULT([no])
177 _PKG_SHORT_ERRORS_SUPPORTED
178 if test $_pkg_short_errors_supported = yes; then
179 $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
180 else
181 $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
182 fi
183 # Put the nasty error message in config.log where it belongs
184 echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
185
186 m4_default([$4], [AC_MSG_ERROR(
187 [Package requirements ($2) were not met:
188
189 $$1_PKG_ERRORS
190
191 Consider adjusting the PKG_CONFIG_PATH environment variable if you
192 installed software in a non-standard prefix.
193
194 _PKG_TEXT])[]dnl
195 ])
196 elif test $pkg_failed = untried; then
197 AC_MSG_RESULT([no])
198 m4_default([$4], [AC_MSG_FAILURE(
199 [The pkg-config script could not be found or is too old. Make sure it
200 is in your PATH or set the PKG_CONFIG environment variable to the full
201 path to pkg-config.
202
203 _PKG_TEXT
204
205 To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
206 ])
207 else
208 $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
209 $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
210 AC_MSG_RESULT([yes])
211 $3
212 fi[]dnl
213 ])dnl PKG_CHECK_MODULES
214
215
216 dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
217 dnl [ACTION-IF-NOT-FOUND])
218 dnl ---------------------------------------------------------------------
219 dnl Since: 0.29
220 dnl
221 dnl Checks for existence of MODULES and gathers its build flags with
222 dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
223 dnl and VARIABLE-PREFIX_LIBS from --libs.
224 dnl
225 dnl Note that if there is a possibility the first call to
226 dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
227 dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
228 dnl configure.ac.
229 AC_DEFUN([PKG_CHECK_MODULES_STATIC],
230 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
231 _save_PKG_CONFIG=$PKG_CONFIG
232 PKG_CONFIG="$PKG_CONFIG --static"
233 PKG_CHECK_MODULES($@)
234 PKG_CONFIG=$_save_PKG_CONFIG[]dnl
235 ])dnl PKG_CHECK_MODULES_STATIC
236
237
238 dnl PKG_INSTALLDIR([DIRECTORY])
239 dnl -------------------------
240 dnl Since: 0.27
241 dnl
242 dnl Substitutes the variable pkgconfigdir as the location where a module
243 dnl should install pkg-config .pc files. By default the directory is
244 dnl $libdir/pkgconfig, but the default can be changed by passing
245 dnl DIRECTORY. The user can override through the --with-pkgconfigdir
246 dnl parameter.
247 AC_DEFUN([PKG_INSTALLDIR],
248 [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
249 m4_pushdef([pkg_description],
250 [pkg-config installation directory @<:@]pkg_default[@:>@])
251 AC_ARG_WITH([pkgconfigdir],
252 [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
253 [with_pkgconfigdir=]pkg_default)
254 AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
255 m4_popdef([pkg_default])
256 m4_popdef([pkg_description])
257 ])dnl PKG_INSTALLDIR
258
259
260 dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
261 dnl --------------------------------
262 dnl Since: 0.27
263 dnl
264 dnl Substitutes the variable noarch_pkgconfigdir as the location where a
265 dnl module should install arch-independent pkg-config .pc files. By
266 dnl default the directory is $datadir/pkgconfig, but the default can be
267 dnl changed by passing DIRECTORY. The user can override through the
268 dnl --with-noarch-pkgconfigdir parameter.
269 AC_DEFUN([PKG_NOARCH_INSTALLDIR],
270 [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
271 m4_pushdef([pkg_description],
272 [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
273 AC_ARG_WITH([noarch-pkgconfigdir],
274 [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
275 [with_noarch_pkgconfigdir=]pkg_default)
276 AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
277 m4_popdef([pkg_default])
278 m4_popdef([pkg_description])
279 ])dnl PKG_NOARCH_INSTALLDIR
280
281
282 dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
283 dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
284 dnl -------------------------------------------
285 dnl Since: 0.28
286 dnl
287 dnl Retrieves the value of the pkg-config variable for the given module.
288 AC_DEFUN([PKG_CHECK_VAR],
289 [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
290 AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
291
292 _PKG_CONFIG([$1], [variable="][$3]["], [$2])
293 AS_VAR_COPY([$1], [pkg_cv_][$1])
294
295 AS_VAR_IF([$1], [""], [$5], [$4])dnl
296 ])dnl PKG_CHECK_VAR
297
298 # Copyright (C) 2002-2018 Free Software Foundation, Inc.
299 #
300 # This file is free software; the Free Software Foundation
301 # gives unlimited permission to copy and/or distribute it,
302 # with or without modifications, as long as this notice is preserved.
303
304 # AM_AUTOMAKE_VERSION(VERSION)
305 # ----------------------------
306 # Automake X.Y traces this macro to ensure aclocal.m4 has been
307 # generated from the m4 files accompanying Automake X.Y.
308 # (This private macro should not be called outside this file.)
309 AC_DEFUN([AM_AUTOMAKE_VERSION],
310 [am__api_version='1.16'
311 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
312 dnl require some minimum version. Point them to the right macro.
313 m4_if([$1], [1.16.1], [],
314 [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
315 ])
316
317 # _AM_AUTOCONF_VERSION(VERSION)
318 # -----------------------------
319 # aclocal traces this macro to find the Autoconf version.
320 # This is a private macro too. Using m4_define simplifies
321 # the logic in aclocal, which can simply ignore this definition.
322 m4_define([_AM_AUTOCONF_VERSION], [])
323
324 # AM_SET_CURRENT_AUTOMAKE_VERSION
325 # -------------------------------
326 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
327 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
328 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
329 [AM_AUTOMAKE_VERSION([1.16.1])dnl
330 m4_ifndef([AC_AUTOCONF_VERSION],
331 [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
332 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
333
334 # Copyright (C) 2011-2018 Free Software Foundation, Inc.
335 #
336 # This file is free software; the Free Software Foundation
337 # gives unlimited permission to copy and/or distribute it,
338 # with or without modifications, as long as this notice is preserved.
339
340 # AM_PROG_AR([ACT-IF-FAIL])
341 # -------------------------
342 # Try to determine the archiver interface, and trigger the ar-lib wrapper
343 # if it is needed. If the detection of archiver interface fails, run
344 # ACT-IF-FAIL (default is to abort configure with a proper error message).
345 AC_DEFUN([AM_PROG_AR],
346 [AC_BEFORE([$0], [LT_INIT])dnl
347 AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl
348 AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
349 AC_REQUIRE_AUX_FILE([ar-lib])dnl
350 AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false])
351 : ${AR=ar}
352
353 AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface],
354 [AC_LANG_PUSH([C])
355 am_cv_ar_interface=ar
356 AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])],
357 [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
358 AC_TRY_EVAL([am_ar_try])
359 if test "$ac_status" -eq 0; then
360 am_cv_ar_interface=ar
361 else
362 am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
363 AC_TRY_EVAL([am_ar_try])
364 if test "$ac_status" -eq 0; then
365 am_cv_ar_interface=lib
366 else
367 am_cv_ar_interface=unknown
368 fi
369 fi
370 rm -f conftest.lib libconftest.a
371 ])
372 AC_LANG_POP([C])])
373
374 case $am_cv_ar_interface in
375 ar)
376 ;;
377 lib)
378 # Microsoft lib, so override with the ar-lib wrapper script.
379 # FIXME: It is wrong to rewrite AR.
380 # But if we don't then we get into trouble of one sort or another.
381 # A longer-term fix would be to have automake use am__AR in this case,
382 # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
383 # similar.
384 AR="$am_aux_dir/ar-lib $AR"
385 ;;
386 unknown)
387 m4_default([$1],
388 [AC_MSG_ERROR([could not determine $AR interface])])
389 ;;
390 esac
391 AC_SUBST([AR])dnl
392 ])
393
394 # AM_AUX_DIR_EXPAND -*- Autoconf -*-
395
396 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
397 #
398 # This file is free software; the Free Software Foundation
399 # gives unlimited permission to copy and/or distribute it,
400 # with or without modifications, as long as this notice is preserved.
401
402 # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
403 # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
404 # '$srcdir', '$srcdir/..', or '$srcdir/../..'.
405 #
406 # Of course, Automake must honor this variable whenever it calls a
407 # tool from the auxiliary directory. The problem is that $srcdir (and
408 # therefore $ac_aux_dir as well) can be either absolute or relative,
409 # depending on how configure is run. This is pretty annoying, since
410 # it makes $ac_aux_dir quite unusable in subdirectories: in the top
411 # source directory, any form will work fine, but in subdirectories a
412 # relative path needs to be adjusted first.
413 #
414 # $ac_aux_dir/missing
415 # fails when called from a subdirectory if $ac_aux_dir is relative
416 # $top_srcdir/$ac_aux_dir/missing
417 # fails if $ac_aux_dir is absolute,
418 # fails when called from a subdirectory in a VPATH build with
419 # a relative $ac_aux_dir
420 #
421 # The reason of the latter failure is that $top_srcdir and $ac_aux_dir
422 # are both prefixed by $srcdir. In an in-source build this is usually
423 # harmless because $srcdir is '.', but things will broke when you
424 # start a VPATH build or use an absolute $srcdir.
425 #
426 # So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
427 # iff we strip the leading $srcdir from $ac_aux_dir. That would be:
428 # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
429 # and then we would define $MISSING as
430 # MISSING="\${SHELL} $am_aux_dir/missing"
431 # This will work as long as MISSING is not called from configure, because
432 # unfortunately $(top_srcdir) has no meaning in configure.
433 # However there are other variables, like CC, which are often used in
434 # configure, and could therefore not use this "fixed" $ac_aux_dir.
435 #
436 # Another solution, used here, is to always expand $ac_aux_dir to an
437 # absolute PATH. The drawback is that using absolute paths prevent a
438 # configured tree to be moved without reconfiguration.
439
440 AC_DEFUN([AM_AUX_DIR_EXPAND],
441 [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
442 # Expand $ac_aux_dir to an absolute path.
443 am_aux_dir=`cd "$ac_aux_dir" && pwd`
444 ])
445
446 # AM_CONDITIONAL -*- Autoconf -*-
447
448 # Copyright (C) 1997-2018 Free Software Foundation, Inc.
449 #
450 # This file is free software; the Free Software Foundation
451 # gives unlimited permission to copy and/or distribute it,
452 # with or without modifications, as long as this notice is preserved.
453
454 # AM_CONDITIONAL(NAME, SHELL-CONDITION)
455 # -------------------------------------
456 # Define a conditional.
457 AC_DEFUN([AM_CONDITIONAL],
458 [AC_PREREQ([2.52])dnl
459 m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
460 [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
461 AC_SUBST([$1_TRUE])dnl
462 AC_SUBST([$1_FALSE])dnl
463 _AM_SUBST_NOTMAKE([$1_TRUE])dnl
464 _AM_SUBST_NOTMAKE([$1_FALSE])dnl
465 m4_define([_AM_COND_VALUE_$1], [$2])dnl
466 if $2; then
467 $1_TRUE=
468 $1_FALSE='#'
469 else
470 $1_TRUE='#'
471 $1_FALSE=
472 fi
473 AC_CONFIG_COMMANDS_PRE(
474 [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
475 AC_MSG_ERROR([[conditional "$1" was never defined.
476 Usually this means the macro was only invoked conditionally.]])
477 fi])])
478
479 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
480 #
481 # This file is free software; the Free Software Foundation
482 # gives unlimited permission to copy and/or distribute it,
483 # with or without modifications, as long as this notice is preserved.
484
485
486 # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
487 # written in clear, in which case automake, when reading aclocal.m4,
488 # will think it sees a *use*, and therefore will trigger all it's
489 # C support machinery. Also note that it means that autoscan, seeing
490 # CC etc. in the Makefile, will ask for an AC_PROG_CC use...
491
492
493 # _AM_DEPENDENCIES(NAME)
494 # ----------------------
495 # See how the compiler implements dependency checking.
496 # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
497 # We try a few techniques and use that to set a single cache variable.
498 #
499 # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
500 # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
501 # dependency, and given that the user is not expected to run this macro,
502 # just rely on AC_PROG_CC.
503 AC_DEFUN([_AM_DEPENDENCIES],
504 [AC_REQUIRE([AM_SET_DEPDIR])dnl
505 AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
506 AC_REQUIRE([AM_MAKE_INCLUDE])dnl
507 AC_REQUIRE([AM_DEP_TRACK])dnl
508
509 m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
510 [$1], [CXX], [depcc="$CXX" am_compiler_list=],
511 [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
512 [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
513 [$1], [UPC], [depcc="$UPC" am_compiler_list=],
514 [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
515 [depcc="$$1" am_compiler_list=])
516
517 AC_CACHE_CHECK([dependency style of $depcc],
518 [am_cv_$1_dependencies_compiler_type],
519 [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
520 # We make a subdir and do the tests there. Otherwise we can end up
521 # making bogus files that we don't know about and never remove. For
522 # instance it was reported that on HP-UX the gcc test will end up
523 # making a dummy file named 'D' -- because '-MD' means "put the output
524 # in D".
525 rm -rf conftest.dir
526 mkdir conftest.dir
527 # Copy depcomp to subdir because otherwise we won't find it if we're
528 # using a relative directory.
529 cp "$am_depcomp" conftest.dir
530 cd conftest.dir
531 # We will build objects and dependencies in a subdirectory because
532 # it helps to detect inapplicable dependency modes. For instance
533 # both Tru64's cc and ICC support -MD to output dependencies as a
534 # side effect of compilation, but ICC will put the dependencies in
535 # the current directory while Tru64 will put them in the object
536 # directory.
537 mkdir sub
538
539 am_cv_$1_dependencies_compiler_type=none
540 if test "$am_compiler_list" = ""; then
541 am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
542 fi
543 am__universal=false
544 m4_case([$1], [CC],
545 [case " $depcc " in #(
546 *\ -arch\ *\ -arch\ *) am__universal=true ;;
547 esac],
548 [CXX],
549 [case " $depcc " in #(
550 *\ -arch\ *\ -arch\ *) am__universal=true ;;
551 esac])
552
553 for depmode in $am_compiler_list; do
554 # Setup a source with many dependencies, because some compilers
555 # like to wrap large dependency lists on column 80 (with \), and
556 # we should not choose a depcomp mode which is confused by this.
557 #
558 # We need to recreate these files for each test, as the compiler may
559 # overwrite some of them when testing with obscure command lines.
560 # This happens at least with the AIX C compiler.
561 : > sub/conftest.c
562 for i in 1 2 3 4 5 6; do
563 echo '#include "conftst'$i'.h"' >> sub/conftest.c
564 # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
565 # Solaris 10 /bin/sh.
566 echo '/* dummy */' > sub/conftst$i.h
567 done
568 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
569
570 # We check with '-c' and '-o' for the sake of the "dashmstdout"
571 # mode. It turns out that the SunPro C++ compiler does not properly
572 # handle '-M -o', and we need to detect this. Also, some Intel
573 # versions had trouble with output in subdirs.
574 am__obj=sub/conftest.${OBJEXT-o}
575 am__minus_obj="-o $am__obj"
576 case $depmode in
577 gcc)
578 # This depmode causes a compiler race in universal mode.
579 test "$am__universal" = false || continue
580 ;;
581 nosideeffect)
582 # After this tag, mechanisms are not by side-effect, so they'll
583 # only be used when explicitly requested.
584 if test "x$enable_dependency_tracking" = xyes; then
585 continue
586 else
587 break
588 fi
589 ;;
590 msvc7 | msvc7msys | msvisualcpp | msvcmsys)
591 # This compiler won't grok '-c -o', but also, the minuso test has
592 # not run yet. These depmodes are late enough in the game, and
593 # so weak that their functioning should not be impacted.
594 am__obj=conftest.${OBJEXT-o}
595 am__minus_obj=
596 ;;
597 none) break ;;
598 esac
599 if depmode=$depmode \
600 source=sub/conftest.c object=$am__obj \
601 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
602 $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
603 >/dev/null 2>conftest.err &&
604 grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
605 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
606 grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
607 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
608 # icc doesn't choke on unknown options, it will just issue warnings
609 # or remarks (even with -Werror). So we grep stderr for any message
610 # that says an option was ignored or not supported.
611 # When given -MP, icc 7.0 and 7.1 complain thusly:
612 # icc: Command line warning: ignoring option '-M'; no argument required
613 # The diagnosis changed in icc 8.0:
614 # icc: Command line remark: option '-MP' not supported
615 if (grep 'ignoring option' conftest.err ||
616 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
617 am_cv_$1_dependencies_compiler_type=$depmode
618 break
619 fi
620 fi
621 done
622
623 cd ..
624 rm -rf conftest.dir
625 else
626 am_cv_$1_dependencies_compiler_type=none
627 fi
628 ])
629 AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
630 AM_CONDITIONAL([am__fastdep$1], [
631 test "x$enable_dependency_tracking" != xno \
632 && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
633 ])
634
635
636 # AM_SET_DEPDIR
637 # -------------
638 # Choose a directory name for dependency files.
639 # This macro is AC_REQUIREd in _AM_DEPENDENCIES.
640 AC_DEFUN([AM_SET_DEPDIR],
641 [AC_REQUIRE([AM_SET_LEADING_DOT])dnl
642 AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
643 ])
644
645
646 # AM_DEP_TRACK
647 # ------------
648 AC_DEFUN([AM_DEP_TRACK],
649 [AC_ARG_ENABLE([dependency-tracking], [dnl
650 AS_HELP_STRING(
651 [--enable-dependency-tracking],
652 [do not reject slow dependency extractors])
653 AS_HELP_STRING(
654 [--disable-dependency-tracking],
655 [speeds up one-time build])])
656 if test "x$enable_dependency_tracking" != xno; then
657 am_depcomp="$ac_aux_dir/depcomp"
658 AMDEPBACKSLASH='\'
659 am__nodep='_no'
660 fi
661 AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
662 AC_SUBST([AMDEPBACKSLASH])dnl
663 _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
664 AC_SUBST([am__nodep])dnl
665 _AM_SUBST_NOTMAKE([am__nodep])dnl
666 ])
667
668 # Generate code to set up dependency tracking. -*- Autoconf -*-
669
670 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
671 #
672 # This file is free software; the Free Software Foundation
673 # gives unlimited permission to copy and/or distribute it,
674 # with or without modifications, as long as this notice is preserved.
675
676 # _AM_OUTPUT_DEPENDENCY_COMMANDS
677 # ------------------------------
678 AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
679 [{
680 # Older Autoconf quotes --file arguments for eval, but not when files
681 # are listed without --file. Let's play safe and only enable the eval
682 # if we detect the quoting.
683 # TODO: see whether this extra hack can be removed once we start
684 # requiring Autoconf 2.70 or later.
685 AS_CASE([$CONFIG_FILES],
686 [*\'*], [eval set x "$CONFIG_FILES"],
687 [*], [set x $CONFIG_FILES])
688 shift
689 # Used to flag and report bootstrapping failures.
690 am_rc=0
691 for am_mf
692 do
693 # Strip MF so we end up with the name of the file.
694 am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
695 # Check whether this is an Automake generated Makefile which includes
696 # dependency-tracking related rules and includes.
697 # Grep'ing the whole file directly is not great: AIX grep has a line
698 # limit of 2048, but all sed's we know have understand at least 4000.
699 sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
700 || continue
701 am_dirpart=`AS_DIRNAME(["$am_mf"])`
702 am_filepart=`AS_BASENAME(["$am_mf"])`
703 AM_RUN_LOG([cd "$am_dirpart" \
704 && sed -e '/# am--include-marker/d' "$am_filepart" \
705 | $MAKE -f - am--depfiles]) || am_rc=$?
706 done
707 if test $am_rc -ne 0; then
708 AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
709 for automatic dependency tracking. Try re-running configure with the
710 '--disable-dependency-tracking' option to at least be able to build
711 the package (albeit without support for automatic dependency tracking).])
712 fi
713 AS_UNSET([am_dirpart])
714 AS_UNSET([am_filepart])
715 AS_UNSET([am_mf])
716 AS_UNSET([am_rc])
717 rm -f conftest-deps.mk
718 }
719 ])# _AM_OUTPUT_DEPENDENCY_COMMANDS
720
721
722 # AM_OUTPUT_DEPENDENCY_COMMANDS
723 # -----------------------------
724 # This macro should only be invoked once -- use via AC_REQUIRE.
725 #
726 # This code is only required when automatic dependency tracking is enabled.
727 # This creates each '.Po' and '.Plo' makefile fragment that we'll need in
728 # order to bootstrap the dependency handling code.
729 AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
730 [AC_CONFIG_COMMANDS([depfiles],
731 [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
732 [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
733
734 # Do all the work for Automake. -*- Autoconf -*-
735
736 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
737 #
738 # This file is free software; the Free Software Foundation
739 # gives unlimited permission to copy and/or distribute it,
740 # with or without modifications, as long as this notice is preserved.
741
742 # This macro actually does too much. Some checks are only needed if
743 # your package does certain things. But this isn't really a big deal.
744
745 dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
746 m4_define([AC_PROG_CC],
747 m4_defn([AC_PROG_CC])
748 [_AM_PROG_CC_C_O
749 ])
750
751 # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
752 # AM_INIT_AUTOMAKE([OPTIONS])
753 # -----------------------------------------------
754 # The call with PACKAGE and VERSION arguments is the old style
755 # call (pre autoconf-2.50), which is being phased out. PACKAGE
756 # and VERSION should now be passed to AC_INIT and removed from
757 # the call to AM_INIT_AUTOMAKE.
758 # We support both call styles for the transition. After
759 # the next Automake release, Autoconf can make the AC_INIT
760 # arguments mandatory, and then we can depend on a new Autoconf
761 # release and drop the old call support.
762 AC_DEFUN([AM_INIT_AUTOMAKE],
763 [AC_PREREQ([2.65])dnl
764 dnl Autoconf wants to disallow AM_ names. We explicitly allow
765 dnl the ones we care about.
766 m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
767 AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
768 AC_REQUIRE([AC_PROG_INSTALL])dnl
769 if test "`cd $srcdir && pwd`" != "`pwd`"; then
770 # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
771 # is not polluted with repeated "-I."
772 AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
773 # test to see if srcdir already configured
774 if test -f $srcdir/config.status; then
775 AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
776 fi
777 fi
778
779 # test whether we have cygpath
780 if test -z "$CYGPATH_W"; then
781 if (cygpath --version) >/dev/null 2>/dev/null; then
782 CYGPATH_W='cygpath -w'
783 else
784 CYGPATH_W=echo
785 fi
786 fi
787 AC_SUBST([CYGPATH_W])
788
789 # Define the identity of the package.
790 dnl Distinguish between old-style and new-style calls.
791 m4_ifval([$2],
792 [AC_DIAGNOSE([obsolete],
793 [$0: two- and three-arguments forms are deprecated.])
794 m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
795 AC_SUBST([PACKAGE], [$1])dnl
796 AC_SUBST([VERSION], [$2])],
797 [_AM_SET_OPTIONS([$1])dnl
798 dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
799 m4_if(
800 m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
801 [ok:ok],,
802 [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
803 AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
804 AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
805
806 _AM_IF_OPTION([no-define],,
807 [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
808 AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
809
810 # Some tools Automake needs.
811 AC_REQUIRE([AM_SANITY_CHECK])dnl
812 AC_REQUIRE([AC_ARG_PROGRAM])dnl
813 AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
814 AM_MISSING_PROG([AUTOCONF], [autoconf])
815 AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
816 AM_MISSING_PROG([AUTOHEADER], [autoheader])
817 AM_MISSING_PROG([MAKEINFO], [makeinfo])
818 AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
819 AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
820 AC_REQUIRE([AC_PROG_MKDIR_P])dnl
821 # For better backward compatibility. To be removed once Automake 1.9.x
822 # dies out for good. For more background, see:
823 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
824 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
825 AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
826 # We need awk for the "check" target (and possibly the TAP driver). The
827 # system "awk" is bad on some platforms.
828 AC_REQUIRE([AC_PROG_AWK])dnl
829 AC_REQUIRE([AC_PROG_MAKE_SET])dnl
830 AC_REQUIRE([AM_SET_LEADING_DOT])dnl
831 _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
832 [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
833 [_AM_PROG_TAR([v7])])])
834 _AM_IF_OPTION([no-dependencies],,
835 [AC_PROVIDE_IFELSE([AC_PROG_CC],
836 [_AM_DEPENDENCIES([CC])],
837 [m4_define([AC_PROG_CC],
838 m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
839 AC_PROVIDE_IFELSE([AC_PROG_CXX],
840 [_AM_DEPENDENCIES([CXX])],
841 [m4_define([AC_PROG_CXX],
842 m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
843 AC_PROVIDE_IFELSE([AC_PROG_OBJC],
844 [_AM_DEPENDENCIES([OBJC])],
845 [m4_define([AC_PROG_OBJC],
846 m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
847 AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
848 [_AM_DEPENDENCIES([OBJCXX])],
849 [m4_define([AC_PROG_OBJCXX],
850 m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
851 ])
852 AC_REQUIRE([AM_SILENT_RULES])dnl
853 dnl The testsuite driver may need to know about EXEEXT, so add the
854 dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
855 dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
856 AC_CONFIG_COMMANDS_PRE(dnl
857 [m4_provide_if([_AM_COMPILER_EXEEXT],
858 [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
859
860 # POSIX will say in a future version that running "rm -f" with no argument
861 # is OK; and we want to be able to make that assumption in our Makefile
862 # recipes. So use an aggressive probe to check that the usage we want is
863 # actually supported "in the wild" to an acceptable degree.
864 # See automake bug#10828.
865 # To make any issue more visible, cause the running configure to be aborted
866 # by default if the 'rm' program in use doesn't match our expectations; the
867 # user can still override this though.
868 if rm -f && rm -fr && rm -rf; then : OK; else
869 cat >&2 <<'END'
870 Oops!
871
872 Your 'rm' program seems unable to run without file operands specified
873 on the command line, even when the '-f' option is present. This is contrary
874 to the behaviour of most rm programs out there, and not conforming with
875 the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
876
877 Please tell bug-automake@gnu.org about your system, including the value
878 of your $PATH and any error possibly output before this message. This
879 can help us improve future automake versions.
880
881 END
882 if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
883 echo 'Configuration will proceed anyway, since you have set the' >&2
884 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
885 echo >&2
886 else
887 cat >&2 <<'END'
888 Aborting the configuration process, to ensure you take notice of the issue.
889
890 You can download and install GNU coreutils to get an 'rm' implementation
891 that behaves properly: <https://www.gnu.org/software/coreutils/>.
892
893 If you want to complete the configuration process using your problematic
894 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
895 to "yes", and re-run configure.
896
897 END
898 AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
899 fi
900 fi
901 dnl The trailing newline in this macro's definition is deliberate, for
902 dnl backward compatibility and to allow trailing 'dnl'-style comments
903 dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
904 ])
905
906 dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
907 dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
908 dnl mangled by Autoconf and run in a shell conditional statement.
909 m4_define([_AC_COMPILER_EXEEXT],
910 m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
911
912 # When config.status generates a header, we must update the stamp-h file.
913 # This file resides in the same directory as the config header
914 # that is generated. The stamp files are numbered to have different names.
915
916 # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
917 # loop where config.status creates the headers, so we can generate
918 # our stamp files there.
919 AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
920 [# Compute $1's index in $config_headers.
921 _am_arg=$1
922 _am_stamp_count=1
923 for _am_header in $config_headers :; do
924 case $_am_header in
925 $_am_arg | $_am_arg:* )
926 break ;;
927 * )
928 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
929 esac
930 done
931 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
932
933 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
934 #
935 # This file is free software; the Free Software Foundation
936 # gives unlimited permission to copy and/or distribute it,
937 # with or without modifications, as long as this notice is preserved.
938
939 # AM_PROG_INSTALL_SH
940 # ------------------
941 # Define $install_sh.
942 AC_DEFUN([AM_PROG_INSTALL_SH],
943 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
944 if test x"${install_sh+set}" != xset; then
945 case $am_aux_dir in
946 *\ * | *\ *)
947 install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
948 *)
949 install_sh="\${SHELL} $am_aux_dir/install-sh"
950 esac
951 fi
952 AC_SUBST([install_sh])])
953
954 # Copyright (C) 2003-2018 Free Software Foundation, Inc.
955 #
956 # This file is free software; the Free Software Foundation
957 # gives unlimited permission to copy and/or distribute it,
958 # with or without modifications, as long as this notice is preserved.
959
960 # Check whether the underlying file-system supports filenames
961 # with a leading dot. For instance MS-DOS doesn't.
962 AC_DEFUN([AM_SET_LEADING_DOT],
963 [rm -rf .tst 2>/dev/null
964 mkdir .tst 2>/dev/null
965 if test -d .tst; then
966 am__leading_dot=.
967 else
968 am__leading_dot=_
969 fi
970 rmdir .tst 2>/dev/null
971 AC_SUBST([am__leading_dot])])
972
973 # Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
974 # From Jim Meyering
975
976 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
977 #
978 # This file is free software; the Free Software Foundation
979 # gives unlimited permission to copy and/or distribute it,
980 # with or without modifications, as long as this notice is preserved.
981
982 # AM_MAINTAINER_MODE([DEFAULT-MODE])
983 # ----------------------------------
984 # Control maintainer-specific portions of Makefiles.
985 # Default is to disable them, unless 'enable' is passed literally.
986 # For symmetry, 'disable' may be passed as well. Anyway, the user
987 # can override the default with the --enable/--disable switch.
988 AC_DEFUN([AM_MAINTAINER_MODE],
989 [m4_case(m4_default([$1], [disable]),
990 [enable], [m4_define([am_maintainer_other], [disable])],
991 [disable], [m4_define([am_maintainer_other], [enable])],
992 [m4_define([am_maintainer_other], [enable])
993 m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
994 AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
995 dnl maintainer-mode's default is 'disable' unless 'enable' is passed
996 AC_ARG_ENABLE([maintainer-mode],
997 [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode],
998 am_maintainer_other[ make rules and dependencies not useful
999 (and sometimes confusing) to the casual installer])],
1000 [USE_MAINTAINER_MODE=$enableval],
1001 [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
1002 AC_MSG_RESULT([$USE_MAINTAINER_MODE])
1003 AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
1004 MAINT=$MAINTAINER_MODE_TRUE
1005 AC_SUBST([MAINT])dnl
1006 ]
1007 )
1008
1009 # Check to see how 'make' treats includes. -*- Autoconf -*-
1010
1011 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
1012 #
1013 # This file is free software; the Free Software Foundation
1014 # gives unlimited permission to copy and/or distribute it,
1015 # with or without modifications, as long as this notice is preserved.
1016
1017 # AM_MAKE_INCLUDE()
1018 # -----------------
1019 # Check whether make has an 'include' directive that can support all
1020 # the idioms we need for our automatic dependency tracking code.
1021 AC_DEFUN([AM_MAKE_INCLUDE],
1022 [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
1023 cat > confinc.mk << 'END'
1024 am__doit:
1025 @echo this is the am__doit target >confinc.out
1026 .PHONY: am__doit
1027 END
1028 am__include="#"
1029 am__quote=
1030 # BSD make does it like this.
1031 echo '.include "confinc.mk" # ignored' > confmf.BSD
1032 # Other make implementations (GNU, Solaris 10, AIX) do it like this.
1033 echo 'include confinc.mk # ignored' > confmf.GNU
1034 _am_result=no
1035 for s in GNU BSD; do
1036 AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
1037 AS_CASE([$?:`cat confinc.out 2>/dev/null`],
1038 ['0:this is the am__doit target'],
1039 [AS_CASE([$s],
1040 [BSD], [am__include='.include' am__quote='"'],
1041 [am__include='include' am__quote=''])])
1042 if test "$am__include" != "#"; then
1043 _am_result="yes ($s style)"
1044 break
1045 fi
1046 done
1047 rm -f confinc.* confmf.*
1048 AC_MSG_RESULT([${_am_result}])
1049 AC_SUBST([am__include])])
1050 AC_SUBST([am__quote])])
1051
1052 # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
1053
1054 # Copyright (C) 1997-2018 Free Software Foundation, Inc.
1055 #
1056 # This file is free software; the Free Software Foundation
1057 # gives unlimited permission to copy and/or distribute it,
1058 # with or without modifications, as long as this notice is preserved.
1059
1060 # AM_MISSING_PROG(NAME, PROGRAM)
1061 # ------------------------------
1062 AC_DEFUN([AM_MISSING_PROG],
1063 [AC_REQUIRE([AM_MISSING_HAS_RUN])
1064 $1=${$1-"${am_missing_run}$2"}
1065 AC_SUBST($1)])
1066
1067 # AM_MISSING_HAS_RUN
1068 # ------------------
1069 # Define MISSING if not defined so far and test if it is modern enough.
1070 # If it is, set am_missing_run to use it, otherwise, to nothing.
1071 AC_DEFUN([AM_MISSING_HAS_RUN],
1072 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
1073 AC_REQUIRE_AUX_FILE([missing])dnl
1074 if test x"${MISSING+set}" != xset; then
1075 case $am_aux_dir in
1076 *\ * | *\ *)
1077 MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
1078 *)
1079 MISSING="\${SHELL} $am_aux_dir/missing" ;;
1080 esac
1081 fi
1082 # Use eval to expand $SHELL
1083 if eval "$MISSING --is-lightweight"; then
1084 am_missing_run="$MISSING "
1085 else
1086 am_missing_run=
1087 AC_MSG_WARN(['missing' script is too old or missing])
1088 fi
1089 ])
1090
1091 # Helper functions for option handling. -*- Autoconf -*-
1092
1093 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
1094 #
1095 # This file is free software; the Free Software Foundation
1096 # gives unlimited permission to copy and/or distribute it,
1097 # with or without modifications, as long as this notice is preserved.
1098
1099 # _AM_MANGLE_OPTION(NAME)
1100 # -----------------------
1101 AC_DEFUN([_AM_MANGLE_OPTION],
1102 [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
1103
1104 # _AM_SET_OPTION(NAME)
1105 # --------------------
1106 # Set option NAME. Presently that only means defining a flag for this option.
1107 AC_DEFUN([_AM_SET_OPTION],
1108 [m4_define(_AM_MANGLE_OPTION([$1]), [1])])
1109
1110 # _AM_SET_OPTIONS(OPTIONS)
1111 # ------------------------
1112 # OPTIONS is a space-separated list of Automake options.
1113 AC_DEFUN([_AM_SET_OPTIONS],
1114 [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
1115
1116 # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
1117 # -------------------------------------------
1118 # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
1119 AC_DEFUN([_AM_IF_OPTION],
1120 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
1121
1122 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
1123 #
1124 # This file is free software; the Free Software Foundation
1125 # gives unlimited permission to copy and/or distribute it,
1126 # with or without modifications, as long as this notice is preserved.
1127
1128 # _AM_PROG_CC_C_O
1129 # ---------------
1130 # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
1131 # to automatically call this.
1132 AC_DEFUN([_AM_PROG_CC_C_O],
1133 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
1134 AC_REQUIRE_AUX_FILE([compile])dnl
1135 AC_LANG_PUSH([C])dnl
1136 AC_CACHE_CHECK(
1137 [whether $CC understands -c and -o together],
1138 [am_cv_prog_cc_c_o],
1139 [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
1140 # Make sure it works both with $CC and with simple cc.
1141 # Following AC_PROG_CC_C_O, we do the test twice because some
1142 # compilers refuse to overwrite an existing .o file with -o,
1143 # though they will create one.
1144 am_cv_prog_cc_c_o=yes
1145 for am_i in 1 2; do
1146 if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
1147 && test -f conftest2.$ac_objext; then
1148 : OK
1149 else
1150 am_cv_prog_cc_c_o=no
1151 break
1152 fi
1153 done
1154 rm -f core conftest*
1155 unset am_i])
1156 if test "$am_cv_prog_cc_c_o" != yes; then
1157 # Losing compiler, so override with the script.
1158 # FIXME: It is wrong to rewrite CC.
1159 # But if we don't then we get into trouble of one sort or another.
1160 # A longer-term fix would be to have automake use am__CC in this case,
1161 # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
1162 CC="$am_aux_dir/compile $CC"
1163 fi
1164 AC_LANG_POP([C])])
1165
1166 # For backward compatibility.
1167 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
1168
1169 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
1170 #
1171 # This file is free software; the Free Software Foundation
1172 # gives unlimited permission to copy and/or distribute it,
1173 # with or without modifications, as long as this notice is preserved.
1174
1175 # AM_RUN_LOG(COMMAND)
1176 # -------------------
1177 # Run COMMAND, save the exit status in ac_status, and log it.
1178 # (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
1179 AC_DEFUN([AM_RUN_LOG],
1180 [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
1181 ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
1182 ac_status=$?
1183 echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
1184 (exit $ac_status); }])
1185
1186 # Check to make sure that the build environment is sane. -*- Autoconf -*-
1187
1188 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
1189 #
1190 # This file is free software; the Free Software Foundation
1191 # gives unlimited permission to copy and/or distribute it,
1192 # with or without modifications, as long as this notice is preserved.
1193
1194 # AM_SANITY_CHECK
1195 # ---------------
1196 AC_DEFUN([AM_SANITY_CHECK],
1197 [AC_MSG_CHECKING([whether build environment is sane])
1198 # Reject unsafe characters in $srcdir or the absolute working directory
1199 # name. Accept space and tab only in the latter.
1200 am_lf='
1201 '
1202 case `pwd` in
1203 *[[\\\"\#\$\&\'\`$am_lf]]*)
1204 AC_MSG_ERROR([unsafe absolute working directory name]);;
1205 esac
1206 case $srcdir in
1207 *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
1208 AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
1209 esac
1210
1211 # Do 'set' in a subshell so we don't clobber the current shell's
1212 # arguments. Must try -L first in case configure is actually a
1213 # symlink; some systems play weird games with the mod time of symlinks
1214 # (eg FreeBSD returns the mod time of the symlink's containing
1215 # directory).
1216 if (
1217 am_has_slept=no
1218 for am_try in 1 2; do
1219 echo "timestamp, slept: $am_has_slept" > conftest.file
1220 set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
1221 if test "$[*]" = "X"; then
1222 # -L didn't work.
1223 set X `ls -t "$srcdir/configure" conftest.file`
1224 fi
1225 if test "$[*]" != "X $srcdir/configure conftest.file" \
1226 && test "$[*]" != "X conftest.file $srcdir/configure"; then
1227
1228 # If neither matched, then we have a broken ls. This can happen
1229 # if, for instance, CONFIG_SHELL is bash and it inherits a
1230 # broken ls alias from the environment. This has actually
1231 # happened. Such a system could not be considered "sane".
1232 AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
1233 alias in your environment])
1234 fi
1235 if test "$[2]" = conftest.file || test $am_try -eq 2; then
1236 break
1237 fi
1238 # Just in case.
1239 sleep 1
1240 am_has_slept=yes
1241 done
1242 test "$[2]" = conftest.file
1243 )
1244 then
1245 # Ok.
1246 :
1247 else
1248 AC_MSG_ERROR([newly created file is older than distributed files!
1249 Check your system clock])
1250 fi
1251 AC_MSG_RESULT([yes])
1252 # If we didn't sleep, we still need to ensure time stamps of config.status and
1253 # generated files are strictly newer.
1254 am_sleep_pid=
1255 if grep 'slept: no' conftest.file >/dev/null 2>&1; then
1256 ( sleep 1 ) &
1257 am_sleep_pid=$!
1258 fi
1259 AC_CONFIG_COMMANDS_PRE(
1260 [AC_MSG_CHECKING([that generated files are newer than configure])
1261 if test -n "$am_sleep_pid"; then
1262 # Hide warnings about reused PIDs.
1263 wait $am_sleep_pid 2>/dev/null
1264 fi
1265 AC_MSG_RESULT([done])])
1266 rm -f conftest.file
1267 ])
1268
1269 # Copyright (C) 2009-2018 Free Software Foundation, Inc.
1270 #
1271 # This file is free software; the Free Software Foundation
1272 # gives unlimited permission to copy and/or distribute it,
1273 # with or without modifications, as long as this notice is preserved.
1274
1275 # AM_SILENT_RULES([DEFAULT])
1276 # --------------------------
1277 # Enable less verbose build rules; with the default set to DEFAULT
1278 # ("yes" being less verbose, "no" or empty being verbose).
1279 AC_DEFUN([AM_SILENT_RULES],
1280 [AC_ARG_ENABLE([silent-rules], [dnl
1281 AS_HELP_STRING(
1282 [--enable-silent-rules],
1283 [less verbose build output (undo: "make V=1")])
1284 AS_HELP_STRING(
1285 [--disable-silent-rules],
1286 [verbose build output (undo: "make V=0")])dnl
1287 ])
1288 case $enable_silent_rules in @%:@ (((
1289 yes) AM_DEFAULT_VERBOSITY=0;;
1290 no) AM_DEFAULT_VERBOSITY=1;;
1291 *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
1292 esac
1293 dnl
1294 dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
1295 dnl do not support nested variable expansions.
1296 dnl See automake bug#9928 and bug#10237.
1297 am_make=${MAKE-make}
1298 AC_CACHE_CHECK([whether $am_make supports nested variables],
1299 [am_cv_make_support_nested_variables],
1300 [if AS_ECHO([['TRUE=$(BAR$(V))
1301 BAR0=false
1302 BAR1=true
1303 V=1
1304 am__doit:
1305 @$(TRUE)
1306 .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
1307 am_cv_make_support_nested_variables=yes
1308 else
1309 am_cv_make_support_nested_variables=no
1310 fi])
1311 if test $am_cv_make_support_nested_variables = yes; then
1312 dnl Using '$V' instead of '$(V)' breaks IRIX make.
1313 AM_V='$(V)'
1314 AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
1315 else
1316 AM_V=$AM_DEFAULT_VERBOSITY
1317 AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
1318 fi
1319 AC_SUBST([AM_V])dnl
1320 AM_SUBST_NOTMAKE([AM_V])dnl
1321 AC_SUBST([AM_DEFAULT_V])dnl
1322 AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
1323 AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
1324 AM_BACKSLASH='\'
1325 AC_SUBST([AM_BACKSLASH])dnl
1326 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
1327 ])
1328
1329 # Copyright (C) 2001-2018 Free Software Foundation, Inc.
1330 #
1331 # This file is free software; the Free Software Foundation
1332 # gives unlimited permission to copy and/or distribute it,
1333 # with or without modifications, as long as this notice is preserved.
1334
1335 # AM_PROG_INSTALL_STRIP
1336 # ---------------------
1337 # One issue with vendor 'install' (even GNU) is that you can't
1338 # specify the program used to strip binaries. This is especially
1339 # annoying in cross-compiling environments, where the build's strip
1340 # is unlikely to handle the host's binaries.
1341 # Fortunately install-sh will honor a STRIPPROG variable, so we
1342 # always use install-sh in "make install-strip", and initialize
1343 # STRIPPROG with the value of the STRIP variable (set by the user).
1344 AC_DEFUN([AM_PROG_INSTALL_STRIP],
1345 [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
1346 # Installed binaries are usually stripped using 'strip' when the user
1347 # run "make install-strip". However 'strip' might not be the right
1348 # tool to use in cross-compilation environments, therefore Automake
1349 # will honor the 'STRIP' environment variable to overrule this program.
1350 dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
1351 if test "$cross_compiling" != no; then
1352 AC_CHECK_TOOL([STRIP], [strip], :)
1353 fi
1354 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
1355 AC_SUBST([INSTALL_STRIP_PROGRAM])])
1356
1357 # Copyright (C) 2006-2018 Free Software Foundation, Inc.
1358 #
1359 # This file is free software; the Free Software Foundation
1360 # gives unlimited permission to copy and/or distribute it,
1361 # with or without modifications, as long as this notice is preserved.
1362
1363 # _AM_SUBST_NOTMAKE(VARIABLE)
1364 # ---------------------------
1365 # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
1366 # This macro is traced by Automake.
1367 AC_DEFUN([_AM_SUBST_NOTMAKE])
1368
1369 # AM_SUBST_NOTMAKE(VARIABLE)
1370 # --------------------------
1371 # Public sister of _AM_SUBST_NOTMAKE.
1372 AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
1373
1374 # Check how to create a tarball. -*- Autoconf -*-
1375
1376 # Copyright (C) 2004-2018 Free Software Foundation, Inc.
1377 #
1378 # This file is free software; the Free Software Foundation
1379 # gives unlimited permission to copy and/or distribute it,
1380 # with or without modifications, as long as this notice is preserved.
1381
1382 # _AM_PROG_TAR(FORMAT)
1383 # --------------------
1384 # Check how to create a tarball in format FORMAT.
1385 # FORMAT should be one of 'v7', 'ustar', or 'pax'.
1386 #
1387 # Substitute a variable $(am__tar) that is a command
1388 # writing to stdout a FORMAT-tarball containing the directory
1389 # $tardir.
1390 # tardir=directory && $(am__tar) > result.tar
1391 #
1392 # Substitute a variable $(am__untar) that extract such
1393 # a tarball read from stdin.
1394 # $(am__untar) < result.tar
1395 #
1396 AC_DEFUN([_AM_PROG_TAR],
1397 [# Always define AMTAR for backward compatibility. Yes, it's still used
1398 # in the wild :-( We should find a proper way to deprecate it ...
1399 AC_SUBST([AMTAR], ['$${TAR-tar}'])
1400
1401 # We'll loop over all known methods to create a tar archive until one works.
1402 _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
1403
1404 m4_if([$1], [v7],
1405 [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
1406
1407 [m4_case([$1],
1408 [ustar],
1409 [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
1410 # There is notably a 21 bits limit for the UID and the GID. In fact,
1411 # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
1412 # and bug#13588).
1413 am_max_uid=2097151 # 2^21 - 1
1414 am_max_gid=$am_max_uid
1415 # The $UID and $GID variables are not portable, so we need to resort
1416 # to the POSIX-mandated id(1) utility. Errors in the 'id' calls
1417 # below are definitely unexpected, so allow the users to see them
1418 # (that is, avoid stderr redirection).
1419 am_uid=`id -u || echo unknown`
1420 am_gid=`id -g || echo unknown`
1421 AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
1422 if test $am_uid -le $am_max_uid; then
1423 AC_MSG_RESULT([yes])
1424 else
1425 AC_MSG_RESULT([no])
1426 _am_tools=none
1427 fi
1428 AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
1429 if test $am_gid -le $am_max_gid; then
1430 AC_MSG_RESULT([yes])
1431 else
1432 AC_MSG_RESULT([no])
1433 _am_tools=none
1434 fi],
1435
1436 [pax],
1437 [],
1438
1439 [m4_fatal([Unknown tar format])])
1440
1441 AC_MSG_CHECKING([how to create a $1 tar archive])
1442
1443 # Go ahead even if we have the value already cached. We do so because we
1444 # need to set the values for the 'am__tar' and 'am__untar' variables.
1445 _am_tools=${am_cv_prog_tar_$1-$_am_tools}
1446
1447 for _am_tool in $_am_tools; do
1448 case $_am_tool in
1449 gnutar)
1450 for _am_tar in tar gnutar gtar; do
1451 AM_RUN_LOG([$_am_tar --version]) && break
1452 done
1453 am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
1454 am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
1455 am__untar="$_am_tar -xf -"
1456 ;;
1457 plaintar)
1458 # Must skip GNU tar: if it does not support --format= it doesn't create
1459 # ustar tarball either.
1460 (tar --version) >/dev/null 2>&1 && continue
1461 am__tar='tar chf - "$$tardir"'
1462 am__tar_='tar chf - "$tardir"'
1463 am__untar='tar xf -'
1464 ;;
1465 pax)
1466 am__tar='pax -L -x $1 -w "$$tardir"'
1467 am__tar_='pax -L -x $1 -w "$tardir"'
1468 am__untar='pax -r'
1469 ;;
1470 cpio)
1471 am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
1472 am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
1473 am__untar='cpio -i -H $1 -d'
1474 ;;
1475 none)
1476 am__tar=false
1477 am__tar_=false
1478 am__untar=false
1479 ;;
1480 esac
1481
1482 # If the value was cached, stop now. We just wanted to have am__tar
1483 # and am__untar set.
1484 test -n "${am_cv_prog_tar_$1}" && break
1485
1486 # tar/untar a dummy directory, and stop if the command works.
1487 rm -rf conftest.dir
1488 mkdir conftest.dir
1489 echo GrepMe > conftest.dir/file
1490 AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
1491 rm -rf conftest.dir
1492 if test -s conftest.tar; then
1493 AM_RUN_LOG([$am__untar <conftest.tar])
1494 AM_RUN_LOG([cat conftest.dir/file])
1495 grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
1496 fi
1497 done
1498 rm -rf conftest.dir
1499
1500 AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
1501 AC_MSG_RESULT([$am_cv_prog_tar_$1])])
1502
1503 AC_SUBST([am__tar])
1504 AC_SUBST([am__untar])
1505 ]) # _AM_PROG_TAR
1506
1507 m4_include([m4/ax_append_flag.m4])
1508 m4_include([m4/ax_cflags_warn_all.m4])
1509 m4_include([m4/ax_check_compile_flag.m4])
1510 m4_include([m4/ax_check_enable_debug.m4])
1511 m4_include([m4/ax_check_gnu_make.m4])
1512 m4_include([m4/ax_check_link_flag.m4])
1513 m4_include([m4/ax_code_coverage.m4])
1514 m4_include([m4/ax_configure_args.m4])
1515 m4_include([m4/ax_enable_builddir.m4])
1516 m4_include([m4/ax_extend_srcdir.m4])
1517 m4_include([m4/ax_pthread.m4])
1518 m4_include([m4/ax_require_defined.m4])
1519 m4_include([m4/ax_sanitizers.m4])
0 #! /bin/sh
1 # Wrapper for Microsoft lib.exe
2
3 me=ar-lib
4 scriptversion=2012-03-01.08; # UTC
5
6 # Copyright (C) 2010-2018 Free Software Foundation, Inc.
7 # Written by Peter Rosin <peda@lysator.liu.se>.
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2, or (at your option)
12 # any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22 # As a special exception to the GNU General Public License, if you
23 # distribute this file as part of a program that contains a
24 # configuration script generated by Autoconf, you may include it under
25 # the same distribution terms that you use for the rest of that program.
26
27 # This file is maintained in Automake, please report
28 # bugs to <bug-automake@gnu.org> or send patches to
29 # <automake-patches@gnu.org>.
30
31
32 # func_error message
33 func_error ()
34 {
35 echo "$me: $1" 1>&2
36 exit 1
37 }
38
39 file_conv=
40
41 # func_file_conv build_file
42 # Convert a $build file to $host form and store it in $file
43 # Currently only supports Windows hosts.
44 func_file_conv ()
45 {
46 file=$1
47 case $file in
48 / | /[!/]*) # absolute file, and not a UNC file
49 if test -z "$file_conv"; then
50 # lazily determine how to convert abs files
51 case `uname -s` in
52 MINGW*)
53 file_conv=mingw
54 ;;
55 CYGWIN*)
56 file_conv=cygwin
57 ;;
58 *)
59 file_conv=wine
60 ;;
61 esac
62 fi
63 case $file_conv in
64 mingw)
65 file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
66 ;;
67 cygwin)
68 file=`cygpath -m "$file" || echo "$file"`
69 ;;
70 wine)
71 file=`winepath -w "$file" || echo "$file"`
72 ;;
73 esac
74 ;;
75 esac
76 }
77
78 # func_at_file at_file operation archive
79 # Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
80 # for each of them.
81 # When interpreting the content of the @FILE, do NOT use func_file_conv,
82 # since the user would need to supply preconverted file names to
83 # binutils ar, at least for MinGW.
84 func_at_file ()
85 {
86 operation=$2
87 archive=$3
88 at_file_contents=`cat "$1"`
89 eval set x "$at_file_contents"
90 shift
91
92 for member
93 do
94 $AR -NOLOGO $operation:"$member" "$archive" || exit $?
95 done
96 }
97
98 case $1 in
99 '')
100 func_error "no command. Try '$0 --help' for more information."
101 ;;
102 -h | --h*)
103 cat <<EOF
104 Usage: $me [--help] [--version] PROGRAM ACTION ARCHIVE [MEMBER...]
105
106 Members may be specified in a file named with @FILE.
107 EOF
108 exit $?
109 ;;
110 -v | --v*)
111 echo "$me, version $scriptversion"
112 exit $?
113 ;;
114 esac
115
116 if test $# -lt 3; then
117 func_error "you must specify a program, an action and an archive"
118 fi
119
120 AR=$1
121 shift
122 while :
123 do
124 if test $# -lt 2; then
125 func_error "you must specify a program, an action and an archive"
126 fi
127 case $1 in
128 -lib | -LIB \
129 | -ltcg | -LTCG \
130 | -machine* | -MACHINE* \
131 | -subsystem* | -SUBSYSTEM* \
132 | -verbose | -VERBOSE \
133 | -wx* | -WX* )
134 AR="$AR $1"
135 shift
136 ;;
137 *)
138 action=$1
139 shift
140 break
141 ;;
142 esac
143 done
144 orig_archive=$1
145 shift
146 func_file_conv "$orig_archive"
147 archive=$file
148
149 # strip leading dash in $action
150 action=${action#-}
151
152 delete=
153 extract=
154 list=
155 quick=
156 replace=
157 index=
158 create=
159
160 while test -n "$action"
161 do
162 case $action in
163 d*) delete=yes ;;
164 x*) extract=yes ;;
165 t*) list=yes ;;
166 q*) quick=yes ;;
167 r*) replace=yes ;;
168 s*) index=yes ;;
169 S*) ;; # the index is always updated implicitly
170 c*) create=yes ;;
171 u*) ;; # TODO: don't ignore the update modifier
172 v*) ;; # TODO: don't ignore the verbose modifier
173 *)
174 func_error "unknown action specified"
175 ;;
176 esac
177 action=${action#?}
178 done
179
180 case $delete$extract$list$quick$replace,$index in
181 yes,* | ,yes)
182 ;;
183 yesyes*)
184 func_error "more than one action specified"
185 ;;
186 *)
187 func_error "no action specified"
188 ;;
189 esac
190
191 if test -n "$delete"; then
192 if test ! -f "$orig_archive"; then
193 func_error "archive not found"
194 fi
195 for member
196 do
197 case $1 in
198 @*)
199 func_at_file "${1#@}" -REMOVE "$archive"
200 ;;
201 *)
202 func_file_conv "$1"
203 $AR -NOLOGO -REMOVE:"$file" "$archive" || exit $?
204 ;;
205 esac
206 done
207
208 elif test -n "$extract"; then
209 if test ! -f "$orig_archive"; then
210 func_error "archive not found"
211 fi
212 if test $# -gt 0; then
213 for member
214 do
215 case $1 in
216 @*)
217 func_at_file "${1#@}" -EXTRACT "$archive"
218 ;;
219 *)
220 func_file_conv "$1"
221 $AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $?
222 ;;
223 esac
224 done
225 else
226 $AR -NOLOGO -LIST "$archive" | sed -e 's/\\/\\\\/g' | while read member
227 do
228 $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
229 done
230 fi
231
232 elif test -n "$quick$replace"; then
233 if test ! -f "$orig_archive"; then
234 if test -z "$create"; then
235 echo "$me: creating $orig_archive"
236 fi
237 orig_archive=
238 else
239 orig_archive=$archive
240 fi
241
242 for member
243 do
244 case $1 in
245 @*)
246 func_file_conv "${1#@}"
247 set x "$@" "@$file"
248 ;;
249 *)
250 func_file_conv "$1"
251 set x "$@" "$file"
252 ;;
253 esac
254 shift
255 shift
256 done
257
258 if test -n "$orig_archive"; then
259 $AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $?
260 else
261 $AR -NOLOGO -OUT:"$archive" "$@" || exit $?
262 fi
263
264 elif test -n "$list"; then
265 if test ! -f "$orig_archive"; then
266 func_error "archive not found"
267 fi
268 $AR -NOLOGO -LIST "$archive" || exit $?
269 fi
0 #! /bin/sh
1 # Wrapper for compilers which do not understand '-c -o'.
2
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
6 # Written by Tom Tromey <tromey@cygnus.com>.
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2, or (at your option)
11 # any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <https://www.gnu.org/licenses/>.
20
21 # As a special exception to the GNU General Public License, if you
22 # distribute this file as part of a program that contains a
23 # configuration script generated by Autoconf, you may include it under
24 # the same distribution terms that you use for the rest of that program.
25
26 # This file is maintained in Automake, please report
27 # bugs to <bug-automake@gnu.org> or send patches to
28 # <automake-patches@gnu.org>.
29
30 nl='
31 '
32
33 # We need space, tab and new line, in precisely that order. Quoting is
34 # there to prevent tools from complaining about whitespace usage.
35 IFS=" "" $nl"
36
37 file_conv=
38
39 # func_file_conv build_file lazy
40 # Convert a $build file to $host form and store it in $file
41 # Currently only supports Windows hosts. If the determined conversion
42 # type is listed in (the comma separated) LAZY, no conversion will
43 # take place.
44 func_file_conv ()
45 {
46 file=$1
47 case $file in
48 / | /[!/]*) # absolute file, and not a UNC file
49 if test -z "$file_conv"; then
50 # lazily determine how to convert abs files
51 case `uname -s` in
52 MINGW*)
53 file_conv=mingw
54 ;;
55 CYGWIN*)
56 file_conv=cygwin
57 ;;
58 *)
59 file_conv=wine
60 ;;
61 esac
62 fi
63 case $file_conv/,$2, in
64 *,$file_conv,*)
65 ;;
66 mingw/*)
67 file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
68 ;;
69 cygwin/*)
70 file=`cygpath -m "$file" || echo "$file"`
71 ;;
72 wine/*)
73 file=`winepath -w "$file" || echo "$file"`
74 ;;
75 esac
76 ;;
77 esac
78 }
79
80 # func_cl_dashL linkdir
81 # Make cl look for libraries in LINKDIR
82 func_cl_dashL ()
83 {
84 func_file_conv "$1"
85 if test -z "$lib_path"; then
86 lib_path=$file
87 else
88 lib_path="$lib_path;$file"
89 fi
90 linker_opts="$linker_opts -LIBPATH:$file"
91 }
92
93 # func_cl_dashl library
94 # Do a library search-path lookup for cl
95 func_cl_dashl ()
96 {
97 lib=$1
98 found=no
99 save_IFS=$IFS
100 IFS=';'
101 for dir in $lib_path $LIB
102 do
103 IFS=$save_IFS
104 if $shared && test -f "$dir/$lib.dll.lib"; then
105 found=yes
106 lib=$dir/$lib.dll.lib
107 break
108 fi
109 if test -f "$dir/$lib.lib"; then
110 found=yes
111 lib=$dir/$lib.lib
112 break
113 fi
114 if test -f "$dir/lib$lib.a"; then
115 found=yes
116 lib=$dir/lib$lib.a
117 break
118 fi
119 done
120 IFS=$save_IFS
121
122 if test "$found" != yes; then
123 lib=$lib.lib
124 fi
125 }
126
127 # func_cl_wrapper cl arg...
128 # Adjust compile command to suit cl
129 func_cl_wrapper ()
130 {
131 # Assume a capable shell
132 lib_path=
133 shared=:
134 linker_opts=
135 for arg
136 do
137 if test -n "$eat"; then
138 eat=
139 else
140 case $1 in
141 -o)
142 # configure might choose to run compile as 'compile cc -o foo foo.c'.
143 eat=1
144 case $2 in
145 *.o | *.[oO][bB][jJ])
146 func_file_conv "$2"
147 set x "$@" -Fo"$file"
148 shift
149 ;;
150 *)
151 func_file_conv "$2"
152 set x "$@" -Fe"$file"
153 shift
154 ;;
155 esac
156 ;;
157 -I)
158 eat=1
159 func_file_conv "$2" mingw
160 set x "$@" -I"$file"
161 shift
162 ;;
163 -I*)
164 func_file_conv "${1#-I}" mingw
165 set x "$@" -I"$file"
166 shift
167 ;;
168 -l)
169 eat=1
170 func_cl_dashl "$2"
171 set x "$@" "$lib"
172 shift
173 ;;
174 -l*)
175 func_cl_dashl "${1#-l}"
176 set x "$@" "$lib"
177 shift
178 ;;
179 -L)
180 eat=1
181 func_cl_dashL "$2"
182 ;;
183 -L*)
184 func_cl_dashL "${1#-L}"
185 ;;
186 -static)
187 shared=false
188 ;;
189 -Wl,*)
190 arg=${1#-Wl,}
191 save_ifs="$IFS"; IFS=','
192 for flag in $arg; do
193 IFS="$save_ifs"
194 linker_opts="$linker_opts $flag"
195 done
196 IFS="$save_ifs"
197 ;;
198 -Xlinker)
199 eat=1
200 linker_opts="$linker_opts $2"
201 ;;
202 -*)
203 set x "$@" "$1"
204 shift
205 ;;
206 *.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
207 func_file_conv "$1"
208 set x "$@" -Tp"$file"
209 shift
210 ;;
211 *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
212 func_file_conv "$1" mingw
213 set x "$@" "$file"
214 shift
215 ;;
216 *)
217 set x "$@" "$1"
218 shift
219 ;;
220 esac
221 fi
222 shift
223 done
224 if test -n "$linker_opts"; then
225 linker_opts="-link$linker_opts"
226 fi
227 exec "$@" $linker_opts
228 exit 1
229 }
230
231 eat=
232
233 case $1 in
234 '')
235 echo "$0: No command. Try '$0 --help' for more information." 1>&2
236 exit 1;
237 ;;
238 -h | --h*)
239 cat <<\EOF
240 Usage: compile [--help] [--version] PROGRAM [ARGS]
241
242 Wrapper for compilers which do not understand '-c -o'.
243 Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
244 arguments, and rename the output as expected.
245
246 If you are trying to build a whole package this is not the
247 right script to run: please start by reading the file 'INSTALL'.
248
249 Report bugs to <bug-automake@gnu.org>.
250 EOF
251 exit $?
252 ;;
253 -v | --v*)
254 echo "compile $scriptversion"
255 exit $?
256 ;;
257 cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
258 icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
259 func_cl_wrapper "$@" # Doesn't return...
260 ;;
261 esac
262
263 ofile=
264 cfile=
265
266 for arg
267 do
268 if test -n "$eat"; then
269 eat=
270 else
271 case $1 in
272 -o)
273 # configure might choose to run compile as 'compile cc -o foo foo.c'.
274 # So we strip '-o arg' only if arg is an object.
275 eat=1
276 case $2 in
277 *.o | *.obj)
278 ofile=$2
279 ;;
280 *)
281 set x "$@" -o "$2"
282 shift
283 ;;
284 esac
285 ;;
286 *.c)
287 cfile=$1
288 set x "$@" "$1"
289 shift
290 ;;
291 *)
292 set x "$@" "$1"
293 shift
294 ;;
295 esac
296 fi
297 shift
298 done
299
300 if test -z "$ofile" || test -z "$cfile"; then
301 # If no '-o' option was seen then we might have been invoked from a
302 # pattern rule where we don't need one. That is ok -- this is a
303 # normal compilation that the losing compiler can handle. If no
304 # '.c' file was seen then we are probably linking. That is also
305 # ok.
306 exec "$@"
307 fi
308
309 # Name of file we expect compiler to create.
310 cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
311
312 # Create the lock directory.
313 # Note: use '[/\\:.-]' here to ensure that we don't use the same name
314 # that we are using for the .o file. Also, base the name on the expected
315 # object file name, since that is what matters with a parallel build.
316 lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
317 while true; do
318 if mkdir "$lockdir" >/dev/null 2>&1; then
319 break
320 fi
321 sleep 1
322 done
323 # FIXME: race condition here if user kills between mkdir and trap.
324 trap "rmdir '$lockdir'; exit 1" 1 2 15
325
326 # Run the compile.
327 "$@"
328 ret=$?
329
330 if test -f "$cofile"; then
331 test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
332 elif test -f "${cofile}bj"; then
333 test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
334 fi
335
336 rmdir "$lockdir"
337 exit $ret
338
339 # Local Variables:
340 # mode: shell-script
341 # sh-indentation: 2
342 # eval: (add-hook 'before-save-hook 'time-stamp)
343 # time-stamp-start: "scriptversion="
344 # time-stamp-format: "%:y-%02m-%02d.%02H"
345 # time-stamp-time-zone: "UTC0"
346 # time-stamp-end: "; # UTC"
347 # End:
0 #! /bin/sh
1 # Attempt to guess a canonical system name.
2 # Copyright 1992-2018 Free Software Foundation, Inc.
3
4 timestamp='2018-02-24'
5
6 # This file is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, see <https://www.gnu.org/licenses/>.
18 #
19 # As a special exception to the GNU General Public License, if you
20 # distribute this file as part of a program that contains a
21 # configuration script generated by Autoconf, you may include it under
22 # the same distribution terms that you use for the rest of that
23 # program. This Exception is an additional permission under section 7
24 # of the GNU General Public License, version 3 ("GPLv3").
25 #
26 # Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
27 #
28 # You can get the latest version of this script from:
29 # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
30 #
31 # Please send patches to <config-patches@gnu.org>.
32
33
34 me=`echo "$0" | sed -e 's,.*/,,'`
35
36 usage="\
37 Usage: $0 [OPTION]
38
39 Output the configuration name of the system \`$me' is run on.
40
41 Options:
42 -h, --help print this help, then exit
43 -t, --time-stamp print date of last modification, then exit
44 -v, --version print version number, then exit
45
46 Report bugs and patches to <config-patches@gnu.org>."
47
48 version="\
49 GNU config.guess ($timestamp)
50
51 Originally written by Per Bothner.
52 Copyright 1992-2018 Free Software Foundation, Inc.
53
54 This is free software; see the source for copying conditions. There is NO
55 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
56
57 help="
58 Try \`$me --help' for more information."
59
60 # Parse command line
61 while test $# -gt 0 ; do
62 case $1 in
63 --time-stamp | --time* | -t )
64 echo "$timestamp" ; exit ;;
65 --version | -v )
66 echo "$version" ; exit ;;
67 --help | --h* | -h )
68 echo "$usage"; exit ;;
69 -- ) # Stop option processing
70 shift; break ;;
71 - ) # Use stdin as input.
72 break ;;
73 -* )
74 echo "$me: invalid option $1$help" >&2
75 exit 1 ;;
76 * )
77 break ;;
78 esac
79 done
80
81 if test $# != 0; then
82 echo "$me: too many arguments$help" >&2
83 exit 1
84 fi
85
86 trap 'exit 1' 1 2 15
87
88 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a
89 # compiler to aid in system detection is discouraged as it requires
90 # temporary files to be created and, as you can see below, it is a
91 # headache to deal with in a portable fashion.
92
93 # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
94 # use `HOST_CC' if defined, but it is deprecated.
95
96 # Portable tmp directory creation inspired by the Autoconf team.
97
98 set_cc_for_build='
99 trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
100 trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
101 : ${TMPDIR=/tmp} ;
102 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
103 { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
104 { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
105 { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
106 dummy=$tmp/dummy ;
107 tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
108 case $CC_FOR_BUILD,$HOST_CC,$CC in
109 ,,) echo "int x;" > "$dummy.c" ;
110 for c in cc gcc c89 c99 ; do
111 if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
112 CC_FOR_BUILD="$c"; break ;
113 fi ;
114 done ;
115 if test x"$CC_FOR_BUILD" = x ; then
116 CC_FOR_BUILD=no_compiler_found ;
117 fi
118 ;;
119 ,,*) CC_FOR_BUILD=$CC ;;
120 ,*,*) CC_FOR_BUILD=$HOST_CC ;;
121 esac ; set_cc_for_build= ;'
122
123 # This is needed to find uname on a Pyramid OSx when run in the BSD universe.
124 # (ghazi@noc.rutgers.edu 1994-08-24)
125 if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
126 PATH=$PATH:/.attbin ; export PATH
127 fi
128
129 UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
130 UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
131 UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
132 UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
133
134 case "$UNAME_SYSTEM" in
135 Linux|GNU|GNU/*)
136 # If the system lacks a compiler, then just pick glibc.
137 # We could probably try harder.
138 LIBC=gnu
139
140 eval "$set_cc_for_build"
141 cat <<-EOF > "$dummy.c"
142 #include <features.h>
143 #if defined(__UCLIBC__)
144 LIBC=uclibc
145 #elif defined(__dietlibc__)
146 LIBC=dietlibc
147 #else
148 LIBC=gnu
149 #endif
150 EOF
151 eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
152
153 # If ldd exists, use it to detect musl libc.
154 if command -v ldd >/dev/null && \
155 ldd --version 2>&1 | grep -q ^musl
156 then
157 LIBC=musl
158 fi
159 ;;
160 esac
161
162 # Note: order is significant - the case branches are not exclusive.
163
164 case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
165 *:NetBSD:*:*)
166 # NetBSD (nbsd) targets should (where applicable) match one or
167 # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
168 # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
169 # switched to ELF, *-*-netbsd* would select the old
170 # object file format. This provides both forward
171 # compatibility and a consistent mechanism for selecting the
172 # object file format.
173 #
174 # Note: NetBSD doesn't particularly care about the vendor
175 # portion of the name. We always set it to "unknown".
176 sysctl="sysctl -n hw.machine_arch"
177 UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
178 "/sbin/$sysctl" 2>/dev/null || \
179 "/usr/sbin/$sysctl" 2>/dev/null || \
180 echo unknown)`
181 case "$UNAME_MACHINE_ARCH" in
182 armeb) machine=armeb-unknown ;;
183 arm*) machine=arm-unknown ;;
184 sh3el) machine=shl-unknown ;;
185 sh3eb) machine=sh-unknown ;;
186 sh5el) machine=sh5le-unknown ;;
187 earmv*)
188 arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
189 endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
190 machine="${arch}${endian}"-unknown
191 ;;
192 *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
193 esac
194 # The Operating System including object format, if it has switched
195 # to ELF recently (or will in the future) and ABI.
196 case "$UNAME_MACHINE_ARCH" in
197 earm*)
198 os=netbsdelf
199 ;;
200 arm*|i386|m68k|ns32k|sh3*|sparc|vax)
201 eval "$set_cc_for_build"
202 if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
203 | grep -q __ELF__
204 then
205 # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
206 # Return netbsd for either. FIX?
207 os=netbsd
208 else
209 os=netbsdelf
210 fi
211 ;;
212 *)
213 os=netbsd
214 ;;
215 esac
216 # Determine ABI tags.
217 case "$UNAME_MACHINE_ARCH" in
218 earm*)
219 expr='s/^earmv[0-9]/-eabi/;s/eb$//'
220 abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
221 ;;
222 esac
223 # The OS release
224 # Debian GNU/NetBSD machines have a different userland, and
225 # thus, need a distinct triplet. However, they do not need
226 # kernel version information, so it can be replaced with a
227 # suitable tag, in the style of linux-gnu.
228 case "$UNAME_VERSION" in
229 Debian*)
230 release='-gnu'
231 ;;
232 *)
233 release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
234 ;;
235 esac
236 # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
237 # contains redundant information, the shorter form:
238 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
239 echo "$machine-${os}${release}${abi}"
240 exit ;;
241 *:Bitrig:*:*)
242 UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
243 echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
244 exit ;;
245 *:OpenBSD:*:*)
246 UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
247 echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
248 exit ;;
249 *:LibertyBSD:*:*)
250 UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
251 echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
252 exit ;;
253 *:MidnightBSD:*:*)
254 echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
255 exit ;;
256 *:ekkoBSD:*:*)
257 echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
258 exit ;;
259 *:SolidBSD:*:*)
260 echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
261 exit ;;
262 macppc:MirBSD:*:*)
263 echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
264 exit ;;
265 *:MirBSD:*:*)
266 echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
267 exit ;;
268 *:Sortix:*:*)
269 echo "$UNAME_MACHINE"-unknown-sortix
270 exit ;;
271 *:Redox:*:*)
272 echo "$UNAME_MACHINE"-unknown-redox
273 exit ;;
274 mips:OSF1:*.*)
275 echo mips-dec-osf1
276 exit ;;
277 alpha:OSF1:*:*)
278 case $UNAME_RELEASE in
279 *4.0)
280 UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
281 ;;
282 *5.*)
283 UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
284 ;;
285 esac
286 # According to Compaq, /usr/sbin/psrinfo has been available on
287 # OSF/1 and Tru64 systems produced since 1995. I hope that
288 # covers most systems running today. This code pipes the CPU
289 # types through head -n 1, so we only detect the type of CPU 0.
290 ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
291 case "$ALPHA_CPU_TYPE" in
292 "EV4 (21064)")
293 UNAME_MACHINE=alpha ;;
294 "EV4.5 (21064)")
295 UNAME_MACHINE=alpha ;;
296 "LCA4 (21066/21068)")
297 UNAME_MACHINE=alpha ;;
298 "EV5 (21164)")
299 UNAME_MACHINE=alphaev5 ;;
300 "EV5.6 (21164A)")
301 UNAME_MACHINE=alphaev56 ;;
302 "EV5.6 (21164PC)")
303 UNAME_MACHINE=alphapca56 ;;
304 "EV5.7 (21164PC)")
305 UNAME_MACHINE=alphapca57 ;;
306 "EV6 (21264)")
307 UNAME_MACHINE=alphaev6 ;;
308 "EV6.7 (21264A)")
309 UNAME_MACHINE=alphaev67 ;;
310 "EV6.8CB (21264C)")
311 UNAME_MACHINE=alphaev68 ;;
312 "EV6.8AL (21264B)")
313 UNAME_MACHINE=alphaev68 ;;
314 "EV6.8CX (21264D)")
315 UNAME_MACHINE=alphaev68 ;;
316 "EV6.9A (21264/EV69A)")
317 UNAME_MACHINE=alphaev69 ;;
318 "EV7 (21364)")
319 UNAME_MACHINE=alphaev7 ;;
320 "EV7.9 (21364A)")
321 UNAME_MACHINE=alphaev79 ;;
322 esac
323 # A Pn.n version is a patched version.
324 # A Vn.n version is a released version.
325 # A Tn.n version is a released field test version.
326 # A Xn.n version is an unreleased experimental baselevel.
327 # 1.2 uses "1.2" for uname -r.
328 echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
329 # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
330 exitcode=$?
331 trap '' 0
332 exit $exitcode ;;
333 Amiga*:UNIX_System_V:4.0:*)
334 echo m68k-unknown-sysv4
335 exit ;;
336 *:[Aa]miga[Oo][Ss]:*:*)
337 echo "$UNAME_MACHINE"-unknown-amigaos
338 exit ;;
339 *:[Mm]orph[Oo][Ss]:*:*)
340 echo "$UNAME_MACHINE"-unknown-morphos
341 exit ;;
342 *:OS/390:*:*)
343 echo i370-ibm-openedition
344 exit ;;
345 *:z/VM:*:*)
346 echo s390-ibm-zvmoe
347 exit ;;
348 *:OS400:*:*)
349 echo powerpc-ibm-os400
350 exit ;;
351 arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
352 echo arm-acorn-riscix"$UNAME_RELEASE"
353 exit ;;
354 arm*:riscos:*:*|arm*:RISCOS:*:*)
355 echo arm-unknown-riscos
356 exit ;;
357 SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
358 echo hppa1.1-hitachi-hiuxmpp
359 exit ;;
360 Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
361 # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
362 if test "`(/bin/universe) 2>/dev/null`" = att ; then
363 echo pyramid-pyramid-sysv3
364 else
365 echo pyramid-pyramid-bsd
366 fi
367 exit ;;
368 NILE*:*:*:dcosx)
369 echo pyramid-pyramid-svr4
370 exit ;;
371 DRS?6000:unix:4.0:6*)
372 echo sparc-icl-nx6
373 exit ;;
374 DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
375 case `/usr/bin/uname -p` in
376 sparc) echo sparc-icl-nx7; exit ;;
377 esac ;;
378 s390x:SunOS:*:*)
379 echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
380 exit ;;
381 sun4H:SunOS:5.*:*)
382 echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
383 exit ;;
384 sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
385 echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
386 exit ;;
387 i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
388 echo i386-pc-auroraux"$UNAME_RELEASE"
389 exit ;;
390 i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
391 eval "$set_cc_for_build"
392 SUN_ARCH=i386
393 # If there is a compiler, see if it is configured for 64-bit objects.
394 # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
395 # This test works for both compilers.
396 if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
397 if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
398 (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
399 grep IS_64BIT_ARCH >/dev/null
400 then
401 SUN_ARCH=x86_64
402 fi
403 fi
404 echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
405 exit ;;
406 sun4*:SunOS:6*:*)
407 # According to config.sub, this is the proper way to canonicalize
408 # SunOS6. Hard to guess exactly what SunOS6 will be like, but
409 # it's likely to be more like Solaris than SunOS4.
410 echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
411 exit ;;
412 sun4*:SunOS:*:*)
413 case "`/usr/bin/arch -k`" in
414 Series*|S4*)
415 UNAME_RELEASE=`uname -v`
416 ;;
417 esac
418 # Japanese Language versions have a version number like `4.1.3-JL'.
419 echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
420 exit ;;
421 sun3*:SunOS:*:*)
422 echo m68k-sun-sunos"$UNAME_RELEASE"
423 exit ;;
424 sun*:*:4.2BSD:*)
425 UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
426 test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
427 case "`/bin/arch`" in
428 sun3)
429 echo m68k-sun-sunos"$UNAME_RELEASE"
430 ;;
431 sun4)
432 echo sparc-sun-sunos"$UNAME_RELEASE"
433 ;;
434 esac
435 exit ;;
436 aushp:SunOS:*:*)
437 echo sparc-auspex-sunos"$UNAME_RELEASE"
438 exit ;;
439 # The situation for MiNT is a little confusing. The machine name
440 # can be virtually everything (everything which is not
441 # "atarist" or "atariste" at least should have a processor
442 # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
443 # to the lowercase version "mint" (or "freemint"). Finally
444 # the system name "TOS" denotes a system which is actually not
445 # MiNT. But MiNT is downward compatible to TOS, so this should
446 # be no problem.
447 atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
448 echo m68k-atari-mint"$UNAME_RELEASE"
449 exit ;;
450 atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
451 echo m68k-atari-mint"$UNAME_RELEASE"
452 exit ;;
453 *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
454 echo m68k-atari-mint"$UNAME_RELEASE"
455 exit ;;
456 milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
457 echo m68k-milan-mint"$UNAME_RELEASE"
458 exit ;;
459 hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
460 echo m68k-hades-mint"$UNAME_RELEASE"
461 exit ;;
462 *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
463 echo m68k-unknown-mint"$UNAME_RELEASE"
464 exit ;;
465 m68k:machten:*:*)
466 echo m68k-apple-machten"$UNAME_RELEASE"
467 exit ;;
468 powerpc:machten:*:*)
469 echo powerpc-apple-machten"$UNAME_RELEASE"
470 exit ;;
471 RISC*:Mach:*:*)
472 echo mips-dec-mach_bsd4.3
473 exit ;;
474 RISC*:ULTRIX:*:*)
475 echo mips-dec-ultrix"$UNAME_RELEASE"
476 exit ;;
477 VAX*:ULTRIX*:*:*)
478 echo vax-dec-ultrix"$UNAME_RELEASE"
479 exit ;;
480 2020:CLIX:*:* | 2430:CLIX:*:*)
481 echo clipper-intergraph-clix"$UNAME_RELEASE"
482 exit ;;
483 mips:*:*:UMIPS | mips:*:*:RISCos)
484 eval "$set_cc_for_build"
485 sed 's/^ //' << EOF > "$dummy.c"
486 #ifdef __cplusplus
487 #include <stdio.h> /* for printf() prototype */
488 int main (int argc, char *argv[]) {
489 #else
490 int main (argc, argv) int argc; char *argv[]; {
491 #endif
492 #if defined (host_mips) && defined (MIPSEB)
493 #if defined (SYSTYPE_SYSV)
494 printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
495 #endif
496 #if defined (SYSTYPE_SVR4)
497 printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
498 #endif
499 #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
500 printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
501 #endif
502 #endif
503 exit (-1);
504 }
505 EOF
506 $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
507 dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
508 SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
509 { echo "$SYSTEM_NAME"; exit; }
510 echo mips-mips-riscos"$UNAME_RELEASE"
511 exit ;;
512 Motorola:PowerMAX_OS:*:*)
513 echo powerpc-motorola-powermax
514 exit ;;
515 Motorola:*:4.3:PL8-*)
516 echo powerpc-harris-powermax
517 exit ;;
518 Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
519 echo powerpc-harris-powermax
520 exit ;;
521 Night_Hawk:Power_UNIX:*:*)
522 echo powerpc-harris-powerunix
523 exit ;;
524 m88k:CX/UX:7*:*)
525 echo m88k-harris-cxux7
526 exit ;;
527 m88k:*:4*:R4*)
528 echo m88k-motorola-sysv4
529 exit ;;
530 m88k:*:3*:R3*)
531 echo m88k-motorola-sysv3
532 exit ;;
533 AViiON:dgux:*:*)
534 # DG/UX returns AViiON for all architectures
535 UNAME_PROCESSOR=`/usr/bin/uname -p`
536 if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
537 then
538 if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
539 [ "$TARGET_BINARY_INTERFACE"x = x ]
540 then
541 echo m88k-dg-dgux"$UNAME_RELEASE"
542 else
543 echo m88k-dg-dguxbcs"$UNAME_RELEASE"
544 fi
545 else
546 echo i586-dg-dgux"$UNAME_RELEASE"
547 fi
548 exit ;;
549 M88*:DolphinOS:*:*) # DolphinOS (SVR3)
550 echo m88k-dolphin-sysv3
551 exit ;;
552 M88*:*:R3*:*)
553 # Delta 88k system running SVR3
554 echo m88k-motorola-sysv3
555 exit ;;
556 XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
557 echo m88k-tektronix-sysv3
558 exit ;;
559 Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
560 echo m68k-tektronix-bsd
561 exit ;;
562 *:IRIX*:*:*)
563 echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
564 exit ;;
565 ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
566 echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
567 exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
568 i*86:AIX:*:*)
569 echo i386-ibm-aix
570 exit ;;
571 ia64:AIX:*:*)
572 if [ -x /usr/bin/oslevel ] ; then
573 IBM_REV=`/usr/bin/oslevel`
574 else
575 IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
576 fi
577 echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
578 exit ;;
579 *:AIX:2:3)
580 if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
581 eval "$set_cc_for_build"
582 sed 's/^ //' << EOF > "$dummy.c"
583 #include <sys/systemcfg.h>
584
585 main()
586 {
587 if (!__power_pc())
588 exit(1);
589 puts("powerpc-ibm-aix3.2.5");
590 exit(0);
591 }
592 EOF
593 if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
594 then
595 echo "$SYSTEM_NAME"
596 else
597 echo rs6000-ibm-aix3.2.5
598 fi
599 elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
600 echo rs6000-ibm-aix3.2.4
601 else
602 echo rs6000-ibm-aix3.2
603 fi
604 exit ;;
605 *:AIX:*:[4567])
606 IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
607 if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
608 IBM_ARCH=rs6000
609 else
610 IBM_ARCH=powerpc
611 fi
612 if [ -x /usr/bin/lslpp ] ; then
613 IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
614 awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
615 else
616 IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
617 fi
618 echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
619 exit ;;
620 *:AIX:*:*)
621 echo rs6000-ibm-aix
622 exit ;;
623 ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
624 echo romp-ibm-bsd4.4
625 exit ;;
626 ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
627 echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
628 exit ;; # report: romp-ibm BSD 4.3
629 *:BOSX:*:*)
630 echo rs6000-bull-bosx
631 exit ;;
632 DPX/2?00:B.O.S.:*:*)
633 echo m68k-bull-sysv3
634 exit ;;
635 9000/[34]??:4.3bsd:1.*:*)
636 echo m68k-hp-bsd
637 exit ;;
638 hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
639 echo m68k-hp-bsd4.4
640 exit ;;
641 9000/[34678]??:HP-UX:*:*)
642 HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
643 case "$UNAME_MACHINE" in
644 9000/31?) HP_ARCH=m68000 ;;
645 9000/[34]??) HP_ARCH=m68k ;;
646 9000/[678][0-9][0-9])
647 if [ -x /usr/bin/getconf ]; then
648 sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
649 sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
650 case "$sc_cpu_version" in
651 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
652 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
653 532) # CPU_PA_RISC2_0
654 case "$sc_kernel_bits" in
655 32) HP_ARCH=hppa2.0n ;;
656 64) HP_ARCH=hppa2.0w ;;
657 '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
658 esac ;;
659 esac
660 fi
661 if [ "$HP_ARCH" = "" ]; then
662 eval "$set_cc_for_build"
663 sed 's/^ //' << EOF > "$dummy.c"
664
665 #define _HPUX_SOURCE
666 #include <stdlib.h>
667 #include <unistd.h>
668
669 int main ()
670 {
671 #if defined(_SC_KERNEL_BITS)
672 long bits = sysconf(_SC_KERNEL_BITS);
673 #endif
674 long cpu = sysconf (_SC_CPU_VERSION);
675
676 switch (cpu)
677 {
678 case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
679 case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
680 case CPU_PA_RISC2_0:
681 #if defined(_SC_KERNEL_BITS)
682 switch (bits)
683 {
684 case 64: puts ("hppa2.0w"); break;
685 case 32: puts ("hppa2.0n"); break;
686 default: puts ("hppa2.0"); break;
687 } break;
688 #else /* !defined(_SC_KERNEL_BITS) */
689 puts ("hppa2.0"); break;
690 #endif
691 default: puts ("hppa1.0"); break;
692 }
693 exit (0);
694 }
695 EOF
696 (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
697 test -z "$HP_ARCH" && HP_ARCH=hppa
698 fi ;;
699 esac
700 if [ "$HP_ARCH" = hppa2.0w ]
701 then
702 eval "$set_cc_for_build"
703
704 # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
705 # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
706 # generating 64-bit code. GNU and HP use different nomenclature:
707 #
708 # $ CC_FOR_BUILD=cc ./config.guess
709 # => hppa2.0w-hp-hpux11.23
710 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
711 # => hppa64-hp-hpux11.23
712
713 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
714 grep -q __LP64__
715 then
716 HP_ARCH=hppa2.0w
717 else
718 HP_ARCH=hppa64
719 fi
720 fi
721 echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
722 exit ;;
723 ia64:HP-UX:*:*)
724 HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
725 echo ia64-hp-hpux"$HPUX_REV"
726 exit ;;
727 3050*:HI-UX:*:*)
728 eval "$set_cc_for_build"
729 sed 's/^ //' << EOF > "$dummy.c"
730 #include <unistd.h>
731 int
732 main ()
733 {
734 long cpu = sysconf (_SC_CPU_VERSION);
735 /* The order matters, because CPU_IS_HP_MC68K erroneously returns
736 true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
737 results, however. */
738 if (CPU_IS_PA_RISC (cpu))
739 {
740 switch (cpu)
741 {
742 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
743 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
744 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
745 default: puts ("hppa-hitachi-hiuxwe2"); break;
746 }
747 }
748 else if (CPU_IS_HP_MC68K (cpu))
749 puts ("m68k-hitachi-hiuxwe2");
750 else puts ("unknown-hitachi-hiuxwe2");
751 exit (0);
752 }
753 EOF
754 $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
755 { echo "$SYSTEM_NAME"; exit; }
756 echo unknown-hitachi-hiuxwe2
757 exit ;;
758 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
759 echo hppa1.1-hp-bsd
760 exit ;;
761 9000/8??:4.3bsd:*:*)
762 echo hppa1.0-hp-bsd
763 exit ;;
764 *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
765 echo hppa1.0-hp-mpeix
766 exit ;;
767 hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
768 echo hppa1.1-hp-osf
769 exit ;;
770 hp8??:OSF1:*:*)
771 echo hppa1.0-hp-osf
772 exit ;;
773 i*86:OSF1:*:*)
774 if [ -x /usr/sbin/sysversion ] ; then
775 echo "$UNAME_MACHINE"-unknown-osf1mk
776 else
777 echo "$UNAME_MACHINE"-unknown-osf1
778 fi
779 exit ;;
780 parisc*:Lites*:*:*)
781 echo hppa1.1-hp-lites
782 exit ;;
783 C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
784 echo c1-convex-bsd
785 exit ;;
786 C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
787 if getsysinfo -f scalar_acc
788 then echo c32-convex-bsd
789 else echo c2-convex-bsd
790 fi
791 exit ;;
792 C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
793 echo c34-convex-bsd
794 exit ;;
795 C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
796 echo c38-convex-bsd
797 exit ;;
798 C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
799 echo c4-convex-bsd
800 exit ;;
801 CRAY*Y-MP:*:*:*)
802 echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
803 exit ;;
804 CRAY*[A-Z]90:*:*:*)
805 echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
806 | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
807 -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
808 -e 's/\.[^.]*$/.X/'
809 exit ;;
810 CRAY*TS:*:*:*)
811 echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
812 exit ;;
813 CRAY*T3E:*:*:*)
814 echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
815 exit ;;
816 CRAY*SV1:*:*:*)
817 echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
818 exit ;;
819 *:UNICOS/mp:*:*)
820 echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
821 exit ;;
822 F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
823 FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
824 FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
825 FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
826 echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
827 exit ;;
828 5000:UNIX_System_V:4.*:*)
829 FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
830 FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
831 echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
832 exit ;;
833 i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
834 echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
835 exit ;;
836 sparc*:BSD/OS:*:*)
837 echo sparc-unknown-bsdi"$UNAME_RELEASE"
838 exit ;;
839 *:BSD/OS:*:*)
840 echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
841 exit ;;
842 *:FreeBSD:*:*)
843 UNAME_PROCESSOR=`/usr/bin/uname -p`
844 case "$UNAME_PROCESSOR" in
845 amd64)
846 UNAME_PROCESSOR=x86_64 ;;
847 i386)
848 UNAME_PROCESSOR=i586 ;;
849 esac
850 echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
851 exit ;;
852 i*:CYGWIN*:*)
853 echo "$UNAME_MACHINE"-pc-cygwin
854 exit ;;
855 *:MINGW64*:*)
856 echo "$UNAME_MACHINE"-pc-mingw64
857 exit ;;
858 *:MINGW*:*)
859 echo "$UNAME_MACHINE"-pc-mingw32
860 exit ;;
861 *:MSYS*:*)
862 echo "$UNAME_MACHINE"-pc-msys
863 exit ;;
864 i*:PW*:*)
865 echo "$UNAME_MACHINE"-pc-pw32
866 exit ;;
867 *:Interix*:*)
868 case "$UNAME_MACHINE" in
869 x86)
870 echo i586-pc-interix"$UNAME_RELEASE"
871 exit ;;
872 authenticamd | genuineintel | EM64T)
873 echo x86_64-unknown-interix"$UNAME_RELEASE"
874 exit ;;
875 IA64)
876 echo ia64-unknown-interix"$UNAME_RELEASE"
877 exit ;;
878 esac ;;
879 i*:UWIN*:*)
880 echo "$UNAME_MACHINE"-pc-uwin
881 exit ;;
882 amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
883 echo x86_64-unknown-cygwin
884 exit ;;
885 prep*:SunOS:5.*:*)
886 echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
887 exit ;;
888 *:GNU:*:*)
889 # the GNU system
890 echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
891 exit ;;
892 *:GNU/*:*:*)
893 # other systems with GNU libc and userland
894 echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
895 exit ;;
896 i*86:Minix:*:*)
897 echo "$UNAME_MACHINE"-pc-minix
898 exit ;;
899 aarch64:Linux:*:*)
900 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
901 exit ;;
902 aarch64_be:Linux:*:*)
903 UNAME_MACHINE=aarch64_be
904 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
905 exit ;;
906 alpha:Linux:*:*)
907 case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
908 EV5) UNAME_MACHINE=alphaev5 ;;
909 EV56) UNAME_MACHINE=alphaev56 ;;
910 PCA56) UNAME_MACHINE=alphapca56 ;;
911 PCA57) UNAME_MACHINE=alphapca56 ;;
912 EV6) UNAME_MACHINE=alphaev6 ;;
913 EV67) UNAME_MACHINE=alphaev67 ;;
914 EV68*) UNAME_MACHINE=alphaev68 ;;
915 esac
916 objdump --private-headers /bin/sh | grep -q ld.so.1
917 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
918 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
919 exit ;;
920 arc:Linux:*:* | arceb:Linux:*:*)
921 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
922 exit ;;
923 arm*:Linux:*:*)
924 eval "$set_cc_for_build"
925 if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
926 | grep -q __ARM_EABI__
927 then
928 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
929 else
930 if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
931 | grep -q __ARM_PCS_VFP
932 then
933 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
934 else
935 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
936 fi
937 fi
938 exit ;;
939 avr32*:Linux:*:*)
940 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
941 exit ;;
942 cris:Linux:*:*)
943 echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
944 exit ;;
945 crisv32:Linux:*:*)
946 echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
947 exit ;;
948 e2k:Linux:*:*)
949 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
950 exit ;;
951 frv:Linux:*:*)
952 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
953 exit ;;
954 hexagon:Linux:*:*)
955 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
956 exit ;;
957 i*86:Linux:*:*)
958 echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
959 exit ;;
960 ia64:Linux:*:*)
961 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
962 exit ;;
963 k1om:Linux:*:*)
964 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
965 exit ;;
966 m32r*:Linux:*:*)
967 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
968 exit ;;
969 m68*:Linux:*:*)
970 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
971 exit ;;
972 mips:Linux:*:* | mips64:Linux:*:*)
973 eval "$set_cc_for_build"
974 sed 's/^ //' << EOF > "$dummy.c"
975 #undef CPU
976 #undef ${UNAME_MACHINE}
977 #undef ${UNAME_MACHINE}el
978 #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
979 CPU=${UNAME_MACHINE}el
980 #else
981 #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
982 CPU=${UNAME_MACHINE}
983 #else
984 CPU=
985 #endif
986 #endif
987 EOF
988 eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`"
989 test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; }
990 ;;
991 mips64el:Linux:*:*)
992 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
993 exit ;;
994 openrisc*:Linux:*:*)
995 echo or1k-unknown-linux-"$LIBC"
996 exit ;;
997 or32:Linux:*:* | or1k*:Linux:*:*)
998 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
999 exit ;;
1000 padre:Linux:*:*)
1001 echo sparc-unknown-linux-"$LIBC"
1002 exit ;;
1003 parisc64:Linux:*:* | hppa64:Linux:*:*)
1004 echo hppa64-unknown-linux-"$LIBC"
1005 exit ;;
1006 parisc:Linux:*:* | hppa:Linux:*:*)
1007 # Look for CPU level
1008 case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
1009 PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
1010 PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
1011 *) echo hppa-unknown-linux-"$LIBC" ;;
1012 esac
1013 exit ;;
1014 ppc64:Linux:*:*)
1015 echo powerpc64-unknown-linux-"$LIBC"
1016 exit ;;
1017 ppc:Linux:*:*)
1018 echo powerpc-unknown-linux-"$LIBC"
1019 exit ;;
1020 ppc64le:Linux:*:*)
1021 echo powerpc64le-unknown-linux-"$LIBC"
1022 exit ;;
1023 ppcle:Linux:*:*)
1024 echo powerpcle-unknown-linux-"$LIBC"
1025 exit ;;
1026 riscv32:Linux:*:* | riscv64:Linux:*:*)
1027 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
1028 exit ;;
1029 s390:Linux:*:* | s390x:Linux:*:*)
1030 echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
1031 exit ;;
1032 sh64*:Linux:*:*)
1033 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
1034 exit ;;
1035 sh*:Linux:*:*)
1036 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
1037 exit ;;
1038 sparc:Linux:*:* | sparc64:Linux:*:*)
1039 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
1040 exit ;;
1041 tile*:Linux:*:*)
1042 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
1043 exit ;;
1044 vax:Linux:*:*)
1045 echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
1046 exit ;;
1047 x86_64:Linux:*:*)
1048 if objdump -f /bin/sh | grep -q elf32-x86-64; then
1049 echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32
1050 else
1051 echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
1052 fi
1053 exit ;;
1054 xtensa*:Linux:*:*)
1055 echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
1056 exit ;;
1057 i*86:DYNIX/ptx:4*:*)
1058 # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
1059 # earlier versions are messed up and put the nodename in both
1060 # sysname and nodename.
1061 echo i386-sequent-sysv4
1062 exit ;;
1063 i*86:UNIX_SV:4.2MP:2.*)
1064 # Unixware is an offshoot of SVR4, but it has its own version
1065 # number series starting with 2...
1066 # I am not positive that other SVR4 systems won't match this,
1067 # I just have to hope. -- rms.
1068 # Use sysv4.2uw... so that sysv4* matches it.
1069 echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
1070 exit ;;
1071 i*86:OS/2:*:*)
1072 # If we were able to find `uname', then EMX Unix compatibility
1073 # is probably installed.
1074 echo "$UNAME_MACHINE"-pc-os2-emx
1075 exit ;;
1076 i*86:XTS-300:*:STOP)
1077 echo "$UNAME_MACHINE"-unknown-stop
1078 exit ;;
1079 i*86:atheos:*:*)
1080 echo "$UNAME_MACHINE"-unknown-atheos
1081 exit ;;
1082 i*86:syllable:*:*)
1083 echo "$UNAME_MACHINE"-pc-syllable
1084 exit ;;
1085 i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
1086 echo i386-unknown-lynxos"$UNAME_RELEASE"
1087 exit ;;
1088 i*86:*DOS:*:*)
1089 echo "$UNAME_MACHINE"-pc-msdosdjgpp
1090 exit ;;
1091 i*86:*:4.*:*)
1092 UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
1093 if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
1094 echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
1095 else
1096 echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
1097 fi
1098 exit ;;
1099 i*86:*:5:[678]*)
1100 # UnixWare 7.x, OpenUNIX and OpenServer 6.
1101 case `/bin/uname -X | grep "^Machine"` in
1102 *486*) UNAME_MACHINE=i486 ;;
1103 *Pentium) UNAME_MACHINE=i586 ;;
1104 *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
1105 esac
1106 echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}"
1107 exit ;;
1108 i*86:*:3.2:*)
1109 if test -f /usr/options/cb.name; then
1110 UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
1111 echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
1112 elif /bin/uname -X 2>/dev/null >/dev/null ; then
1113 UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
1114 (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
1115 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
1116 && UNAME_MACHINE=i586
1117 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
1118 && UNAME_MACHINE=i686
1119 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
1120 && UNAME_MACHINE=i686
1121 echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
1122 else
1123 echo "$UNAME_MACHINE"-pc-sysv32
1124 fi
1125 exit ;;
1126 pc:*:*:*)
1127 # Left here for compatibility:
1128 # uname -m prints for DJGPP always 'pc', but it prints nothing about
1129 # the processor, so we play safe by assuming i586.
1130 # Note: whatever this is, it MUST be the same as what config.sub
1131 # prints for the "djgpp" host, or else GDB configure will decide that
1132 # this is a cross-build.
1133 echo i586-pc-msdosdjgpp
1134 exit ;;
1135 Intel:Mach:3*:*)
1136 echo i386-pc-mach3
1137 exit ;;
1138 paragon:*:*:*)
1139 echo i860-intel-osf1
1140 exit ;;
1141 i860:*:4.*:*) # i860-SVR4
1142 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
1143 echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
1144 else # Add other i860-SVR4 vendors below as they are discovered.
1145 echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
1146 fi
1147 exit ;;
1148 mini*:CTIX:SYS*5:*)
1149 # "miniframe"
1150 echo m68010-convergent-sysv
1151 exit ;;
1152 mc68k:UNIX:SYSTEM5:3.51m)
1153 echo m68k-convergent-sysv
1154 exit ;;
1155 M680?0:D-NIX:5.3:*)
1156 echo m68k-diab-dnix
1157 exit ;;
1158 M68*:*:R3V[5678]*:*)
1159 test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
1160 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
1161 OS_REL=''
1162 test -r /etc/.relid \
1163 && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
1164 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1165 && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
1166 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
1167 && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
1168 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
1169 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1170 && { echo i486-ncr-sysv4; exit; } ;;
1171 NCR*:*:4.2:* | MPRAS*:*:4.2:*)
1172 OS_REL='.3'
1173 test -r /etc/.relid \
1174 && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
1175 /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
1176 && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
1177 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
1178 && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
1179 /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
1180 && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
1181 m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
1182 echo m68k-unknown-lynxos"$UNAME_RELEASE"
1183 exit ;;
1184 mc68030:UNIX_System_V:4.*:*)
1185 echo m68k-atari-sysv4
1186 exit ;;
1187 TSUNAMI:LynxOS:2.*:*)
1188 echo sparc-unknown-lynxos"$UNAME_RELEASE"
1189 exit ;;
1190 rs6000:LynxOS:2.*:*)
1191 echo rs6000-unknown-lynxos"$UNAME_RELEASE"
1192 exit ;;
1193 PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
1194 echo powerpc-unknown-lynxos"$UNAME_RELEASE"
1195 exit ;;
1196 SM[BE]S:UNIX_SV:*:*)
1197 echo mips-dde-sysv"$UNAME_RELEASE"
1198 exit ;;
1199 RM*:ReliantUNIX-*:*:*)
1200 echo mips-sni-sysv4
1201 exit ;;
1202 RM*:SINIX-*:*:*)
1203 echo mips-sni-sysv4
1204 exit ;;
1205 *:SINIX-*:*:*)
1206 if uname -p 2>/dev/null >/dev/null ; then
1207 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1208 echo "$UNAME_MACHINE"-sni-sysv4
1209 else
1210 echo ns32k-sni-sysv
1211 fi
1212 exit ;;
1213 PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
1214 # says <Richard.M.Bartel@ccMail.Census.GOV>
1215 echo i586-unisys-sysv4
1216 exit ;;
1217 *:UNIX_System_V:4*:FTX*)
1218 # From Gerald Hewes <hewes@openmarket.com>.
1219 # How about differentiating between stratus architectures? -djm
1220 echo hppa1.1-stratus-sysv4
1221 exit ;;
1222 *:*:*:FTX*)
1223 # From seanf@swdc.stratus.com.
1224 echo i860-stratus-sysv4
1225 exit ;;
1226 i*86:VOS:*:*)
1227 # From Paul.Green@stratus.com.
1228 echo "$UNAME_MACHINE"-stratus-vos
1229 exit ;;
1230 *:VOS:*:*)
1231 # From Paul.Green@stratus.com.
1232 echo hppa1.1-stratus-vos
1233 exit ;;
1234 mc68*:A/UX:*:*)
1235 echo m68k-apple-aux"$UNAME_RELEASE"
1236 exit ;;
1237 news*:NEWS-OS:6*:*)
1238 echo mips-sony-newsos6
1239 exit ;;
1240 R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
1241 if [ -d /usr/nec ]; then
1242 echo mips-nec-sysv"$UNAME_RELEASE"
1243 else
1244 echo mips-unknown-sysv"$UNAME_RELEASE"
1245 fi
1246 exit ;;
1247 BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
1248 echo powerpc-be-beos
1249 exit ;;
1250 BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
1251 echo powerpc-apple-beos
1252 exit ;;
1253 BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
1254 echo i586-pc-beos
1255 exit ;;
1256 BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
1257 echo i586-pc-haiku
1258 exit ;;
1259 x86_64:Haiku:*:*)
1260 echo x86_64-unknown-haiku
1261 exit ;;
1262 SX-4:SUPER-UX:*:*)
1263 echo sx4-nec-superux"$UNAME_RELEASE"
1264 exit ;;
1265 SX-5:SUPER-UX:*:*)
1266 echo sx5-nec-superux"$UNAME_RELEASE"
1267 exit ;;
1268 SX-6:SUPER-UX:*:*)
1269 echo sx6-nec-superux"$UNAME_RELEASE"
1270 exit ;;
1271 SX-7:SUPER-UX:*:*)
1272 echo sx7-nec-superux"$UNAME_RELEASE"
1273 exit ;;
1274 SX-8:SUPER-UX:*:*)
1275 echo sx8-nec-superux"$UNAME_RELEASE"
1276 exit ;;
1277 SX-8R:SUPER-UX:*:*)
1278 echo sx8r-nec-superux"$UNAME_RELEASE"
1279 exit ;;
1280 SX-ACE:SUPER-UX:*:*)
1281 echo sxace-nec-superux"$UNAME_RELEASE"
1282 exit ;;
1283 Power*:Rhapsody:*:*)
1284 echo powerpc-apple-rhapsody"$UNAME_RELEASE"
1285 exit ;;
1286 *:Rhapsody:*:*)
1287 echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
1288 exit ;;
1289 *:Darwin:*:*)
1290 UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
1291 eval "$set_cc_for_build"
1292 if test "$UNAME_PROCESSOR" = unknown ; then
1293 UNAME_PROCESSOR=powerpc
1294 fi
1295 if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
1296 if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
1297 if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
1298 (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
1299 grep IS_64BIT_ARCH >/dev/null
1300 then
1301 case $UNAME_PROCESSOR in
1302 i386) UNAME_PROCESSOR=x86_64 ;;
1303 powerpc) UNAME_PROCESSOR=powerpc64 ;;
1304 esac
1305 fi
1306 # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
1307 if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
1308 (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
1309 grep IS_PPC >/dev/null
1310 then
1311 UNAME_PROCESSOR=powerpc
1312 fi
1313 fi
1314 elif test "$UNAME_PROCESSOR" = i386 ; then
1315 # Avoid executing cc on OS X 10.9, as it ships with a stub
1316 # that puts up a graphical alert prompting to install
1317 # developer tools. Any system running Mac OS X 10.7 or
1318 # later (Darwin 11 and later) is required to have a 64-bit
1319 # processor. This is not true of the ARM version of Darwin
1320 # that Apple uses in portable devices.
1321 UNAME_PROCESSOR=x86_64
1322 fi
1323 echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
1324 exit ;;
1325 *:procnto*:*:* | *:QNX:[0123456789]*:*)
1326 UNAME_PROCESSOR=`uname -p`
1327 if test "$UNAME_PROCESSOR" = x86; then
1328 UNAME_PROCESSOR=i386
1329 UNAME_MACHINE=pc
1330 fi
1331 echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
1332 exit ;;
1333 *:QNX:*:4*)
1334 echo i386-pc-qnx
1335 exit ;;
1336 NEO-*:NONSTOP_KERNEL:*:*)
1337 echo neo-tandem-nsk"$UNAME_RELEASE"
1338 exit ;;
1339 NSE-*:NONSTOP_KERNEL:*:*)
1340 echo nse-tandem-nsk"$UNAME_RELEASE"
1341 exit ;;
1342 NSR-*:NONSTOP_KERNEL:*:*)
1343 echo nsr-tandem-nsk"$UNAME_RELEASE"
1344 exit ;;
1345 NSV-*:NONSTOP_KERNEL:*:*)
1346 echo nsv-tandem-nsk"$UNAME_RELEASE"
1347 exit ;;
1348 NSX-*:NONSTOP_KERNEL:*:*)
1349 echo nsx-tandem-nsk"$UNAME_RELEASE"
1350 exit ;;
1351 *:NonStop-UX:*:*)
1352 echo mips-compaq-nonstopux
1353 exit ;;
1354 BS2000:POSIX*:*:*)
1355 echo bs2000-siemens-sysv
1356 exit ;;
1357 DS/*:UNIX_System_V:*:*)
1358 echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
1359 exit ;;
1360 *:Plan9:*:*)
1361 # "uname -m" is not consistent, so use $cputype instead. 386
1362 # is converted to i386 for consistency with other x86
1363 # operating systems.
1364 if test "$cputype" = 386; then
1365 UNAME_MACHINE=i386
1366 else
1367 UNAME_MACHINE="$cputype"
1368 fi
1369 echo "$UNAME_MACHINE"-unknown-plan9
1370 exit ;;
1371 *:TOPS-10:*:*)
1372 echo pdp10-unknown-tops10
1373 exit ;;
1374 *:TENEX:*:*)
1375 echo pdp10-unknown-tenex
1376 exit ;;
1377 KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
1378 echo pdp10-dec-tops20
1379 exit ;;
1380 XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
1381 echo pdp10-xkl-tops20
1382 exit ;;
1383 *:TOPS-20:*:*)
1384 echo pdp10-unknown-tops20
1385 exit ;;
1386 *:ITS:*:*)
1387 echo pdp10-unknown-its
1388 exit ;;
1389 SEI:*:*:SEIUX)
1390 echo mips-sei-seiux"$UNAME_RELEASE"
1391 exit ;;
1392 *:DragonFly:*:*)
1393 echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
1394 exit ;;
1395 *:*VMS:*:*)
1396 UNAME_MACHINE=`(uname -p) 2>/dev/null`
1397 case "$UNAME_MACHINE" in
1398 A*) echo alpha-dec-vms ; exit ;;
1399 I*) echo ia64-dec-vms ; exit ;;
1400 V*) echo vax-dec-vms ; exit ;;
1401 esac ;;
1402 *:XENIX:*:SysV)
1403 echo i386-pc-xenix
1404 exit ;;
1405 i*86:skyos:*:*)
1406 echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
1407 exit ;;
1408 i*86:rdos:*:*)
1409 echo "$UNAME_MACHINE"-pc-rdos
1410 exit ;;
1411 i*86:AROS:*:*)
1412 echo "$UNAME_MACHINE"-pc-aros
1413 exit ;;
1414 x86_64:VMkernel:*:*)
1415 echo "$UNAME_MACHINE"-unknown-esx
1416 exit ;;
1417 amd64:Isilon\ OneFS:*:*)
1418 echo x86_64-unknown-onefs
1419 exit ;;
1420 esac
1421
1422 echo "$0: unable to guess system type" >&2
1423
1424 case "$UNAME_MACHINE:$UNAME_SYSTEM" in
1425 mips:Linux | mips64:Linux)
1426 # If we got here on MIPS GNU/Linux, output extra information.
1427 cat >&2 <<EOF
1428
1429 NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
1430 the system type. Please install a C compiler and try again.
1431 EOF
1432 ;;
1433 esac
1434
1435 cat >&2 <<EOF
1436
1437 This script (version $timestamp), has failed to recognize the
1438 operating system you are using. If your script is old, overwrite *all*
1439 copies of config.guess and config.sub with the latest versions from:
1440
1441 https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
1442 and
1443 https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
1444
1445 If $0 has already been updated, send the following data and any
1446 information you think might be pertinent to config-patches@gnu.org to
1447 provide the necessary information to handle your system.
1448
1449 config.guess timestamp = $timestamp
1450
1451 uname -m = `(uname -m) 2>/dev/null || echo unknown`
1452 uname -r = `(uname -r) 2>/dev/null || echo unknown`
1453 uname -s = `(uname -s) 2>/dev/null || echo unknown`
1454 uname -v = `(uname -v) 2>/dev/null || echo unknown`
1455
1456 /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
1457 /bin/uname -X = `(/bin/uname -X) 2>/dev/null`
1458
1459 hostinfo = `(hostinfo) 2>/dev/null`
1460 /bin/universe = `(/bin/universe) 2>/dev/null`
1461 /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
1462 /bin/arch = `(/bin/arch) 2>/dev/null`
1463 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
1464 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
1465
1466 UNAME_MACHINE = "$UNAME_MACHINE"
1467 UNAME_RELEASE = "$UNAME_RELEASE"
1468 UNAME_SYSTEM = "$UNAME_SYSTEM"
1469 UNAME_VERSION = "$UNAME_VERSION"
1470 EOF
1471
1472 exit 1
1473
1474 # Local variables:
1475 # eval: (add-hook 'write-file-functions 'time-stamp)
1476 # time-stamp-start: "timestamp='"
1477 # time-stamp-format: "%:y-%02m-%02d"
1478 # time-stamp-end: "'"
1479 # End:
0 /* config.h.in. Generated from configure.ac by autoheader. */
1
2 /* Define to 1 if you have the `atexit' function. */
3 #undef HAVE_ATEXIT
4
5 /* Define to 1 if you have the `dup2' function. */
6 #undef HAVE_DUP2
7
8 /* Define to 1 if you have the <fcntl.h> header file. */
9 #undef HAVE_FCNTL_H
10
11 /* Define to 1 if you have the <float.h> header file. */
12 #undef HAVE_FLOAT_H
13
14 /* Define to 1 if you have the `fork' function. */
15 #undef HAVE_FORK
16
17 /* Define to 1 if you have the `ftruncate' function. */
18 #undef HAVE_FTRUNCATE
19
20 /* Define to 1 if you have the `getcwd' function. */
21 #undef HAVE_GETCWD
22
23 /* Define to 1 if you have the `gettimeofday' function. */
24 #undef HAVE_GETTIMEOFDAY
25
26 /* Define to 1 if you have the <inttypes.h> header file. */
27 #undef HAVE_INTTYPES_H
28
29 /* Define to 1 if you have the <limits.h> header file. */
30 #undef HAVE_LIMITS_H
31
32 /* Define to 1 if you have the <locale.h> header file. */
33 #undef HAVE_LOCALE_H
34
35 /* Define to 1 if you have the `localtime_r' function. */
36 #undef HAVE_LOCALTIME_R
37
38 /* Define to 1 if you have the `memchr' function. */
39 #undef HAVE_MEMCHR
40
41 /* Define to 1 if you have the <memory.h> header file. */
42 #undef HAVE_MEMORY_H
43
44 /* Define to 1 if you have the `memset' function. */
45 #undef HAVE_MEMSET
46
47 /* Define to 1 if you have the `mkdir' function. */
48 #undef HAVE_MKDIR
49
50 /* Define to 1 if the system has the type `mode_t'. */
51 #undef HAVE_MODE_T
52
53 /* Define to 1 if you have the <netinet/in.h> header file. */
54 #undef HAVE_NETINET_IN_H
55
56 /* Define to 1 if the system has the type `off_t'. */
57 #undef HAVE_OFF_T
58
59 /* Define to 1 if you have the <paths.h> header file. */
60 #undef HAVE_PATHS_H
61
62 /* Define to 1 if the system has the type `pid_t'. */
63 #undef HAVE_PID_T
64
65 /* Define if you have POSIX threads libraries and header files. */
66 #undef HAVE_PTHREAD
67
68 /* Have PTHREAD_PRIO_INHERIT. */
69 #undef HAVE_PTHREAD_PRIO_INHERIT
70
71 /* Define to 1 if you have the `rmdir' function. */
72 #undef HAVE_RMDIR
73
74 /* Define to 1 if you have the `setlocale' function. */
75 #undef HAVE_SETLOCALE
76
77 /* Define to 1 if the system has the type `size_t'. */
78 #undef HAVE_SIZE_T
79
80 /* Define to 1 if you have the `socket' function. */
81 #undef HAVE_SOCKET
82
83 /* Define to 1 if the system has the type `ssize_t'. */
84 #undef HAVE_SSIZE_T
85
86 /* Define to 1 if you have the <stddef.h> header file. */
87 #undef HAVE_STDDEF_H
88
89 /* Define to 1 if you have the <stdint.h> header file. */
90 #undef HAVE_STDINT_H
91
92 /* Define to 1 if you have the <stdlib.h> header file. */
93 #undef HAVE_STDLIB_H
94
95 /* Define to 1 if you have the `strcasecmp' function. */
96 #undef HAVE_STRCASECMP
97
98 /* Define to 1 if you have the `strchr' function. */
99 #undef HAVE_STRCHR
100
101 /* Define to 1 if you have the `strdup' function. */
102 #undef HAVE_STRDUP
103
104 /* Define to 1 if you have the `strerror' function. */
105 #undef HAVE_STRERROR
106
107 /* Define to 1 if you have the <strings.h> header file. */
108 #undef HAVE_STRINGS_H
109
110 /* Define to 1 if you have the <string.h> header file. */
111 #undef HAVE_STRING_H
112
113 /* Define to 1 if you have the `strncasecmp' function. */
114 #undef HAVE_STRNCASECMP
115
116 /* Define to 1 if you have the `strndup' function. */
117 #undef HAVE_STRNDUP
118
119 /* Define to 1 if you have the `strrchr' function. */
120 #undef HAVE_STRRCHR
121
122 /* Define to 1 if you have the `strspn' function. */
123 #undef HAVE_STRSPN
124
125 /* Define to 1 if you have the `strstr' function. */
126 #undef HAVE_STRSTR
127
128 /* Define to 1 if you have the `strtol' function. */
129 #undef HAVE_STRTOL
130
131 /* Define to 1 if you have the `strtoul' function. */
132 #undef HAVE_STRTOUL
133
134 /* Define to 1 if you have the <sys/param.h> header file. */
135 #undef HAVE_SYS_PARAM_H
136
137 /* Define to 1 if you have the <sys/socket.h> header file. */
138 #undef HAVE_SYS_SOCKET_H
139
140 /* Define to 1 if you have the <sys/stat.h> header file. */
141 #undef HAVE_SYS_STAT_H
142
143 /* Define to 1 if you have the <sys/time.h> header file. */
144 #undef HAVE_SYS_TIME_H
145
146 /* Define to 1 if you have the <sys/types.h> header file. */
147 #undef HAVE_SYS_TYPES_H
148
149 /* Define to 1 if you have the <unistd.h> header file. */
150 #undef HAVE_UNISTD_H
151
152 /* Define to 1 if you have the `vfork' function. */
153 #undef HAVE_VFORK
154
155 /* Define to 1 if you have the <vfork.h> header file. */
156 #undef HAVE_VFORK_H
157
158 /* Define to 1 if `fork' works. */
159 #undef HAVE_WORKING_FORK
160
161 /* Define to 1 if `vfork' works. */
162 #undef HAVE_WORKING_VFORK
163
164 /* Define to 1 if the system has the type `_Bool'. */
165 #undef HAVE__BOOL
166
167 /* Enable ASAN */
168 #undef I3_ASAN_ENABLED
169
170 /* i3 version */
171 #undef I3_VERSION
172
173 /* Define to 1 if `lstat' dereferences a symlink specified with a trailing
174 slash. */
175 #undef LSTAT_FOLLOWS_SLASHED_SYMLINK
176
177 /* i3 major version */
178 #undef MAJOR_VERSION
179
180 /* i3 minor version */
181 #undef MINOR_VERSION
182
183 /* Name of package */
184 #undef PACKAGE
185
186 /* Define to the address where bug reports for this package should be sent. */
187 #undef PACKAGE_BUGREPORT
188
189 /* Define to the full name of this package. */
190 #undef PACKAGE_NAME
191
192 /* Define to the full name and version of this package. */
193 #undef PACKAGE_STRING
194
195 /* Define to the one symbol short name of this package. */
196 #undef PACKAGE_TARNAME
197
198 /* Define to the home page for this package. */
199 #undef PACKAGE_URL
200
201 /* Define to the version of this package. */
202 #undef PACKAGE_VERSION
203
204 /* i3 patch version */
205 #undef PATCH_VERSION
206
207 /* Define to necessary symbol if this constant uses a non-standard name on
208 your system. */
209 #undef PTHREAD_CREATE_JOINABLE
210
211 /* Define to 1 if you have the ANSI C header files. */
212 #undef STDC_HEADERS
213
214 /* Define if debugging is disabled */
215 #undef UNUSED_NDEBUG
216
217 /* Enable extensions on AIX 3, Interix. */
218 #ifndef _ALL_SOURCE
219 # undef _ALL_SOURCE
220 #endif
221 /* Enable GNU extensions on systems that have them. */
222 #ifndef _GNU_SOURCE
223 # undef _GNU_SOURCE
224 #endif
225 /* Enable threading extensions on Solaris. */
226 #ifndef _POSIX_PTHREAD_SEMANTICS
227 # undef _POSIX_PTHREAD_SEMANTICS
228 #endif
229 /* Enable extensions on HP NonStop. */
230 #ifndef _TANDEM_SOURCE
231 # undef _TANDEM_SOURCE
232 #endif
233 /* Enable general extensions on Solaris. */
234 #ifndef __EXTENSIONS__
235 # undef __EXTENSIONS__
236 #endif
237
238
239 /* Version number of package */
240 #undef VERSION
241
242 /* Define to 1 if on MINIX. */
243 #undef _MINIX
244
245 /* Define to 2 if the system does not provide POSIX.1 features except with
246 this defined. */
247 #undef _POSIX_1_SOURCE
248
249 /* Define to 1 if you need to in order for `stat' and other things to work. */
250 #undef _POSIX_SOURCE
251
252 /* Define to `int' if <sys/types.h> does not define. */
253 #undef pid_t
254
255 /* Define as `fork' if `vfork' does not work. */
256 #undef vfork
0 #! /bin/sh
1 # Configuration validation subroutine script.
2 # Copyright 1992-2018 Free Software Foundation, Inc.
3
4 timestamp='2018-02-22'
5
6 # This file is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, see <https://www.gnu.org/licenses/>.
18 #
19 # As a special exception to the GNU General Public License, if you
20 # distribute this file as part of a program that contains a
21 # configuration script generated by Autoconf, you may include it under
22 # the same distribution terms that you use for the rest of that
23 # program. This Exception is an additional permission under section 7
24 # of the GNU General Public License, version 3 ("GPLv3").
25
26
27 # Please send patches to <config-patches@gnu.org>.
28 #
29 # Configuration subroutine to validate and canonicalize a configuration type.
30 # Supply the specified configuration type as an argument.
31 # If it is invalid, we print an error message on stderr and exit with code 1.
32 # Otherwise, we print the canonical config type on stdout and succeed.
33
34 # You can get the latest version of this script from:
35 # https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
36
37 # This file is supposed to be the same for all GNU packages
38 # and recognize all the CPU types, system types and aliases
39 # that are meaningful with *any* GNU software.
40 # Each package is responsible for reporting which valid configurations
41 # it does not support. The user should be able to distinguish
42 # a failure to support a valid configuration from a meaningless
43 # configuration.
44
45 # The goal of this file is to map all the various variations of a given
46 # machine specification into a single specification in the form:
47 # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
48 # or in some cases, the newer four-part form:
49 # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
50 # It is wrong to echo any other type of specification.
51
52 me=`echo "$0" | sed -e 's,.*/,,'`
53
54 usage="\
55 Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
56
57 Canonicalize a configuration name.
58
59 Options:
60 -h, --help print this help, then exit
61 -t, --time-stamp print date of last modification, then exit
62 -v, --version print version number, then exit
63
64 Report bugs and patches to <config-patches@gnu.org>."
65
66 version="\
67 GNU config.sub ($timestamp)
68
69 Copyright 1992-2018 Free Software Foundation, Inc.
70
71 This is free software; see the source for copying conditions. There is NO
72 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
73
74 help="
75 Try \`$me --help' for more information."
76
77 # Parse command line
78 while test $# -gt 0 ; do
79 case $1 in
80 --time-stamp | --time* | -t )
81 echo "$timestamp" ; exit ;;
82 --version | -v )
83 echo "$version" ; exit ;;
84 --help | --h* | -h )
85 echo "$usage"; exit ;;
86 -- ) # Stop option processing
87 shift; break ;;
88 - ) # Use stdin as input.
89 break ;;
90 -* )
91 echo "$me: invalid option $1$help"
92 exit 1 ;;
93
94 *local*)
95 # First pass through any local machine types.
96 echo "$1"
97 exit ;;
98
99 * )
100 break ;;
101 esac
102 done
103
104 case $# in
105 0) echo "$me: missing argument$help" >&2
106 exit 1;;
107 1) ;;
108 *) echo "$me: too many arguments$help" >&2
109 exit 1;;
110 esac
111
112 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
113 # Here we must recognize all the valid KERNEL-OS combinations.
114 maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
115 case $maybe_os in
116 nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
117 linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
118 knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
119 kopensolaris*-gnu* | cloudabi*-eabi* | \
120 storm-chaos* | os2-emx* | rtmk-nova*)
121 os=-$maybe_os
122 basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
123 ;;
124 android-linux)
125 os=-linux-android
126 basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
127 ;;
128 *)
129 basic_machine=`echo "$1" | sed 's/-[^-]*$//'`
130 if [ "$basic_machine" != "$1" ]
131 then os=`echo "$1" | sed 's/.*-/-/'`
132 else os=; fi
133 ;;
134 esac
135
136 ### Let's recognize common machines as not being operating systems so
137 ### that things like config.sub decstation-3100 work. We also
138 ### recognize some manufacturers as not being operating systems, so we
139 ### can provide default operating systems below.
140 case $os in
141 -sun*os*)
142 # Prevent following clause from handling this invalid input.
143 ;;
144 -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
145 -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
146 -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
147 -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
148 -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
149 -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
150 -apple | -axis | -knuth | -cray | -microblaze*)
151 os=
152 basic_machine=$1
153 ;;
154 -bluegene*)
155 os=-cnk
156 ;;
157 -sim | -cisco | -oki | -wec | -winbond)
158 os=
159 basic_machine=$1
160 ;;
161 -scout)
162 ;;
163 -wrs)
164 os=-vxworks
165 basic_machine=$1
166 ;;
167 -chorusos*)
168 os=-chorusos
169 basic_machine=$1
170 ;;
171 -chorusrdb)
172 os=-chorusrdb
173 basic_machine=$1
174 ;;
175 -hiux*)
176 os=-hiuxwe2
177 ;;
178 -sco6)
179 os=-sco5v6
180 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
181 ;;
182 -sco5)
183 os=-sco3.2v5
184 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
185 ;;
186 -sco4)
187 os=-sco3.2v4
188 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
189 ;;
190 -sco3.2.[4-9]*)
191 os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
192 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
193 ;;
194 -sco3.2v[4-9]*)
195 # Don't forget version if it is 3.2v4 or newer.
196 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
197 ;;
198 -sco5v6*)
199 # Don't forget version if it is 3.2v4 or newer.
200 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
201 ;;
202 -sco*)
203 os=-sco3.2v2
204 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
205 ;;
206 -udk*)
207 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
208 ;;
209 -isc)
210 os=-isc2.2
211 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
212 ;;
213 -clix*)
214 basic_machine=clipper-intergraph
215 ;;
216 -isc*)
217 basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
218 ;;
219 -lynx*178)
220 os=-lynxos178
221 ;;
222 -lynx*5)
223 os=-lynxos5
224 ;;
225 -lynx*)
226 os=-lynxos
227 ;;
228 -ptx*)
229 basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'`
230 ;;
231 -psos*)
232 os=-psos
233 ;;
234 -mint | -mint[0-9]*)
235 basic_machine=m68k-atari
236 os=-mint
237 ;;
238 esac
239
240 # Decode aliases for certain CPU-COMPANY combinations.
241 case $basic_machine in
242 # Recognize the basic CPU types without company name.
243 # Some are omitted here because they have special meanings below.
244 1750a | 580 \
245 | a29k \
246 | aarch64 | aarch64_be \
247 | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
248 | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
249 | am33_2.0 \
250 | arc | arceb \
251 | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
252 | avr | avr32 \
253 | ba \
254 | be32 | be64 \
255 | bfin \
256 | c4x | c8051 | clipper \
257 | d10v | d30v | dlx | dsp16xx \
258 | e2k | epiphany \
259 | fido | fr30 | frv | ft32 \
260 | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
261 | hexagon \
262 | i370 | i860 | i960 | ia16 | ia64 \
263 | ip2k | iq2000 \
264 | k1om \
265 | le32 | le64 \
266 | lm32 \
267 | m32c | m32r | m32rle | m68000 | m68k | m88k \
268 | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
269 | mips | mipsbe | mipseb | mipsel | mipsle \
270 | mips16 \
271 | mips64 | mips64el \
272 | mips64octeon | mips64octeonel \
273 | mips64orion | mips64orionel \
274 | mips64r5900 | mips64r5900el \
275 | mips64vr | mips64vrel \
276 | mips64vr4100 | mips64vr4100el \
277 | mips64vr4300 | mips64vr4300el \
278 | mips64vr5000 | mips64vr5000el \
279 | mips64vr5900 | mips64vr5900el \
280 | mipsisa32 | mipsisa32el \
281 | mipsisa32r2 | mipsisa32r2el \
282 | mipsisa32r6 | mipsisa32r6el \
283 | mipsisa64 | mipsisa64el \
284 | mipsisa64r2 | mipsisa64r2el \
285 | mipsisa64r6 | mipsisa64r6el \
286 | mipsisa64sb1 | mipsisa64sb1el \
287 | mipsisa64sr71k | mipsisa64sr71kel \
288 | mipsr5900 | mipsr5900el \
289 | mipstx39 | mipstx39el \
290 | mn10200 | mn10300 \
291 | moxie \
292 | mt \
293 | msp430 \
294 | nds32 | nds32le | nds32be \
295 | nios | nios2 | nios2eb | nios2el \
296 | ns16k | ns32k \
297 | open8 | or1k | or1knd | or32 \
298 | pdp10 | pj | pjl \
299 | powerpc | powerpc64 | powerpc64le | powerpcle \
300 | pru \
301 | pyramid \
302 | riscv32 | riscv64 \
303 | rl78 | rx \
304 | score \
305 | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
306 | sh64 | sh64le \
307 | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
308 | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
309 | spu \
310 | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
311 | ubicom32 \
312 | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
313 | visium \
314 | wasm32 \
315 | x86 | xc16x | xstormy16 | xtensa \
316 | z8k | z80)
317 basic_machine=$basic_machine-unknown
318 ;;
319 c54x)
320 basic_machine=tic54x-unknown
321 ;;
322 c55x)
323 basic_machine=tic55x-unknown
324 ;;
325 c6x)
326 basic_machine=tic6x-unknown
327 ;;
328 leon|leon[3-9])
329 basic_machine=sparc-$basic_machine
330 ;;
331 m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
332 basic_machine=$basic_machine-unknown
333 os=-none
334 ;;
335 m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
336 ;;
337 ms1)
338 basic_machine=mt-unknown
339 ;;
340
341 strongarm | thumb | xscale)
342 basic_machine=arm-unknown
343 ;;
344 xgate)
345 basic_machine=$basic_machine-unknown
346 os=-none
347 ;;
348 xscaleeb)
349 basic_machine=armeb-unknown
350 ;;
351
352 xscaleel)
353 basic_machine=armel-unknown
354 ;;
355
356 # We use `pc' rather than `unknown'
357 # because (1) that's what they normally are, and
358 # (2) the word "unknown" tends to confuse beginning users.
359 i*86 | x86_64)
360 basic_machine=$basic_machine-pc
361 ;;
362 # Object if more than one company name word.
363 *-*-*)
364 echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
365 exit 1
366 ;;
367 # Recognize the basic CPU types with company name.
368 580-* \
369 | a29k-* \
370 | aarch64-* | aarch64_be-* \
371 | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
372 | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
373 | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
374 | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
375 | avr-* | avr32-* \
376 | ba-* \
377 | be32-* | be64-* \
378 | bfin-* | bs2000-* \
379 | c[123]* | c30-* | [cjt]90-* | c4x-* \
380 | c8051-* | clipper-* | craynv-* | cydra-* \
381 | d10v-* | d30v-* | dlx-* \
382 | e2k-* | elxsi-* \
383 | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
384 | h8300-* | h8500-* \
385 | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
386 | hexagon-* \
387 | i*86-* | i860-* | i960-* | ia16-* | ia64-* \
388 | ip2k-* | iq2000-* \
389 | k1om-* \
390 | le32-* | le64-* \
391 | lm32-* \
392 | m32c-* | m32r-* | m32rle-* \
393 | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
394 | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
395 | microblaze-* | microblazeel-* \
396 | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
397 | mips16-* \
398 | mips64-* | mips64el-* \
399 | mips64octeon-* | mips64octeonel-* \
400 | mips64orion-* | mips64orionel-* \
401 | mips64r5900-* | mips64r5900el-* \
402 | mips64vr-* | mips64vrel-* \
403 | mips64vr4100-* | mips64vr4100el-* \
404 | mips64vr4300-* | mips64vr4300el-* \
405 | mips64vr5000-* | mips64vr5000el-* \
406 | mips64vr5900-* | mips64vr5900el-* \
407 | mipsisa32-* | mipsisa32el-* \
408 | mipsisa32r2-* | mipsisa32r2el-* \
409 | mipsisa32r6-* | mipsisa32r6el-* \
410 | mipsisa64-* | mipsisa64el-* \
411 | mipsisa64r2-* | mipsisa64r2el-* \
412 | mipsisa64r6-* | mipsisa64r6el-* \
413 | mipsisa64sb1-* | mipsisa64sb1el-* \
414 | mipsisa64sr71k-* | mipsisa64sr71kel-* \
415 | mipsr5900-* | mipsr5900el-* \
416 | mipstx39-* | mipstx39el-* \
417 | mmix-* \
418 | mt-* \
419 | msp430-* \
420 | nds32-* | nds32le-* | nds32be-* \
421 | nios-* | nios2-* | nios2eb-* | nios2el-* \
422 | none-* | np1-* | ns16k-* | ns32k-* \
423 | open8-* \
424 | or1k*-* \
425 | orion-* \
426 | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
427 | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
428 | pru-* \
429 | pyramid-* \
430 | riscv32-* | riscv64-* \
431 | rl78-* | romp-* | rs6000-* | rx-* \
432 | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
433 | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
434 | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
435 | sparclite-* \
436 | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
437 | tahoe-* \
438 | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
439 | tile*-* \
440 | tron-* \
441 | ubicom32-* \
442 | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
443 | vax-* \
444 | visium-* \
445 | wasm32-* \
446 | we32k-* \
447 | x86-* | x86_64-* | xc16x-* | xps100-* \
448 | xstormy16-* | xtensa*-* \
449 | ymp-* \
450 | z8k-* | z80-*)
451 ;;
452 # Recognize the basic CPU types without company name, with glob match.
453 xtensa*)
454 basic_machine=$basic_machine-unknown
455 ;;
456 # Recognize the various machine names and aliases which stand
457 # for a CPU type and a company and sometimes even an OS.
458 386bsd)
459 basic_machine=i386-pc
460 os=-bsd
461 ;;
462 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
463 basic_machine=m68000-att
464 ;;
465 3b*)
466 basic_machine=we32k-att
467 ;;
468 a29khif)
469 basic_machine=a29k-amd
470 os=-udi
471 ;;
472 abacus)
473 basic_machine=abacus-unknown
474 ;;
475 adobe68k)
476 basic_machine=m68010-adobe
477 os=-scout
478 ;;
479 alliant | fx80)
480 basic_machine=fx80-alliant
481 ;;
482 altos | altos3068)
483 basic_machine=m68k-altos
484 ;;
485 am29k)
486 basic_machine=a29k-none
487 os=-bsd
488 ;;
489 amd64)
490 basic_machine=x86_64-pc
491 ;;
492 amd64-*)
493 basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
494 ;;
495 amdahl)
496 basic_machine=580-amdahl
497 os=-sysv
498 ;;
499 amiga | amiga-*)
500 basic_machine=m68k-unknown
501 ;;
502 amigaos | amigados)
503 basic_machine=m68k-unknown
504 os=-amigaos
505 ;;
506 amigaunix | amix)
507 basic_machine=m68k-unknown
508 os=-sysv4
509 ;;
510 apollo68)
511 basic_machine=m68k-apollo
512 os=-sysv
513 ;;
514 apollo68bsd)
515 basic_machine=m68k-apollo
516 os=-bsd
517 ;;
518 aros)
519 basic_machine=i386-pc
520 os=-aros
521 ;;
522 asmjs)
523 basic_machine=asmjs-unknown
524 ;;
525 aux)
526 basic_machine=m68k-apple
527 os=-aux
528 ;;
529 balance)
530 basic_machine=ns32k-sequent
531 os=-dynix
532 ;;
533 blackfin)
534 basic_machine=bfin-unknown
535 os=-linux
536 ;;
537 blackfin-*)
538 basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
539 os=-linux
540 ;;
541 bluegene*)
542 basic_machine=powerpc-ibm
543 os=-cnk
544 ;;
545 c54x-*)
546 basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
547 ;;
548 c55x-*)
549 basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
550 ;;
551 c6x-*)
552 basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
553 ;;
554 c90)
555 basic_machine=c90-cray
556 os=-unicos
557 ;;
558 cegcc)
559 basic_machine=arm-unknown
560 os=-cegcc
561 ;;
562 convex-c1)
563 basic_machine=c1-convex
564 os=-bsd
565 ;;
566 convex-c2)
567 basic_machine=c2-convex
568 os=-bsd
569 ;;
570 convex-c32)
571 basic_machine=c32-convex
572 os=-bsd
573 ;;
574 convex-c34)
575 basic_machine=c34-convex
576 os=-bsd
577 ;;
578 convex-c38)
579 basic_machine=c38-convex
580 os=-bsd
581 ;;
582 cray | j90)
583 basic_machine=j90-cray
584 os=-unicos
585 ;;
586 craynv)
587 basic_machine=craynv-cray
588 os=-unicosmp
589 ;;
590 cr16 | cr16-*)
591 basic_machine=cr16-unknown
592 os=-elf
593 ;;
594 crds | unos)
595 basic_machine=m68k-crds
596 ;;
597 crisv32 | crisv32-* | etraxfs*)
598 basic_machine=crisv32-axis
599 ;;
600 cris | cris-* | etrax*)
601 basic_machine=cris-axis
602 ;;
603 crx)
604 basic_machine=crx-unknown
605 os=-elf
606 ;;
607 da30 | da30-*)
608 basic_machine=m68k-da30
609 ;;
610 decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
611 basic_machine=mips-dec
612 ;;
613 decsystem10* | dec10*)
614 basic_machine=pdp10-dec
615 os=-tops10
616 ;;
617 decsystem20* | dec20*)
618 basic_machine=pdp10-dec
619 os=-tops20
620 ;;
621 delta | 3300 | motorola-3300 | motorola-delta \
622 | 3300-motorola | delta-motorola)
623 basic_machine=m68k-motorola
624 ;;
625 delta88)
626 basic_machine=m88k-motorola
627 os=-sysv3
628 ;;
629 dicos)
630 basic_machine=i686-pc
631 os=-dicos
632 ;;
633 djgpp)
634 basic_machine=i586-pc
635 os=-msdosdjgpp
636 ;;
637 dpx20 | dpx20-*)
638 basic_machine=rs6000-bull
639 os=-bosx
640 ;;
641 dpx2*)
642 basic_machine=m68k-bull
643 os=-sysv3
644 ;;
645 e500v[12])
646 basic_machine=powerpc-unknown
647 os=$os"spe"
648 ;;
649 e500v[12]-*)
650 basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
651 os=$os"spe"
652 ;;
653 ebmon29k)
654 basic_machine=a29k-amd
655 os=-ebmon
656 ;;
657 elxsi)
658 basic_machine=elxsi-elxsi
659 os=-bsd
660 ;;
661 encore | umax | mmax)
662 basic_machine=ns32k-encore
663 ;;
664 es1800 | OSE68k | ose68k | ose | OSE)
665 basic_machine=m68k-ericsson
666 os=-ose
667 ;;
668 fx2800)
669 basic_machine=i860-alliant
670 ;;
671 genix)
672 basic_machine=ns32k-ns
673 ;;
674 gmicro)
675 basic_machine=tron-gmicro
676 os=-sysv
677 ;;
678 go32)
679 basic_machine=i386-pc
680 os=-go32
681 ;;
682 h3050r* | hiux*)
683 basic_machine=hppa1.1-hitachi
684 os=-hiuxwe2
685 ;;
686 h8300hms)
687 basic_machine=h8300-hitachi
688 os=-hms
689 ;;
690 h8300xray)
691 basic_machine=h8300-hitachi
692 os=-xray
693 ;;
694 h8500hms)
695 basic_machine=h8500-hitachi
696 os=-hms
697 ;;
698 harris)
699 basic_machine=m88k-harris
700 os=-sysv3
701 ;;
702 hp300-*)
703 basic_machine=m68k-hp
704 ;;
705 hp300bsd)
706 basic_machine=m68k-hp
707 os=-bsd
708 ;;
709 hp300hpux)
710 basic_machine=m68k-hp
711 os=-hpux
712 ;;
713 hp3k9[0-9][0-9] | hp9[0-9][0-9])
714 basic_machine=hppa1.0-hp
715 ;;
716 hp9k2[0-9][0-9] | hp9k31[0-9])
717 basic_machine=m68000-hp
718 ;;
719 hp9k3[2-9][0-9])
720 basic_machine=m68k-hp
721 ;;
722 hp9k6[0-9][0-9] | hp6[0-9][0-9])
723 basic_machine=hppa1.0-hp
724 ;;
725 hp9k7[0-79][0-9] | hp7[0-79][0-9])
726 basic_machine=hppa1.1-hp
727 ;;
728 hp9k78[0-9] | hp78[0-9])
729 # FIXME: really hppa2.0-hp
730 basic_machine=hppa1.1-hp
731 ;;
732 hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
733 # FIXME: really hppa2.0-hp
734 basic_machine=hppa1.1-hp
735 ;;
736 hp9k8[0-9][13679] | hp8[0-9][13679])
737 basic_machine=hppa1.1-hp
738 ;;
739 hp9k8[0-9][0-9] | hp8[0-9][0-9])
740 basic_machine=hppa1.0-hp
741 ;;
742 hppaosf)
743 basic_machine=hppa1.1-hp
744 os=-osf
745 ;;
746 hppro)
747 basic_machine=hppa1.1-hp
748 os=-proelf
749 ;;
750 i370-ibm* | ibm*)
751 basic_machine=i370-ibm
752 ;;
753 i*86v32)
754 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
755 os=-sysv32
756 ;;
757 i*86v4*)
758 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
759 os=-sysv4
760 ;;
761 i*86v)
762 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
763 os=-sysv
764 ;;
765 i*86sol2)
766 basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
767 os=-solaris2
768 ;;
769 i386mach)
770 basic_machine=i386-mach
771 os=-mach
772 ;;
773 vsta)
774 basic_machine=i386-unknown
775 os=-vsta
776 ;;
777 iris | iris4d)
778 basic_machine=mips-sgi
779 case $os in
780 -irix*)
781 ;;
782 *)
783 os=-irix4
784 ;;
785 esac
786 ;;
787 isi68 | isi)
788 basic_machine=m68k-isi
789 os=-sysv
790 ;;
791 leon-*|leon[3-9]-*)
792 basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
793 ;;
794 m68knommu)
795 basic_machine=m68k-unknown
796 os=-linux
797 ;;
798 m68knommu-*)
799 basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
800 os=-linux
801 ;;
802 magnum | m3230)
803 basic_machine=mips-mips
804 os=-sysv
805 ;;
806 merlin)
807 basic_machine=ns32k-utek
808 os=-sysv
809 ;;
810 microblaze*)
811 basic_machine=microblaze-xilinx
812 ;;
813 mingw64)
814 basic_machine=x86_64-pc
815 os=-mingw64
816 ;;
817 mingw32)
818 basic_machine=i686-pc
819 os=-mingw32
820 ;;
821 mingw32ce)
822 basic_machine=arm-unknown
823 os=-mingw32ce
824 ;;
825 miniframe)
826 basic_machine=m68000-convergent
827 ;;
828 *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
829 basic_machine=m68k-atari
830 os=-mint
831 ;;
832 mips3*-*)
833 basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
834 ;;
835 mips3*)
836 basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
837 ;;
838 monitor)
839 basic_machine=m68k-rom68k
840 os=-coff
841 ;;
842 morphos)
843 basic_machine=powerpc-unknown
844 os=-morphos
845 ;;
846 moxiebox)
847 basic_machine=moxie-unknown
848 os=-moxiebox
849 ;;
850 msdos)
851 basic_machine=i386-pc
852 os=-msdos
853 ;;
854 ms1-*)
855 basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
856 ;;
857 msys)
858 basic_machine=i686-pc
859 os=-msys
860 ;;
861 mvs)
862 basic_machine=i370-ibm
863 os=-mvs
864 ;;
865 nacl)
866 basic_machine=le32-unknown
867 os=-nacl
868 ;;
869 ncr3000)
870 basic_machine=i486-ncr
871 os=-sysv4
872 ;;
873 netbsd386)
874 basic_machine=i386-unknown
875 os=-netbsd
876 ;;
877 netwinder)
878 basic_machine=armv4l-rebel
879 os=-linux
880 ;;
881 news | news700 | news800 | news900)
882 basic_machine=m68k-sony
883 os=-newsos
884 ;;
885 news1000)
886 basic_machine=m68030-sony
887 os=-newsos
888 ;;
889 news-3600 | risc-news)
890 basic_machine=mips-sony
891 os=-newsos
892 ;;
893 necv70)
894 basic_machine=v70-nec
895 os=-sysv
896 ;;
897 next | m*-next)
898 basic_machine=m68k-next
899 case $os in
900 -nextstep* )
901 ;;
902 -ns2*)
903 os=-nextstep2
904 ;;
905 *)
906 os=-nextstep3
907 ;;
908 esac
909 ;;
910 nh3000)
911 basic_machine=m68k-harris
912 os=-cxux
913 ;;
914 nh[45]000)
915 basic_machine=m88k-harris
916 os=-cxux
917 ;;
918 nindy960)
919 basic_machine=i960-intel
920 os=-nindy
921 ;;
922 mon960)
923 basic_machine=i960-intel
924 os=-mon960
925 ;;
926 nonstopux)
927 basic_machine=mips-compaq
928 os=-nonstopux
929 ;;
930 np1)
931 basic_machine=np1-gould
932 ;;
933 neo-tandem)
934 basic_machine=neo-tandem
935 ;;
936 nse-tandem)
937 basic_machine=nse-tandem
938 ;;
939 nsr-tandem)
940 basic_machine=nsr-tandem
941 ;;
942 nsv-tandem)
943 basic_machine=nsv-tandem
944 ;;
945 nsx-tandem)
946 basic_machine=nsx-tandem
947 ;;
948 op50n-* | op60c-*)
949 basic_machine=hppa1.1-oki
950 os=-proelf
951 ;;
952 openrisc | openrisc-*)
953 basic_machine=or32-unknown
954 ;;
955 os400)
956 basic_machine=powerpc-ibm
957 os=-os400
958 ;;
959 OSE68000 | ose68000)
960 basic_machine=m68000-ericsson
961 os=-ose
962 ;;
963 os68k)
964 basic_machine=m68k-none
965 os=-os68k
966 ;;
967 pa-hitachi)
968 basic_machine=hppa1.1-hitachi
969 os=-hiuxwe2
970 ;;
971 paragon)
972 basic_machine=i860-intel
973 os=-osf
974 ;;
975 parisc)
976 basic_machine=hppa-unknown
977 os=-linux
978 ;;
979 parisc-*)
980 basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
981 os=-linux
982 ;;
983 pbd)
984 basic_machine=sparc-tti
985 ;;
986 pbb)
987 basic_machine=m68k-tti
988 ;;
989 pc532 | pc532-*)
990 basic_machine=ns32k-pc532
991 ;;
992 pc98)
993 basic_machine=i386-pc
994 ;;
995 pc98-*)
996 basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
997 ;;
998 pentium | p5 | k5 | k6 | nexgen | viac3)
999 basic_machine=i586-pc
1000 ;;
1001 pentiumpro | p6 | 6x86 | athlon | athlon_*)
1002 basic_machine=i686-pc
1003 ;;
1004 pentiumii | pentium2 | pentiumiii | pentium3)
1005 basic_machine=i686-pc
1006 ;;
1007 pentium4)
1008 basic_machine=i786-pc
1009 ;;
1010 pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
1011 basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1012 ;;
1013 pentiumpro-* | p6-* | 6x86-* | athlon-*)
1014 basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1015 ;;
1016 pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
1017 basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1018 ;;
1019 pentium4-*)
1020 basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1021 ;;
1022 pn)
1023 basic_machine=pn-gould
1024 ;;
1025 power) basic_machine=power-ibm
1026 ;;
1027 ppc | ppcbe) basic_machine=powerpc-unknown
1028 ;;
1029 ppc-* | ppcbe-*)
1030 basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1031 ;;
1032 ppcle | powerpclittle)
1033 basic_machine=powerpcle-unknown
1034 ;;
1035 ppcle-* | powerpclittle-*)
1036 basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1037 ;;
1038 ppc64) basic_machine=powerpc64-unknown
1039 ;;
1040 ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1041 ;;
1042 ppc64le | powerpc64little)
1043 basic_machine=powerpc64le-unknown
1044 ;;
1045 ppc64le-* | powerpc64little-*)
1046 basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1047 ;;
1048 ps2)
1049 basic_machine=i386-ibm
1050 ;;
1051 pw32)
1052 basic_machine=i586-unknown
1053 os=-pw32
1054 ;;
1055 rdos | rdos64)
1056 basic_machine=x86_64-pc
1057 os=-rdos
1058 ;;
1059 rdos32)
1060 basic_machine=i386-pc
1061 os=-rdos
1062 ;;
1063 rom68k)
1064 basic_machine=m68k-rom68k
1065 os=-coff
1066 ;;
1067 rm[46]00)
1068 basic_machine=mips-siemens
1069 ;;
1070 rtpc | rtpc-*)
1071 basic_machine=romp-ibm
1072 ;;
1073 s390 | s390-*)
1074 basic_machine=s390-ibm
1075 ;;
1076 s390x | s390x-*)
1077 basic_machine=s390x-ibm
1078 ;;
1079 sa29200)
1080 basic_machine=a29k-amd
1081 os=-udi
1082 ;;
1083 sb1)
1084 basic_machine=mipsisa64sb1-unknown
1085 ;;
1086 sb1el)
1087 basic_machine=mipsisa64sb1el-unknown
1088 ;;
1089 sde)
1090 basic_machine=mipsisa32-sde
1091 os=-elf
1092 ;;
1093 sei)
1094 basic_machine=mips-sei
1095 os=-seiux
1096 ;;
1097 sequent)
1098 basic_machine=i386-sequent
1099 ;;
1100 sh5el)
1101 basic_machine=sh5le-unknown
1102 ;;
1103 simso-wrs)
1104 basic_machine=sparclite-wrs
1105 os=-vxworks
1106 ;;
1107 sps7)
1108 basic_machine=m68k-bull
1109 os=-sysv2
1110 ;;
1111 spur)
1112 basic_machine=spur-unknown
1113 ;;
1114 st2000)
1115 basic_machine=m68k-tandem
1116 ;;
1117 stratus)
1118 basic_machine=i860-stratus
1119 os=-sysv4
1120 ;;
1121 strongarm-* | thumb-*)
1122 basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
1123 ;;
1124 sun2)
1125 basic_machine=m68000-sun
1126 ;;
1127 sun2os3)
1128 basic_machine=m68000-sun
1129 os=-sunos3
1130 ;;
1131 sun2os4)
1132 basic_machine=m68000-sun
1133 os=-sunos4
1134 ;;
1135 sun3os3)
1136 basic_machine=m68k-sun
1137 os=-sunos3
1138 ;;
1139 sun3os4)
1140 basic_machine=m68k-sun
1141 os=-sunos4
1142 ;;
1143 sun4os3)
1144 basic_machine=sparc-sun
1145 os=-sunos3
1146 ;;
1147 sun4os4)
1148 basic_machine=sparc-sun
1149 os=-sunos4
1150 ;;
1151 sun4sol2)
1152 basic_machine=sparc-sun
1153 os=-solaris2
1154 ;;
1155 sun3 | sun3-*)
1156 basic_machine=m68k-sun
1157 ;;
1158 sun4)
1159 basic_machine=sparc-sun
1160 ;;
1161 sun386 | sun386i | roadrunner)
1162 basic_machine=i386-sun
1163 ;;
1164 sv1)
1165 basic_machine=sv1-cray
1166 os=-unicos
1167 ;;
1168 symmetry)
1169 basic_machine=i386-sequent
1170 os=-dynix
1171 ;;
1172 t3e)
1173 basic_machine=alphaev5-cray
1174 os=-unicos
1175 ;;
1176 t90)
1177 basic_machine=t90-cray
1178 os=-unicos
1179 ;;
1180 tile*)
1181 basic_machine=$basic_machine-unknown
1182 os=-linux-gnu
1183 ;;
1184 tx39)
1185 basic_machine=mipstx39-unknown
1186 ;;
1187 tx39el)
1188 basic_machine=mipstx39el-unknown
1189 ;;
1190 toad1)
1191 basic_machine=pdp10-xkl
1192 os=-tops20
1193 ;;
1194 tower | tower-32)
1195 basic_machine=m68k-ncr
1196 ;;
1197 tpf)
1198 basic_machine=s390x-ibm
1199 os=-tpf
1200 ;;
1201 udi29k)
1202 basic_machine=a29k-amd
1203 os=-udi
1204 ;;
1205 ultra3)
1206 basic_machine=a29k-nyu
1207 os=-sym1
1208 ;;
1209 v810 | necv810)
1210 basic_machine=v810-nec
1211 os=-none
1212 ;;
1213 vaxv)
1214 basic_machine=vax-dec
1215 os=-sysv
1216 ;;
1217 vms)
1218 basic_machine=vax-dec
1219 os=-vms
1220 ;;
1221 vpp*|vx|vx-*)
1222 basic_machine=f301-fujitsu
1223 ;;
1224 vxworks960)
1225 basic_machine=i960-wrs
1226 os=-vxworks
1227 ;;
1228 vxworks68)
1229 basic_machine=m68k-wrs
1230 os=-vxworks
1231 ;;
1232 vxworks29k)
1233 basic_machine=a29k-wrs
1234 os=-vxworks
1235 ;;
1236 w65*)
1237 basic_machine=w65-wdc
1238 os=-none
1239 ;;
1240 w89k-*)
1241 basic_machine=hppa1.1-winbond
1242 os=-proelf
1243 ;;
1244 x64)
1245 basic_machine=x86_64-pc
1246 ;;
1247 xbox)
1248 basic_machine=i686-pc
1249 os=-mingw32
1250 ;;
1251 xps | xps100)
1252 basic_machine=xps100-honeywell
1253 ;;
1254 xscale-* | xscalee[bl]-*)
1255 basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
1256 ;;
1257 ymp)
1258 basic_machine=ymp-cray
1259 os=-unicos
1260 ;;
1261 none)
1262 basic_machine=none-none
1263 os=-none
1264 ;;
1265
1266 # Here we handle the default manufacturer of certain CPU types. It is in
1267 # some cases the only manufacturer, in others, it is the most popular.
1268 w89k)
1269 basic_machine=hppa1.1-winbond
1270 ;;
1271 op50n)
1272 basic_machine=hppa1.1-oki
1273 ;;
1274 op60c)
1275 basic_machine=hppa1.1-oki
1276 ;;
1277 romp)
1278 basic_machine=romp-ibm
1279 ;;
1280 mmix)
1281 basic_machine=mmix-knuth
1282 ;;
1283 rs6000)
1284 basic_machine=rs6000-ibm
1285 ;;
1286 vax)
1287 basic_machine=vax-dec
1288 ;;
1289 pdp11)
1290 basic_machine=pdp11-dec
1291 ;;
1292 we32k)
1293 basic_machine=we32k-att
1294 ;;
1295 sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
1296 basic_machine=sh-unknown
1297 ;;
1298 cydra)
1299 basic_machine=cydra-cydrome
1300 ;;
1301 orion)
1302 basic_machine=orion-highlevel
1303 ;;
1304 orion105)
1305 basic_machine=clipper-highlevel
1306 ;;
1307 mac | mpw | mac-mpw)
1308 basic_machine=m68k-apple
1309 ;;
1310 pmac | pmac-mpw)
1311 basic_machine=powerpc-apple
1312 ;;
1313 *-unknown)
1314 # Make sure to match an already-canonicalized machine name.
1315 ;;
1316 *)
1317 echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
1318 exit 1
1319 ;;
1320 esac
1321
1322 # Here we canonicalize certain aliases for manufacturers.
1323 case $basic_machine in
1324 *-digital*)
1325 basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`
1326 ;;
1327 *-commodore*)
1328 basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
1329 ;;
1330 *)
1331 ;;
1332 esac
1333
1334 # Decode manufacturer-specific aliases for certain operating systems.
1335
1336 if [ x"$os" != x"" ]
1337 then
1338 case $os in
1339 # First match some system type aliases that might get confused
1340 # with valid system types.
1341 # -solaris* is a basic system type, with this one exception.
1342 -auroraux)
1343 os=-auroraux
1344 ;;
1345 -solaris1 | -solaris1.*)
1346 os=`echo $os | sed -e 's|solaris1|sunos4|'`
1347 ;;
1348 -solaris)
1349 os=-solaris2
1350 ;;
1351 -unixware*)
1352 os=-sysv4.2uw
1353 ;;
1354 -gnu/linux*)
1355 os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
1356 ;;
1357 # es1800 is here to avoid being matched by es* (a different OS)
1358 -es1800*)
1359 os=-ose
1360 ;;
1361 # Now accept the basic system types.
1362 # The portable systems comes first.
1363 # Each alternative MUST end in a * to match a version number.
1364 # -sysv* is not here because it comes later, after sysvr4.
1365 -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
1366 | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
1367 | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
1368 | -sym* | -kopensolaris* | -plan9* \
1369 | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
1370 | -aos* | -aros* | -cloudabi* | -sortix* \
1371 | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
1372 | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
1373 | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \
1374 | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
1375 | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
1376 | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
1377 | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
1378 | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
1379 | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
1380 | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
1381 | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
1382 | -linux-newlib* | -linux-musl* | -linux-uclibc* \
1383 | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
1384 | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \
1385 | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
1386 | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
1387 | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
1388 | -morphos* | -superux* | -rtmk* | -windiss* \
1389 | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
1390 | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
1391 | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \
1392 | -midnightbsd*)
1393 # Remember, each alternative MUST END IN *, to match a version number.
1394 ;;
1395 -qnx*)
1396 case $basic_machine in
1397 x86-* | i*86-*)
1398 ;;
1399 *)
1400 os=-nto$os
1401 ;;
1402 esac
1403 ;;
1404 -nto-qnx*)
1405 ;;
1406 -nto*)
1407 os=`echo $os | sed -e 's|nto|nto-qnx|'`
1408 ;;
1409 -sim | -xray | -os68k* | -v88r* \
1410 | -windows* | -osx | -abug | -netware* | -os9* \
1411 | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
1412 ;;
1413 -mac*)
1414 os=`echo "$os" | sed -e 's|mac|macos|'`
1415 ;;
1416 -linux-dietlibc)
1417 os=-linux-dietlibc
1418 ;;
1419 -linux*)
1420 os=`echo $os | sed -e 's|linux|linux-gnu|'`
1421 ;;
1422 -sunos5*)
1423 os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
1424 ;;
1425 -sunos6*)
1426 os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
1427 ;;
1428 -opened*)
1429 os=-openedition
1430 ;;
1431 -os400*)
1432 os=-os400
1433 ;;
1434 -wince*)
1435 os=-wince
1436 ;;
1437 -utek*)
1438 os=-bsd
1439 ;;
1440 -dynix*)
1441 os=-bsd
1442 ;;
1443 -acis*)
1444 os=-aos
1445 ;;
1446 -atheos*)
1447 os=-atheos
1448 ;;
1449 -syllable*)
1450 os=-syllable
1451 ;;
1452 -386bsd)
1453 os=-bsd
1454 ;;
1455 -ctix* | -uts*)
1456 os=-sysv
1457 ;;
1458 -nova*)
1459 os=-rtmk-nova
1460 ;;
1461 -ns2)
1462 os=-nextstep2
1463 ;;
1464 -nsk*)
1465 os=-nsk
1466 ;;
1467 # Preserve the version number of sinix5.
1468 -sinix5.*)
1469 os=`echo $os | sed -e 's|sinix|sysv|'`
1470 ;;
1471 -sinix*)
1472 os=-sysv4
1473 ;;
1474 -tpf*)
1475 os=-tpf
1476 ;;
1477 -triton*)
1478 os=-sysv3
1479 ;;
1480 -oss*)
1481 os=-sysv3
1482 ;;
1483 -svr4*)
1484 os=-sysv4
1485 ;;
1486 -svr3)
1487 os=-sysv3
1488 ;;
1489 -sysvr4)
1490 os=-sysv4
1491 ;;
1492 # This must come after -sysvr4.
1493 -sysv*)
1494 ;;
1495 -ose*)
1496 os=-ose
1497 ;;
1498 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
1499 os=-mint
1500 ;;
1501 -zvmoe)
1502 os=-zvmoe
1503 ;;
1504 -dicos*)
1505 os=-dicos
1506 ;;
1507 -pikeos*)
1508 # Until real need of OS specific support for
1509 # particular features comes up, bare metal
1510 # configurations are quite functional.
1511 case $basic_machine in
1512 arm*)
1513 os=-eabi
1514 ;;
1515 *)
1516 os=-elf
1517 ;;
1518 esac
1519 ;;
1520 -nacl*)
1521 ;;
1522 -ios)
1523 ;;
1524 -none)
1525 ;;
1526 *)
1527 # Get rid of the `-' at the beginning of $os.
1528 os=`echo $os | sed 's/[^-]*-//'`
1529 echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
1530 exit 1
1531 ;;
1532 esac
1533 else
1534
1535 # Here we handle the default operating systems that come with various machines.
1536 # The value should be what the vendor currently ships out the door with their
1537 # machine or put another way, the most popular os provided with the machine.
1538
1539 # Note that if you're going to try to match "-MANUFACTURER" here (say,
1540 # "-sun"), then you have to tell the case statement up towards the top
1541 # that MANUFACTURER isn't an operating system. Otherwise, code above
1542 # will signal an error saying that MANUFACTURER isn't an operating
1543 # system, and we'll never get to this point.
1544
1545 case $basic_machine in
1546 score-*)
1547 os=-elf
1548 ;;
1549 spu-*)
1550 os=-elf
1551 ;;
1552 *-acorn)
1553 os=-riscix1.2
1554 ;;
1555 arm*-rebel)
1556 os=-linux
1557 ;;
1558 arm*-semi)
1559 os=-aout
1560 ;;
1561 c4x-* | tic4x-*)
1562 os=-coff
1563 ;;
1564 c8051-*)
1565 os=-elf
1566 ;;
1567 hexagon-*)
1568 os=-elf
1569 ;;
1570 tic54x-*)
1571 os=-coff
1572 ;;
1573 tic55x-*)
1574 os=-coff
1575 ;;
1576 tic6x-*)
1577 os=-coff
1578 ;;
1579 # This must come before the *-dec entry.
1580 pdp10-*)
1581 os=-tops20
1582 ;;
1583 pdp11-*)
1584 os=-none
1585 ;;
1586 *-dec | vax-*)
1587 os=-ultrix4.2
1588 ;;
1589 m68*-apollo)
1590 os=-domain
1591 ;;
1592 i386-sun)
1593 os=-sunos4.0.2
1594 ;;
1595 m68000-sun)
1596 os=-sunos3
1597 ;;
1598 m68*-cisco)
1599 os=-aout
1600 ;;
1601 mep-*)
1602 os=-elf
1603 ;;
1604 mips*-cisco)
1605 os=-elf
1606 ;;
1607 mips*-*)
1608 os=-elf
1609 ;;
1610 or32-*)
1611 os=-coff
1612 ;;
1613 *-tti) # must be before sparc entry or we get the wrong os.
1614 os=-sysv3
1615 ;;
1616 sparc-* | *-sun)
1617 os=-sunos4.1.1
1618 ;;
1619 pru-*)
1620 os=-elf
1621 ;;
1622 *-be)
1623 os=-beos
1624 ;;
1625 *-ibm)
1626 os=-aix
1627 ;;
1628 *-knuth)
1629 os=-mmixware
1630 ;;
1631 *-wec)
1632 os=-proelf
1633 ;;
1634 *-winbond)
1635 os=-proelf
1636 ;;
1637 *-oki)
1638 os=-proelf
1639 ;;
1640 *-hp)
1641 os=-hpux
1642 ;;
1643 *-hitachi)
1644 os=-hiux
1645 ;;
1646 i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
1647 os=-sysv
1648 ;;
1649 *-cbm)
1650 os=-amigaos
1651 ;;
1652 *-dg)
1653 os=-dgux
1654 ;;
1655 *-dolphin)
1656 os=-sysv3
1657 ;;
1658 m68k-ccur)
1659 os=-rtu
1660 ;;
1661 m88k-omron*)
1662 os=-luna
1663 ;;
1664 *-next)
1665 os=-nextstep
1666 ;;
1667 *-sequent)
1668 os=-ptx
1669 ;;
1670 *-crds)
1671 os=-unos
1672 ;;
1673 *-ns)
1674 os=-genix
1675 ;;
1676 i370-*)
1677 os=-mvs
1678 ;;
1679 *-gould)
1680 os=-sysv
1681 ;;
1682 *-highlevel)
1683 os=-bsd
1684 ;;
1685 *-encore)
1686 os=-bsd
1687 ;;
1688 *-sgi)
1689 os=-irix
1690 ;;
1691 *-siemens)
1692 os=-sysv4
1693 ;;
1694 *-masscomp)
1695 os=-rtu
1696 ;;
1697 f30[01]-fujitsu | f700-fujitsu)
1698 os=-uxpv
1699 ;;
1700 *-rom68k)
1701 os=-coff
1702 ;;
1703 *-*bug)
1704 os=-coff
1705 ;;
1706 *-apple)
1707 os=-macos
1708 ;;
1709 *-atari*)
1710 os=-mint
1711 ;;
1712 *)
1713 os=-none
1714 ;;
1715 esac
1716 fi
1717
1718 # Here we handle the case where we know the os, and the CPU type, but not the
1719 # manufacturer. We pick the logical manufacturer.
1720 vendor=unknown
1721 case $basic_machine in
1722 *-unknown)
1723 case $os in
1724 -riscix*)
1725 vendor=acorn
1726 ;;
1727 -sunos*)
1728 vendor=sun
1729 ;;
1730 -cnk*|-aix*)
1731 vendor=ibm
1732 ;;
1733 -beos*)
1734 vendor=be
1735 ;;
1736 -hpux*)
1737 vendor=hp
1738 ;;
1739 -mpeix*)
1740 vendor=hp
1741 ;;
1742 -hiux*)
1743 vendor=hitachi
1744 ;;
1745 -unos*)
1746 vendor=crds
1747 ;;
1748 -dgux*)
1749 vendor=dg
1750 ;;
1751 -luna*)
1752 vendor=omron
1753 ;;
1754 -genix*)
1755 vendor=ns
1756 ;;
1757 -mvs* | -opened*)
1758 vendor=ibm
1759 ;;
1760 -os400*)
1761 vendor=ibm
1762 ;;
1763 -ptx*)
1764 vendor=sequent
1765 ;;
1766 -tpf*)
1767 vendor=ibm
1768 ;;
1769 -vxsim* | -vxworks* | -windiss*)
1770 vendor=wrs
1771 ;;
1772 -aux*)
1773 vendor=apple
1774 ;;
1775 -hms*)
1776 vendor=hitachi
1777 ;;
1778 -mpw* | -macos*)
1779 vendor=apple
1780 ;;
1781 -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
1782 vendor=atari
1783 ;;
1784 -vos*)
1785 vendor=stratus
1786 ;;
1787 esac
1788 basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
1789 ;;
1790 esac
1791
1792 echo "$basic_machine$os"
1793 exit
1794
1795 # Local variables:
1796 # eval: (add-hook 'write-file-functions 'time-stamp)
1797 # time-stamp-start: "timestamp='"
1798 # time-stamp-format: "%:y-%02m-%02d"
1799 # time-stamp-end: "'"
1800 # End:
0 #! /bin/sh
1 # Guess values for system-dependent variables and create Makefiles.
2 # Generated by GNU Autoconf 2.69 for i3 4.16.1.
3 #
4 # Report bugs to <https://github.com/i3/i3/issues>.
5 #
6 #
7 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
8 #
9 #
10 # This configure script is free software; the Free Software Foundation
11 # gives unlimited permission to copy, distribute and modify it.
12 ## -------------------- ##
13 ## M4sh Initialization. ##
14 ## -------------------- ##
15
16 # Be more Bourne compatible
17 DUALCASE=1; export DUALCASE # for MKS sh
18 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
19 emulate sh
20 NULLCMD=:
21 # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
22 # is contrary to our usage. Disable this feature.
23 alias -g '${1+"$@"}'='"$@"'
24 setopt NO_GLOB_SUBST
25 else
26 case `(set -o) 2>/dev/null` in #(
27 *posix*) :
28 set -o posix ;; #(
29 *) :
30 ;;
31 esac
32 fi
33
34
35 as_nl='
36 '
37 export as_nl
38 # Printing a long string crashes Solaris 7 /usr/bin/printf.
39 as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
40 as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
41 as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
42 # Prefer a ksh shell builtin over an external printf program on Solaris,
43 # but without wasting forks for bash or zsh.
44 if test -z "$BASH_VERSION$ZSH_VERSION" \
45 && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
46 as_echo='print -r --'
47 as_echo_n='print -rn --'
48 elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
49 as_echo='printf %s\n'
50 as_echo_n='printf %s'
51 else
52 if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
53 as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
54 as_echo_n='/usr/ucb/echo -n'
55 else
56 as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
57 as_echo_n_body='eval
58 arg=$1;
59 case $arg in #(
60 *"$as_nl"*)
61 expr "X$arg" : "X\\(.*\\)$as_nl";
62 arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
63 esac;
64 expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
65 '
66 export as_echo_n_body
67 as_echo_n='sh -c $as_echo_n_body as_echo'
68 fi
69 export as_echo_body
70 as_echo='sh -c $as_echo_body as_echo'
71 fi
72
73 # The user is always right.
74 if test "${PATH_SEPARATOR+set}" != set; then
75 PATH_SEPARATOR=:
76 (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
77 (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
78 PATH_SEPARATOR=';'
79 }
80 fi
81
82
83 # IFS
84 # We need space, tab and new line, in precisely that order. Quoting is
85 # there to prevent editors from complaining about space-tab.
86 # (If _AS_PATH_WALK were called with IFS unset, it would disable word
87 # splitting by setting IFS to empty value.)
88 IFS=" "" $as_nl"
89
90 # Find who we are. Look in the path if we contain no directory separator.
91 as_myself=
92 case $0 in #((
93 *[\\/]* ) as_myself=$0 ;;
94 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
95 for as_dir in $PATH
96 do
97 IFS=$as_save_IFS
98 test -z "$as_dir" && as_dir=.
99 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
100 done
101 IFS=$as_save_IFS
102
103 ;;
104 esac
105 # We did not find ourselves, most probably we were run as `sh COMMAND'
106 # in which case we are not to be found in the path.
107 if test "x$as_myself" = x; then
108 as_myself=$0
109 fi
110 if test ! -f "$as_myself"; then
111 $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
112 exit 1
113 fi
114
115 # Unset variables that we do not need and which cause bugs (e.g. in
116 # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
117 # suppresses any "Segmentation fault" message there. '((' could
118 # trigger a bug in pdksh 5.2.14.
119 for as_var in BASH_ENV ENV MAIL MAILPATH
120 do eval test x\${$as_var+set} = xset \
121 && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
122 done
123 PS1='$ '
124 PS2='> '
125 PS4='+ '
126
127 # NLS nuisances.
128 LC_ALL=C
129 export LC_ALL
130 LANGUAGE=C
131 export LANGUAGE
132
133 # CDPATH.
134 (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
135
136 # Use a proper internal environment variable to ensure we don't fall
137 # into an infinite loop, continuously re-executing ourselves.
138 if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
139 _as_can_reexec=no; export _as_can_reexec;
140 # We cannot yet assume a decent shell, so we have to provide a
141 # neutralization value for shells without unset; and this also
142 # works around shells that cannot unset nonexistent variables.
143 # Preserve -v and -x to the replacement shell.
144 BASH_ENV=/dev/null
145 ENV=/dev/null
146 (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
147 case $- in # ((((
148 *v*x* | *x*v* ) as_opts=-vx ;;
149 *v* ) as_opts=-v ;;
150 *x* ) as_opts=-x ;;
151 * ) as_opts= ;;
152 esac
153 exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
154 # Admittedly, this is quite paranoid, since all the known shells bail
155 # out after a failed `exec'.
156 $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
157 as_fn_exit 255
158 fi
159 # We don't want this to propagate to other subprocesses.
160 { _as_can_reexec=; unset _as_can_reexec;}
161 if test "x$CONFIG_SHELL" = x; then
162 as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
163 emulate sh
164 NULLCMD=:
165 # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
166 # is contrary to our usage. Disable this feature.
167 alias -g '\${1+\"\$@\"}'='\"\$@\"'
168 setopt NO_GLOB_SUBST
169 else
170 case \`(set -o) 2>/dev/null\` in #(
171 *posix*) :
172 set -o posix ;; #(
173 *) :
174 ;;
175 esac
176 fi
177 "
178 as_required="as_fn_return () { (exit \$1); }
179 as_fn_success () { as_fn_return 0; }
180 as_fn_failure () { as_fn_return 1; }
181 as_fn_ret_success () { return 0; }
182 as_fn_ret_failure () { return 1; }
183
184 exitcode=0
185 as_fn_success || { exitcode=1; echo as_fn_success failed.; }
186 as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
187 as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
188 as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
189 if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
190
191 else
192 exitcode=1; echo positional parameters were not saved.
193 fi
194 test x\$exitcode = x0 || exit 1
195 test -x / || exit 1"
196 as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
197 as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
198 eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
199 test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
200 test \$(( 1 + 1 )) = 2 || exit 1"
201 if (eval "$as_required") 2>/dev/null; then :
202 as_have_required=yes
203 else
204 as_have_required=no
205 fi
206 if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
207
208 else
209 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
210 as_found=false
211 for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
212 do
213 IFS=$as_save_IFS
214 test -z "$as_dir" && as_dir=.
215 as_found=:
216 case $as_dir in #(
217 /*)
218 for as_base in sh bash ksh sh5; do
219 # Try only shells that exist, to save several forks.
220 as_shell=$as_dir/$as_base
221 if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
222 { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
223 CONFIG_SHELL=$as_shell as_have_required=yes
224 if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
225 break 2
226 fi
227 fi
228 done;;
229 esac
230 as_found=false
231 done
232 $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
233 { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
234 CONFIG_SHELL=$SHELL as_have_required=yes
235 fi; }
236 IFS=$as_save_IFS
237
238
239 if test "x$CONFIG_SHELL" != x; then :
240 export CONFIG_SHELL
241 # We cannot yet assume a decent shell, so we have to provide a
242 # neutralization value for shells without unset; and this also
243 # works around shells that cannot unset nonexistent variables.
244 # Preserve -v and -x to the replacement shell.
245 BASH_ENV=/dev/null
246 ENV=/dev/null
247 (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
248 case $- in # ((((
249 *v*x* | *x*v* ) as_opts=-vx ;;
250 *v* ) as_opts=-v ;;
251 *x* ) as_opts=-x ;;
252 * ) as_opts= ;;
253 esac
254 exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
255 # Admittedly, this is quite paranoid, since all the known shells bail
256 # out after a failed `exec'.
257 $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
258 exit 255
259 fi
260
261 if test x$as_have_required = xno; then :
262 $as_echo "$0: This script requires a shell more modern than all"
263 $as_echo "$0: the shells that I found on your system."
264 if test x${ZSH_VERSION+set} = xset ; then
265 $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
266 $as_echo "$0: be upgraded to zsh 4.3.4 or later."
267 else
268 $as_echo "$0: Please tell bug-autoconf@gnu.org and
269 $0: https://github.com/i3/i3/issues about your system,
270 $0: including any error possibly output before this
271 $0: message. Then install a modern shell, or manually run
272 $0: the script under such a shell if you do have one."
273 fi
274 exit 1
275 fi
276 fi
277 fi
278 SHELL=${CONFIG_SHELL-/bin/sh}
279 export SHELL
280 # Unset more variables known to interfere with behavior of common tools.
281 CLICOLOR_FORCE= GREP_OPTIONS=
282 unset CLICOLOR_FORCE GREP_OPTIONS
283
284 ## --------------------- ##
285 ## M4sh Shell Functions. ##
286 ## --------------------- ##
287 # as_fn_unset VAR
288 # ---------------
289 # Portably unset VAR.
290 as_fn_unset ()
291 {
292 { eval $1=; unset $1;}
293 }
294 as_unset=as_fn_unset
295
296 # as_fn_set_status STATUS
297 # -----------------------
298 # Set $? to STATUS, without forking.
299 as_fn_set_status ()
300 {
301 return $1
302 } # as_fn_set_status
303
304 # as_fn_exit STATUS
305 # -----------------
306 # Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
307 as_fn_exit ()
308 {
309 set +e
310 as_fn_set_status $1
311 exit $1
312 } # as_fn_exit
313
314 # as_fn_mkdir_p
315 # -------------
316 # Create "$as_dir" as a directory, including parents if necessary.
317 as_fn_mkdir_p ()
318 {
319
320 case $as_dir in #(
321 -*) as_dir=./$as_dir;;
322 esac
323 test -d "$as_dir" || eval $as_mkdir_p || {
324 as_dirs=
325 while :; do
326 case $as_dir in #(
327 *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
328 *) as_qdir=$as_dir;;
329 esac
330 as_dirs="'$as_qdir' $as_dirs"
331 as_dir=`$as_dirname -- "$as_dir" ||
332 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
333 X"$as_dir" : 'X\(//\)[^/]' \| \
334 X"$as_dir" : 'X\(//\)$' \| \
335 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
336 $as_echo X"$as_dir" |
337 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
338 s//\1/
339 q
340 }
341 /^X\(\/\/\)[^/].*/{
342 s//\1/
343 q
344 }
345 /^X\(\/\/\)$/{
346 s//\1/
347 q
348 }
349 /^X\(\/\).*/{
350 s//\1/
351 q
352 }
353 s/.*/./; q'`
354 test -d "$as_dir" && break
355 done
356 test -z "$as_dirs" || eval "mkdir $as_dirs"
357 } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
358
359
360 } # as_fn_mkdir_p
361
362 # as_fn_executable_p FILE
363 # -----------------------
364 # Test if FILE is an executable regular file.
365 as_fn_executable_p ()
366 {
367 test -f "$1" && test -x "$1"
368 } # as_fn_executable_p
369 # as_fn_append VAR VALUE
370 # ----------------------
371 # Append the text in VALUE to the end of the definition contained in VAR. Take
372 # advantage of any shell optimizations that allow amortized linear growth over
373 # repeated appends, instead of the typical quadratic growth present in naive
374 # implementations.
375 if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
376 eval 'as_fn_append ()
377 {
378 eval $1+=\$2
379 }'
380 else
381 as_fn_append ()
382 {
383 eval $1=\$$1\$2
384 }
385 fi # as_fn_append
386
387 # as_fn_arith ARG...
388 # ------------------
389 # Perform arithmetic evaluation on the ARGs, and store the result in the
390 # global $as_val. Take advantage of shells that can avoid forks. The arguments
391 # must be portable across $(()) and expr.
392 if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
393 eval 'as_fn_arith ()
394 {
395 as_val=$(( $* ))
396 }'
397 else
398 as_fn_arith ()
399 {
400 as_val=`expr "$@" || test $? -eq 1`
401 }
402 fi # as_fn_arith
403
404
405 # as_fn_error STATUS ERROR [LINENO LOG_FD]
406 # ----------------------------------------
407 # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
408 # provided, also output the error to LOG_FD, referencing LINENO. Then exit the
409 # script with STATUS, using 1 if that was 0.
410 as_fn_error ()
411 {
412 as_status=$1; test $as_status -eq 0 && as_status=1
413 if test "$4"; then
414 as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
415 $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
416 fi
417 $as_echo "$as_me: error: $2" >&2
418 as_fn_exit $as_status
419 } # as_fn_error
420
421 if expr a : '\(a\)' >/dev/null 2>&1 &&
422 test "X`expr 00001 : '.*\(...\)'`" = X001; then
423 as_expr=expr
424 else
425 as_expr=false
426 fi
427
428 if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
429 as_basename=basename
430 else
431 as_basename=false
432 fi
433
434 if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
435 as_dirname=dirname
436 else
437 as_dirname=false
438 fi
439
440 as_me=`$as_basename -- "$0" ||
441 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
442 X"$0" : 'X\(//\)$' \| \
443 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
444 $as_echo X/"$0" |
445 sed '/^.*\/\([^/][^/]*\)\/*$/{
446 s//\1/
447 q
448 }
449 /^X\/\(\/\/\)$/{
450 s//\1/
451 q
452 }
453 /^X\/\(\/\).*/{
454 s//\1/
455 q
456 }
457 s/.*/./; q'`
458
459 # Avoid depending upon Character Ranges.
460 as_cr_letters='abcdefghijklmnopqrstuvwxyz'
461 as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
462 as_cr_Letters=$as_cr_letters$as_cr_LETTERS
463 as_cr_digits='0123456789'
464 as_cr_alnum=$as_cr_Letters$as_cr_digits
465
466
467 as_lineno_1=$LINENO as_lineno_1a=$LINENO
468 as_lineno_2=$LINENO as_lineno_2a=$LINENO
469 eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
470 test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
471 # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
472 sed -n '
473 p
474 /[$]LINENO/=
475 ' <$as_myself |
476 sed '
477 s/[$]LINENO.*/&-/
478 t lineno
479 b
480 :lineno
481 N
482 :loop
483 s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
484 t loop
485 s/-\n.*//
486 ' >$as_me.lineno &&
487 chmod +x "$as_me.lineno" ||
488 { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
489
490 # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
491 # already done that, so ensure we don't try to do so again and fall
492 # in an infinite loop. This has already happened in practice.
493 _as_can_reexec=no; export _as_can_reexec
494 # Don't try to exec as it changes $[0], causing all sort of problems
495 # (the dirname of $[0] is not the place where we might find the
496 # original and so on. Autoconf is especially sensitive to this).
497 . "./$as_me.lineno"
498 # Exit status is that of the last command.
499 exit
500 }
501
502 ECHO_C= ECHO_N= ECHO_T=
503 case `echo -n x` in #(((((
504 -n*)
505 case `echo 'xy\c'` in
506 *c*) ECHO_T=' ';; # ECHO_T is single tab character.
507 xy) ECHO_C='\c';;
508 *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
509 ECHO_T=' ';;
510 esac;;
511 *)
512 ECHO_N='-n';;
513 esac
514
515 rm -f conf$$ conf$$.exe conf$$.file
516 if test -d conf$$.dir; then
517 rm -f conf$$.dir/conf$$.file
518 else
519 rm -f conf$$.dir
520 mkdir conf$$.dir 2>/dev/null
521 fi
522 if (echo >conf$$.file) 2>/dev/null; then
523 if ln -s conf$$.file conf$$ 2>/dev/null; then
524 as_ln_s='ln -s'
525 # ... but there are two gotchas:
526 # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
527 # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
528 # In both cases, we have to default to `cp -pR'.
529 ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
530 as_ln_s='cp -pR'
531 elif ln conf$$.file conf$$ 2>/dev/null; then
532 as_ln_s=ln
533 else
534 as_ln_s='cp -pR'
535 fi
536 else
537 as_ln_s='cp -pR'
538 fi
539 rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
540 rmdir conf$$.dir 2>/dev/null
541
542 if mkdir -p . 2>/dev/null; then
543 as_mkdir_p='mkdir -p "$as_dir"'
544 else
545 test -d ./-p && rmdir ./-p
546 as_mkdir_p=false
547 fi
548
549 as_test_x='test -x'
550 as_executable_p=as_fn_executable_p
551
552 # Sed expression to map a string onto a valid CPP name.
553 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
554
555 # Sed expression to map a string onto a valid variable name.
556 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
557
558
559 test -n "$DJDIR" || exec 7<&0 </dev/null
560 exec 6>&1
561
562 # Name of the host.
563 # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
564 # so uname gets run too.
565 ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
566
567 #
568 # Initializations.
569 #
570 ac_default_prefix=/usr/local
571 ac_clean_files=
572 ac_config_libobj_dir=.
573 LIBOBJS=
574 cross_compiling=no
575 subdirs=
576 MFLAGS=
577 MAKEFLAGS=
578
579 # Identity of this package.
580 PACKAGE_NAME='i3'
581 PACKAGE_TARNAME='i3'
582 PACKAGE_VERSION='4.16.1'
583 PACKAGE_STRING='i3 4.16.1'
584 PACKAGE_BUGREPORT='https://github.com/i3/i3/issues'
585 PACKAGE_URL=''
586
587 ac_unique_file="libi3/ipc_recv_message.c"
588 # Factoring default headers for most tests.
589 ac_includes_default="\
590 #include <stdio.h>
591 #ifdef HAVE_SYS_TYPES_H
592 # include <sys/types.h>
593 #endif
594 #ifdef HAVE_SYS_STAT_H
595 # include <sys/stat.h>
596 #endif
597 #ifdef STDC_HEADERS
598 # include <stdlib.h>
599 # include <stddef.h>
600 #else
601 # ifdef HAVE_STDLIB_H
602 # include <stdlib.h>
603 # endif
604 #endif
605 #ifdef HAVE_STRING_H
606 # if !defined STDC_HEADERS && defined HAVE_MEMORY_H
607 # include <memory.h>
608 # endif
609 # include <string.h>
610 #endif
611 #ifdef HAVE_STRINGS_H
612 # include <strings.h>
613 #endif
614 #ifdef HAVE_INTTYPES_H
615 # include <inttypes.h>
616 #endif
617 #ifdef HAVE_STDINT_H
618 # include <stdint.h>
619 #endif
620 #ifdef HAVE_UNISTD_H
621 # include <unistd.h>
622 #endif"
623
624 ac_subst_vars='am__EXEEXT_FALSE
625 am__EXEEXT_TRUE
626 LTLIBOBJS
627 AM_CFLAGS
628 ac_ct_AR
629 AR
630 BUILD_DOCS_FALSE
631 BUILD_DOCS_TRUE
632 BUILD_MANS_FALSE
633 BUILD_MANS_TRUE
634 PATH_POD2MAN
635 PATH_XMLTO
636 PATH_ASCIIDOC
637 LN_S
638 RANLIB
639 PANGOCAIRO_LIBS
640 PANGOCAIRO_CFLAGS
641 LIBPCRE_LIBS
642 LIBPCRE_CFLAGS
643 YAJL_LIBS
644 YAJL_CFLAGS
645 XKBCOMMON_LIBS
646 XKBCOMMON_CFLAGS
647 XCB_UTIL_XRM_LIBS
648 XCB_UTIL_XRM_CFLAGS
649 XCB_UTIL_WM_LIBS
650 XCB_UTIL_WM_CFLAGS
651 XCB_UTIL_KEYSYMS_LIBS
652 XCB_UTIL_KEYSYMS_CFLAGS
653 XCB_UTIL_CURSOR_LIBS
654 XCB_UTIL_CURSOR_CFLAGS
655 XCB_UTIL_LIBS
656 XCB_UTIL_CFLAGS
657 XCB_LIBS
658 XCB_CFLAGS
659 LIBSN_LIBS
660 LIBSN_CFLAGS
661 PKG_CONFIG_LIBDIR
662 PKG_CONFIG_PATH
663 PKG_CONFIG
664 PTHREAD_CFLAGS
665 PTHREAD_LIBS
666 PTHREAD_CC
667 ax_pthread_config
668 LIBOBJS
669 EGREP
670 GREP
671 CPP
672 am__fastdepCC_FALSE
673 am__fastdepCC_TRUE
674 CCDEPMODE
675 am__nodep
676 AMDEPBACKSLASH
677 AMDEP_FALSE
678 AMDEP_TRUE
679 am__include
680 DEPDIR
681 OBJEXT
682 EXEEXT
683 ac_ct_CC
684 CPPFLAGS
685 LDFLAGS
686 CFLAGS
687 CC
688 CODE_COVERAGE_RULES
689 CODE_COVERAGE_LDFLAGS
690 CODE_COVERAGE_CXXFLAGS
691 CODE_COVERAGE_CFLAGS
692 CODE_COVERAGE_CPPFLAGS
693 GENHTML
694 LCOV
695 GCOV
696 CODE_COVERAGE_ENABLED
697 CODE_COVERAGE_ENABLED_FALSE
698 CODE_COVERAGE_ENABLED_TRUE
699 SED
700 I3_VERSION
701 AX_EXTEND_SRCDIR_CPPFLAGS
702 ifGNUmake
703 MAINT
704 MAINTAINER_MODE_FALSE
705 MAINTAINER_MODE_TRUE
706 AM_BACKSLASH
707 AM_DEFAULT_VERBOSITY
708 AM_DEFAULT_V
709 AM_V
710 am__untar
711 am__tar
712 AMTAR
713 am__leading_dot
714 SET_MAKE
715 AWK
716 mkdir_p
717 MKDIR_P
718 INSTALL_STRIP_PROGRAM
719 STRIP
720 install_sh
721 MAKEINFO
722 AUTOHEADER
723 AUTOMAKE
724 AUTOCONF
725 ACLOCAL
726 VERSION
727 PACKAGE
728 CYGPATH_W
729 am__isrc
730 INSTALL_DATA
731 INSTALL_SCRIPT
732 INSTALL_PROGRAM
733 ax_enable_builddir_sed
734 target_os
735 target_vendor
736 target_cpu
737 target
738 host_os
739 host_vendor
740 host_cpu
741 host
742 build_os
743 build_vendor
744 build_cpu
745 build
746 target_alias
747 host_alias
748 build_alias
749 LIBS
750 ECHO_T
751 ECHO_N
752 ECHO_C
753 DEFS
754 mandir
755 localedir
756 libdir
757 psdir
758 pdfdir
759 dvidir
760 htmldir
761 infodir
762 docdir
763 oldincludedir
764 includedir
765 runstatedir
766 localstatedir
767 sharedstatedir
768 sysconfdir
769 datadir
770 datarootdir
771 libexecdir
772 sbindir
773 bindir
774 program_transform_name
775 prefix
776 exec_prefix
777 PACKAGE_URL
778 PACKAGE_BUGREPORT
779 PACKAGE_STRING
780 PACKAGE_VERSION
781 PACKAGE_TARNAME
782 PACKAGE_NAME
783 PATH_SEPARATOR
784 SHELL
785 am__quote'
786 ac_subst_files=''
787 ac_user_opts='
788 enable_option_checking
789 enable_builddir
790 enable_silent_rules
791 enable_maintainer_mode
792 with_gcov
793 enable_code_coverage
794 enable_debug
795 enable_dependency_tracking
796 enable_docs
797 enable_mans
798 enable_sanitizers
799 enable_address_sanitizer
800 enable_memory_sanitizer
801 enable_undefined_sanitizer
802 '
803 ac_precious_vars='build_alias
804 host_alias
805 target_alias
806 CC
807 CFLAGS
808 LDFLAGS
809 LIBS
810 CPPFLAGS
811 CPP
812 PKG_CONFIG
813 PKG_CONFIG_PATH
814 PKG_CONFIG_LIBDIR
815 LIBSN_CFLAGS
816 LIBSN_LIBS
817 XCB_CFLAGS
818 XCB_LIBS
819 XCB_UTIL_CFLAGS
820 XCB_UTIL_LIBS
821 XCB_UTIL_CURSOR_CFLAGS
822 XCB_UTIL_CURSOR_LIBS
823 XCB_UTIL_KEYSYMS_CFLAGS
824 XCB_UTIL_KEYSYMS_LIBS
825 XCB_UTIL_WM_CFLAGS
826 XCB_UTIL_WM_LIBS
827 XCB_UTIL_XRM_CFLAGS
828 XCB_UTIL_XRM_LIBS
829 XKBCOMMON_CFLAGS
830 XKBCOMMON_LIBS
831 YAJL_CFLAGS
832 YAJL_LIBS
833 LIBPCRE_CFLAGS
834 LIBPCRE_LIBS
835 PANGOCAIRO_CFLAGS
836 PANGOCAIRO_LIBS'
837
838
839 # Initialize some variables set by options.
840 ac_init_help=
841 ac_init_version=false
842 ac_unrecognized_opts=
843 ac_unrecognized_sep=
844 # The variables have the same names as the options, with
845 # dashes changed to underlines.
846 cache_file=/dev/null
847 exec_prefix=NONE
848 no_create=
849 no_recursion=
850 prefix=NONE
851 program_prefix=NONE
852 program_suffix=NONE
853 program_transform_name=s,x,x,
854 silent=
855 site=
856 srcdir=
857 verbose=
858 x_includes=NONE
859 x_libraries=NONE
860
861 # Installation directory options.
862 # These are left unexpanded so users can "make install exec_prefix=/foo"
863 # and all the variables that are supposed to be based on exec_prefix
864 # by default will actually change.
865 # Use braces instead of parens because sh, perl, etc. also accept them.
866 # (The list follows the same order as the GNU Coding Standards.)
867 bindir='${exec_prefix}/bin'
868 sbindir='${exec_prefix}/sbin'
869 libexecdir='${exec_prefix}/libexec'
870 datarootdir='${prefix}/share'
871 datadir='${datarootdir}'
872 sysconfdir='${prefix}/etc'
873 sharedstatedir='${prefix}/com'
874 localstatedir='${prefix}/var'
875 runstatedir='${localstatedir}/run'
876 includedir='${prefix}/include'
877 oldincludedir='/usr/include'
878 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
879 infodir='${datarootdir}/info'
880 htmldir='${docdir}'
881 dvidir='${docdir}'
882 pdfdir='${docdir}'
883 psdir='${docdir}'
884 libdir='${exec_prefix}/lib'
885 localedir='${datarootdir}/locale'
886 mandir='${datarootdir}/man'
887
888 ac_prev=
889 ac_dashdash=
890 for ac_option
891 do
892 # If the previous option needs an argument, assign it.
893 if test -n "$ac_prev"; then
894 eval $ac_prev=\$ac_option
895 ac_prev=
896 continue
897 fi
898
899 case $ac_option in
900 *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
901 *=) ac_optarg= ;;
902 *) ac_optarg=yes ;;
903 esac
904
905 # Accept the important Cygnus configure options, so we can diagnose typos.
906
907 case $ac_dashdash$ac_option in
908 --)
909 ac_dashdash=yes ;;
910
911 -bindir | --bindir | --bindi | --bind | --bin | --bi)
912 ac_prev=bindir ;;
913 -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
914 bindir=$ac_optarg ;;
915
916 -build | --build | --buil | --bui | --bu)
917 ac_prev=build_alias ;;
918 -build=* | --build=* | --buil=* | --bui=* | --bu=*)
919 build_alias=$ac_optarg ;;
920
921 -cache-file | --cache-file | --cache-fil | --cache-fi \
922 | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
923 ac_prev=cache_file ;;
924 -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
925 | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
926 cache_file=$ac_optarg ;;
927
928 --config-cache | -C)
929 cache_file=config.cache ;;
930
931 -datadir | --datadir | --datadi | --datad)
932 ac_prev=datadir ;;
933 -datadir=* | --datadir=* | --datadi=* | --datad=*)
934 datadir=$ac_optarg ;;
935
936 -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
937 | --dataroo | --dataro | --datar)
938 ac_prev=datarootdir ;;
939 -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
940 | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
941 datarootdir=$ac_optarg ;;
942
943 -disable-* | --disable-*)
944 ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
945 # Reject names that are not valid shell variable names.
946 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
947 as_fn_error $? "invalid feature name: $ac_useropt"
948 ac_useropt_orig=$ac_useropt
949 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
950 case $ac_user_opts in
951 *"
952 "enable_$ac_useropt"
953 "*) ;;
954 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
955 ac_unrecognized_sep=', ';;
956 esac
957 eval enable_$ac_useropt=no ;;
958
959 -docdir | --docdir | --docdi | --doc | --do)
960 ac_prev=docdir ;;
961 -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
962 docdir=$ac_optarg ;;
963
964 -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
965 ac_prev=dvidir ;;
966 -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
967 dvidir=$ac_optarg ;;
968
969 -enable-* | --enable-*)
970 ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
971 # Reject names that are not valid shell variable names.
972 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
973 as_fn_error $? "invalid feature name: $ac_useropt"
974 ac_useropt_orig=$ac_useropt
975 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
976 case $ac_user_opts in
977 *"
978 "enable_$ac_useropt"
979 "*) ;;
980 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
981 ac_unrecognized_sep=', ';;
982 esac
983 eval enable_$ac_useropt=\$ac_optarg ;;
984
985 -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
986 | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
987 | --exec | --exe | --ex)
988 ac_prev=exec_prefix ;;
989 -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
990 | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
991 | --exec=* | --exe=* | --ex=*)
992 exec_prefix=$ac_optarg ;;
993
994 -gas | --gas | --ga | --g)
995 # Obsolete; use --with-gas.
996 with_gas=yes ;;
997
998 -help | --help | --hel | --he | -h)
999 ac_init_help=long ;;
1000 -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
1001 ac_init_help=recursive ;;
1002 -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
1003 ac_init_help=short ;;
1004
1005 -host | --host | --hos | --ho)
1006 ac_prev=host_alias ;;
1007 -host=* | --host=* | --hos=* | --ho=*)
1008 host_alias=$ac_optarg ;;
1009
1010 -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
1011 ac_prev=htmldir ;;
1012 -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
1013 | --ht=*)
1014 htmldir=$ac_optarg ;;
1015
1016 -includedir | --includedir | --includedi | --included | --include \
1017 | --includ | --inclu | --incl | --inc)
1018 ac_prev=includedir ;;
1019 -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
1020 | --includ=* | --inclu=* | --incl=* | --inc=*)
1021 includedir=$ac_optarg ;;
1022
1023 -infodir | --infodir | --infodi | --infod | --info | --inf)
1024 ac_prev=infodir ;;
1025 -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
1026 infodir=$ac_optarg ;;
1027
1028 -libdir | --libdir | --libdi | --libd)
1029 ac_prev=libdir ;;
1030 -libdir=* | --libdir=* | --libdi=* | --libd=*)
1031 libdir=$ac_optarg ;;
1032
1033 -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
1034 | --libexe | --libex | --libe)
1035 ac_prev=libexecdir ;;
1036 -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
1037 | --libexe=* | --libex=* | --libe=*)
1038 libexecdir=$ac_optarg ;;
1039
1040 -localedir | --localedir | --localedi | --localed | --locale)
1041 ac_prev=localedir ;;
1042 -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
1043 localedir=$ac_optarg ;;
1044
1045 -localstatedir | --localstatedir | --localstatedi | --localstated \
1046 | --localstate | --localstat | --localsta | --localst | --locals)
1047 ac_prev=localstatedir ;;
1048 -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
1049 | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
1050 localstatedir=$ac_optarg ;;
1051
1052 -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
1053 ac_prev=mandir ;;
1054 -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
1055 mandir=$ac_optarg ;;
1056
1057 -nfp | --nfp | --nf)
1058 # Obsolete; use --without-fp.
1059 with_fp=no ;;
1060
1061 -no-create | --no-create | --no-creat | --no-crea | --no-cre \
1062 | --no-cr | --no-c | -n)
1063 no_create=yes ;;
1064
1065 -no-recursion | --no-recursion | --no-recursio | --no-recursi \
1066 | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
1067 no_recursion=yes ;;
1068
1069 -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
1070 | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
1071 | --oldin | --oldi | --old | --ol | --o)
1072 ac_prev=oldincludedir ;;
1073 -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
1074 | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
1075 | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
1076 oldincludedir=$ac_optarg ;;
1077
1078 -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
1079 ac_prev=prefix ;;
1080 -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
1081 prefix=$ac_optarg ;;
1082
1083 -program-prefix | --program-prefix | --program-prefi | --program-pref \
1084 | --program-pre | --program-pr | --program-p)
1085 ac_prev=program_prefix ;;
1086 -program-prefix=* | --program-prefix=* | --program-prefi=* \
1087 | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
1088 program_prefix=$ac_optarg ;;
1089
1090 -program-suffix | --program-suffix | --program-suffi | --program-suff \
1091 | --program-suf | --program-su | --program-s)
1092 ac_prev=program_suffix ;;
1093 -program-suffix=* | --program-suffix=* | --program-suffi=* \
1094 | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
1095 program_suffix=$ac_optarg ;;
1096
1097 -program-transform-name | --program-transform-name \
1098 | --program-transform-nam | --program-transform-na \
1099 | --program-transform-n | --program-transform- \
1100 | --program-transform | --program-transfor \
1101 | --program-transfo | --program-transf \
1102 | --program-trans | --program-tran \
1103 | --progr-tra | --program-tr | --program-t)
1104 ac_prev=program_transform_name ;;
1105 -program-transform-name=* | --program-transform-name=* \
1106 | --program-transform-nam=* | --program-transform-na=* \
1107 | --program-transform-n=* | --program-transform-=* \
1108 | --program-transform=* | --program-transfor=* \
1109 | --program-transfo=* | --program-transf=* \
1110 | --program-trans=* | --program-tran=* \
1111 | --progr-tra=* | --program-tr=* | --program-t=*)
1112 program_transform_name=$ac_optarg ;;
1113
1114 -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
1115 ac_prev=pdfdir ;;
1116 -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
1117 pdfdir=$ac_optarg ;;
1118
1119 -psdir | --psdir | --psdi | --psd | --ps)
1120 ac_prev=psdir ;;
1121 -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
1122 psdir=$ac_optarg ;;
1123
1124 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
1125 | -silent | --silent | --silen | --sile | --sil)
1126 silent=yes ;;
1127
1128 -runstatedir | --runstatedir | --runstatedi | --runstated \
1129 | --runstate | --runstat | --runsta | --runst | --runs \
1130 | --run | --ru | --r)
1131 ac_prev=runstatedir ;;
1132 -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
1133 | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
1134 | --run=* | --ru=* | --r=*)
1135 runstatedir=$ac_optarg ;;
1136
1137 -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
1138 ac_prev=sbindir ;;
1139 -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
1140 | --sbi=* | --sb=*)
1141 sbindir=$ac_optarg ;;
1142
1143 -sharedstatedir | --sharedstatedir | --sharedstatedi \
1144 | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
1145 | --sharedst | --shareds | --shared | --share | --shar \
1146 | --sha | --sh)
1147 ac_prev=sharedstatedir ;;
1148 -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
1149 | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
1150 | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
1151 | --sha=* | --sh=*)
1152 sharedstatedir=$ac_optarg ;;
1153
1154 -site | --site | --sit)
1155 ac_prev=site ;;
1156 -site=* | --site=* | --sit=*)
1157 site=$ac_optarg ;;
1158
1159 -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
1160 ac_prev=srcdir ;;
1161 -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
1162 srcdir=$ac_optarg ;;
1163
1164 -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
1165 | --syscon | --sysco | --sysc | --sys | --sy)
1166 ac_prev=sysconfdir ;;
1167 -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
1168 | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
1169 sysconfdir=$ac_optarg ;;
1170
1171 -target | --target | --targe | --targ | --tar | --ta | --t)
1172 ac_prev=target_alias ;;
1173 -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
1174 target_alias=$ac_optarg ;;
1175
1176 -v | -verbose | --verbose | --verbos | --verbo | --verb)
1177 verbose=yes ;;
1178
1179 -version | --version | --versio | --versi | --vers | -V)
1180 ac_init_version=: ;;
1181
1182 -with-* | --with-*)
1183 ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
1184 # Reject names that are not valid shell variable names.
1185 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
1186 as_fn_error $? "invalid package name: $ac_useropt"
1187 ac_useropt_orig=$ac_useropt
1188 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
1189 case $ac_user_opts in
1190 *"
1191 "with_$ac_useropt"
1192 "*) ;;
1193 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
1194 ac_unrecognized_sep=', ';;
1195 esac
1196 eval with_$ac_useropt=\$ac_optarg ;;
1197
1198 -without-* | --without-*)
1199 ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
1200 # Reject names that are not valid shell variable names.
1201 expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
1202 as_fn_error $? "invalid package name: $ac_useropt"
1203 ac_useropt_orig=$ac_useropt
1204 ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
1205 case $ac_user_opts in
1206 *"
1207 "with_$ac_useropt"
1208 "*) ;;
1209 *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
1210 ac_unrecognized_sep=', ';;
1211 esac
1212 eval with_$ac_useropt=no ;;
1213
1214 --x)
1215 # Obsolete; use --with-x.
1216 with_x=yes ;;
1217
1218 -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
1219 | --x-incl | --x-inc | --x-in | --x-i)
1220 ac_prev=x_includes ;;
1221 -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
1222 | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
1223 x_includes=$ac_optarg ;;
1224
1225 -x-libraries | --x-libraries | --x-librarie | --x-librari \
1226 | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
1227 ac_prev=x_libraries ;;
1228 -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
1229 | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
1230 x_libraries=$ac_optarg ;;
1231
1232 -*) as_fn_error $? "unrecognized option: \`$ac_option'
1233 Try \`$0 --help' for more information"
1234 ;;
1235
1236 *=*)
1237 ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
1238 # Reject names that are not valid shell variable names.
1239 case $ac_envvar in #(
1240 '' | [0-9]* | *[!_$as_cr_alnum]* )
1241 as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
1242 esac
1243 eval $ac_envvar=\$ac_optarg
1244 export $ac_envvar ;;
1245
1246 *)
1247 # FIXME: should be removed in autoconf 3.0.
1248 $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
1249 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
1250 $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
1251 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
1252 ;;
1253
1254 esac
1255 done
1256
1257 if test -n "$ac_prev"; then
1258 ac_option=--`echo $ac_prev | sed 's/_/-/g'`
1259 as_fn_error $? "missing argument to $ac_option"
1260 fi
1261
1262 if test -n "$ac_unrecognized_opts"; then
1263 case $enable_option_checking in
1264 no) ;;
1265 fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
1266 *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
1267 esac
1268 fi
1269
1270 # Check all directory arguments for consistency.
1271 for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
1272 datadir sysconfdir sharedstatedir localstatedir includedir \
1273 oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
1274 libdir localedir mandir runstatedir
1275 do
1276 eval ac_val=\$$ac_var
1277 # Remove trailing slashes.
1278 case $ac_val in
1279 */ )
1280 ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
1281 eval $ac_var=\$ac_val;;
1282 esac
1283 # Be sure to have absolute directory names.
1284 case $ac_val in
1285 [\\/$]* | ?:[\\/]* ) continue;;
1286 NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
1287 esac
1288 as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
1289 done
1290
1291 # There might be people who depend on the old broken behavior: `$host'
1292 # used to hold the argument of --host etc.
1293 # FIXME: To remove some day.
1294 build=$build_alias
1295 host=$host_alias
1296 target=$target_alias
1297
1298 # FIXME: To remove some day.
1299 if test "x$host_alias" != x; then
1300 if test "x$build_alias" = x; then
1301 cross_compiling=maybe
1302 elif test "x$build_alias" != "x$host_alias"; then
1303 cross_compiling=yes
1304 fi
1305 fi
1306
1307 ac_tool_prefix=
1308 test -n "$host_alias" && ac_tool_prefix=$host_alias-
1309
1310 test "$silent" = yes && exec 6>/dev/null
1311
1312
1313 ac_pwd=`pwd` && test -n "$ac_pwd" &&
1314 ac_ls_di=`ls -di .` &&
1315 ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
1316 as_fn_error $? "working directory cannot be determined"
1317 test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
1318 as_fn_error $? "pwd does not report name of working directory"
1319
1320
1321 # Find the source files, if location was not specified.
1322 if test -z "$srcdir"; then
1323 ac_srcdir_defaulted=yes
1324 # Try the directory containing this script, then the parent directory.
1325 ac_confdir=`$as_dirname -- "$as_myself" ||
1326 $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
1327 X"$as_myself" : 'X\(//\)[^/]' \| \
1328 X"$as_myself" : 'X\(//\)$' \| \
1329 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
1330 $as_echo X"$as_myself" |
1331 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
1332 s//\1/
1333 q
1334 }
1335 /^X\(\/\/\)[^/].*/{
1336 s//\1/
1337 q
1338 }
1339 /^X\(\/\/\)$/{
1340 s//\1/
1341 q
1342 }
1343 /^X\(\/\).*/{
1344 s//\1/
1345 q
1346 }
1347 s/.*/./; q'`
1348 srcdir=$ac_confdir
1349 if test ! -r "$srcdir/$ac_unique_file"; then
1350 srcdir=..
1351 fi
1352 else
1353 ac_srcdir_defaulted=no
1354 fi
1355 if test ! -r "$srcdir/$ac_unique_file"; then
1356 test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
1357 as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
1358 fi
1359 ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
1360 ac_abs_confdir=`(
1361 cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
1362 pwd)`
1363 # When building in place, set srcdir=.
1364 if test "$ac_abs_confdir" = "$ac_pwd"; then
1365 srcdir=.
1366 fi
1367 # Remove unnecessary trailing slashes from srcdir.
1368 # Double slashes in file names in object file debugging info
1369 # mess up M-x gdb in Emacs.
1370 case $srcdir in
1371 */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
1372 esac
1373 for ac_var in $ac_precious_vars; do
1374 eval ac_env_${ac_var}_set=\${${ac_var}+set}
1375 eval ac_env_${ac_var}_value=\$${ac_var}
1376 eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
1377 eval ac_cv_env_${ac_var}_value=\$${ac_var}
1378 done
1379
1380 #
1381 # Report the --help message.
1382 #
1383 if test "$ac_init_help" = "long"; then
1384 # Omit some internal or obsolete options to make the list less imposing.
1385 # This message is too long to be a string in the A/UX 3.1 sh.
1386 cat <<_ACEOF
1387 \`configure' configures i3 4.16.1 to adapt to many kinds of systems.
1388
1389 Usage: $0 [OPTION]... [VAR=VALUE]...
1390
1391 To assign environment variables (e.g., CC, CFLAGS...), specify them as
1392 VAR=VALUE. See below for descriptions of some of the useful variables.
1393
1394 Defaults for the options are specified in brackets.
1395
1396 Configuration:
1397 -h, --help display this help and exit
1398 --help=short display options specific to this package
1399 --help=recursive display the short help of all the included packages
1400 -V, --version display version information and exit
1401 -q, --quiet, --silent do not print \`checking ...' messages
1402 --cache-file=FILE cache test results in FILE [disabled]
1403 -C, --config-cache alias for \`--cache-file=config.cache'
1404 -n, --no-create do not create output files
1405 --srcdir=DIR find the sources in DIR [configure dir or \`..']
1406
1407 Installation directories:
1408 --prefix=PREFIX install architecture-independent files in PREFIX
1409 [$ac_default_prefix]
1410 --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
1411 [PREFIX]
1412
1413 By default, \`make install' will install all the files in
1414 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
1415 an installation prefix other than \`$ac_default_prefix' using \`--prefix',
1416 for instance \`--prefix=\$HOME'.
1417
1418 For better control, use the options below.
1419
1420 Fine tuning of the installation directories:
1421 --bindir=DIR user executables [EPREFIX/bin]
1422 --sbindir=DIR system admin executables [EPREFIX/sbin]
1423 --libexecdir=DIR program executables [EPREFIX/libexec]
1424 --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
1425 --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
1426 --localstatedir=DIR modifiable single-machine data [PREFIX/var]
1427 --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
1428 --libdir=DIR object code libraries [EPREFIX/lib]
1429 --includedir=DIR C header files [PREFIX/include]
1430 --oldincludedir=DIR C header files for non-gcc [/usr/include]
1431 --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
1432 --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
1433 --infodir=DIR info documentation [DATAROOTDIR/info]
1434 --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
1435 --mandir=DIR man documentation [DATAROOTDIR/man]
1436 --docdir=DIR documentation root [DATAROOTDIR/doc/i3]
1437 --htmldir=DIR html documentation [DOCDIR]
1438 --dvidir=DIR dvi documentation [DOCDIR]
1439 --pdfdir=DIR pdf documentation [DOCDIR]
1440 --psdir=DIR ps documentation [DOCDIR]
1441 _ACEOF
1442
1443 cat <<\_ACEOF
1444
1445 Program names:
1446 --program-prefix=PREFIX prepend PREFIX to installed program names
1447 --program-suffix=SUFFIX append SUFFIX to installed program names
1448 --program-transform-name=PROGRAM run sed PROGRAM on installed program names
1449
1450 System types:
1451 --build=BUILD configure for building on BUILD [guessed]
1452 --host=HOST cross-compile to build programs to run on HOST [BUILD]
1453 --target=TARGET configure for building compilers for TARGET [HOST]
1454 _ACEOF
1455 fi
1456
1457 if test -n "$ac_init_help"; then
1458 case $ac_init_help in
1459 short | recursive ) echo "Configuration of i3 4.16.1:";;
1460 esac
1461 cat <<\_ACEOF
1462
1463 Optional Features:
1464 --disable-option-checking ignore unrecognized --enable/--with options
1465 --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
1466 --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
1467 --disable-builddir disable automatic build in subdir of sources
1468
1469 --enable-silent-rules less verbose build output (undo: "make V=1")
1470 --disable-silent-rules verbose build output (undo: "make V=0")
1471 --disable-maintainer-mode
1472 disable make rules and dependencies not useful (and
1473 sometimes confusing) to the casual installer
1474 --enable-code-coverage Whether to enable code coverage support
1475 --enable-debug=[yes/info/profile/no]
1476 compile with debugging
1477 --enable-dependency-tracking
1478 do not reject slow dependency extractors
1479 --disable-dependency-tracking
1480 speeds up one-time build
1481 --disable-docs disable building documentation
1482 --disable-mans disable building manual pages
1483 --enable-sanitizers enable all known sanitizers
1484 --enable-address-sanitizer
1485 enable -fsanitize=address
1486 --enable-memory-sanitizer
1487 enable -fsanitize=memory
1488 --enable-undefined-sanitizer
1489 enable -fsanitize=undefined
1490
1491 Optional Packages:
1492 --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
1493 --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
1494 --with-gcov=GCOV use given GCOV for coverage (GCOV=gcov).
1495
1496 Some influential environment variables:
1497 CC C compiler command
1498 CFLAGS C compiler flags
1499 LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
1500 nonstandard directory <lib dir>
1501 LIBS libraries to pass to the linker, e.g. -l<library>
1502 CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
1503 you have headers in a nonstandard directory <include dir>
1504 CPP C preprocessor
1505 PKG_CONFIG path to pkg-config utility
1506 PKG_CONFIG_PATH
1507 directories to add to pkg-config's search path
1508 PKG_CONFIG_LIBDIR
1509 path overriding pkg-config's built-in search path
1510 LIBSN_CFLAGS
1511 C compiler flags for LIBSN, overriding pkg-config
1512 LIBSN_LIBS linker flags for LIBSN, overriding pkg-config
1513 XCB_CFLAGS C compiler flags for XCB, overriding pkg-config
1514 XCB_LIBS linker flags for XCB, overriding pkg-config
1515 XCB_UTIL_CFLAGS
1516 C compiler flags for XCB_UTIL, overriding pkg-config
1517 XCB_UTIL_LIBS
1518 linker flags for XCB_UTIL, overriding pkg-config
1519 XCB_UTIL_CURSOR_CFLAGS
1520 C compiler flags for XCB_UTIL_CURSOR, overriding pkg-config
1521 XCB_UTIL_CURSOR_LIBS
1522 linker flags for XCB_UTIL_CURSOR, overriding pkg-config
1523 XCB_UTIL_KEYSYMS_CFLAGS
1524 C compiler flags for XCB_UTIL_KEYSYMS, overriding pkg-config
1525 XCB_UTIL_KEYSYMS_LIBS
1526 linker flags for XCB_UTIL_KEYSYMS, overriding pkg-config
1527 XCB_UTIL_WM_CFLAGS
1528 C compiler flags for XCB_UTIL_WM, overriding pkg-config
1529 XCB_UTIL_WM_LIBS
1530 linker flags for XCB_UTIL_WM, overriding pkg-config
1531 XCB_UTIL_XRM_CFLAGS
1532 C compiler flags for XCB_UTIL_XRM, overriding pkg-config
1533 XCB_UTIL_XRM_LIBS
1534 linker flags for XCB_UTIL_XRM, overriding pkg-config
1535 XKBCOMMON_CFLAGS
1536 C compiler flags for XKBCOMMON, overriding pkg-config
1537 XKBCOMMON_LIBS
1538 linker flags for XKBCOMMON, overriding pkg-config
1539 YAJL_CFLAGS C compiler flags for YAJL, overriding pkg-config
1540 YAJL_LIBS linker flags for YAJL, overriding pkg-config
1541 LIBPCRE_CFLAGS
1542 C compiler flags for LIBPCRE, overriding pkg-config
1543 LIBPCRE_LIBS
1544 linker flags for LIBPCRE, overriding pkg-config
1545 PANGOCAIRO_CFLAGS
1546 C compiler flags for PANGOCAIRO, overriding pkg-config
1547 PANGOCAIRO_LIBS
1548 linker flags for PANGOCAIRO, overriding pkg-config
1549
1550 Use these variables to override the choices made by `configure' or to help
1551 it to find libraries and programs with nonstandard names/locations.
1552
1553 Report bugs to <https://github.com/i3/i3/issues>.
1554 _ACEOF
1555 ac_status=$?
1556 fi
1557
1558 if test "$ac_init_help" = "recursive"; then
1559 # If there are subdirs, report their specific --help.
1560 for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
1561 test -d "$ac_dir" ||
1562 { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
1563 continue
1564 ac_builddir=.
1565
1566 case "$ac_dir" in
1567 .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
1568 *)
1569 ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
1570 # A ".." for each directory in $ac_dir_suffix.
1571 ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
1572 case $ac_top_builddir_sub in
1573 "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
1574 *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
1575 esac ;;
1576 esac
1577 ac_abs_top_builddir=$ac_pwd
1578 ac_abs_builddir=$ac_pwd$ac_dir_suffix
1579 # for backward compatibility:
1580 ac_top_builddir=$ac_top_build_prefix
1581
1582 case $srcdir in
1583 .) # We are building in place.
1584 ac_srcdir=.
1585 ac_top_srcdir=$ac_top_builddir_sub
1586 ac_abs_top_srcdir=$ac_pwd ;;
1587 [\\/]* | ?:[\\/]* ) # Absolute name.
1588 ac_srcdir=$srcdir$ac_dir_suffix;
1589 ac_top_srcdir=$srcdir
1590 ac_abs_top_srcdir=$srcdir ;;
1591 *) # Relative name.
1592 ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
1593 ac_top_srcdir=$ac_top_build_prefix$srcdir
1594 ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
1595 esac
1596 ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
1597
1598 cd "$ac_dir" || { ac_status=$?; continue; }
1599 # Check for guested configure.
1600 if test -f "$ac_srcdir/configure.gnu"; then
1601 echo &&
1602 $SHELL "$ac_srcdir/configure.gnu" --help=recursive
1603 elif test -f "$ac_srcdir/configure"; then
1604 echo &&
1605 $SHELL "$ac_srcdir/configure" --help=recursive
1606 else
1607 $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
1608 fi || ac_status=$?
1609 cd "$ac_pwd" || { ac_status=$?; break; }
1610 done
1611 fi
1612
1613 test -n "$ac_init_help" && exit $ac_status
1614 if $ac_init_version; then
1615 cat <<\_ACEOF
1616 i3 configure 4.16.1
1617 generated by GNU Autoconf 2.69
1618
1619 Copyright (C) 2012 Free Software Foundation, Inc.
1620 This configure script is free software; the Free Software Foundation
1621 gives unlimited permission to copy, distribute and modify it.
1622 _ACEOF
1623 exit
1624 fi
1625
1626 ## ------------------------ ##
1627 ## Autoconf initialization. ##
1628 ## ------------------------ ##
1629
1630 # ac_fn_c_try_compile LINENO
1631 # --------------------------
1632 # Try to compile conftest.$ac_ext, and return whether this succeeded.
1633 ac_fn_c_try_compile ()
1634 {
1635 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1636 rm -f conftest.$ac_objext
1637 if { { ac_try="$ac_compile"
1638 case "(($ac_try" in
1639 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1640 *) ac_try_echo=$ac_try;;
1641 esac
1642 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1643 $as_echo "$ac_try_echo"; } >&5
1644 (eval "$ac_compile") 2>conftest.err
1645 ac_status=$?
1646 if test -s conftest.err; then
1647 grep -v '^ *+' conftest.err >conftest.er1
1648 cat conftest.er1 >&5
1649 mv -f conftest.er1 conftest.err
1650 fi
1651 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1652 test $ac_status = 0; } && {
1653 test -z "$ac_c_werror_flag" ||
1654 test ! -s conftest.err
1655 } && test -s conftest.$ac_objext; then :
1656 ac_retval=0
1657 else
1658 $as_echo "$as_me: failed program was:" >&5
1659 sed 's/^/| /' conftest.$ac_ext >&5
1660
1661 ac_retval=1
1662 fi
1663 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1664 as_fn_set_status $ac_retval
1665
1666 } # ac_fn_c_try_compile
1667
1668 # ac_fn_c_try_cpp LINENO
1669 # ----------------------
1670 # Try to preprocess conftest.$ac_ext, and return whether this succeeded.
1671 ac_fn_c_try_cpp ()
1672 {
1673 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1674 if { { ac_try="$ac_cpp conftest.$ac_ext"
1675 case "(($ac_try" in
1676 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1677 *) ac_try_echo=$ac_try;;
1678 esac
1679 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1680 $as_echo "$ac_try_echo"; } >&5
1681 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
1682 ac_status=$?
1683 if test -s conftest.err; then
1684 grep -v '^ *+' conftest.err >conftest.er1
1685 cat conftest.er1 >&5
1686 mv -f conftest.er1 conftest.err
1687 fi
1688 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1689 test $ac_status = 0; } > conftest.i && {
1690 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
1691 test ! -s conftest.err
1692 }; then :
1693 ac_retval=0
1694 else
1695 $as_echo "$as_me: failed program was:" >&5
1696 sed 's/^/| /' conftest.$ac_ext >&5
1697
1698 ac_retval=1
1699 fi
1700 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1701 as_fn_set_status $ac_retval
1702
1703 } # ac_fn_c_try_cpp
1704
1705 # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
1706 # -------------------------------------------------------
1707 # Tests whether HEADER exists, giving a warning if it cannot be compiled using
1708 # the include files in INCLUDES and setting the cache variable VAR
1709 # accordingly.
1710 ac_fn_c_check_header_mongrel ()
1711 {
1712 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1713 if eval \${$3+:} false; then :
1714 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
1715 $as_echo_n "checking for $2... " >&6; }
1716 if eval \${$3+:} false; then :
1717 $as_echo_n "(cached) " >&6
1718 fi
1719 eval ac_res=\$$3
1720 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
1721 $as_echo "$ac_res" >&6; }
1722 else
1723 # Is the header compilable?
1724 { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
1725 $as_echo_n "checking $2 usability... " >&6; }
1726 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1727 /* end confdefs.h. */
1728 $4
1729 #include <$2>
1730 _ACEOF
1731 if ac_fn_c_try_compile "$LINENO"; then :
1732 ac_header_compiler=yes
1733 else
1734 ac_header_compiler=no
1735 fi
1736 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
1737 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
1738 $as_echo "$ac_header_compiler" >&6; }
1739
1740 # Is the header present?
1741 { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
1742 $as_echo_n "checking $2 presence... " >&6; }
1743 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1744 /* end confdefs.h. */
1745 #include <$2>
1746 _ACEOF
1747 if ac_fn_c_try_cpp "$LINENO"; then :
1748 ac_header_preproc=yes
1749 else
1750 ac_header_preproc=no
1751 fi
1752 rm -f conftest.err conftest.i conftest.$ac_ext
1753 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
1754 $as_echo "$ac_header_preproc" >&6; }
1755
1756 # So? What about this header?
1757 case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
1758 yes:no: )
1759 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
1760 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
1761 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
1762 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
1763 ;;
1764 no:yes:* )
1765 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
1766 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
1767 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
1768 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
1769 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
1770 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
1771 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
1772 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
1773 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
1774 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
1775 ( $as_echo "## ---------------------------------------------- ##
1776 ## Report this to https://github.com/i3/i3/issues ##
1777 ## ---------------------------------------------- ##"
1778 ) | sed "s/^/$as_me: WARNING: /" >&2
1779 ;;
1780 esac
1781 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
1782 $as_echo_n "checking for $2... " >&6; }
1783 if eval \${$3+:} false; then :
1784 $as_echo_n "(cached) " >&6
1785 else
1786 eval "$3=\$ac_header_compiler"
1787 fi
1788 eval ac_res=\$$3
1789 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
1790 $as_echo "$ac_res" >&6; }
1791 fi
1792 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1793
1794 } # ac_fn_c_check_header_mongrel
1795
1796 # ac_fn_c_try_run LINENO
1797 # ----------------------
1798 # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
1799 # that executables *can* be run.
1800 ac_fn_c_try_run ()
1801 {
1802 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1803 if { { ac_try="$ac_link"
1804 case "(($ac_try" in
1805 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1806 *) ac_try_echo=$ac_try;;
1807 esac
1808 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1809 $as_echo "$ac_try_echo"; } >&5
1810 (eval "$ac_link") 2>&5
1811 ac_status=$?
1812 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1813 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
1814 { { case "(($ac_try" in
1815 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1816 *) ac_try_echo=$ac_try;;
1817 esac
1818 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1819 $as_echo "$ac_try_echo"; } >&5
1820 (eval "$ac_try") 2>&5
1821 ac_status=$?
1822 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1823 test $ac_status = 0; }; }; then :
1824 ac_retval=0
1825 else
1826 $as_echo "$as_me: program exited with status $ac_status" >&5
1827 $as_echo "$as_me: failed program was:" >&5
1828 sed 's/^/| /' conftest.$ac_ext >&5
1829
1830 ac_retval=$ac_status
1831 fi
1832 rm -rf conftest.dSYM conftest_ipa8_conftest.oo
1833 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1834 as_fn_set_status $ac_retval
1835
1836 } # ac_fn_c_try_run
1837
1838 # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
1839 # -------------------------------------------------------
1840 # Tests whether HEADER exists and can be compiled using the include files in
1841 # INCLUDES, setting the cache variable VAR accordingly.
1842 ac_fn_c_check_header_compile ()
1843 {
1844 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1845 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
1846 $as_echo_n "checking for $2... " >&6; }
1847 if eval \${$3+:} false; then :
1848 $as_echo_n "(cached) " >&6
1849 else
1850 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1851 /* end confdefs.h. */
1852 $4
1853 #include <$2>
1854 _ACEOF
1855 if ac_fn_c_try_compile "$LINENO"; then :
1856 eval "$3=yes"
1857 else
1858 eval "$3=no"
1859 fi
1860 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
1861 fi
1862 eval ac_res=\$$3
1863 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
1864 $as_echo "$ac_res" >&6; }
1865 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1866
1867 } # ac_fn_c_check_header_compile
1868
1869 # ac_fn_c_check_type LINENO TYPE VAR INCLUDES
1870 # -------------------------------------------
1871 # Tests whether TYPE exists after having included INCLUDES, setting cache
1872 # variable VAR accordingly.
1873 ac_fn_c_check_type ()
1874 {
1875 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1876 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
1877 $as_echo_n "checking for $2... " >&6; }
1878 if eval \${$3+:} false; then :
1879 $as_echo_n "(cached) " >&6
1880 else
1881 eval "$3=no"
1882 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1883 /* end confdefs.h. */
1884 $4
1885 int
1886 main ()
1887 {
1888 if (sizeof ($2))
1889 return 0;
1890 ;
1891 return 0;
1892 }
1893 _ACEOF
1894 if ac_fn_c_try_compile "$LINENO"; then :
1895 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1896 /* end confdefs.h. */
1897 $4
1898 int
1899 main ()
1900 {
1901 if (sizeof (($2)))
1902 return 0;
1903 ;
1904 return 0;
1905 }
1906 _ACEOF
1907 if ac_fn_c_try_compile "$LINENO"; then :
1908
1909 else
1910 eval "$3=yes"
1911 fi
1912 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
1913 fi
1914 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
1915 fi
1916 eval ac_res=\$$3
1917 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
1918 $as_echo "$ac_res" >&6; }
1919 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1920
1921 } # ac_fn_c_check_type
1922
1923 # ac_fn_c_try_link LINENO
1924 # -----------------------
1925 # Try to link conftest.$ac_ext, and return whether this succeeded.
1926 ac_fn_c_try_link ()
1927 {
1928 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1929 rm -f conftest.$ac_objext conftest$ac_exeext
1930 if { { ac_try="$ac_link"
1931 case "(($ac_try" in
1932 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
1933 *) ac_try_echo=$ac_try;;
1934 esac
1935 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
1936 $as_echo "$ac_try_echo"; } >&5
1937 (eval "$ac_link") 2>conftest.err
1938 ac_status=$?
1939 if test -s conftest.err; then
1940 grep -v '^ *+' conftest.err >conftest.er1
1941 cat conftest.er1 >&5
1942 mv -f conftest.er1 conftest.err
1943 fi
1944 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
1945 test $ac_status = 0; } && {
1946 test -z "$ac_c_werror_flag" ||
1947 test ! -s conftest.err
1948 } && test -s conftest$ac_exeext && {
1949 test "$cross_compiling" = yes ||
1950 test -x conftest$ac_exeext
1951 }; then :
1952 ac_retval=0
1953 else
1954 $as_echo "$as_me: failed program was:" >&5
1955 sed 's/^/| /' conftest.$ac_ext >&5
1956
1957 ac_retval=1
1958 fi
1959 # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
1960 # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
1961 # interfere with the next link command; also delete a directory that is
1962 # left behind by Apple's compiler. We do this before executing the actions.
1963 rm -rf conftest.dSYM conftest_ipa8_conftest.oo
1964 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
1965 as_fn_set_status $ac_retval
1966
1967 } # ac_fn_c_try_link
1968
1969 # ac_fn_c_check_func LINENO FUNC VAR
1970 # ----------------------------------
1971 # Tests whether FUNC exists, setting the cache variable VAR accordingly
1972 ac_fn_c_check_func ()
1973 {
1974 as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
1975 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
1976 $as_echo_n "checking for $2... " >&6; }
1977 if eval \${$3+:} false; then :
1978 $as_echo_n "(cached) " >&6
1979 else
1980 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
1981 /* end confdefs.h. */
1982 /* Define $2 to an innocuous variant, in case <limits.h> declares $2.
1983 For example, HP-UX 11i <limits.h> declares gettimeofday. */
1984 #define $2 innocuous_$2
1985
1986 /* System header to define __stub macros and hopefully few prototypes,
1987 which can conflict with char $2 (); below.
1988 Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
1989 <limits.h> exists even on freestanding compilers. */
1990
1991 #ifdef __STDC__
1992 # include <limits.h>
1993 #else
1994 # include <assert.h>
1995 #endif
1996
1997 #undef $2
1998
1999 /* Override any GCC internal prototype to avoid an error.
2000 Use char because int might match the return type of a GCC
2001 builtin and then its argument prototype would still apply. */
2002 #ifdef __cplusplus
2003 extern "C"
2004 #endif
2005 char $2 ();
2006 /* The GNU C library defines this for functions which it implements
2007 to always fail with ENOSYS. Some functions are actually named
2008 something starting with __ and the normal name is an alias. */
2009 #if defined __stub_$2 || defined __stub___$2
2010 choke me
2011 #endif
2012
2013 int
2014 main ()
2015 {
2016 return $2 ();
2017 ;
2018 return 0;
2019 }
2020 _ACEOF
2021 if ac_fn_c_try_link "$LINENO"; then :
2022 eval "$3=yes"
2023 else
2024 eval "$3=no"
2025 fi
2026 rm -f core conftest.err conftest.$ac_objext \
2027 conftest$ac_exeext conftest.$ac_ext
2028 fi
2029 eval ac_res=\$$3
2030 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
2031 $as_echo "$ac_res" >&6; }
2032 eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
2033
2034 } # ac_fn_c_check_func
2035 cat >config.log <<_ACEOF
2036 This file contains any messages produced by compilers while
2037 running configure, to aid debugging if configure makes a mistake.
2038
2039 It was created by i3 $as_me 4.16.1, which was
2040 generated by GNU Autoconf 2.69. Invocation command line was
2041
2042 $ $0 $@
2043
2044 _ACEOF
2045 exec 5>>config.log
2046 {
2047 cat <<_ASUNAME
2048 ## --------- ##
2049 ## Platform. ##
2050 ## --------- ##
2051
2052 hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
2053 uname -m = `(uname -m) 2>/dev/null || echo unknown`
2054 uname -r = `(uname -r) 2>/dev/null || echo unknown`
2055 uname -s = `(uname -s) 2>/dev/null || echo unknown`
2056 uname -v = `(uname -v) 2>/dev/null || echo unknown`
2057
2058 /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
2059 /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
2060
2061 /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
2062 /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
2063 /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
2064 /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
2065 /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
2066 /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
2067 /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
2068
2069 _ASUNAME
2070
2071 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2072 for as_dir in $PATH
2073 do
2074 IFS=$as_save_IFS
2075 test -z "$as_dir" && as_dir=.
2076 $as_echo "PATH: $as_dir"
2077 done
2078 IFS=$as_save_IFS
2079
2080 } >&5
2081
2082 cat >&5 <<_ACEOF
2083
2084
2085 ## ----------- ##
2086 ## Core tests. ##
2087 ## ----------- ##
2088
2089 _ACEOF
2090
2091
2092 # Keep a trace of the command line.
2093 # Strip out --no-create and --no-recursion so they do not pile up.
2094 # Strip out --silent because we don't want to record it for future runs.
2095 # Also quote any args containing shell meta-characters.
2096 # Make two passes to allow for proper duplicate-argument suppression.
2097 ac_configure_args=
2098 ac_configure_args0=
2099 ac_configure_args1=
2100 ac_must_keep_next=false
2101 for ac_pass in 1 2
2102 do
2103 for ac_arg
2104 do
2105 case $ac_arg in
2106 -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
2107 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
2108 | -silent | --silent | --silen | --sile | --sil)
2109 continue ;;
2110 *\'*)
2111 ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
2112 esac
2113 case $ac_pass in
2114 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
2115 2)
2116 as_fn_append ac_configure_args1 " '$ac_arg'"
2117 if test $ac_must_keep_next = true; then
2118 ac_must_keep_next=false # Got value, back to normal.
2119 else
2120 case $ac_arg in
2121 *=* | --config-cache | -C | -disable-* | --disable-* \
2122 | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
2123 | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
2124 | -with-* | --with-* | -without-* | --without-* | --x)
2125 case "$ac_configure_args0 " in
2126 "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
2127 esac
2128 ;;
2129 -* ) ac_must_keep_next=true ;;
2130 esac
2131 fi
2132 as_fn_append ac_configure_args " '$ac_arg'"
2133 ;;
2134 esac
2135 done
2136 done
2137 { ac_configure_args0=; unset ac_configure_args0;}
2138 { ac_configure_args1=; unset ac_configure_args1;}
2139
2140 # When interrupted or exit'd, cleanup temporary files, and complete
2141 # config.log. We remove comments because anyway the quotes in there
2142 # would cause problems or look ugly.
2143 # WARNING: Use '\'' to represent an apostrophe within the trap.
2144 # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
2145 trap 'exit_status=$?
2146 # Save into config.log some information that might help in debugging.
2147 {
2148 echo
2149
2150 $as_echo "## ---------------- ##
2151 ## Cache variables. ##
2152 ## ---------------- ##"
2153 echo
2154 # The following way of writing the cache mishandles newlines in values,
2155 (
2156 for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
2157 eval ac_val=\$$ac_var
2158 case $ac_val in #(
2159 *${as_nl}*)
2160 case $ac_var in #(
2161 *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
2162 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
2163 esac
2164 case $ac_var in #(
2165 _ | IFS | as_nl) ;; #(
2166 BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
2167 *) { eval $ac_var=; unset $ac_var;} ;;
2168 esac ;;
2169 esac
2170 done
2171 (set) 2>&1 |
2172 case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
2173 *${as_nl}ac_space=\ *)
2174 sed -n \
2175 "s/'\''/'\''\\\\'\'''\''/g;
2176 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
2177 ;; #(
2178 *)
2179 sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
2180 ;;
2181 esac |
2182 sort
2183 )
2184 echo
2185
2186 $as_echo "## ----------------- ##
2187 ## Output variables. ##
2188 ## ----------------- ##"
2189 echo
2190 for ac_var in $ac_subst_vars
2191 do
2192 eval ac_val=\$$ac_var
2193 case $ac_val in
2194 *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
2195 esac
2196 $as_echo "$ac_var='\''$ac_val'\''"
2197 done | sort
2198 echo
2199
2200 if test -n "$ac_subst_files"; then
2201 $as_echo "## ------------------- ##
2202 ## File substitutions. ##
2203 ## ------------------- ##"
2204 echo
2205 for ac_var in $ac_subst_files
2206 do
2207 eval ac_val=\$$ac_var
2208 case $ac_val in
2209 *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
2210 esac
2211 $as_echo "$ac_var='\''$ac_val'\''"
2212 done | sort
2213 echo
2214 fi
2215
2216 if test -s confdefs.h; then
2217 $as_echo "## ----------- ##
2218 ## confdefs.h. ##
2219 ## ----------- ##"
2220 echo
2221 cat confdefs.h
2222 echo
2223 fi
2224 test "$ac_signal" != 0 &&
2225 $as_echo "$as_me: caught signal $ac_signal"
2226 $as_echo "$as_me: exit $exit_status"
2227 } >&5
2228 rm -f core *.core core.conftest.* &&
2229 rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
2230 exit $exit_status
2231 ' 0
2232 for ac_signal in 1 2 13 15; do
2233 trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
2234 done
2235 ac_signal=0
2236
2237 # confdefs.h avoids OS command line length limits that DEFS can exceed.
2238 rm -f -r conftest* confdefs.h
2239
2240 $as_echo "/* confdefs.h */" > confdefs.h
2241
2242 # Predefined preprocessor variables.
2243
2244 cat >>confdefs.h <<_ACEOF
2245 #define PACKAGE_NAME "$PACKAGE_NAME"
2246 _ACEOF
2247
2248 cat >>confdefs.h <<_ACEOF
2249 #define PACKAGE_TARNAME "$PACKAGE_TARNAME"
2250 _ACEOF
2251
2252 cat >>confdefs.h <<_ACEOF
2253 #define PACKAGE_VERSION "$PACKAGE_VERSION"
2254 _ACEOF
2255
2256 cat >>confdefs.h <<_ACEOF
2257 #define PACKAGE_STRING "$PACKAGE_STRING"
2258 _ACEOF
2259
2260 cat >>confdefs.h <<_ACEOF
2261 #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
2262 _ACEOF
2263
2264 cat >>confdefs.h <<_ACEOF
2265 #define PACKAGE_URL "$PACKAGE_URL"
2266 _ACEOF
2267
2268
2269 # Let the site file select an alternate cache file if it wants to.
2270 # Prefer an explicitly selected file to automatically selected ones.
2271 ac_site_file1=NONE
2272 ac_site_file2=NONE
2273 if test -n "$CONFIG_SITE"; then
2274 # We do not want a PATH search for config.site.
2275 case $CONFIG_SITE in #((
2276 -*) ac_site_file1=./$CONFIG_SITE;;
2277 */*) ac_site_file1=$CONFIG_SITE;;
2278 *) ac_site_file1=./$CONFIG_SITE;;
2279 esac
2280 elif test "x$prefix" != xNONE; then
2281 ac_site_file1=$prefix/share/config.site
2282 ac_site_file2=$prefix/etc/config.site
2283 else
2284 ac_site_file1=$ac_default_prefix/share/config.site
2285 ac_site_file2=$ac_default_prefix/etc/config.site
2286 fi
2287 for ac_site_file in "$ac_site_file1" "$ac_site_file2"
2288 do
2289 test "x$ac_site_file" = xNONE && continue
2290 if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
2291 { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
2292 $as_echo "$as_me: loading site script $ac_site_file" >&6;}
2293 sed 's/^/| /' "$ac_site_file" >&5
2294 . "$ac_site_file" \
2295 || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
2296 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
2297 as_fn_error $? "failed to load site script $ac_site_file
2298 See \`config.log' for more details" "$LINENO" 5; }
2299 fi
2300 done
2301
2302 if test -r "$cache_file"; then
2303 # Some versions of bash will fail to source /dev/null (special files
2304 # actually), so we avoid doing that. DJGPP emulates it as a regular file.
2305 if test /dev/null != "$cache_file" && test -f "$cache_file"; then
2306 { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
2307 $as_echo "$as_me: loading cache $cache_file" >&6;}
2308 case $cache_file in
2309 [\\/]* | ?:[\\/]* ) . "$cache_file";;
2310 *) . "./$cache_file";;
2311 esac
2312 fi
2313 else
2314 { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
2315 $as_echo "$as_me: creating cache $cache_file" >&6;}
2316 >$cache_file
2317 fi
2318
2319 # Check that the precious variables saved in the cache have kept the same
2320 # value.
2321 ac_cache_corrupted=false
2322 for ac_var in $ac_precious_vars; do
2323 eval ac_old_set=\$ac_cv_env_${ac_var}_set
2324 eval ac_new_set=\$ac_env_${ac_var}_set
2325 eval ac_old_val=\$ac_cv_env_${ac_var}_value
2326 eval ac_new_val=\$ac_env_${ac_var}_value
2327 case $ac_old_set,$ac_new_set in
2328 set,)
2329 { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
2330 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
2331 ac_cache_corrupted=: ;;
2332 ,set)
2333 { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
2334 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
2335 ac_cache_corrupted=: ;;
2336 ,);;
2337 *)
2338 if test "x$ac_old_val" != "x$ac_new_val"; then
2339 # differences in whitespace do not lead to failure.
2340 ac_old_val_w=`echo x $ac_old_val`
2341 ac_new_val_w=`echo x $ac_new_val`
2342 if test "$ac_old_val_w" != "$ac_new_val_w"; then
2343 { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
2344 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
2345 ac_cache_corrupted=:
2346 else
2347 { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
2348 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
2349 eval $ac_var=\$ac_old_val
2350 fi
2351 { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
2352 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
2353 { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
2354 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
2355 fi;;
2356 esac
2357 # Pass precious variables to config.status.
2358 if test "$ac_new_set" = set; then
2359 case $ac_new_val in
2360 *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
2361 *) ac_arg=$ac_var=$ac_new_val ;;
2362 esac
2363 case " $ac_configure_args " in
2364 *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
2365 *) as_fn_append ac_configure_args " '$ac_arg'" ;;
2366 esac
2367 fi
2368 done
2369 if $ac_cache_corrupted; then
2370 { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
2371 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
2372 { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
2373 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
2374 as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
2375 fi
2376 ## -------------------- ##
2377 ## Main body of script. ##
2378 ## -------------------- ##
2379
2380 ac_ext=c
2381 ac_cpp='$CPP $CPPFLAGS'
2382 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
2383 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
2384 ac_compiler_gnu=$ac_cv_c_compiler_gnu
2385
2386
2387 # For AX_EXTEND_SRCDIR
2388 ac_aux_dir=
2389 for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
2390 if test -f "$ac_dir/install-sh"; then
2391 ac_aux_dir=$ac_dir
2392 ac_install_sh="$ac_aux_dir/install-sh -c"
2393 break
2394 elif test -f "$ac_dir/install.sh"; then
2395 ac_aux_dir=$ac_dir
2396 ac_install_sh="$ac_aux_dir/install.sh -c"
2397 break
2398 elif test -f "$ac_dir/shtool"; then
2399 ac_aux_dir=$ac_dir
2400 ac_install_sh="$ac_aux_dir/shtool install -c"
2401 break
2402 fi
2403 done
2404 if test -z "$ac_aux_dir"; then
2405 as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
2406 fi
2407
2408 # These three variables are undocumented and unsupported,
2409 # and are intended to be withdrawn in a future Autoconf release.
2410 # They can cause serious problems if a builder's source tree is in a directory
2411 # whose full name contains unusual characters.
2412 ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
2413 ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
2414 ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
2415
2416
2417 # Make sure we can run config.sub.
2418 $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
2419 as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
2420
2421 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
2422 $as_echo_n "checking build system type... " >&6; }
2423 if ${ac_cv_build+:} false; then :
2424 $as_echo_n "(cached) " >&6
2425 else
2426 ac_build_alias=$build_alias
2427 test "x$ac_build_alias" = x &&
2428 ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
2429 test "x$ac_build_alias" = x &&
2430 as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
2431 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
2432 as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
2433
2434 fi
2435 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
2436 $as_echo "$ac_cv_build" >&6; }
2437 case $ac_cv_build in
2438 *-*-*) ;;
2439 *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
2440 esac
2441 build=$ac_cv_build
2442 ac_save_IFS=$IFS; IFS='-'
2443 set x $ac_cv_build
2444 shift
2445 build_cpu=$1
2446 build_vendor=$2
2447 shift; shift
2448 # Remember, the first character of IFS is used to create $*,
2449 # except with old shells:
2450 build_os=$*
2451 IFS=$ac_save_IFS
2452 case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
2453
2454
2455 { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
2456 $as_echo_n "checking host system type... " >&6; }
2457 if ${ac_cv_host+:} false; then :
2458 $as_echo_n "(cached) " >&6
2459 else
2460 if test "x$host_alias" = x; then
2461 ac_cv_host=$ac_cv_build
2462 else
2463 ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
2464 as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
2465 fi
2466
2467 fi
2468 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
2469 $as_echo "$ac_cv_host" >&6; }
2470 case $ac_cv_host in
2471 *-*-*) ;;
2472 *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
2473 esac
2474 host=$ac_cv_host
2475 ac_save_IFS=$IFS; IFS='-'
2476 set x $ac_cv_host
2477 shift
2478 host_cpu=$1
2479 host_vendor=$2
2480 shift; shift
2481 # Remember, the first character of IFS is used to create $*,
2482 # except with old shells:
2483 host_os=$*
2484 IFS=$ac_save_IFS
2485 case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
2486
2487
2488 { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
2489 $as_echo_n "checking target system type... " >&6; }
2490 if ${ac_cv_target+:} false; then :
2491 $as_echo_n "(cached) " >&6
2492 else
2493 if test "x$target_alias" = x; then
2494 ac_cv_target=$ac_cv_host
2495 else
2496 ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` ||
2497 as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5
2498 fi
2499
2500 fi
2501 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5
2502 $as_echo "$ac_cv_target" >&6; }
2503 case $ac_cv_target in
2504 *-*-*) ;;
2505 *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;;
2506 esac
2507 target=$ac_cv_target
2508 ac_save_IFS=$IFS; IFS='-'
2509 set x $ac_cv_target
2510 shift
2511 target_cpu=$1
2512 target_vendor=$2
2513 shift; shift
2514 # Remember, the first character of IFS is used to create $*,
2515 # except with old shells:
2516 target_os=$*
2517 IFS=$ac_save_IFS
2518 case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
2519
2520
2521 # The aliases save the names the user supplied, while $host etc.
2522 # will get canonicalized.
2523 test -n "$target_alias" &&
2524 test "$program_prefix$program_suffix$program_transform_name" = \
2525 NONENONEs,x,x, &&
2526 program_prefix=${target_alias}-
2527
2528 # [$]@ is unsable in 2.60+ but earlier autoconf had no ac_configure_args
2529 if test "${ac_configure_args+set}" != "set" ; then
2530 ac_configure_args=
2531 for ac_arg in ${1+"$@"}; do
2532 ac_configure_args="$ac_configure_args '$ac_arg'"
2533 done
2534 fi
2535
2536 # Expand $ac_aux_dir to an absolute path.
2537 am_aux_dir=`cd "$ac_aux_dir" && pwd`
2538
2539
2540 ax_enable_builddir="."
2541 # Check whether --enable-builddir was given.
2542 if test "${enable_builddir+set}" = set; then :
2543 enableval=$enable_builddir; ax_enable_builddir="$enableval"
2544 else
2545 ax_enable_builddir="auto"
2546 fi
2547
2548 if test ".$ac_srcdir_defaulted" != ".no" ; then
2549 if test ".$srcdir" = ".." ; then
2550 if test -f config.status ; then
2551 { $as_echo "$as_me:${as_lineno-$LINENO}: toplevel srcdir already configured... skipping subdir build" >&5
2552 $as_echo "$as_me: toplevel srcdir already configured... skipping subdir build" >&6;}
2553 else
2554 test ".$ax_enable_builddir" = "." && ax_enable_builddir="."
2555 test ".$ax_enable_builddir" = ".no" && ax_enable_builddir="."
2556 test ".$TARGET" = "." && TARGET="$target"
2557 test ".$ax_enable_builddir" = ".auto" && ax_enable_builddir="$TARGET"
2558 if test ".$ax_enable_builddir" != ".." ; then # we know where to go and
2559 as_dir=$ax_enable_builddir; as_fn_mkdir_p
2560 echo __.$ax_enable_builddir.__ > $ax_enable_builddir/conftest.tmp
2561 cd $ax_enable_builddir
2562 if grep __.$ax_enable_builddir.__ conftest.tmp >/dev/null 2>/dev/null ; then
2563 rm conftest.tmp
2564 { $as_echo "$as_me:${as_lineno-$LINENO}: result: continue configure in default builddir \"./$ax_enable_builddir\"" >&5
2565 $as_echo "continue configure in default builddir \"./$ax_enable_builddir\"" >&6; }
2566 else
2567 as_fn_error $? "could not change to default builddir \"./$ax_enable_builddir\"" "$LINENO" 5
2568 fi
2569 srcdir=`echo "$ax_enable_builddir" |
2570 sed -e 's,^\./,,;s,[^/]$,&/,;s,[^/]*/,../,g;s,[/]$,,;'`
2571 # going to restart from subdirectory location
2572 test -f $srcdir/config.log && mv $srcdir/config.log .
2573 test -f $srcdir/confdefs.h && mv $srcdir/confdefs.h .
2574 test -f $srcdir/conftest.log && mv $srcdir/conftest.log .
2575 test -f $srcdir/$cache_file && mv $srcdir/$cache_file .
2576 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ....exec $SHELL $srcdir/$0 \"--srcdir=$srcdir\" \"--enable-builddir=$ax_enable_builddir\" ${1+\"$@\"}" >&5
2577 $as_echo "....exec $SHELL $srcdir/$0 \"--srcdir=$srcdir\" \"--enable-builddir=$ax_enable_builddir\" ${1+\"$@\"}" >&6; }
2578 case "$0" in # restart
2579 [\\/]* | ?:[\\/]*) # Asbolute name
2580 eval $SHELL "'$0'" "'--srcdir=$srcdir'" "'--enable-builddir=$ax_enable_builddir'" $ac_configure_args ;;
2581 *) eval $SHELL "'$srcdir/$0'" "'--srcdir=$srcdir'" "'--enable-builddir=$ax_enable_builddir'" $ac_configure_args ;;
2582 esac ; exit $?
2583 fi
2584 fi
2585 fi fi
2586 test ".$ax_enable_builddir" = ".auto" && ax_enable_builddir="."
2587 # Extract the first word of "gsed sed", so it can be a program name with args.
2588 set dummy gsed sed; ac_word=$2
2589 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2590 $as_echo_n "checking for $ac_word... " >&6; }
2591 if ${ac_cv_path_ax_enable_builddir_sed+:} false; then :
2592 $as_echo_n "(cached) " >&6
2593 else
2594 case $ax_enable_builddir_sed in
2595 [\\/]* | ?:[\\/]*)
2596 ac_cv_path_ax_enable_builddir_sed="$ax_enable_builddir_sed" # Let the user override the test with a path.
2597 ;;
2598 *)
2599 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2600 for as_dir in $PATH
2601 do
2602 IFS=$as_save_IFS
2603 test -z "$as_dir" && as_dir=.
2604 for ac_exec_ext in '' $ac_executable_extensions; do
2605 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2606 ac_cv_path_ax_enable_builddir_sed="$as_dir/$ac_word$ac_exec_ext"
2607 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2608 break 2
2609 fi
2610 done
2611 done
2612 IFS=$as_save_IFS
2613
2614 test -z "$ac_cv_path_ax_enable_builddir_sed" && ac_cv_path_ax_enable_builddir_sed="sed"
2615 ;;
2616 esac
2617 fi
2618 ax_enable_builddir_sed=$ac_cv_path_ax_enable_builddir_sed
2619 if test -n "$ax_enable_builddir_sed"; then
2620 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_enable_builddir_sed" >&5
2621 $as_echo "$ax_enable_builddir_sed" >&6; }
2622 else
2623 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2624 $as_echo "no" >&6; }
2625 fi
2626
2627
2628 ax_enable_builddir_auxdir="$am_aux_dir"
2629 ac_config_commands="$ac_config_commands buildir"
2630
2631 am__api_version='1.16'
2632
2633 # Find a good install program. We prefer a C program (faster),
2634 # so one script is as good as another. But avoid the broken or
2635 # incompatible versions:
2636 # SysV /etc/install, /usr/sbin/install
2637 # SunOS /usr/etc/install
2638 # IRIX /sbin/install
2639 # AIX /bin/install
2640 # AmigaOS /C/install, which installs bootblocks on floppy discs
2641 # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
2642 # AFS /usr/afsws/bin/install, which mishandles nonexistent args
2643 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
2644 # OS/2's system install, which has a completely different semantic
2645 # ./install, which can be erroneously created by make from ./install.sh.
2646 # Reject install programs that cannot install multiple files.
2647 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
2648 $as_echo_n "checking for a BSD-compatible install... " >&6; }
2649 if test -z "$INSTALL"; then
2650 if ${ac_cv_path_install+:} false; then :
2651 $as_echo_n "(cached) " >&6
2652 else
2653 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2654 for as_dir in $PATH
2655 do
2656 IFS=$as_save_IFS
2657 test -z "$as_dir" && as_dir=.
2658 # Account for people who put trailing slashes in PATH elements.
2659 case $as_dir/ in #((
2660 ./ | .// | /[cC]/* | \
2661 /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
2662 ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
2663 /usr/ucb/* ) ;;
2664 *)
2665 # OSF1 and SCO ODT 3.0 have their own names for install.
2666 # Don't use installbsd from OSF since it installs stuff as root
2667 # by default.
2668 for ac_prog in ginstall scoinst install; do
2669 for ac_exec_ext in '' $ac_executable_extensions; do
2670 if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
2671 if test $ac_prog = install &&
2672 grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
2673 # AIX install. It has an incompatible calling convention.
2674 :
2675 elif test $ac_prog = install &&
2676 grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
2677 # program-specific install script used by HP pwplus--don't use.
2678 :
2679 else
2680 rm -rf conftest.one conftest.two conftest.dir
2681 echo one > conftest.one
2682 echo two > conftest.two
2683 mkdir conftest.dir
2684 if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
2685 test -s conftest.one && test -s conftest.two &&
2686 test -s conftest.dir/conftest.one &&
2687 test -s conftest.dir/conftest.two
2688 then
2689 ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
2690 break 3
2691 fi
2692 fi
2693 fi
2694 done
2695 done
2696 ;;
2697 esac
2698
2699 done
2700 IFS=$as_save_IFS
2701
2702 rm -rf conftest.one conftest.two conftest.dir
2703
2704 fi
2705 if test "${ac_cv_path_install+set}" = set; then
2706 INSTALL=$ac_cv_path_install
2707 else
2708 # As a last resort, use the slow shell script. Don't cache a
2709 # value for INSTALL within a source directory, because that will
2710 # break other packages using the cache if that directory is
2711 # removed, or if the value is a relative name.
2712 INSTALL=$ac_install_sh
2713 fi
2714 fi
2715 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
2716 $as_echo "$INSTALL" >&6; }
2717
2718 # Use test -z because SunOS4 sh mishandles braces in ${var-val}.
2719 # It thinks the first close brace ends the variable substitution.
2720 test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
2721
2722 test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
2723
2724 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
2725
2726 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
2727 $as_echo_n "checking whether build environment is sane... " >&6; }
2728 # Reject unsafe characters in $srcdir or the absolute working directory
2729 # name. Accept space and tab only in the latter.
2730 am_lf='
2731 '
2732 case `pwd` in
2733 *[\\\"\#\$\&\'\`$am_lf]*)
2734 as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
2735 esac
2736 case $srcdir in
2737 *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
2738 as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
2739 esac
2740
2741 # Do 'set' in a subshell so we don't clobber the current shell's
2742 # arguments. Must try -L first in case configure is actually a
2743 # symlink; some systems play weird games with the mod time of symlinks
2744 # (eg FreeBSD returns the mod time of the symlink's containing
2745 # directory).
2746 if (
2747 am_has_slept=no
2748 for am_try in 1 2; do
2749 echo "timestamp, slept: $am_has_slept" > conftest.file
2750 set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
2751 if test "$*" = "X"; then
2752 # -L didn't work.
2753 set X `ls -t "$srcdir/configure" conftest.file`
2754 fi
2755 if test "$*" != "X $srcdir/configure conftest.file" \
2756 && test "$*" != "X conftest.file $srcdir/configure"; then
2757
2758 # If neither matched, then we have a broken ls. This can happen
2759 # if, for instance, CONFIG_SHELL is bash and it inherits a
2760 # broken ls alias from the environment. This has actually
2761 # happened. Such a system could not be considered "sane".
2762 as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
2763 alias in your environment" "$LINENO" 5
2764 fi
2765 if test "$2" = conftest.file || test $am_try -eq 2; then
2766 break
2767 fi
2768 # Just in case.
2769 sleep 1
2770 am_has_slept=yes
2771 done
2772 test "$2" = conftest.file
2773 )
2774 then
2775 # Ok.
2776 :
2777 else
2778 as_fn_error $? "newly created file is older than distributed files!
2779 Check your system clock" "$LINENO" 5
2780 fi
2781 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
2782 $as_echo "yes" >&6; }
2783 # If we didn't sleep, we still need to ensure time stamps of config.status and
2784 # generated files are strictly newer.
2785 am_sleep_pid=
2786 if grep 'slept: no' conftest.file >/dev/null 2>&1; then
2787 ( sleep 1 ) &
2788 am_sleep_pid=$!
2789 fi
2790
2791 rm -f conftest.file
2792
2793 test "$program_prefix" != NONE &&
2794 program_transform_name="s&^&$program_prefix&;$program_transform_name"
2795 # Use a double $ so make ignores it.
2796 test "$program_suffix" != NONE &&
2797 program_transform_name="s&\$&$program_suffix&;$program_transform_name"
2798 # Double any \ or $.
2799 # By default was `s,x,x', remove it if useless.
2800 ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
2801 program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
2802
2803 if test x"${MISSING+set}" != xset; then
2804 case $am_aux_dir in
2805 *\ * | *\ *)
2806 MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
2807 *)
2808 MISSING="\${SHELL} $am_aux_dir/missing" ;;
2809 esac
2810 fi
2811 # Use eval to expand $SHELL
2812 if eval "$MISSING --is-lightweight"; then
2813 am_missing_run="$MISSING "
2814 else
2815 am_missing_run=
2816 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
2817 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
2818 fi
2819
2820 if test x"${install_sh+set}" != xset; then
2821 case $am_aux_dir in
2822 *\ * | *\ *)
2823 install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
2824 *)
2825 install_sh="\${SHELL} $am_aux_dir/install-sh"
2826 esac
2827 fi
2828
2829 # Installed binaries are usually stripped using 'strip' when the user
2830 # run "make install-strip". However 'strip' might not be the right
2831 # tool to use in cross-compilation environments, therefore Automake
2832 # will honor the 'STRIP' environment variable to overrule this program.
2833 if test "$cross_compiling" != no; then
2834 if test -n "$ac_tool_prefix"; then
2835 # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
2836 set dummy ${ac_tool_prefix}strip; ac_word=$2
2837 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2838 $as_echo_n "checking for $ac_word... " >&6; }
2839 if ${ac_cv_prog_STRIP+:} false; then :
2840 $as_echo_n "(cached) " >&6
2841 else
2842 if test -n "$STRIP"; then
2843 ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
2844 else
2845 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2846 for as_dir in $PATH
2847 do
2848 IFS=$as_save_IFS
2849 test -z "$as_dir" && as_dir=.
2850 for ac_exec_ext in '' $ac_executable_extensions; do
2851 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2852 ac_cv_prog_STRIP="${ac_tool_prefix}strip"
2853 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2854 break 2
2855 fi
2856 done
2857 done
2858 IFS=$as_save_IFS
2859
2860 fi
2861 fi
2862 STRIP=$ac_cv_prog_STRIP
2863 if test -n "$STRIP"; then
2864 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
2865 $as_echo "$STRIP" >&6; }
2866 else
2867 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2868 $as_echo "no" >&6; }
2869 fi
2870
2871
2872 fi
2873 if test -z "$ac_cv_prog_STRIP"; then
2874 ac_ct_STRIP=$STRIP
2875 # Extract the first word of "strip", so it can be a program name with args.
2876 set dummy strip; ac_word=$2
2877 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2878 $as_echo_n "checking for $ac_word... " >&6; }
2879 if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
2880 $as_echo_n "(cached) " >&6
2881 else
2882 if test -n "$ac_ct_STRIP"; then
2883 ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
2884 else
2885 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2886 for as_dir in $PATH
2887 do
2888 IFS=$as_save_IFS
2889 test -z "$as_dir" && as_dir=.
2890 for ac_exec_ext in '' $ac_executable_extensions; do
2891 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2892 ac_cv_prog_ac_ct_STRIP="strip"
2893 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2894 break 2
2895 fi
2896 done
2897 done
2898 IFS=$as_save_IFS
2899
2900 fi
2901 fi
2902 ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
2903 if test -n "$ac_ct_STRIP"; then
2904 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
2905 $as_echo "$ac_ct_STRIP" >&6; }
2906 else
2907 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
2908 $as_echo "no" >&6; }
2909 fi
2910
2911 if test "x$ac_ct_STRIP" = x; then
2912 STRIP=":"
2913 else
2914 case $cross_compiling:$ac_tool_warned in
2915 yes:)
2916 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
2917 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
2918 ac_tool_warned=yes ;;
2919 esac
2920 STRIP=$ac_ct_STRIP
2921 fi
2922 else
2923 STRIP="$ac_cv_prog_STRIP"
2924 fi
2925
2926 fi
2927 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
2928
2929 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
2930 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
2931 if test -z "$MKDIR_P"; then
2932 if ${ac_cv_path_mkdir+:} false; then :
2933 $as_echo_n "(cached) " >&6
2934 else
2935 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2936 for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
2937 do
2938 IFS=$as_save_IFS
2939 test -z "$as_dir" && as_dir=.
2940 for ac_prog in mkdir gmkdir; do
2941 for ac_exec_ext in '' $ac_executable_extensions; do
2942 as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
2943 case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
2944 'mkdir (GNU coreutils) '* | \
2945 'mkdir (coreutils) '* | \
2946 'mkdir (fileutils) '4.1*)
2947 ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
2948 break 3;;
2949 esac
2950 done
2951 done
2952 done
2953 IFS=$as_save_IFS
2954
2955 fi
2956
2957 test -d ./--version && rmdir ./--version
2958 if test "${ac_cv_path_mkdir+set}" = set; then
2959 MKDIR_P="$ac_cv_path_mkdir -p"
2960 else
2961 # As a last resort, use the slow shell script. Don't cache a
2962 # value for MKDIR_P within a source directory, because that will
2963 # break other packages using the cache if that directory is
2964 # removed, or if the value is a relative name.
2965 MKDIR_P="$ac_install_sh -d"
2966 fi
2967 fi
2968 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
2969 $as_echo "$MKDIR_P" >&6; }
2970
2971 for ac_prog in gawk mawk nawk awk
2972 do
2973 # Extract the first word of "$ac_prog", so it can be a program name with args.
2974 set dummy $ac_prog; ac_word=$2
2975 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
2976 $as_echo_n "checking for $ac_word... " >&6; }
2977 if ${ac_cv_prog_AWK+:} false; then :
2978 $as_echo_n "(cached) " >&6
2979 else
2980 if test -n "$AWK"; then
2981 ac_cv_prog_AWK="$AWK" # Let the user override the test.
2982 else
2983 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
2984 for as_dir in $PATH
2985 do
2986 IFS=$as_save_IFS
2987 test -z "$as_dir" && as_dir=.
2988 for ac_exec_ext in '' $ac_executable_extensions; do
2989 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
2990 ac_cv_prog_AWK="$ac_prog"
2991 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
2992 break 2
2993 fi
2994 done
2995 done
2996 IFS=$as_save_IFS
2997
2998 fi
2999 fi
3000 AWK=$ac_cv_prog_AWK
3001 if test -n "$AWK"; then
3002 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
3003 $as_echo "$AWK" >&6; }
3004 else
3005 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3006 $as_echo "no" >&6; }
3007 fi
3008
3009
3010 test -n "$AWK" && break
3011 done
3012
3013 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
3014 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
3015 set x ${MAKE-make}
3016 ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
3017 if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
3018 $as_echo_n "(cached) " >&6
3019 else
3020 cat >conftest.make <<\_ACEOF
3021 SHELL = /bin/sh
3022 all:
3023 @echo '@@@%%%=$(MAKE)=@@@%%%'
3024 _ACEOF
3025 # GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
3026 case `${MAKE-make} -f conftest.make 2>/dev/null` in
3027 *@@@%%%=?*=@@@%%%*)
3028 eval ac_cv_prog_make_${ac_make}_set=yes;;
3029 *)
3030 eval ac_cv_prog_make_${ac_make}_set=no;;
3031 esac
3032 rm -f conftest.make
3033 fi
3034 if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
3035 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
3036 $as_echo "yes" >&6; }
3037 SET_MAKE=
3038 else
3039 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3040 $as_echo "no" >&6; }
3041 SET_MAKE="MAKE=${MAKE-make}"
3042 fi
3043
3044 rm -rf .tst 2>/dev/null
3045 mkdir .tst 2>/dev/null
3046 if test -d .tst; then
3047 am__leading_dot=.
3048 else
3049 am__leading_dot=_
3050 fi
3051 rmdir .tst 2>/dev/null
3052
3053 # Check whether --enable-silent-rules was given.
3054 if test "${enable_silent_rules+set}" = set; then :
3055 enableval=$enable_silent_rules;
3056 fi
3057
3058 case $enable_silent_rules in # (((
3059 yes) AM_DEFAULT_VERBOSITY=0;;
3060 no) AM_DEFAULT_VERBOSITY=1;;
3061 *) AM_DEFAULT_VERBOSITY=1;;
3062 esac
3063 am_make=${MAKE-make}
3064 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
3065 $as_echo_n "checking whether $am_make supports nested variables... " >&6; }
3066 if ${am_cv_make_support_nested_variables+:} false; then :
3067 $as_echo_n "(cached) " >&6
3068 else
3069 if $as_echo 'TRUE=$(BAR$(V))
3070 BAR0=false
3071 BAR1=true
3072 V=1
3073 am__doit:
3074 @$(TRUE)
3075 .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
3076 am_cv_make_support_nested_variables=yes
3077 else
3078 am_cv_make_support_nested_variables=no
3079 fi
3080 fi
3081 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
3082 $as_echo "$am_cv_make_support_nested_variables" >&6; }
3083 if test $am_cv_make_support_nested_variables = yes; then
3084 AM_V='$(V)'
3085 AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
3086 else
3087 AM_V=$AM_DEFAULT_VERBOSITY
3088 AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
3089 fi
3090 AM_BACKSLASH='\'
3091
3092 if test "`cd $srcdir && pwd`" != "`pwd`"; then
3093 # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
3094 # is not polluted with repeated "-I."
3095 am__isrc=' -I$(srcdir)'
3096 # test to see if srcdir already configured
3097 if test -f $srcdir/config.status; then
3098 as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
3099 fi
3100 fi
3101
3102 # test whether we have cygpath
3103 if test -z "$CYGPATH_W"; then
3104 if (cygpath --version) >/dev/null 2>/dev/null; then
3105 CYGPATH_W='cygpath -w'
3106 else
3107 CYGPATH_W=echo
3108 fi
3109 fi
3110
3111
3112 # Define the identity of the package.
3113 PACKAGE='i3'
3114 VERSION='4.16.1'
3115
3116
3117 cat >>confdefs.h <<_ACEOF
3118 #define PACKAGE "$PACKAGE"
3119 _ACEOF
3120
3121
3122 cat >>confdefs.h <<_ACEOF
3123 #define VERSION "$VERSION"
3124 _ACEOF
3125
3126 # Some tools Automake needs.
3127
3128 ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
3129
3130
3131 AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
3132
3133
3134 AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
3135
3136
3137 AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
3138
3139
3140 MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
3141
3142 # For better backward compatibility. To be removed once Automake 1.9.x
3143 # dies out for good. For more background, see:
3144 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
3145 # <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
3146 mkdir_p='$(MKDIR_P)'
3147
3148 # We need awk for the "check" target (and possibly the TAP driver). The
3149 # system "awk" is bad on some platforms.
3150 # Always define AMTAR for backward compatibility. Yes, it's still used
3151 # in the wild :-( We should find a proper way to deprecate it ...
3152 AMTAR='$${TAR-tar}'
3153
3154
3155 # We'll loop over all known methods to create a tar archive until one works.
3156 _am_tools='gnutar pax cpio none'
3157
3158 am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
3159
3160
3161
3162
3163
3164
3165 # POSIX will say in a future version that running "rm -f" with no argument
3166 # is OK; and we want to be able to make that assumption in our Makefile
3167 # recipes. So use an aggressive probe to check that the usage we want is
3168 # actually supported "in the wild" to an acceptable degree.
3169 # See automake bug#10828.
3170 # To make any issue more visible, cause the running configure to be aborted
3171 # by default if the 'rm' program in use doesn't match our expectations; the
3172 # user can still override this though.
3173 if rm -f && rm -fr && rm -rf; then : OK; else
3174 cat >&2 <<'END'
3175 Oops!
3176
3177 Your 'rm' program seems unable to run without file operands specified
3178 on the command line, even when the '-f' option is present. This is contrary
3179 to the behaviour of most rm programs out there, and not conforming with
3180 the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
3181
3182 Please tell bug-automake@gnu.org about your system, including the value
3183 of your $PATH and any error possibly output before this message. This
3184 can help us improve future automake versions.
3185
3186 END
3187 if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
3188 echo 'Configuration will proceed anyway, since you have set the' >&2
3189 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
3190 echo >&2
3191 else
3192 cat >&2 <<'END'
3193 Aborting the configuration process, to ensure you take notice of the issue.
3194
3195 You can download and install GNU coreutils to get an 'rm' implementation
3196 that behaves properly: <https://www.gnu.org/software/coreutils/>.
3197
3198 If you want to complete the configuration process using your problematic
3199 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
3200 to "yes", and re-run configure.
3201
3202 END
3203 as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
3204 fi
3205 fi
3206
3207 # Default to silent rules, use V=1 to get verbose compilation output.
3208 # Check whether --enable-silent-rules was given.
3209 if test "${enable_silent_rules+set}" = set; then :
3210 enableval=$enable_silent_rules;
3211 fi
3212
3213 case $enable_silent_rules in # (((
3214 yes) AM_DEFAULT_VERBOSITY=0;;
3215 no) AM_DEFAULT_VERBOSITY=1;;
3216 *) AM_DEFAULT_VERBOSITY=0;;
3217 esac
3218 am_make=${MAKE-make}
3219 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
3220 $as_echo_n "checking whether $am_make supports nested variables... " >&6; }
3221 if ${am_cv_make_support_nested_variables+:} false; then :
3222 $as_echo_n "(cached) " >&6
3223 else
3224 if $as_echo 'TRUE=$(BAR$(V))
3225 BAR0=false
3226 BAR1=true
3227 V=1
3228 am__doit:
3229 @$(TRUE)
3230 .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
3231 am_cv_make_support_nested_variables=yes
3232 else
3233 am_cv_make_support_nested_variables=no
3234 fi
3235 fi
3236 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
3237 $as_echo "$am_cv_make_support_nested_variables" >&6; }
3238 if test $am_cv_make_support_nested_variables = yes; then
3239 AM_V='$(V)'
3240 AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
3241 else
3242 AM_V=$AM_DEFAULT_VERBOSITY
3243 AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
3244 fi
3245 AM_BACKSLASH='\'
3246
3247 # Make it possible to disable maintainer mode to disable re-generation of build
3248 # system files.
3249
3250 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
3251 $as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
3252 # Check whether --enable-maintainer-mode was given.
3253 if test "${enable_maintainer_mode+set}" = set; then :
3254 enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
3255 else
3256 USE_MAINTAINER_MODE=yes
3257 fi
3258
3259 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
3260 $as_echo "$USE_MAINTAINER_MODE" >&6; }
3261 if test $USE_MAINTAINER_MODE = yes; then
3262 MAINTAINER_MODE_TRUE=
3263 MAINTAINER_MODE_FALSE='#'
3264 else
3265 MAINTAINER_MODE_TRUE='#'
3266 MAINTAINER_MODE_FALSE=
3267 fi
3268
3269 MAINT=$MAINTAINER_MODE_TRUE
3270
3271
3272
3273 ac_config_headers="$ac_config_headers config.h"
3274
3275
3276
3277
3278
3279 # Verify we are using GNU make because we use '%'-style pattern rules in
3280 # Makefile.am, which are a GNU make extension. Pull requests to replace
3281 # '%'-style pattern rules with a more portable alternative are welcome.
3282 for ac_prog in gawk mawk nawk awk
3283 do
3284 # Extract the first word of "$ac_prog", so it can be a program name with args.
3285 set dummy $ac_prog; ac_word=$2
3286 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3287 $as_echo_n "checking for $ac_word... " >&6; }
3288 if ${ac_cv_prog_AWK+:} false; then :
3289 $as_echo_n "(cached) " >&6
3290 else
3291 if test -n "$AWK"; then
3292 ac_cv_prog_AWK="$AWK" # Let the user override the test.
3293 else
3294 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3295 for as_dir in $PATH
3296 do
3297 IFS=$as_save_IFS
3298 test -z "$as_dir" && as_dir=.
3299 for ac_exec_ext in '' $ac_executable_extensions; do
3300 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3301 ac_cv_prog_AWK="$ac_prog"
3302 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3303 break 2
3304 fi
3305 done
3306 done
3307 IFS=$as_save_IFS
3308
3309 fi
3310 fi
3311 AWK=$ac_cv_prog_AWK
3312 if test -n "$AWK"; then
3313 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
3314 $as_echo "$AWK" >&6; }
3315 else
3316 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3317 $as_echo "no" >&6; }
3318 fi
3319
3320
3321 test -n "$AWK" && break
3322 done
3323
3324 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU make" >&5
3325 $as_echo_n "checking for GNU make... " >&6; }
3326 if ${_cv_gnu_make_command+:} false; then :
3327 $as_echo_n "(cached) " >&6
3328 else
3329 _cv_gnu_make_command="" ;
3330 for a in "$MAKE" make gmake gnumake ; do
3331 if test -z "$a" ; then continue ; fi ;
3332 if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then
3333 _cv_gnu_make_command=$a ;
3334 AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make")
3335 ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }')
3336 break ;
3337 fi
3338 done ;
3339 fi
3340 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_cv_gnu_make_command" >&5
3341 $as_echo "$_cv_gnu_make_command" >&6; }
3342 if test "x$_cv_gnu_make_command" = x""; then :
3343 ifGNUmake="#"
3344 else
3345 ifGNUmake=""
3346 fi
3347 if test "x$_cv_gnu_make_command" = x""; then :
3348 { ax_cv_gnu_make_command=; unset ax_cv_gnu_make_command;}
3349 else
3350 ax_cv_gnu_make_command=${_cv_gnu_make_command}
3351 fi
3352
3353
3354 if test "x$_cv_gnu_make_command" = x""; then :
3355 as_fn_error $? "the i3 Makefile.am requires GNU make" "$LINENO" 5
3356 fi
3357
3358 case $srcdir in #(
3359 .|.*|/*) :
3360
3361 # pwd -P is specified in IEEE 1003.1 from 2004
3362 as_dir=`cd "$srcdir" && pwd -P`
3363 as_base=`$as_basename -- $as_dir ||
3364 $as_expr X/$as_dir : '.*/\([^/][^/]*\)/*$' \| \
3365 X$as_dir : 'X\(//\)$' \| \
3366 X$as_dir : 'X\(/\)' \| . 2>/dev/null ||
3367 $as_echo X/$as_dir |
3368 sed '/^.*\/\([^/][^/]*\)\/*$/{
3369 s//\1/
3370 q
3371 }
3372 /^X\/\(\/\/\)$/{
3373 s//\1/
3374 q
3375 }
3376 /^X\/\(\/\).*/{
3377 s//\1/
3378 q
3379 }
3380 s/.*/./; q'`
3381 srcdir=${srcdir}/../${as_base}
3382
3383 AX_EXTEND_SRCDIR_CPPFLAGS="-DSTRIPPED__FILE__=\\\"\$\$(basename \$<)\\\""
3384
3385 ;; #(
3386 *) :
3387 ;;
3388 esac
3389
3390
3391 if test -d ${srcdir}/.git; then :
3392
3393 VERSION="$(git -C ${srcdir} describe --tags --abbrev=0)"
3394 I3_VERSION="$(git -C ${srcdir} describe --tags --always) ($(git -C ${srcdir} log --pretty=format:%cd --date=short -n1), branch \\\"$(git -C ${srcdir} describe --tags --always --all | sed s:heads/::)\\\")"
3395 # Mirrors what libi3/is_debug_build.c does:
3396 is_release=$(test $(echo "${I3_VERSION}" | cut -d '(' -f 1 | wc -m) -lt 10 && echo yes || echo no)
3397
3398 else
3399
3400 VERSION="$(cut -d '-' -f 1 ${srcdir}/I3_VERSION | cut -d ' ' -f 1)"
3401 I3_VERSION="$(sed -e 's/[\"?\\]/\\&/g' ${srcdir}/I3_VERSION)"
3402 is_release="$(grep -q non-git ${srcdir}/I3_VERSION && echo no || echo yes)"
3403
3404 fi
3405 I3_VERSION=$I3_VERSION
3406
3407 MAJOR_VERSION="$(echo ${VERSION} | cut -d '.' -f 1)"
3408 MINOR_VERSION="$(echo ${VERSION} | cut -d '.' -f 2)"
3409 PATCH_VERSION="$(echo ${VERSION} | cut -d '.' -f 3)"
3410 if test "x${PATCH_VERSION}" = x; then :
3411 PATCH_VERSION=0
3412 fi
3413
3414 cat >>confdefs.h <<_ACEOF
3415 #define I3_VERSION "${I3_VERSION}"
3416 _ACEOF
3417
3418
3419 cat >>confdefs.h <<_ACEOF
3420 #define MAJOR_VERSION ${MAJOR_VERSION}
3421 _ACEOF
3422
3423
3424 cat >>confdefs.h <<_ACEOF
3425 #define MINOR_VERSION ${MINOR_VERSION}
3426 _ACEOF
3427
3428
3429 cat >>confdefs.h <<_ACEOF
3430 #define PATCH_VERSION ${PATCH_VERSION}
3431 _ACEOF
3432
3433
3434 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
3435 $as_echo_n "checking for a sed that does not truncate output... " >&6; }
3436 if ${ac_cv_path_SED+:} false; then :
3437 $as_echo_n "(cached) " >&6
3438 else
3439 ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
3440 for ac_i in 1 2 3 4 5 6 7; do
3441 ac_script="$ac_script$as_nl$ac_script"
3442 done
3443 echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
3444 { ac_script=; unset ac_script;}
3445 if test -z "$SED"; then
3446 ac_path_SED_found=false
3447 # Loop through the user's path and test for each of PROGNAME-LIST
3448 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3449 for as_dir in $PATH
3450 do
3451 IFS=$as_save_IFS
3452 test -z "$as_dir" && as_dir=.
3453 for ac_prog in sed gsed; do
3454 for ac_exec_ext in '' $ac_executable_extensions; do
3455 ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
3456 as_fn_executable_p "$ac_path_SED" || continue
3457 # Check for GNU ac_path_SED and select it if it is found.
3458 # Check for GNU $ac_path_SED
3459 case `"$ac_path_SED" --version 2>&1` in
3460 *GNU*)
3461 ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
3462 *)
3463 ac_count=0
3464 $as_echo_n 0123456789 >"conftest.in"
3465 while :
3466 do
3467 cat "conftest.in" "conftest.in" >"conftest.tmp"
3468 mv "conftest.tmp" "conftest.in"
3469 cp "conftest.in" "conftest.nl"
3470 $as_echo '' >> "conftest.nl"
3471 "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
3472 diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
3473 as_fn_arith $ac_count + 1 && ac_count=$as_val
3474 if test $ac_count -gt ${ac_path_SED_max-0}; then
3475 # Best one so far, save it but keep looking for a better one
3476 ac_cv_path_SED="$ac_path_SED"
3477 ac_path_SED_max=$ac_count
3478 fi
3479 # 10*(2^10) chars as input seems more than enough
3480 test $ac_count -gt 10 && break
3481 done
3482 rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
3483 esac
3484
3485 $ac_path_SED_found && break 3
3486 done
3487 done
3488 done
3489 IFS=$as_save_IFS
3490 if test -z "$ac_cv_path_SED"; then
3491 as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
3492 fi
3493 else
3494 ac_cv_path_SED=$SED
3495 fi
3496
3497 fi
3498 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
3499 $as_echo "$ac_cv_path_SED" >&6; }
3500 SED="$ac_cv_path_SED"
3501 rm -f conftest.sed
3502
3503
3504
3505
3506 # allow to override gcov location
3507
3508 # Check whether --with-gcov was given.
3509 if test "${with_gcov+set}" = set; then :
3510 withval=$with_gcov; _AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov
3511 else
3512 _AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov
3513 fi
3514
3515
3516 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with code coverage support" >&5
3517 $as_echo_n "checking whether to build with code coverage support... " >&6; }
3518 # Check whether --enable-code-coverage was given.
3519 if test "${enable_code_coverage+set}" = set; then :
3520 enableval=$enable_code_coverage;
3521 else
3522 enable_code_coverage=no
3523 fi
3524
3525
3526 if test x$enable_code_coverage = xyes; then
3527 CODE_COVERAGE_ENABLED_TRUE=
3528 CODE_COVERAGE_ENABLED_FALSE='#'
3529 else
3530 CODE_COVERAGE_ENABLED_TRUE='#'
3531 CODE_COVERAGE_ENABLED_FALSE=
3532 fi
3533
3534 CODE_COVERAGE_ENABLED=$enable_code_coverage
3535
3536 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_code_coverage" >&5
3537 $as_echo "$enable_code_coverage" >&6; }
3538
3539 if test "$enable_code_coverage" = "yes" ; then :
3540
3541 # check for gcov
3542 if test -n "$ac_tool_prefix"; then
3543 # Extract the first word of "${ac_tool_prefix}$_AX_CODE_COVERAGE_GCOV_PROG_WITH", so it can be a program name with args.
3544 set dummy ${ac_tool_prefix}$_AX_CODE_COVERAGE_GCOV_PROG_WITH; ac_word=$2
3545 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3546 $as_echo_n "checking for $ac_word... " >&6; }
3547 if ${ac_cv_prog_GCOV+:} false; then :
3548 $as_echo_n "(cached) " >&6
3549 else
3550 if test -n "$GCOV"; then
3551 ac_cv_prog_GCOV="$GCOV" # Let the user override the test.
3552 else
3553 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3554 for as_dir in $PATH
3555 do
3556 IFS=$as_save_IFS
3557 test -z "$as_dir" && as_dir=.
3558 for ac_exec_ext in '' $ac_executable_extensions; do
3559 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3560 ac_cv_prog_GCOV="${ac_tool_prefix}$_AX_CODE_COVERAGE_GCOV_PROG_WITH"
3561 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3562 break 2
3563 fi
3564 done
3565 done
3566 IFS=$as_save_IFS
3567
3568 fi
3569 fi
3570 GCOV=$ac_cv_prog_GCOV
3571 if test -n "$GCOV"; then
3572 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GCOV" >&5
3573 $as_echo "$GCOV" >&6; }
3574 else
3575 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3576 $as_echo "no" >&6; }
3577 fi
3578
3579
3580 fi
3581 if test -z "$ac_cv_prog_GCOV"; then
3582 ac_ct_GCOV=$GCOV
3583 # Extract the first word of "$_AX_CODE_COVERAGE_GCOV_PROG_WITH", so it can be a program name with args.
3584 set dummy $_AX_CODE_COVERAGE_GCOV_PROG_WITH; ac_word=$2
3585 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3586 $as_echo_n "checking for $ac_word... " >&6; }
3587 if ${ac_cv_prog_ac_ct_GCOV+:} false; then :
3588 $as_echo_n "(cached) " >&6
3589 else
3590 if test -n "$ac_ct_GCOV"; then
3591 ac_cv_prog_ac_ct_GCOV="$ac_ct_GCOV" # Let the user override the test.
3592 else
3593 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3594 for as_dir in $PATH
3595 do
3596 IFS=$as_save_IFS
3597 test -z "$as_dir" && as_dir=.
3598 for ac_exec_ext in '' $ac_executable_extensions; do
3599 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3600 ac_cv_prog_ac_ct_GCOV="$_AX_CODE_COVERAGE_GCOV_PROG_WITH"
3601 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3602 break 2
3603 fi
3604 done
3605 done
3606 IFS=$as_save_IFS
3607
3608 fi
3609 fi
3610 ac_ct_GCOV=$ac_cv_prog_ac_ct_GCOV
3611 if test -n "$ac_ct_GCOV"; then
3612 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_GCOV" >&5
3613 $as_echo "$ac_ct_GCOV" >&6; }
3614 else
3615 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3616 $as_echo "no" >&6; }
3617 fi
3618
3619 if test "x$ac_ct_GCOV" = x; then
3620 GCOV=":"
3621 else
3622 case $cross_compiling:$ac_tool_warned in
3623 yes:)
3624 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
3625 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
3626 ac_tool_warned=yes ;;
3627 esac
3628 GCOV=$ac_ct_GCOV
3629 fi
3630 else
3631 GCOV="$ac_cv_prog_GCOV"
3632 fi
3633
3634 if test "X$GCOV" = "X:"; then :
3635 as_fn_error $? "gcov is needed to do coverage" "$LINENO" 5
3636 fi
3637
3638
3639 if test "$GCC" = "no" ; then :
3640
3641 as_fn_error $? "not compiling with gcc, which is required for gcov code coverage" "$LINENO" 5
3642
3643 fi
3644
3645 # List of supported lcov versions.
3646 lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11 1.12"
3647
3648 # Extract the first word of "lcov", so it can be a program name with args.
3649 set dummy lcov; ac_word=$2
3650 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3651 $as_echo_n "checking for $ac_word... " >&6; }
3652 if ${ac_cv_prog_LCOV+:} false; then :
3653 $as_echo_n "(cached) " >&6
3654 else
3655 if test -n "$LCOV"; then
3656 ac_cv_prog_LCOV="$LCOV" # Let the user override the test.
3657 else
3658 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3659 for as_dir in $PATH
3660 do
3661 IFS=$as_save_IFS
3662 test -z "$as_dir" && as_dir=.
3663 for ac_exec_ext in '' $ac_executable_extensions; do
3664 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3665 ac_cv_prog_LCOV="lcov"
3666 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3667 break 2
3668 fi
3669 done
3670 done
3671 IFS=$as_save_IFS
3672
3673 fi
3674 fi
3675 LCOV=$ac_cv_prog_LCOV
3676 if test -n "$LCOV"; then
3677 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LCOV" >&5
3678 $as_echo "$LCOV" >&6; }
3679 else
3680 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3681 $as_echo "no" >&6; }
3682 fi
3683
3684
3685 # Extract the first word of "genhtml", so it can be a program name with args.
3686 set dummy genhtml; ac_word=$2
3687 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3688 $as_echo_n "checking for $ac_word... " >&6; }
3689 if ${ac_cv_prog_GENHTML+:} false; then :
3690 $as_echo_n "(cached) " >&6
3691 else
3692 if test -n "$GENHTML"; then
3693 ac_cv_prog_GENHTML="$GENHTML" # Let the user override the test.
3694 else
3695 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3696 for as_dir in $PATH
3697 do
3698 IFS=$as_save_IFS
3699 test -z "$as_dir" && as_dir=.
3700 for ac_exec_ext in '' $ac_executable_extensions; do
3701 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3702 ac_cv_prog_GENHTML="genhtml"
3703 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3704 break 2
3705 fi
3706 done
3707 done
3708 IFS=$as_save_IFS
3709
3710 fi
3711 fi
3712 GENHTML=$ac_cv_prog_GENHTML
3713 if test -n "$GENHTML"; then
3714 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GENHTML" >&5
3715 $as_echo "$GENHTML" >&6; }
3716 else
3717 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3718 $as_echo "no" >&6; }
3719 fi
3720
3721
3722
3723 if test "$LCOV" ; then :
3724
3725 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcov version" >&5
3726 $as_echo_n "checking for lcov version... " >&6; }
3727 if ${ax_cv_lcov_version+:} false; then :
3728 $as_echo_n "(cached) " >&6
3729 else
3730
3731 ax_cv_lcov_version=invalid
3732 lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
3733 for lcov_check_version in $lcov_version_list; do
3734 if test "$lcov_version" = "$lcov_check_version"; then
3735 ax_cv_lcov_version="$lcov_check_version (ok)"
3736 fi
3737 done
3738
3739 fi
3740 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_lcov_version" >&5
3741 $as_echo "$ax_cv_lcov_version" >&6; }
3742
3743 else
3744
3745 lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
3746 as_fn_error $? "$lcov_msg" "$LINENO" 5
3747
3748 fi
3749
3750 case $ax_cv_lcov_version in
3751 ""|invalid)
3752 lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
3753 as_fn_error $? "$lcov_msg" "$LINENO" 5
3754 LCOV="exit 0;"
3755 ;;
3756 esac
3757
3758 if test -z "$GENHTML" ; then :
3759
3760 as_fn_error $? "Could not find genhtml from the lcov package" "$LINENO" 5
3761
3762 fi
3763
3764 CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
3765 CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
3766 CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
3767 CODE_COVERAGE_LDFLAGS="-lgcov"
3768
3769
3770
3771
3772
3773
3774 fi
3775
3776 CODE_COVERAGE_RULES='
3777 # Code coverage
3778 #
3779 # Optional:
3780 # - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
3781 # Multiple directories may be specified, separated by whitespace.
3782 # (Default: $(top_builddir))
3783 # - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
3784 # by lcov for code coverage. (Default:
3785 # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
3786 # - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
3787 # reports to be created. (Default:
3788 # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
3789 # - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
3790 # set to 0 to disable it and leave empty to stay with the default.
3791 # (Default: empty)
3792 # - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
3793 # instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
3794 # - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
3795 # instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
3796 # - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
3797 # - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
3798 # collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
3799 # - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
3800 # instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
3801 # - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
3802 # lcov instance. (Default: empty)
3803 # - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
3804 # instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
3805 # - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
3806 # genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
3807 # - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
3808 # instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
3809 # - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
3810 #
3811 # The generated report will be titled using the $(PACKAGE_NAME) and
3812 # $(PACKAGE_VERSION). In order to add the current git hash to the title,
3813 # use the git-version-gen script, available online.
3814
3815 # Optional variables
3816 CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
3817 CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
3818 CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
3819 CODE_COVERAGE_BRANCH_COVERAGE ?=
3820 CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
3821 --rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
3822 CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
3823 CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
3824 CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
3825 CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
3826 CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
3827 CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
3828 CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
3829 $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
3830 --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
3831 CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS)
3832 CODE_COVERAGE_IGNORE_PATTERN ?=
3833
3834 code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
3835 code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
3836 code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\
3837 $(CODE_COVERAGE_OUTPUT_FILE);
3838 code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
3839 code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
3840 code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\
3841 $(CODE_COVERAGE_IGNORE_PATTERN);
3842 code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
3843 code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
3844 code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
3845 code_coverage_quiet = $(code_coverage_quiet_$(V))
3846 code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
3847 code_coverage_quiet_0 = --quiet
3848
3849 # sanitizes the test-name: replaces with underscores: dashes and dots
3850 code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
3851
3852 # Use recursive makes in order to ignore errors during check
3853 check-code-coverage:
3854 ifeq ($(CODE_COVERAGE_ENABLED),yes)
3855 -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check
3856 $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
3857 else
3858 @echo "Need to reconfigure with --enable-code-coverage"
3859 endif
3860
3861 # Capture code coverage data
3862 code-coverage-capture: code-coverage-capture-hook
3863 ifeq ($(CODE_COVERAGE_ENABLED),yes)
3864 $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS)
3865 $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
3866 -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
3867 $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
3868 @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
3869 else
3870 @echo "Need to reconfigure with --enable-code-coverage"
3871 endif
3872
3873 # Hook rule executed before code-coverage-capture, overridable by the user
3874 code-coverage-capture-hook:
3875
3876 ifeq ($(CODE_COVERAGE_ENABLED),yes)
3877 clean: code-coverage-clean
3878 code-coverage-clean:
3879 -$(LCOV) --directory $(top_builddir) -z
3880 -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
3881 -find . -name "*.gcda" -o -name "*.gcov" -delete
3882 endif
3883
3884 GITIGNOREFILES ?=
3885 GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
3886
3887 A''M_DISTCHECK_CONFIGURE_FLAGS ?=
3888 A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
3889
3890 .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
3891 '
3892
3893
3894
3895
3896
3897
3898
3899 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable debugging" >&5
3900 $as_echo_n "checking whether to enable debugging... " >&6; }
3901
3902 ax_enable_debug_default=yes
3903 ax_enable_debug_is_release=$is_release
3904
3905 # If this is a release, override the default.
3906 if test "$ax_enable_debug_is_release" = "yes"; then :
3907 ax_enable_debug_default="no"
3908 fi
3909
3910
3911
3912
3913 # Check whether --enable-debug was given.
3914 if test "${enable_debug+set}" = set; then :
3915 enableval=$enable_debug;
3916 else
3917 enable_debug=$ax_enable_debug_default
3918 fi
3919
3920
3921 # empty mean debug yes
3922 if test "x$enable_debug" = "x"; then :
3923 enable_debug="yes"
3924 fi
3925
3926 # case of debug
3927 case $enable_debug in #(
3928 yes) :
3929
3930 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
3931 $as_echo "yes" >&6; }
3932 CFLAGS="${CFLAGS} -g -O0"
3933 CXXFLAGS="${CXXFLAGS} -g -O0"
3934 FFLAGS="${FFLAGS} -g -O0"
3935 FCFLAGS="${FCFLAGS} -g -O0"
3936 OBJCFLAGS="${OBJCFLAGS} -g -O0"
3937 ;; #(
3938 info) :
3939
3940 { $as_echo "$as_me:${as_lineno-$LINENO}: result: info" >&5
3941 $as_echo "info" >&6; }
3942 CFLAGS="${CFLAGS} -g"
3943 CXXFLAGS="${CXXFLAGS} -g"
3944 FFLAGS="${FFLAGS} -g"
3945 FCFLAGS="${FCFLAGS} -g"
3946 OBJCFLAGS="${OBJCFLAGS} -g"
3947 ;; #(
3948 profile) :
3949
3950 { $as_echo "$as_me:${as_lineno-$LINENO}: result: profile" >&5
3951 $as_echo "profile" >&6; }
3952 CFLAGS="${CFLAGS} -g -pg"
3953 CXXFLAGS="${CXXFLAGS} -g -pg"
3954 FFLAGS="${FFLAGS} -g -pg"
3955 FCFLAGS="${FCFLAGS} -g -pg"
3956 OBJCFLAGS="${OBJCFLAGS} -g -pg"
3957 LDFLAGS="${LDFLAGS} -pg"
3958 ;; #(
3959 *) :
3960
3961 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3962 $as_echo "no" >&6; }
3963 if test "x${CFLAGS+set}" != "xset"; then :
3964 CFLAGS=""
3965 fi
3966 if test "x${CXXFLAGS+set}" != "xset"; then :
3967 CXXFLAGS=""
3968 fi
3969 if test "x${FFLAGS+set}" != "xset"; then :
3970 FFLAGS=""
3971 fi
3972 if test "x${FCFLAGS+set}" != "xset"; then :
3973 FCFLAGS=""
3974 fi
3975 if test "x${OBJCFLAGS+set}" != "xset"; then :
3976 OBJCFLAGS=""
3977 fi
3978 ;;
3979 esac
3980
3981 if test "x$enable_debug" = "xyes"; then :
3982
3983 else
3984
3985 $as_echo "#define UNUSED_NDEBUG /**/" >>confdefs.h
3986
3987 fi
3988 ax_enable_debug=$enable_debug
3989
3990
3991 DEPDIR="${am__leading_dot}deps"
3992
3993 ac_config_commands="$ac_config_commands depfiles"
3994
3995 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
3996 $as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
3997 cat > confinc.mk << 'END'
3998 am__doit:
3999 @echo this is the am__doit target >confinc.out
4000 .PHONY: am__doit
4001 END
4002 am__include="#"
4003 am__quote=
4004 # BSD make does it like this.
4005 echo '.include "confinc.mk" # ignored' > confmf.BSD
4006 # Other make implementations (GNU, Solaris 10, AIX) do it like this.
4007 echo 'include confinc.mk # ignored' > confmf.GNU
4008 _am_result=no
4009 for s in GNU BSD; do
4010 { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
4011 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
4012 ac_status=$?
4013 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4014 (exit $ac_status); }
4015 case $?:`cat confinc.out 2>/dev/null` in #(
4016 '0:this is the am__doit target') :
4017 case $s in #(
4018 BSD) :
4019 am__include='.include' am__quote='"' ;; #(
4020 *) :
4021 am__include='include' am__quote='' ;;
4022 esac ;; #(
4023 *) :
4024 ;;
4025 esac
4026 if test "$am__include" != "#"; then
4027 _am_result="yes ($s style)"
4028 break
4029 fi
4030 done
4031 rm -f confinc.* confmf.*
4032 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
4033 $as_echo "${_am_result}" >&6; }
4034
4035 # Check whether --enable-dependency-tracking was given.
4036 if test "${enable_dependency_tracking+set}" = set; then :
4037 enableval=$enable_dependency_tracking;
4038 fi
4039
4040 if test "x$enable_dependency_tracking" != xno; then
4041 am_depcomp="$ac_aux_dir/depcomp"
4042 AMDEPBACKSLASH='\'
4043 am__nodep='_no'
4044 fi
4045 if test "x$enable_dependency_tracking" != xno; then
4046 AMDEP_TRUE=
4047 AMDEP_FALSE='#'
4048 else
4049 AMDEP_TRUE='#'
4050 AMDEP_FALSE=
4051 fi
4052
4053
4054 ac_ext=c
4055 ac_cpp='$CPP $CPPFLAGS'
4056 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4057 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4058 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4059 if test -n "$ac_tool_prefix"; then
4060 # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
4061 set dummy ${ac_tool_prefix}gcc; ac_word=$2
4062 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4063 $as_echo_n "checking for $ac_word... " >&6; }
4064 if ${ac_cv_prog_CC+:} false; then :
4065 $as_echo_n "(cached) " >&6
4066 else
4067 if test -n "$CC"; then
4068 ac_cv_prog_CC="$CC" # Let the user override the test.
4069 else
4070 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4071 for as_dir in $PATH
4072 do
4073 IFS=$as_save_IFS
4074 test -z "$as_dir" && as_dir=.
4075 for ac_exec_ext in '' $ac_executable_extensions; do
4076 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4077 ac_cv_prog_CC="${ac_tool_prefix}gcc"
4078 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4079 break 2
4080 fi
4081 done
4082 done
4083 IFS=$as_save_IFS
4084
4085 fi
4086 fi
4087 CC=$ac_cv_prog_CC
4088 if test -n "$CC"; then
4089 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
4090 $as_echo "$CC" >&6; }
4091 else
4092 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4093 $as_echo "no" >&6; }
4094 fi
4095
4096
4097 fi
4098 if test -z "$ac_cv_prog_CC"; then
4099 ac_ct_CC=$CC
4100 # Extract the first word of "gcc", so it can be a program name with args.
4101 set dummy gcc; ac_word=$2
4102 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4103 $as_echo_n "checking for $ac_word... " >&6; }
4104 if ${ac_cv_prog_ac_ct_CC+:} false; then :
4105 $as_echo_n "(cached) " >&6
4106 else
4107 if test -n "$ac_ct_CC"; then
4108 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
4109 else
4110 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4111 for as_dir in $PATH
4112 do
4113 IFS=$as_save_IFS
4114 test -z "$as_dir" && as_dir=.
4115 for ac_exec_ext in '' $ac_executable_extensions; do
4116 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4117 ac_cv_prog_ac_ct_CC="gcc"
4118 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4119 break 2
4120 fi
4121 done
4122 done
4123 IFS=$as_save_IFS
4124
4125 fi
4126 fi
4127 ac_ct_CC=$ac_cv_prog_ac_ct_CC
4128 if test -n "$ac_ct_CC"; then
4129 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
4130 $as_echo "$ac_ct_CC" >&6; }
4131 else
4132 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4133 $as_echo "no" >&6; }
4134 fi
4135
4136 if test "x$ac_ct_CC" = x; then
4137 CC=""
4138 else
4139 case $cross_compiling:$ac_tool_warned in
4140 yes:)
4141 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
4142 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
4143 ac_tool_warned=yes ;;
4144 esac
4145 CC=$ac_ct_CC
4146 fi
4147 else
4148 CC="$ac_cv_prog_CC"
4149 fi
4150
4151 if test -z "$CC"; then
4152 if test -n "$ac_tool_prefix"; then
4153 # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
4154 set dummy ${ac_tool_prefix}cc; ac_word=$2
4155 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4156 $as_echo_n "checking for $ac_word... " >&6; }
4157 if ${ac_cv_prog_CC+:} false; then :
4158 $as_echo_n "(cached) " >&6
4159 else
4160 if test -n "$CC"; then
4161 ac_cv_prog_CC="$CC" # Let the user override the test.
4162 else
4163 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4164 for as_dir in $PATH
4165 do
4166 IFS=$as_save_IFS
4167 test -z "$as_dir" && as_dir=.
4168 for ac_exec_ext in '' $ac_executable_extensions; do
4169 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4170 ac_cv_prog_CC="${ac_tool_prefix}cc"
4171 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4172 break 2
4173 fi
4174 done
4175 done
4176 IFS=$as_save_IFS
4177
4178 fi
4179 fi
4180 CC=$ac_cv_prog_CC
4181 if test -n "$CC"; then
4182 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
4183 $as_echo "$CC" >&6; }
4184 else
4185 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4186 $as_echo "no" >&6; }
4187 fi
4188
4189
4190 fi
4191 fi
4192 if test -z "$CC"; then
4193 # Extract the first word of "cc", so it can be a program name with args.
4194 set dummy cc; ac_word=$2
4195 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4196 $as_echo_n "checking for $ac_word... " >&6; }
4197 if ${ac_cv_prog_CC+:} false; then :
4198 $as_echo_n "(cached) " >&6
4199 else
4200 if test -n "$CC"; then
4201 ac_cv_prog_CC="$CC" # Let the user override the test.
4202 else
4203 ac_prog_rejected=no
4204 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4205 for as_dir in $PATH
4206 do
4207 IFS=$as_save_IFS
4208 test -z "$as_dir" && as_dir=.
4209 for ac_exec_ext in '' $ac_executable_extensions; do
4210 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4211 if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
4212 ac_prog_rejected=yes
4213 continue
4214 fi
4215 ac_cv_prog_CC="cc"
4216 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4217 break 2
4218 fi
4219 done
4220 done
4221 IFS=$as_save_IFS
4222
4223 if test $ac_prog_rejected = yes; then
4224 # We found a bogon in the path, so make sure we never use it.
4225 set dummy $ac_cv_prog_CC
4226 shift
4227 if test $# != 0; then
4228 # We chose a different compiler from the bogus one.
4229 # However, it has the same basename, so the bogon will be chosen
4230 # first if we set CC to just the basename; use the full file name.
4231 shift
4232 ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
4233 fi
4234 fi
4235 fi
4236 fi
4237 CC=$ac_cv_prog_CC
4238 if test -n "$CC"; then
4239 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
4240 $as_echo "$CC" >&6; }
4241 else
4242 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4243 $as_echo "no" >&6; }
4244 fi
4245
4246
4247 fi
4248 if test -z "$CC"; then
4249 if test -n "$ac_tool_prefix"; then
4250 for ac_prog in cl.exe
4251 do
4252 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
4253 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
4254 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4255 $as_echo_n "checking for $ac_word... " >&6; }
4256 if ${ac_cv_prog_CC+:} false; then :
4257 $as_echo_n "(cached) " >&6
4258 else
4259 if test -n "$CC"; then
4260 ac_cv_prog_CC="$CC" # Let the user override the test.
4261 else
4262 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4263 for as_dir in $PATH
4264 do
4265 IFS=$as_save_IFS
4266 test -z "$as_dir" && as_dir=.
4267 for ac_exec_ext in '' $ac_executable_extensions; do
4268 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4269 ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
4270 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4271 break 2
4272 fi
4273 done
4274 done
4275 IFS=$as_save_IFS
4276
4277 fi
4278 fi
4279 CC=$ac_cv_prog_CC
4280 if test -n "$CC"; then
4281 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
4282 $as_echo "$CC" >&6; }
4283 else
4284 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4285 $as_echo "no" >&6; }
4286 fi
4287
4288
4289 test -n "$CC" && break
4290 done
4291 fi
4292 if test -z "$CC"; then
4293 ac_ct_CC=$CC
4294 for ac_prog in cl.exe
4295 do
4296 # Extract the first word of "$ac_prog", so it can be a program name with args.
4297 set dummy $ac_prog; ac_word=$2
4298 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
4299 $as_echo_n "checking for $ac_word... " >&6; }
4300 if ${ac_cv_prog_ac_ct_CC+:} false; then :
4301 $as_echo_n "(cached) " >&6
4302 else
4303 if test -n "$ac_ct_CC"; then
4304 ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
4305 else
4306 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
4307 for as_dir in $PATH
4308 do
4309 IFS=$as_save_IFS
4310 test -z "$as_dir" && as_dir=.
4311 for ac_exec_ext in '' $ac_executable_extensions; do
4312 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
4313 ac_cv_prog_ac_ct_CC="$ac_prog"
4314 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
4315 break 2
4316 fi
4317 done
4318 done
4319 IFS=$as_save_IFS
4320
4321 fi
4322 fi
4323 ac_ct_CC=$ac_cv_prog_ac_ct_CC
4324 if test -n "$ac_ct_CC"; then
4325 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
4326 $as_echo "$ac_ct_CC" >&6; }
4327 else
4328 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4329 $as_echo "no" >&6; }
4330 fi
4331
4332
4333 test -n "$ac_ct_CC" && break
4334 done
4335
4336 if test "x$ac_ct_CC" = x; then
4337 CC=""
4338 else
4339 case $cross_compiling:$ac_tool_warned in
4340 yes:)
4341 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
4342 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
4343 ac_tool_warned=yes ;;
4344 esac
4345 CC=$ac_ct_CC
4346 fi
4347 fi
4348
4349 fi
4350
4351
4352 test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
4353 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
4354 as_fn_error $? "no acceptable C compiler found in \$PATH
4355 See \`config.log' for more details" "$LINENO" 5; }
4356
4357 # Provide some information about the compiler.
4358 $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
4359 set X $ac_compile
4360 ac_compiler=$2
4361 for ac_option in --version -v -V -qversion; do
4362 { { ac_try="$ac_compiler $ac_option >&5"
4363 case "(($ac_try" in
4364 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
4365 *) ac_try_echo=$ac_try;;
4366 esac
4367 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
4368 $as_echo "$ac_try_echo"; } >&5
4369 (eval "$ac_compiler $ac_option >&5") 2>conftest.err
4370 ac_status=$?
4371 if test -s conftest.err; then
4372 sed '10a\
4373 ... rest of stderr output deleted ...
4374 10q' conftest.err >conftest.er1
4375 cat conftest.er1 >&5
4376 fi
4377 rm -f conftest.er1 conftest.err
4378 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
4379 test $ac_status = 0; }
4380 done
4381
4382 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4383 /* end confdefs.h. */
4384
4385 int
4386 main ()
4387 {
4388
4389 ;
4390 return 0;
4391 }
4392 _ACEOF
4393 ac_clean_files_save=$ac_clean_files
4394 ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
4395 # Try to create an executable without -o first, disregard a.out.
4396 # It will help us diagnose broken compilers, and finding out an intuition
4397 # of exeext.
4398 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
4399 $as_echo_n "checking whether the C compiler works... " >&6; }
4400 ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
4401
4402 # The possible output files:
4403 ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
4404
4405 ac_rmfiles=
4406 for ac_file in $ac_files
4407 do
4408 case $ac_file in
4409 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
4410 * ) ac_rmfiles="$ac_rmfiles $ac_file";;
4411 esac
4412 done
4413 rm -f $ac_rmfiles
4414
4415 if { { ac_try="$ac_link_default"
4416 case "(($ac_try" in
4417 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
4418 *) ac_try_echo=$ac_try;;
4419 esac
4420 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
4421 $as_echo "$ac_try_echo"; } >&5
4422 (eval "$ac_link_default") 2>&5
4423 ac_status=$?
4424 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
4425 test $ac_status = 0; }; then :
4426 # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
4427 # So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
4428 # in a Makefile. We should not override ac_cv_exeext if it was cached,
4429 # so that the user can short-circuit this test for compilers unknown to
4430 # Autoconf.
4431 for ac_file in $ac_files ''
4432 do
4433 test -f "$ac_file" || continue
4434 case $ac_file in
4435 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
4436 ;;
4437 [ab].out )
4438 # We found the default executable, but exeext='' is most
4439 # certainly right.
4440 break;;
4441 *.* )
4442 if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
4443 then :; else
4444 ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
4445 fi
4446 # We set ac_cv_exeext here because the later test for it is not
4447 # safe: cross compilers may not add the suffix if given an `-o'
4448 # argument, so we may need to know it at that point already.
4449 # Even if this section looks crufty: it has the advantage of
4450 # actually working.
4451 break;;
4452 * )
4453 break;;
4454 esac
4455 done
4456 test "$ac_cv_exeext" = no && ac_cv_exeext=
4457
4458 else
4459 ac_file=''
4460 fi
4461 if test -z "$ac_file"; then :
4462 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
4463 $as_echo "no" >&6; }
4464 $as_echo "$as_me: failed program was:" >&5
4465 sed 's/^/| /' conftest.$ac_ext >&5
4466
4467 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
4468 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
4469 as_fn_error 77 "C compiler cannot create executables
4470 See \`config.log' for more details" "$LINENO" 5; }
4471 else
4472 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
4473 $as_echo "yes" >&6; }
4474 fi
4475 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
4476 $as_echo_n "checking for C compiler default output file name... " >&6; }
4477 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
4478 $as_echo "$ac_file" >&6; }
4479 ac_exeext=$ac_cv_exeext
4480
4481 rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
4482 ac_clean_files=$ac_clean_files_save
4483 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
4484 $as_echo_n "checking for suffix of executables... " >&6; }
4485 if { { ac_try="$ac_link"
4486 case "(($ac_try" in
4487 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
4488 *) ac_try_echo=$ac_try;;
4489 esac
4490 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
4491 $as_echo "$ac_try_echo"; } >&5
4492 (eval "$ac_link") 2>&5
4493 ac_status=$?
4494 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
4495 test $ac_status = 0; }; then :
4496 # If both `conftest.exe' and `conftest' are `present' (well, observable)
4497 # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
4498 # work properly (i.e., refer to `conftest.exe'), while it won't with
4499 # `rm'.
4500 for ac_file in conftest.exe conftest conftest.*; do
4501 test -f "$ac_file" || continue
4502 case $ac_file in
4503 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
4504 *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
4505 break;;
4506 * ) break;;
4507 esac
4508 done
4509 else
4510 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
4511 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
4512 as_fn_error $? "cannot compute suffix of executables: cannot compile and link
4513 See \`config.log' for more details" "$LINENO" 5; }
4514 fi
4515 rm -f conftest conftest$ac_cv_exeext
4516 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
4517 $as_echo "$ac_cv_exeext" >&6; }
4518
4519 rm -f conftest.$ac_ext
4520 EXEEXT=$ac_cv_exeext
4521 ac_exeext=$EXEEXT
4522 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4523 /* end confdefs.h. */
4524 #include <stdio.h>
4525 int
4526 main ()
4527 {
4528 FILE *f = fopen ("conftest.out", "w");
4529 return ferror (f) || fclose (f) != 0;
4530
4531 ;
4532 return 0;
4533 }
4534 _ACEOF
4535 ac_clean_files="$ac_clean_files conftest.out"
4536 # Check that the compiler produces executables we can run. If not, either
4537 # the compiler is broken, or we cross compile.
4538 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
4539 $as_echo_n "checking whether we are cross compiling... " >&6; }
4540 if test "$cross_compiling" != yes; then
4541 { { ac_try="$ac_link"
4542 case "(($ac_try" in
4543 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
4544 *) ac_try_echo=$ac_try;;
4545 esac
4546 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
4547 $as_echo "$ac_try_echo"; } >&5
4548 (eval "$ac_link") 2>&5
4549 ac_status=$?
4550 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
4551 test $ac_status = 0; }
4552 if { ac_try='./conftest$ac_cv_exeext'
4553 { { case "(($ac_try" in
4554 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
4555 *) ac_try_echo=$ac_try;;
4556 esac
4557 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
4558 $as_echo "$ac_try_echo"; } >&5
4559 (eval "$ac_try") 2>&5
4560 ac_status=$?
4561 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
4562 test $ac_status = 0; }; }; then
4563 cross_compiling=no
4564 else
4565 if test "$cross_compiling" = maybe; then
4566 cross_compiling=yes
4567 else
4568 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
4569 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
4570 as_fn_error $? "cannot run C compiled programs.
4571 If you meant to cross compile, use \`--host'.
4572 See \`config.log' for more details" "$LINENO" 5; }
4573 fi
4574 fi
4575 fi
4576 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
4577 $as_echo "$cross_compiling" >&6; }
4578
4579 rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
4580 ac_clean_files=$ac_clean_files_save
4581 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
4582 $as_echo_n "checking for suffix of object files... " >&6; }
4583 if ${ac_cv_objext+:} false; then :
4584 $as_echo_n "(cached) " >&6
4585 else
4586 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4587 /* end confdefs.h. */
4588
4589 int
4590 main ()
4591 {
4592
4593 ;
4594 return 0;
4595 }
4596 _ACEOF
4597 rm -f conftest.o conftest.obj
4598 if { { ac_try="$ac_compile"
4599 case "(($ac_try" in
4600 *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
4601 *) ac_try_echo=$ac_try;;
4602 esac
4603 eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
4604 $as_echo "$ac_try_echo"; } >&5
4605 (eval "$ac_compile") 2>&5
4606 ac_status=$?
4607 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
4608 test $ac_status = 0; }; then :
4609 for ac_file in conftest.o conftest.obj conftest.*; do
4610 test -f "$ac_file" || continue;
4611 case $ac_file in
4612 *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
4613 *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
4614 break;;
4615 esac
4616 done
4617 else
4618 $as_echo "$as_me: failed program was:" >&5
4619 sed 's/^/| /' conftest.$ac_ext >&5
4620
4621 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
4622 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
4623 as_fn_error $? "cannot compute suffix of object files: cannot compile
4624 See \`config.log' for more details" "$LINENO" 5; }
4625 fi
4626 rm -f conftest.$ac_cv_objext conftest.$ac_ext
4627 fi
4628 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
4629 $as_echo "$ac_cv_objext" >&6; }
4630 OBJEXT=$ac_cv_objext
4631 ac_objext=$OBJEXT
4632 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
4633 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
4634 if ${ac_cv_c_compiler_gnu+:} false; then :
4635 $as_echo_n "(cached) " >&6
4636 else
4637 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4638 /* end confdefs.h. */
4639
4640 int
4641 main ()
4642 {
4643 #ifndef __GNUC__
4644 choke me
4645 #endif
4646
4647 ;
4648 return 0;
4649 }
4650 _ACEOF
4651 if ac_fn_c_try_compile "$LINENO"; then :
4652 ac_compiler_gnu=yes
4653 else
4654 ac_compiler_gnu=no
4655 fi
4656 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
4657 ac_cv_c_compiler_gnu=$ac_compiler_gnu
4658
4659 fi
4660 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
4661 $as_echo "$ac_cv_c_compiler_gnu" >&6; }
4662 if test $ac_compiler_gnu = yes; then
4663 GCC=yes
4664 else
4665 GCC=
4666 fi
4667 ac_test_CFLAGS=${CFLAGS+set}
4668 ac_save_CFLAGS=$CFLAGS
4669 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
4670 $as_echo_n "checking whether $CC accepts -g... " >&6; }
4671 if ${ac_cv_prog_cc_g+:} false; then :
4672 $as_echo_n "(cached) " >&6
4673 else
4674 ac_save_c_werror_flag=$ac_c_werror_flag
4675 ac_c_werror_flag=yes
4676 ac_cv_prog_cc_g=no
4677 CFLAGS="-g"
4678 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4679 /* end confdefs.h. */
4680
4681 int
4682 main ()
4683 {
4684
4685 ;
4686 return 0;
4687 }
4688 _ACEOF
4689 if ac_fn_c_try_compile "$LINENO"; then :
4690 ac_cv_prog_cc_g=yes
4691 else
4692 CFLAGS=""
4693 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4694 /* end confdefs.h. */
4695
4696 int
4697 main ()
4698 {
4699
4700 ;
4701 return 0;
4702 }
4703 _ACEOF
4704 if ac_fn_c_try_compile "$LINENO"; then :
4705
4706 else
4707 ac_c_werror_flag=$ac_save_c_werror_flag
4708 CFLAGS="-g"
4709 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4710 /* end confdefs.h. */
4711
4712 int
4713 main ()
4714 {
4715
4716 ;
4717 return 0;
4718 }
4719 _ACEOF
4720 if ac_fn_c_try_compile "$LINENO"; then :
4721 ac_cv_prog_cc_g=yes
4722 fi
4723 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
4724 fi
4725 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
4726 fi
4727 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
4728 ac_c_werror_flag=$ac_save_c_werror_flag
4729 fi
4730 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
4731 $as_echo "$ac_cv_prog_cc_g" >&6; }
4732 if test "$ac_test_CFLAGS" = set; then
4733 CFLAGS=$ac_save_CFLAGS
4734 elif test $ac_cv_prog_cc_g = yes; then
4735 if test "$GCC" = yes; then
4736 CFLAGS="-g -O2"
4737 else
4738 CFLAGS="-g"
4739 fi
4740 else
4741 if test "$GCC" = yes; then
4742 CFLAGS="-O2"
4743 else
4744 CFLAGS=
4745 fi
4746 fi
4747 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
4748 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
4749 if ${ac_cv_prog_cc_c89+:} false; then :
4750 $as_echo_n "(cached) " >&6
4751 else
4752 ac_cv_prog_cc_c89=no
4753 ac_save_CC=$CC
4754 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4755 /* end confdefs.h. */
4756 #include <stdarg.h>
4757 #include <stdio.h>
4758 struct stat;
4759 /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
4760 struct buf { int x; };
4761 FILE * (*rcsopen) (struct buf *, struct stat *, int);
4762 static char *e (p, i)
4763 char **p;
4764 int i;
4765 {
4766 return p[i];
4767 }
4768 static char *f (char * (*g) (char **, int), char **p, ...)
4769 {
4770 char *s;
4771 va_list v;
4772 va_start (v,p);
4773 s = g (p, va_arg (v,int));
4774 va_end (v);
4775 return s;
4776 }
4777
4778 /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
4779 function prototypes and stuff, but not '\xHH' hex character constants.
4780 These don't provoke an error unfortunately, instead are silently treated
4781 as 'x'. The following induces an error, until -std is added to get
4782 proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
4783 array size at least. It's necessary to write '\x00'==0 to get something
4784 that's true only with -std. */
4785 int osf4_cc_array ['\x00' == 0 ? 1 : -1];
4786
4787 /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
4788 inside strings and character constants. */
4789 #define FOO(x) 'x'
4790 int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
4791
4792 int test (int i, double x);
4793 struct s1 {int (*f) (int a);};
4794 struct s2 {int (*f) (double a);};
4795 int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
4796 int argc;
4797 char **argv;
4798 int
4799 main ()
4800 {
4801 return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
4802 ;
4803 return 0;
4804 }
4805 _ACEOF
4806 for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
4807 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
4808 do
4809 CC="$ac_save_CC $ac_arg"
4810 if ac_fn_c_try_compile "$LINENO"; then :
4811 ac_cv_prog_cc_c89=$ac_arg
4812 fi
4813 rm -f core conftest.err conftest.$ac_objext
4814 test "x$ac_cv_prog_cc_c89" != "xno" && break
4815 done
4816 rm -f conftest.$ac_ext
4817 CC=$ac_save_CC
4818
4819 fi
4820 # AC_CACHE_VAL
4821 case "x$ac_cv_prog_cc_c89" in
4822 x)
4823 { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
4824 $as_echo "none needed" >&6; } ;;
4825 xno)
4826 { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
4827 $as_echo "unsupported" >&6; } ;;
4828 *)
4829 CC="$CC $ac_cv_prog_cc_c89"
4830 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
4831 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
4832 esac
4833 if test "x$ac_cv_prog_cc_c89" != xno; then :
4834
4835 fi
4836
4837 ac_ext=c
4838 ac_cpp='$CPP $CPPFLAGS'
4839 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4840 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4841 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4842
4843 ac_ext=c
4844 ac_cpp='$CPP $CPPFLAGS'
4845 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4846 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4847 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4848 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
4849 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
4850 if ${am_cv_prog_cc_c_o+:} false; then :
4851 $as_echo_n "(cached) " >&6
4852 else
4853 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
4854 /* end confdefs.h. */
4855
4856 int
4857 main ()
4858 {
4859
4860 ;
4861 return 0;
4862 }
4863 _ACEOF
4864 # Make sure it works both with $CC and with simple cc.
4865 # Following AC_PROG_CC_C_O, we do the test twice because some
4866 # compilers refuse to overwrite an existing .o file with -o,
4867 # though they will create one.
4868 am_cv_prog_cc_c_o=yes
4869 for am_i in 1 2; do
4870 if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
4871 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
4872 ac_status=$?
4873 echo "$as_me:$LINENO: \$? = $ac_status" >&5
4874 (exit $ac_status); } \
4875 && test -f conftest2.$ac_objext; then
4876 : OK
4877 else
4878 am_cv_prog_cc_c_o=no
4879 break
4880 fi
4881 done
4882 rm -f core conftest*
4883 unset am_i
4884 fi
4885 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
4886 $as_echo "$am_cv_prog_cc_c_o" >&6; }
4887 if test "$am_cv_prog_cc_c_o" != yes; then
4888 # Losing compiler, so override with the script.
4889 # FIXME: It is wrong to rewrite CC.
4890 # But if we don't then we get into trouble of one sort or another.
4891 # A longer-term fix would be to have automake use am__CC in this case,
4892 # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
4893 CC="$am_aux_dir/compile $CC"
4894 fi
4895 ac_ext=c
4896 ac_cpp='$CPP $CPPFLAGS'
4897 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
4898 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
4899 ac_compiler_gnu=$ac_cv_c_compiler_gnu
4900
4901
4902 depcc="$CC" am_compiler_list=
4903
4904 { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
4905 $as_echo_n "checking dependency style of $depcc... " >&6; }
4906 if ${am_cv_CC_dependencies_compiler_type+:} false; then :
4907 $as_echo_n "(cached) " >&6
4908 else
4909 if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
4910 # We make a subdir and do the tests there. Otherwise we can end up
4911 # making bogus files that we don't know about and never remove. For
4912 # instance it was reported that on HP-UX the gcc test will end up
4913 # making a dummy file named 'D' -- because '-MD' means "put the output
4914 # in D".
4915 rm -rf conftest.dir
4916 mkdir conftest.dir
4917 # Copy depcomp to subdir because otherwise we won't find it if we're
4918 # using a relative directory.
4919 cp "$am_depcomp" conftest.dir
4920 cd conftest.dir
4921 # We will build objects and dependencies in a subdirectory because
4922 # it helps to detect inapplicable dependency modes. For instance
4923 # both Tru64's cc and ICC support -MD to output dependencies as a
4924 # side effect of compilation, but ICC will put the dependencies in
4925 # the current directory while Tru64 will put them in the object
4926 # directory.
4927 mkdir sub
4928
4929 am_cv_CC_dependencies_compiler_type=none
4930 if test "$am_compiler_list" = ""; then
4931 am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
4932 fi
4933 am__universal=false
4934 case " $depcc " in #(
4935 *\ -arch\ *\ -arch\ *) am__universal=true ;;
4936 esac
4937
4938 for depmode in $am_compiler_list; do
4939 # Setup a source with many dependencies, because some compilers
4940 # like to wrap large dependency lists on column 80 (with \), and
4941 # we should not choose a depcomp mode which is confused by this.
4942 #
4943 # We need to recreate these files for each test, as the compiler may
4944 # overwrite some of them when testing with obscure command lines.
4945 # This happens at least with the AIX C compiler.
4946 : > sub/conftest.c
4947 for i in 1 2 3 4 5 6; do
4948 echo '#include "conftst'$i'.h"' >> sub/conftest.c
4949 # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
4950 # Solaris 10 /bin/sh.
4951 echo '/* dummy */' > sub/conftst$i.h
4952 done
4953 echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
4954
4955 # We check with '-c' and '-o' for the sake of the "dashmstdout"
4956 # mode. It turns out that the SunPro C++ compiler does not properly
4957 # handle '-M -o', and we need to detect this. Also, some Intel
4958 # versions had trouble with output in subdirs.
4959 am__obj=sub/conftest.${OBJEXT-o}
4960 am__minus_obj="-o $am__obj"
4961 case $depmode in
4962 gcc)
4963 # This depmode causes a compiler race in universal mode.
4964 test "$am__universal" = false || continue
4965 ;;
4966 nosideeffect)
4967 # After this tag, mechanisms are not by side-effect, so they'll
4968 # only be used when explicitly requested.
4969 if test "x$enable_dependency_tracking" = xyes; then
4970 continue
4971 else
4972 break
4973 fi
4974 ;;
4975 msvc7 | msvc7msys | msvisualcpp | msvcmsys)
4976 # This compiler won't grok '-c -o', but also, the minuso test has
4977 # not run yet. These depmodes are late enough in the game, and
4978 # so weak that their functioning should not be impacted.
4979 am__obj=conftest.${OBJEXT-o}
4980 am__minus_obj=
4981 ;;
4982 none) break ;;
4983 esac
4984 if depmode=$depmode \
4985 source=sub/conftest.c object=$am__obj \
4986 depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
4987 $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
4988 >/dev/null 2>conftest.err &&
4989 grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
4990 grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
4991 grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
4992 ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
4993 # icc doesn't choke on unknown options, it will just issue warnings
4994 # or remarks (even with -Werror). So we grep stderr for any message
4995 # that says an option was ignored or not supported.
4996 # When given -MP, icc 7.0 and 7.1 complain thusly:
4997 # icc: Command line warning: ignoring option '-M'; no argument required
4998 # The diagnosis changed in icc 8.0:
4999 # icc: Command line remark: option '-MP' not supported
5000 if (grep 'ignoring option' conftest.err ||
5001 grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
5002 am_cv_CC_dependencies_compiler_type=$depmode
5003 break
5004 fi
5005 fi
5006 done
5007
5008 cd ..
5009 rm -rf conftest.dir
5010 else
5011 am_cv_CC_dependencies_compiler_type=none
5012 fi
5013
5014 fi
5015 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
5016 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
5017 CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
5018
5019 if
5020 test "x$enable_dependency_tracking" != xno \
5021 && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
5022 am__fastdepCC_TRUE=
5023 am__fastdepCC_FALSE='#'
5024 else
5025 am__fastdepCC_TRUE='#'
5026 am__fastdepCC_FALSE=
5027 fi
5028
5029
5030 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
5031 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
5032 if ${ac_cv_prog_cc_c99+:} false; then :
5033 $as_echo_n "(cached) " >&6
5034 else
5035 ac_cv_prog_cc_c99=no
5036 ac_save_CC=$CC
5037 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5038 /* end confdefs.h. */
5039 #include <stdarg.h>
5040 #include <stdbool.h>
5041 #include <stdlib.h>
5042 #include <wchar.h>
5043 #include <stdio.h>
5044
5045 // Check varargs macros. These examples are taken from C99 6.10.3.5.
5046 #define debug(...) fprintf (stderr, __VA_ARGS__)
5047 #define showlist(...) puts (#__VA_ARGS__)
5048 #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
5049 static void
5050 test_varargs_macros (void)
5051 {
5052 int x = 1234;
5053 int y = 5678;
5054 debug ("Flag");
5055 debug ("X = %d\n", x);
5056 showlist (The first, second, and third items.);
5057 report (x>y, "x is %d but y is %d", x, y);
5058 }
5059
5060 // Check long long types.
5061 #define BIG64 18446744073709551615ull
5062 #define BIG32 4294967295ul
5063 #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
5064 #if !BIG_OK
5065 your preprocessor is broken;
5066 #endif
5067 #if BIG_OK
5068 #else
5069 your preprocessor is broken;
5070 #endif
5071 static long long int bignum = -9223372036854775807LL;
5072 static unsigned long long int ubignum = BIG64;
5073
5074 struct incomplete_array
5075 {
5076 int datasize;
5077 double data[];
5078 };
5079
5080 struct named_init {
5081 int number;
5082 const wchar_t *name;
5083 double average;
5084 };
5085
5086 typedef const char *ccp;
5087
5088 static inline int
5089 test_restrict (ccp restrict text)
5090 {
5091 // See if C++-style comments work.
5092 // Iterate through items via the restricted pointer.
5093 // Also check for declarations in for loops.
5094 for (unsigned int i = 0; *(text+i) != '\0'; ++i)
5095 continue;
5096 return 0;
5097 }
5098
5099 // Check varargs and va_copy.
5100 static void
5101 test_varargs (const char *format, ...)
5102 {
5103 va_list args;
5104 va_start (args, format);
5105 va_list args_copy;
5106 va_copy (args_copy, args);
5107
5108 const char *str;
5109 int number;
5110 float fnumber;
5111
5112 while (*format)
5113 {
5114 switch (*format++)
5115 {
5116 case 's': // string
5117 str = va_arg (args_copy, const char *);
5118 break;
5119 case 'd': // int
5120 number = va_arg (args_copy, int);
5121 break;
5122 case 'f': // float
5123 fnumber = va_arg (args_copy, double);
5124 break;
5125 default:
5126 break;
5127 }
5128 }
5129 va_end (args_copy);
5130 va_end (args);
5131 }
5132
5133 int
5134 main ()
5135 {
5136
5137 // Check bool.
5138 _Bool success = false;
5139
5140 // Check restrict.
5141 if (test_restrict ("String literal") == 0)
5142 success = true;
5143 char *restrict newvar = "Another string";
5144
5145 // Check varargs.
5146 test_varargs ("s, d' f .", "string", 65, 34.234);
5147 test_varargs_macros ();
5148
5149 // Check flexible array members.
5150 struct incomplete_array *ia =
5151 malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
5152 ia->datasize = 10;
5153 for (int i = 0; i < ia->datasize; ++i)
5154 ia->data[i] = i * 1.234;
5155
5156 // Check named initializers.
5157 struct named_init ni = {
5158 .number = 34,
5159 .name = L"Test wide string",
5160 .average = 543.34343,
5161 };
5162
5163 ni.number = 58;
5164
5165 int dynamic_array[ni.number];
5166 dynamic_array[ni.number - 1] = 543;
5167
5168 // work around unused variable warnings
5169 return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
5170 || dynamic_array[ni.number - 1] != 543);
5171
5172 ;
5173 return 0;
5174 }
5175 _ACEOF
5176 for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99
5177 do
5178 CC="$ac_save_CC $ac_arg"
5179 if ac_fn_c_try_compile "$LINENO"; then :
5180 ac_cv_prog_cc_c99=$ac_arg
5181 fi
5182 rm -f core conftest.err conftest.$ac_objext
5183 test "x$ac_cv_prog_cc_c99" != "xno" && break
5184 done
5185 rm -f conftest.$ac_ext
5186 CC=$ac_save_CC
5187
5188 fi
5189 # AC_CACHE_VAL
5190 case "x$ac_cv_prog_cc_c99" in
5191 x)
5192 { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
5193 $as_echo "none needed" >&6; } ;;
5194 xno)
5195 { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
5196 $as_echo "unsupported" >&6; } ;;
5197 *)
5198 CC="$CC $ac_cv_prog_cc_c99"
5199 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
5200 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
5201 esac
5202 if test "x$ac_cv_prog_cc_c99" != xno; then :
5203
5204 fi
5205
5206
5207
5208 # For strnlen() and vasprintf().
5209
5210 ac_ext=c
5211 ac_cpp='$CPP $CPPFLAGS'
5212 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
5213 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
5214 ac_compiler_gnu=$ac_cv_c_compiler_gnu
5215 { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
5216 $as_echo_n "checking how to run the C preprocessor... " >&6; }
5217 # On Suns, sometimes $CPP names a directory.
5218 if test -n "$CPP" && test -d "$CPP"; then
5219 CPP=
5220 fi
5221 if test -z "$CPP"; then
5222 if ${ac_cv_prog_CPP+:} false; then :
5223 $as_echo_n "(cached) " >&6
5224 else
5225 # Double quotes because CPP needs to be expanded
5226 for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
5227 do
5228 ac_preproc_ok=false
5229 for ac_c_preproc_warn_flag in '' yes
5230 do
5231 # Use a header file that comes with gcc, so configuring glibc
5232 # with a fresh cross-compiler works.
5233 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
5234 # <limits.h> exists even on freestanding compilers.
5235 # On the NeXT, cc -E runs the code through the compiler's parser,
5236 # not just through cpp. "Syntax error" is here to catch this case.
5237 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5238 /* end confdefs.h. */
5239 #ifdef __STDC__
5240 # include <limits.h>
5241 #else
5242 # include <assert.h>
5243 #endif
5244 Syntax error
5245 _ACEOF
5246 if ac_fn_c_try_cpp "$LINENO"; then :
5247
5248 else
5249 # Broken: fails on valid input.
5250 continue
5251 fi
5252 rm -f conftest.err conftest.i conftest.$ac_ext
5253
5254 # OK, works on sane cases. Now check whether nonexistent headers
5255 # can be detected and how.
5256 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5257 /* end confdefs.h. */
5258 #include <ac_nonexistent.h>
5259 _ACEOF
5260 if ac_fn_c_try_cpp "$LINENO"; then :
5261 # Broken: success on invalid input.
5262 continue
5263 else
5264 # Passes both tests.
5265 ac_preproc_ok=:
5266 break
5267 fi
5268 rm -f conftest.err conftest.i conftest.$ac_ext
5269
5270 done
5271 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
5272 rm -f conftest.i conftest.err conftest.$ac_ext
5273 if $ac_preproc_ok; then :
5274 break
5275 fi
5276
5277 done
5278 ac_cv_prog_CPP=$CPP
5279
5280 fi
5281 CPP=$ac_cv_prog_CPP
5282 else
5283 ac_cv_prog_CPP=$CPP
5284 fi
5285 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
5286 $as_echo "$CPP" >&6; }
5287 ac_preproc_ok=false
5288 for ac_c_preproc_warn_flag in '' yes
5289 do
5290 # Use a header file that comes with gcc, so configuring glibc
5291 # with a fresh cross-compiler works.
5292 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
5293 # <limits.h> exists even on freestanding compilers.
5294 # On the NeXT, cc -E runs the code through the compiler's parser,
5295 # not just through cpp. "Syntax error" is here to catch this case.
5296 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5297 /* end confdefs.h. */
5298 #ifdef __STDC__
5299 # include <limits.h>
5300 #else
5301 # include <assert.h>
5302 #endif
5303 Syntax error
5304 _ACEOF
5305 if ac_fn_c_try_cpp "$LINENO"; then :
5306
5307 else
5308 # Broken: fails on valid input.
5309 continue
5310 fi
5311 rm -f conftest.err conftest.i conftest.$ac_ext
5312
5313 # OK, works on sane cases. Now check whether nonexistent headers
5314 # can be detected and how.
5315 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5316 /* end confdefs.h. */
5317 #include <ac_nonexistent.h>
5318 _ACEOF
5319 if ac_fn_c_try_cpp "$LINENO"; then :
5320 # Broken: success on invalid input.
5321 continue
5322 else
5323 # Passes both tests.
5324 ac_preproc_ok=:
5325 break
5326 fi
5327 rm -f conftest.err conftest.i conftest.$ac_ext
5328
5329 done
5330 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
5331 rm -f conftest.i conftest.err conftest.$ac_ext
5332 if $ac_preproc_ok; then :
5333
5334 else
5335 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
5336 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
5337 as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
5338 See \`config.log' for more details" "$LINENO" 5; }
5339 fi
5340
5341 ac_ext=c
5342 ac_cpp='$CPP $CPPFLAGS'
5343 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
5344 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
5345 ac_compiler_gnu=$ac_cv_c_compiler_gnu
5346
5347
5348 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
5349 $as_echo_n "checking for grep that handles long lines and -e... " >&6; }
5350 if ${ac_cv_path_GREP+:} false; then :
5351 $as_echo_n "(cached) " >&6
5352 else
5353 if test -z "$GREP"; then
5354 ac_path_GREP_found=false
5355 # Loop through the user's path and test for each of PROGNAME-LIST
5356 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
5357 for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
5358 do
5359 IFS=$as_save_IFS
5360 test -z "$as_dir" && as_dir=.
5361 for ac_prog in grep ggrep; do
5362 for ac_exec_ext in '' $ac_executable_extensions; do
5363 ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
5364 as_fn_executable_p "$ac_path_GREP" || continue
5365 # Check for GNU ac_path_GREP and select it if it is found.
5366 # Check for GNU $ac_path_GREP
5367 case `"$ac_path_GREP" --version 2>&1` in
5368 *GNU*)
5369 ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
5370 *)
5371 ac_count=0
5372 $as_echo_n 0123456789 >"conftest.in"
5373 while :
5374 do
5375 cat "conftest.in" "conftest.in" >"conftest.tmp"
5376 mv "conftest.tmp" "conftest.in"
5377 cp "conftest.in" "conftest.nl"
5378 $as_echo 'GREP' >> "conftest.nl"
5379 "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
5380 diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
5381 as_fn_arith $ac_count + 1 && ac_count=$as_val
5382 if test $ac_count -gt ${ac_path_GREP_max-0}; then
5383 # Best one so far, save it but keep looking for a better one
5384 ac_cv_path_GREP="$ac_path_GREP"
5385 ac_path_GREP_max=$ac_count
5386 fi
5387 # 10*(2^10) chars as input seems more than enough
5388 test $ac_count -gt 10 && break
5389 done
5390 rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
5391 esac
5392
5393 $ac_path_GREP_found && break 3
5394 done
5395 done
5396 done
5397 IFS=$as_save_IFS
5398 if test -z "$ac_cv_path_GREP"; then
5399 as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
5400 fi
5401 else
5402 ac_cv_path_GREP=$GREP
5403 fi
5404
5405 fi
5406 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
5407 $as_echo "$ac_cv_path_GREP" >&6; }
5408 GREP="$ac_cv_path_GREP"
5409
5410
5411 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
5412 $as_echo_n "checking for egrep... " >&6; }
5413 if ${ac_cv_path_EGREP+:} false; then :
5414 $as_echo_n "(cached) " >&6
5415 else
5416 if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
5417 then ac_cv_path_EGREP="$GREP -E"
5418 else
5419 if test -z "$EGREP"; then
5420 ac_path_EGREP_found=false
5421 # Loop through the user's path and test for each of PROGNAME-LIST
5422 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
5423 for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
5424 do
5425 IFS=$as_save_IFS
5426 test -z "$as_dir" && as_dir=.
5427 for ac_prog in egrep; do
5428 for ac_exec_ext in '' $ac_executable_extensions; do
5429 ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
5430 as_fn_executable_p "$ac_path_EGREP" || continue
5431 # Check for GNU ac_path_EGREP and select it if it is found.
5432 # Check for GNU $ac_path_EGREP
5433 case `"$ac_path_EGREP" --version 2>&1` in
5434 *GNU*)
5435 ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
5436 *)
5437 ac_count=0
5438 $as_echo_n 0123456789 >"conftest.in"
5439 while :
5440 do
5441 cat "conftest.in" "conftest.in" >"conftest.tmp"
5442 mv "conftest.tmp" "conftest.in"
5443 cp "conftest.in" "conftest.nl"
5444 $as_echo 'EGREP' >> "conftest.nl"
5445 "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
5446 diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
5447 as_fn_arith $ac_count + 1 && ac_count=$as_val
5448 if test $ac_count -gt ${ac_path_EGREP_max-0}; then
5449 # Best one so far, save it but keep looking for a better one
5450 ac_cv_path_EGREP="$ac_path_EGREP"
5451 ac_path_EGREP_max=$ac_count
5452 fi
5453 # 10*(2^10) chars as input seems more than enough
5454 test $ac_count -gt 10 && break
5455 done
5456 rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
5457 esac
5458
5459 $ac_path_EGREP_found && break 3
5460 done
5461 done
5462 done
5463 IFS=$as_save_IFS
5464 if test -z "$ac_cv_path_EGREP"; then
5465 as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
5466 fi
5467 else
5468 ac_cv_path_EGREP=$EGREP
5469 fi
5470
5471 fi
5472 fi
5473 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
5474 $as_echo "$ac_cv_path_EGREP" >&6; }
5475 EGREP="$ac_cv_path_EGREP"
5476
5477
5478 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
5479 $as_echo_n "checking for ANSI C header files... " >&6; }
5480 if ${ac_cv_header_stdc+:} false; then :
5481 $as_echo_n "(cached) " >&6
5482 else
5483 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5484 /* end confdefs.h. */
5485 #include <stdlib.h>
5486 #include <stdarg.h>
5487 #include <string.h>
5488 #include <float.h>
5489
5490 int
5491 main ()
5492 {
5493
5494 ;
5495 return 0;
5496 }
5497 _ACEOF
5498 if ac_fn_c_try_compile "$LINENO"; then :
5499 ac_cv_header_stdc=yes
5500 else
5501 ac_cv_header_stdc=no
5502 fi
5503 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
5504
5505 if test $ac_cv_header_stdc = yes; then
5506 # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
5507 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5508 /* end confdefs.h. */
5509 #include <string.h>
5510
5511 _ACEOF
5512 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
5513 $EGREP "memchr" >/dev/null 2>&1; then :
5514
5515 else
5516 ac_cv_header_stdc=no
5517 fi
5518 rm -f conftest*
5519
5520 fi
5521
5522 if test $ac_cv_header_stdc = yes; then
5523 # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
5524 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5525 /* end confdefs.h. */
5526 #include <stdlib.h>
5527
5528 _ACEOF
5529 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
5530 $EGREP "free" >/dev/null 2>&1; then :
5531
5532 else
5533 ac_cv_header_stdc=no
5534 fi
5535 rm -f conftest*
5536
5537 fi
5538
5539 if test $ac_cv_header_stdc = yes; then
5540 # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
5541 if test "$cross_compiling" = yes; then :
5542 :
5543 else
5544 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5545 /* end confdefs.h. */
5546 #include <ctype.h>
5547 #include <stdlib.h>
5548 #if ((' ' & 0x0FF) == 0x020)
5549 # define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
5550 # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
5551 #else
5552 # define ISLOWER(c) \
5553 (('a' <= (c) && (c) <= 'i') \
5554 || ('j' <= (c) && (c) <= 'r') \
5555 || ('s' <= (c) && (c) <= 'z'))
5556 # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
5557 #endif
5558
5559 #define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
5560 int
5561 main ()
5562 {
5563 int i;
5564 for (i = 0; i < 256; i++)
5565 if (XOR (islower (i), ISLOWER (i))
5566 || toupper (i) != TOUPPER (i))
5567 return 2;
5568 return 0;
5569 }
5570 _ACEOF
5571 if ac_fn_c_try_run "$LINENO"; then :
5572
5573 else
5574 ac_cv_header_stdc=no
5575 fi
5576 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
5577 conftest.$ac_objext conftest.beam conftest.$ac_ext
5578 fi
5579
5580 fi
5581 fi
5582 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
5583 $as_echo "$ac_cv_header_stdc" >&6; }
5584 if test $ac_cv_header_stdc = yes; then
5585
5586 $as_echo "#define STDC_HEADERS 1" >>confdefs.h
5587
5588 fi
5589
5590 # On IRIX 5.3, sys/types and inttypes.h are conflicting.
5591 for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
5592 inttypes.h stdint.h unistd.h
5593 do :
5594 as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
5595 ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
5596 "
5597 if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
5598 cat >>confdefs.h <<_ACEOF
5599 #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
5600 _ACEOF
5601
5602 fi
5603
5604 done
5605
5606
5607
5608 ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
5609 if test "x$ac_cv_header_minix_config_h" = xyes; then :
5610 MINIX=yes
5611 else
5612 MINIX=
5613 fi
5614
5615
5616 if test "$MINIX" = yes; then
5617
5618 $as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
5619
5620
5621 $as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
5622
5623
5624 $as_echo "#define _MINIX 1" >>confdefs.h
5625
5626 fi
5627
5628
5629 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
5630 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
5631 if ${ac_cv_safe_to_define___extensions__+:} false; then :
5632 $as_echo_n "(cached) " >&6
5633 else
5634 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5635 /* end confdefs.h. */
5636
5637 # define __EXTENSIONS__ 1
5638 $ac_includes_default
5639 int
5640 main ()
5641 {
5642
5643 ;
5644 return 0;
5645 }
5646 _ACEOF
5647 if ac_fn_c_try_compile "$LINENO"; then :
5648 ac_cv_safe_to_define___extensions__=yes
5649 else
5650 ac_cv_safe_to_define___extensions__=no
5651 fi
5652 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
5653 fi
5654 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
5655 $as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
5656 test $ac_cv_safe_to_define___extensions__ = yes &&
5657 $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
5658
5659 $as_echo "#define _ALL_SOURCE 1" >>confdefs.h
5660
5661 $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
5662
5663 $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
5664
5665 $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
5666
5667
5668
5669 # Checks for typedefs, structures, and compiler characteristics.
5670 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
5671 $as_echo_n "checking for stdbool.h that conforms to C99... " >&6; }
5672 if ${ac_cv_header_stdbool_h+:} false; then :
5673 $as_echo_n "(cached) " >&6
5674 else
5675 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5676 /* end confdefs.h. */
5677
5678 #include <stdbool.h>
5679 #ifndef bool
5680 "error: bool is not defined"
5681 #endif
5682 #ifndef false
5683 "error: false is not defined"
5684 #endif
5685 #if false
5686 "error: false is not 0"
5687 #endif
5688 #ifndef true
5689 "error: true is not defined"
5690 #endif
5691 #if true != 1
5692 "error: true is not 1"
5693 #endif
5694 #ifndef __bool_true_false_are_defined
5695 "error: __bool_true_false_are_defined is not defined"
5696 #endif
5697
5698 struct s { _Bool s: 1; _Bool t; } s;
5699
5700 char a[true == 1 ? 1 : -1];
5701 char b[false == 0 ? 1 : -1];
5702 char c[__bool_true_false_are_defined == 1 ? 1 : -1];
5703 char d[(bool) 0.5 == true ? 1 : -1];
5704 /* See body of main program for 'e'. */
5705 char f[(_Bool) 0.0 == false ? 1 : -1];
5706 char g[true];
5707 char h[sizeof (_Bool)];
5708 char i[sizeof s.t];
5709 enum { j = false, k = true, l = false * true, m = true * 256 };
5710 /* The following fails for
5711 HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
5712 _Bool n[m];
5713 char o[sizeof n == m * sizeof n[0] ? 1 : -1];
5714 char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
5715 /* Catch a bug in an HP-UX C compiler. See
5716 http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
5717 http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
5718 */
5719 _Bool q = true;
5720 _Bool *pq = &q;
5721
5722 int
5723 main ()
5724 {
5725
5726 bool e = &s;
5727 *pq |= q;
5728 *pq |= ! q;
5729 /* Refer to every declared value, to avoid compiler optimizations. */
5730 return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
5731 + !m + !n + !o + !p + !q + !pq);
5732
5733 ;
5734 return 0;
5735 }
5736 _ACEOF
5737 if ac_fn_c_try_compile "$LINENO"; then :
5738 ac_cv_header_stdbool_h=yes
5739 else
5740 ac_cv_header_stdbool_h=no
5741 fi
5742 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
5743 fi
5744 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
5745 $as_echo "$ac_cv_header_stdbool_h" >&6; }
5746 ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
5747 if test "x$ac_cv_type__Bool" = xyes; then :
5748
5749 cat >>confdefs.h <<_ACEOF
5750 #define HAVE__BOOL 1
5751 _ACEOF
5752
5753
5754 fi
5755
5756
5757 ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
5758 if test "x$ac_cv_type_mode_t" = xyes; then :
5759
5760 cat >>confdefs.h <<_ACEOF
5761 #define HAVE_MODE_T 1
5762 _ACEOF
5763
5764
5765 else
5766 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
5767 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
5768 as_fn_error $? "cannot find required type
5769 See \`config.log' for more details" "$LINENO" 5; }
5770 fi
5771 ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
5772 if test "x$ac_cv_type_off_t" = xyes; then :
5773
5774 cat >>confdefs.h <<_ACEOF
5775 #define HAVE_OFF_T 1
5776 _ACEOF
5777
5778
5779 else
5780 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
5781 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
5782 as_fn_error $? "cannot find required type
5783 See \`config.log' for more details" "$LINENO" 5; }
5784 fi
5785 ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
5786 if test "x$ac_cv_type_pid_t" = xyes; then :
5787
5788 cat >>confdefs.h <<_ACEOF
5789 #define HAVE_PID_T 1
5790 _ACEOF
5791
5792
5793 else
5794 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
5795 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
5796 as_fn_error $? "cannot find required type
5797 See \`config.log' for more details" "$LINENO" 5; }
5798 fi
5799 ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
5800 if test "x$ac_cv_type_size_t" = xyes; then :
5801
5802 cat >>confdefs.h <<_ACEOF
5803 #define HAVE_SIZE_T 1
5804 _ACEOF
5805
5806
5807 else
5808 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
5809 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
5810 as_fn_error $? "cannot find required type
5811 See \`config.log' for more details" "$LINENO" 5; }
5812 fi
5813 ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default"
5814 if test "x$ac_cv_type_ssize_t" = xyes; then :
5815
5816 cat >>confdefs.h <<_ACEOF
5817 #define HAVE_SSIZE_T 1
5818 _ACEOF
5819
5820
5821 else
5822 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
5823 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
5824 as_fn_error $? "cannot find required type
5825 See \`config.log' for more details" "$LINENO" 5; }
5826 fi
5827
5828
5829 # Checks for library functions.
5830 ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
5831 if test "x$ac_cv_type_pid_t" = xyes; then :
5832
5833 else
5834
5835 cat >>confdefs.h <<_ACEOF
5836 #define pid_t int
5837 _ACEOF
5838
5839 fi
5840
5841 for ac_header in vfork.h
5842 do :
5843 ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
5844 if test "x$ac_cv_header_vfork_h" = xyes; then :
5845 cat >>confdefs.h <<_ACEOF
5846 #define HAVE_VFORK_H 1
5847 _ACEOF
5848
5849 fi
5850
5851 done
5852
5853 for ac_func in fork vfork
5854 do :
5855 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
5856 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
5857 if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
5858 cat >>confdefs.h <<_ACEOF
5859 #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
5860 _ACEOF
5861
5862 fi
5863 done
5864
5865 if test "x$ac_cv_func_fork" = xyes; then
5866 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
5867 $as_echo_n "checking for working fork... " >&6; }
5868 if ${ac_cv_func_fork_works+:} false; then :
5869 $as_echo_n "(cached) " >&6
5870 else
5871 if test "$cross_compiling" = yes; then :
5872 ac_cv_func_fork_works=cross
5873 else
5874 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5875 /* end confdefs.h. */
5876 $ac_includes_default
5877 int
5878 main ()
5879 {
5880
5881 /* By Ruediger Kuhlmann. */
5882 return fork () < 0;
5883
5884 ;
5885 return 0;
5886 }
5887 _ACEOF
5888 if ac_fn_c_try_run "$LINENO"; then :
5889 ac_cv_func_fork_works=yes
5890 else
5891 ac_cv_func_fork_works=no
5892 fi
5893 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
5894 conftest.$ac_objext conftest.beam conftest.$ac_ext
5895 fi
5896
5897 fi
5898 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
5899 $as_echo "$ac_cv_func_fork_works" >&6; }
5900
5901 else
5902 ac_cv_func_fork_works=$ac_cv_func_fork
5903 fi
5904 if test "x$ac_cv_func_fork_works" = xcross; then
5905 case $host in
5906 *-*-amigaos* | *-*-msdosdjgpp*)
5907 # Override, as these systems have only a dummy fork() stub
5908 ac_cv_func_fork_works=no
5909 ;;
5910 *)
5911 ac_cv_func_fork_works=yes
5912 ;;
5913 esac
5914 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
5915 $as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
5916 fi
5917 ac_cv_func_vfork_works=$ac_cv_func_vfork
5918 if test "x$ac_cv_func_vfork" = xyes; then
5919 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
5920 $as_echo_n "checking for working vfork... " >&6; }
5921 if ${ac_cv_func_vfork_works+:} false; then :
5922 $as_echo_n "(cached) " >&6
5923 else
5924 if test "$cross_compiling" = yes; then :
5925 ac_cv_func_vfork_works=cross
5926 else
5927 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
5928 /* end confdefs.h. */
5929 /* Thanks to Paul Eggert for this test. */
5930 $ac_includes_default
5931 #include <sys/wait.h>
5932 #ifdef HAVE_VFORK_H
5933 # include <vfork.h>
5934 #endif
5935 /* On some sparc systems, changes by the child to local and incoming
5936 argument registers are propagated back to the parent. The compiler
5937 is told about this with #include <vfork.h>, but some compilers
5938 (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
5939 static variable whose address is put into a register that is
5940 clobbered by the vfork. */
5941 static void
5942 #ifdef __cplusplus
5943 sparc_address_test (int arg)
5944 # else
5945 sparc_address_test (arg) int arg;
5946 #endif
5947 {
5948 static pid_t child;
5949 if (!child) {
5950 child = vfork ();
5951 if (child < 0) {
5952 perror ("vfork");
5953 _exit(2);
5954 }
5955 if (!child) {
5956 arg = getpid();
5957 write(-1, "", 0);
5958 _exit (arg);
5959 }
5960 }
5961 }
5962
5963 int
5964 main ()
5965 {
5966 pid_t parent = getpid ();
5967 pid_t child;
5968
5969 sparc_address_test (0);
5970
5971 child = vfork ();
5972
5973 if (child == 0) {
5974 /* Here is another test for sparc vfork register problems. This
5975 test uses lots of local variables, at least as many local
5976 variables as main has allocated so far including compiler
5977 temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
5978 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
5979 reuse the register of parent for one of the local variables,
5980 since it will think that parent can't possibly be used any more
5981 in this routine. Assigning to the local variable will thus
5982 munge parent in the parent process. */
5983 pid_t
5984 p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
5985 p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
5986 /* Convince the compiler that p..p7 are live; otherwise, it might
5987 use the same hardware register for all 8 local variables. */
5988 if (p != p1 || p != p2 || p != p3 || p != p4
5989 || p != p5 || p != p6 || p != p7)
5990 _exit(1);
5991
5992 /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
5993 from child file descriptors. If the child closes a descriptor
5994 before it execs or exits, this munges the parent's descriptor
5995 as well. Test for this by closing stdout in the child. */
5996 _exit(close(fileno(stdout)) != 0);
5997 } else {
5998 int status;
5999 struct stat st;
6000
6001 while (wait(&status) != child)
6002 ;
6003 return (
6004 /* Was there some problem with vforking? */
6005 child < 0
6006
6007 /* Did the child fail? (This shouldn't happen.) */
6008 || status
6009
6010 /* Did the vfork/compiler bug occur? */
6011 || parent != getpid()
6012
6013 /* Did the file descriptor bug occur? */
6014 || fstat(fileno(stdout), &st) != 0
6015 );
6016 }
6017 }
6018 _ACEOF
6019 if ac_fn_c_try_run "$LINENO"; then :
6020 ac_cv_func_vfork_works=yes
6021 else
6022 ac_cv_func_vfork_works=no
6023 fi
6024 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
6025 conftest.$ac_objext conftest.beam conftest.$ac_ext
6026 fi
6027
6028 fi
6029 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
6030 $as_echo "$ac_cv_func_vfork_works" >&6; }
6031
6032 fi;
6033 if test "x$ac_cv_func_fork_works" = xcross; then
6034 ac_cv_func_vfork_works=$ac_cv_func_vfork
6035 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
6036 $as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
6037 fi
6038
6039 if test "x$ac_cv_func_vfork_works" = xyes; then
6040
6041 $as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h
6042
6043 else
6044
6045 $as_echo "#define vfork fork" >>confdefs.h
6046
6047 fi
6048 if test "x$ac_cv_func_fork_works" = xyes; then
6049
6050 $as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
6051
6052 fi
6053
6054 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5
6055 $as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; }
6056 if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then :
6057 $as_echo_n "(cached) " >&6
6058 else
6059 rm -f conftest.sym conftest.file
6060 echo >conftest.file
6061 if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then
6062 if test "$cross_compiling" = yes; then :
6063 ac_cv_func_lstat_dereferences_slashed_symlink=no
6064 else
6065 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6066 /* end confdefs.h. */
6067 $ac_includes_default
6068 int
6069 main ()
6070 {
6071 struct stat sbuf;
6072 /* Linux will dereference the symlink and fail, as required by POSIX.
6073 That is better in the sense that it means we will not
6074 have to compile and use the lstat wrapper. */
6075 return lstat ("conftest.sym/", &sbuf) == 0;
6076 ;
6077 return 0;
6078 }
6079 _ACEOF
6080 if ac_fn_c_try_run "$LINENO"; then :
6081 ac_cv_func_lstat_dereferences_slashed_symlink=yes
6082 else
6083 ac_cv_func_lstat_dereferences_slashed_symlink=no
6084 fi
6085 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
6086 conftest.$ac_objext conftest.beam conftest.$ac_ext
6087 fi
6088
6089 else
6090 # If the `ln -s' command failed, then we probably don't even
6091 # have an lstat function.
6092 ac_cv_func_lstat_dereferences_slashed_symlink=no
6093 fi
6094 rm -f conftest.sym conftest.file
6095
6096 fi
6097 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5
6098 $as_echo "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; }
6099
6100 test $ac_cv_func_lstat_dereferences_slashed_symlink = yes &&
6101
6102 cat >>confdefs.h <<_ACEOF
6103 #define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
6104 _ACEOF
6105
6106
6107 if test "x$ac_cv_func_lstat_dereferences_slashed_symlink" = xno; then
6108 case " $LIBOBJS " in
6109 *" lstat.$ac_objext "* ) ;;
6110 *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
6111 ;;
6112 esac
6113
6114 fi
6115
6116 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5
6117 $as_echo_n "checking for working strnlen... " >&6; }
6118 if ${ac_cv_func_strnlen_working+:} false; then :
6119 $as_echo_n "(cached) " >&6
6120 else
6121 if test "$cross_compiling" = yes; then :
6122 # Guess no on AIX systems, yes otherwise.
6123 case "$host_os" in
6124 aix*) ac_cv_func_strnlen_working=no;;
6125 *) ac_cv_func_strnlen_working=yes;;
6126 esac
6127 else
6128 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6129 /* end confdefs.h. */
6130 $ac_includes_default
6131 int
6132 main ()
6133 {
6134
6135 #define S "foobar"
6136 #define S_LEN (sizeof S - 1)
6137
6138 /* At least one implementation is buggy: that of AIX 4.3 would
6139 give strnlen (S, 1) == 3. */
6140
6141 int i;
6142 for (i = 0; i < S_LEN + 1; ++i)
6143 {
6144 int expected = i <= S_LEN ? i : S_LEN;
6145 if (strnlen (S, i) != expected)
6146 return 1;
6147 }
6148 return 0;
6149
6150 ;
6151 return 0;
6152 }
6153 _ACEOF
6154 if ac_fn_c_try_run "$LINENO"; then :
6155 ac_cv_func_strnlen_working=yes
6156 else
6157 ac_cv_func_strnlen_working=no
6158 fi
6159 rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
6160 conftest.$ac_objext conftest.beam conftest.$ac_ext
6161 fi
6162
6163 fi
6164 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strnlen_working" >&5
6165 $as_echo "$ac_cv_func_strnlen_working" >&6; }
6166 test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in
6167 *" strnlen.$ac_objext "* ) ;;
6168 *) LIBOBJS="$LIBOBJS strnlen.$ac_objext"
6169 ;;
6170 esac
6171
6172
6173 for ac_func in atexit dup2 ftruncate getcwd gettimeofday localtime_r memchr memset mkdir rmdir setlocale socket strcasecmp strchr strdup strerror strncasecmp strndup strrchr strspn strstr strtol strtoul
6174 do :
6175 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
6176 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
6177 if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
6178 cat >>confdefs.h <<_ACEOF
6179 #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
6180 _ACEOF
6181
6182 else
6183 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
6184 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
6185 as_fn_error $? "cannot find the $ac_func function, which i3 requires
6186 See \`config.log' for more details" "$LINENO" 5; }
6187 fi
6188 done
6189
6190
6191 # Checks for libraries.
6192
6193 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing floor" >&5
6194 $as_echo_n "checking for library containing floor... " >&6; }
6195 if ${ac_cv_search_floor+:} false; then :
6196 $as_echo_n "(cached) " >&6
6197 else
6198 ac_func_search_save_LIBS=$LIBS
6199 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6200 /* end confdefs.h. */
6201
6202 /* Override any GCC internal prototype to avoid an error.
6203 Use char because int might match the return type of a GCC
6204 builtin and then its argument prototype would still apply. */
6205 #ifdef __cplusplus
6206 extern "C"
6207 #endif
6208 char floor ();
6209 int
6210 main ()
6211 {
6212 return floor ();
6213 ;
6214 return 0;
6215 }
6216 _ACEOF
6217 for ac_lib in '' m; do
6218 if test -z "$ac_lib"; then
6219 ac_res="none required"
6220 else
6221 ac_res=-l$ac_lib
6222 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
6223 fi
6224 if ac_fn_c_try_link "$LINENO"; then :
6225 ac_cv_search_floor=$ac_res
6226 fi
6227 rm -f core conftest.err conftest.$ac_objext \
6228 conftest$ac_exeext
6229 if ${ac_cv_search_floor+:} false; then :
6230 break
6231 fi
6232 done
6233 if ${ac_cv_search_floor+:} false; then :
6234
6235 else
6236 ac_cv_search_floor=no
6237 fi
6238 rm conftest.$ac_ext
6239 LIBS=$ac_func_search_save_LIBS
6240 fi
6241 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_floor" >&5
6242 $as_echo "$ac_cv_search_floor" >&6; }
6243 ac_res=$ac_cv_search_floor
6244 if test "$ac_res" != no; then :
6245 test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
6246
6247 else
6248 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
6249 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
6250 as_fn_error $? "cannot find the required floor() function despite trying to link with -lm
6251 See \`config.log' for more details" "$LINENO" 5; }
6252 fi
6253
6254
6255 # libev does not ship with a pkg-config file :(.
6256 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ev_run" >&5
6257 $as_echo_n "checking for library containing ev_run... " >&6; }
6258 if ${ac_cv_search_ev_run+:} false; then :
6259 $as_echo_n "(cached) " >&6
6260 else
6261 ac_func_search_save_LIBS=$LIBS
6262 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6263 /* end confdefs.h. */
6264
6265 /* Override any GCC internal prototype to avoid an error.
6266 Use char because int might match the return type of a GCC
6267 builtin and then its argument prototype would still apply. */
6268 #ifdef __cplusplus
6269 extern "C"
6270 #endif
6271 char ev_run ();
6272 int
6273 main ()
6274 {
6275 return ev_run ();
6276 ;
6277 return 0;
6278 }
6279 _ACEOF
6280 for ac_lib in '' ev; do
6281 if test -z "$ac_lib"; then
6282 ac_res="none required"
6283 else
6284 ac_res=-l$ac_lib
6285 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
6286 fi
6287 if ac_fn_c_try_link "$LINENO"; then :
6288 ac_cv_search_ev_run=$ac_res
6289 fi
6290 rm -f core conftest.err conftest.$ac_objext \
6291 conftest$ac_exeext
6292 if ${ac_cv_search_ev_run+:} false; then :
6293 break
6294 fi
6295 done
6296 if ${ac_cv_search_ev_run+:} false; then :
6297
6298 else
6299 ac_cv_search_ev_run=no
6300 fi
6301 rm conftest.$ac_ext
6302 LIBS=$ac_func_search_save_LIBS
6303 fi
6304 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ev_run" >&5
6305 $as_echo "$ac_cv_search_ev_run" >&6; }
6306 ac_res=$ac_cv_search_ev_run
6307 if test "$ac_res" != no; then :
6308 test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
6309
6310 else
6311 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
6312 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
6313 as_fn_error $? "cannot find the required ev_run() function despite trying to link with -lev
6314 See \`config.log' for more details" "$LINENO" 5; }
6315 fi
6316
6317
6318 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing shm_open" >&5
6319 $as_echo_n "checking for library containing shm_open... " >&6; }
6320 if ${ac_cv_search_shm_open+:} false; then :
6321 $as_echo_n "(cached) " >&6
6322 else
6323 ac_func_search_save_LIBS=$LIBS
6324 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6325 /* end confdefs.h. */
6326
6327 /* Override any GCC internal prototype to avoid an error.
6328 Use char because int might match the return type of a GCC
6329 builtin and then its argument prototype would still apply. */
6330 #ifdef __cplusplus
6331 extern "C"
6332 #endif
6333 char shm_open ();
6334 int
6335 main ()
6336 {
6337 return shm_open ();
6338 ;
6339 return 0;
6340 }
6341 _ACEOF
6342 for ac_lib in '' rt; do
6343 if test -z "$ac_lib"; then
6344 ac_res="none required"
6345 else
6346 ac_res=-l$ac_lib
6347 LIBS="-l$ac_lib -pthread $ac_func_search_save_LIBS"
6348 fi
6349 if ac_fn_c_try_link "$LINENO"; then :
6350 ac_cv_search_shm_open=$ac_res
6351 fi
6352 rm -f core conftest.err conftest.$ac_objext \
6353 conftest$ac_exeext
6354 if ${ac_cv_search_shm_open+:} false; then :
6355 break
6356 fi
6357 done
6358 if ${ac_cv_search_shm_open+:} false; then :
6359
6360 else
6361 ac_cv_search_shm_open=no
6362 fi
6363 rm conftest.$ac_ext
6364 LIBS=$ac_func_search_save_LIBS
6365 fi
6366 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_shm_open" >&5
6367 $as_echo "$ac_cv_search_shm_open" >&6; }
6368 ac_res=$ac_cv_search_shm_open
6369 if test "$ac_res" != no; then :
6370 test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
6371
6372 fi
6373
6374
6375 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing iconv_open" >&5
6376 $as_echo_n "checking for library containing iconv_open... " >&6; }
6377 if ${ac_cv_search_iconv_open+:} false; then :
6378 $as_echo_n "(cached) " >&6
6379 else
6380 ac_func_search_save_LIBS=$LIBS
6381 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6382 /* end confdefs.h. */
6383
6384 /* Override any GCC internal prototype to avoid an error.
6385 Use char because int might match the return type of a GCC
6386 builtin and then its argument prototype would still apply. */
6387 #ifdef __cplusplus
6388 extern "C"
6389 #endif
6390 char iconv_open ();
6391 int
6392 main ()
6393 {
6394 return iconv_open ();
6395 ;
6396 return 0;
6397 }
6398 _ACEOF
6399 for ac_lib in '' iconv; do
6400 if test -z "$ac_lib"; then
6401 ac_res="none required"
6402 else
6403 ac_res=-l$ac_lib
6404 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
6405 fi
6406 if ac_fn_c_try_link "$LINENO"; then :
6407 ac_cv_search_iconv_open=$ac_res
6408 fi
6409 rm -f core conftest.err conftest.$ac_objext \
6410 conftest$ac_exeext
6411 if ${ac_cv_search_iconv_open+:} false; then :
6412 break
6413 fi
6414 done
6415 if ${ac_cv_search_iconv_open+:} false; then :
6416
6417 else
6418 ac_cv_search_iconv_open=no
6419 fi
6420 rm conftest.$ac_ext
6421 LIBS=$ac_func_search_save_LIBS
6422 fi
6423 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_iconv_open" >&5
6424 $as_echo "$ac_cv_search_iconv_open" >&6; }
6425 ac_res=$ac_cv_search_iconv_open
6426 if test "$ac_res" != no; then :
6427 test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
6428
6429 else
6430 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing libiconv_open" >&5
6431 $as_echo_n "checking for library containing libiconv_open... " >&6; }
6432 if ${ac_cv_search_libiconv_open+:} false; then :
6433 $as_echo_n "(cached) " >&6
6434 else
6435 ac_func_search_save_LIBS=$LIBS
6436 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6437 /* end confdefs.h. */
6438
6439 /* Override any GCC internal prototype to avoid an error.
6440 Use char because int might match the return type of a GCC
6441 builtin and then its argument prototype would still apply. */
6442 #ifdef __cplusplus
6443 extern "C"
6444 #endif
6445 char libiconv_open ();
6446 int
6447 main ()
6448 {
6449 return libiconv_open ();
6450 ;
6451 return 0;
6452 }
6453 _ACEOF
6454 for ac_lib in '' iconv; do
6455 if test -z "$ac_lib"; then
6456 ac_res="none required"
6457 else
6458 ac_res=-l$ac_lib
6459 LIBS="-l$ac_lib $ac_func_search_save_LIBS"
6460 fi
6461 if ac_fn_c_try_link "$LINENO"; then :
6462 ac_cv_search_libiconv_open=$ac_res
6463 fi
6464 rm -f core conftest.err conftest.$ac_objext \
6465 conftest$ac_exeext
6466 if ${ac_cv_search_libiconv_open+:} false; then :
6467 break
6468 fi
6469 done
6470 if ${ac_cv_search_libiconv_open+:} false; then :
6471
6472 else
6473 ac_cv_search_libiconv_open=no
6474 fi
6475 rm conftest.$ac_ext
6476 LIBS=$ac_func_search_save_LIBS
6477 fi
6478 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_libiconv_open" >&5
6479 $as_echo "$ac_cv_search_libiconv_open" >&6; }
6480 ac_res=$ac_cv_search_libiconv_open
6481 if test "$ac_res" != no; then :
6482 test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
6483
6484 else
6485 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
6486 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
6487 as_fn_error $? "cannot find the required iconv_open() function despite trying to link with -liconv
6488 See \`config.log' for more details" "$LINENO" 5; }
6489 fi
6490
6491 fi
6492
6493
6494
6495
6496
6497
6498 ac_ext=c
6499 ac_cpp='$CPP $CPPFLAGS'
6500 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
6501 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
6502 ac_compiler_gnu=$ac_cv_c_compiler_gnu
6503
6504 ax_pthread_ok=no
6505
6506 # We used to check for pthread.h first, but this fails if pthread.h
6507 # requires special compiler flags (e.g. on Tru64 or Sequent).
6508 # It gets checked for in the link test anyway.
6509
6510 # First of all, check if the user has set any of the PTHREAD_LIBS,
6511 # etcetera environment variables, and if threads linking works using
6512 # them:
6513 if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
6514 ax_pthread_save_CC="$CC"
6515 ax_pthread_save_CFLAGS="$CFLAGS"
6516 ax_pthread_save_LIBS="$LIBS"
6517 if test "x$PTHREAD_CC" != "x"; then :
6518 CC="$PTHREAD_CC"
6519 fi
6520 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
6521 LIBS="$PTHREAD_LIBS $LIBS"
6522 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS" >&5
6523 $as_echo_n "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS... " >&6; }
6524 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6525 /* end confdefs.h. */
6526
6527 /* Override any GCC internal prototype to avoid an error.
6528 Use char because int might match the return type of a GCC
6529 builtin and then its argument prototype would still apply. */
6530 #ifdef __cplusplus
6531 extern "C"
6532 #endif
6533 char pthread_join ();
6534 int
6535 main ()
6536 {
6537 return pthread_join ();
6538 ;
6539 return 0;
6540 }
6541 _ACEOF
6542 if ac_fn_c_try_link "$LINENO"; then :
6543 ax_pthread_ok=yes
6544 fi
6545 rm -f core conftest.err conftest.$ac_objext \
6546 conftest$ac_exeext conftest.$ac_ext
6547 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5
6548 $as_echo "$ax_pthread_ok" >&6; }
6549 if test "x$ax_pthread_ok" = "xno"; then
6550 PTHREAD_LIBS=""
6551 PTHREAD_CFLAGS=""
6552 fi
6553 CC="$ax_pthread_save_CC"
6554 CFLAGS="$ax_pthread_save_CFLAGS"
6555 LIBS="$ax_pthread_save_LIBS"
6556 fi
6557
6558 # We must check for the threads library under a number of different
6559 # names; the ordering is very important because some systems
6560 # (e.g. DEC) have both -lpthread and -lpthreads, where one of the
6561 # libraries is broken (non-POSIX).
6562
6563 # Create a list of thread flags to try. Items starting with a "-" are
6564 # C compiler flags, and other items are library names, except for "none"
6565 # which indicates that we try without any flags at all, and "pthread-config"
6566 # which is a program returning the flags for the Pth emulation library.
6567
6568 ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
6569
6570 # The ordering *is* (sometimes) important. Some notes on the
6571 # individual items follow:
6572
6573 # pthreads: AIX (must check this before -lpthread)
6574 # none: in case threads are in libc; should be tried before -Kthread and
6575 # other compiler flags to prevent continual compiler warnings
6576 # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
6577 # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
6578 # (Note: HP C rejects this with "bad form for `-t' option")
6579 # -pthreads: Solaris/gcc (Note: HP C also rejects)
6580 # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
6581 # doesn't hurt to check since this sometimes defines pthreads and
6582 # -D_REENTRANT too), HP C (must be checked before -lpthread, which
6583 # is present but should not be used directly; and before -mthreads,
6584 # because the compiler interprets this as "-mt" + "-hreads")
6585 # -mthreads: Mingw32/gcc, Lynx/gcc
6586 # pthread: Linux, etcetera
6587 # --thread-safe: KAI C++
6588 # pthread-config: use pthread-config program (for GNU Pth library)
6589
6590 case $host_os in
6591
6592 freebsd*)
6593
6594 # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
6595 # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
6596
6597 ax_pthread_flags="-kthread lthread $ax_pthread_flags"
6598 ;;
6599
6600 hpux*)
6601
6602 # From the cc(1) man page: "[-mt] Sets various -D flags to enable
6603 # multi-threading and also sets -lpthread."
6604
6605 ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
6606 ;;
6607
6608 openedition*)
6609
6610 # IBM z/OS requires a feature-test macro to be defined in order to
6611 # enable POSIX threads at all, so give the user a hint if this is
6612 # not set. (We don't define these ourselves, as they can affect
6613 # other portions of the system API in unpredictable ways.)
6614
6615 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6616 /* end confdefs.h. */
6617
6618 # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
6619 AX_PTHREAD_ZOS_MISSING
6620 # endif
6621
6622 _ACEOF
6623 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
6624 $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1; then :
6625 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5
6626 $as_echo "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;}
6627 fi
6628 rm -f conftest*
6629
6630 ;;
6631
6632 solaris*)
6633
6634 # On Solaris (at least, for some versions), libc contains stubbed
6635 # (non-functional) versions of the pthreads routines, so link-based
6636 # tests will erroneously succeed. (N.B.: The stubs are missing
6637 # pthread_cleanup_push, or rather a function called by this macro,
6638 # so we could check for that, but who knows whether they'll stub
6639 # that too in a future libc.) So we'll check first for the
6640 # standard Solaris way of linking pthreads (-mt -lpthread).
6641
6642 ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
6643 ;;
6644 esac
6645
6646 # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
6647
6648 if test "x$GCC" = "xyes"; then :
6649 ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"
6650 fi
6651
6652 # The presence of a feature test macro requesting re-entrant function
6653 # definitions is, on some systems, a strong hint that pthreads support is
6654 # correctly enabled
6655
6656 case $host_os in
6657 darwin* | hpux* | linux* | osf* | solaris*)
6658 ax_pthread_check_macro="_REENTRANT"
6659 ;;
6660
6661 aix*)
6662 ax_pthread_check_macro="_THREAD_SAFE"
6663 ;;
6664
6665 *)
6666 ax_pthread_check_macro="--"
6667 ;;
6668 esac
6669 if test "x$ax_pthread_check_macro" = "x--"; then :
6670 ax_pthread_check_cond=0
6671 else
6672 ax_pthread_check_cond="!defined($ax_pthread_check_macro)"
6673 fi
6674
6675 # Are we compiling with Clang?
6676
6677 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC is Clang" >&5
6678 $as_echo_n "checking whether $CC is Clang... " >&6; }
6679 if ${ax_cv_PTHREAD_CLANG+:} false; then :
6680 $as_echo_n "(cached) " >&6
6681 else
6682 ax_cv_PTHREAD_CLANG=no
6683 # Note that Autoconf sets GCC=yes for Clang as well as GCC
6684 if test "x$GCC" = "xyes"; then
6685 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6686 /* end confdefs.h. */
6687 /* Note: Clang 2.7 lacks __clang_[a-z]+__ */
6688 # if defined(__clang__) && defined(__llvm__)
6689 AX_PTHREAD_CC_IS_CLANG
6690 # endif
6691
6692 _ACEOF
6693 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
6694 $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1; then :
6695 ax_cv_PTHREAD_CLANG=yes
6696 fi
6697 rm -f conftest*
6698
6699 fi
6700
6701 fi
6702 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5
6703 $as_echo "$ax_cv_PTHREAD_CLANG" >&6; }
6704 ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
6705
6706 ax_pthread_clang_warning=no
6707
6708 # Clang needs special handling, because older versions handle the -pthread
6709 # option in a rather... idiosyncratic way
6710
6711 if test "x$ax_pthread_clang" = "xyes"; then
6712
6713 # Clang takes -pthread; it has never supported any other flag
6714
6715 # (Note 1: This will need to be revisited if a system that Clang
6716 # supports has POSIX threads in a separate library. This tends not
6717 # to be the way of modern systems, but it's conceivable.)
6718
6719 # (Note 2: On some systems, notably Darwin, -pthread is not needed
6720 # to get POSIX threads support; the API is always present and
6721 # active. We could reasonably leave PTHREAD_CFLAGS empty. But
6722 # -pthread does define _REENTRANT, and while the Darwin headers
6723 # ignore this macro, third-party headers might not.)
6724
6725 PTHREAD_CFLAGS="-pthread"
6726 PTHREAD_LIBS=
6727
6728 ax_pthread_ok=yes
6729
6730 # However, older versions of Clang make a point of warning the user
6731 # that, in an invocation where only linking and no compilation is
6732 # taking place, the -pthread option has no effect ("argument unused
6733 # during compilation"). They expect -pthread to be passed in only
6734 # when source code is being compiled.
6735 #
6736 # Problem is, this is at odds with the way Automake and most other
6737 # C build frameworks function, which is that the same flags used in
6738 # compilation (CFLAGS) are also used in linking. Many systems
6739 # supported by AX_PTHREAD require exactly this for POSIX threads
6740 # support, and in fact it is often not straightforward to specify a
6741 # flag that is used only in the compilation phase and not in
6742 # linking. Such a scenario is extremely rare in practice.
6743 #
6744 # Even though use of the -pthread flag in linking would only print
6745 # a warning, this can be a nuisance for well-run software projects
6746 # that build with -Werror. So if the active version of Clang has
6747 # this misfeature, we search for an option to squash it.
6748
6749 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread" >&5
6750 $as_echo_n "checking whether Clang needs flag to prevent \"argument unused\" warning when linking with -pthread... " >&6; }
6751 if ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+:} false; then :
6752 $as_echo_n "(cached) " >&6
6753 else
6754 ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
6755 # Create an alternate version of $ac_link that compiles and
6756 # links in two steps (.c -> .o, .o -> exe) instead of one
6757 # (.c -> exe), because the warning occurs only in the second
6758 # step
6759 ax_pthread_save_ac_link="$ac_link"
6760 ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
6761 ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
6762 ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
6763 ax_pthread_save_CFLAGS="$CFLAGS"
6764 for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
6765 if test "x$ax_pthread_try" = "xunknown"; then :
6766 break
6767 fi
6768 CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
6769 ac_link="$ax_pthread_save_ac_link"
6770 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6771 /* end confdefs.h. */
6772 int main(void){return 0;}
6773 _ACEOF
6774 if ac_fn_c_try_link "$LINENO"; then :
6775 ac_link="$ax_pthread_2step_ac_link"
6776 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6777 /* end confdefs.h. */
6778 int main(void){return 0;}
6779 _ACEOF
6780 if ac_fn_c_try_link "$LINENO"; then :
6781 break
6782 fi
6783 rm -f core conftest.err conftest.$ac_objext \
6784 conftest$ac_exeext conftest.$ac_ext
6785
6786 fi
6787 rm -f core conftest.err conftest.$ac_objext \
6788 conftest$ac_exeext conftest.$ac_ext
6789 done
6790 ac_link="$ax_pthread_save_ac_link"
6791 CFLAGS="$ax_pthread_save_CFLAGS"
6792 if test "x$ax_pthread_try" = "x"; then :
6793 ax_pthread_try=no
6794 fi
6795 ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
6796
6797 fi
6798 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5
6799 $as_echo "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; }
6800
6801 case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
6802 no | unknown) ;;
6803 *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
6804 esac
6805
6806 fi # $ax_pthread_clang = yes
6807
6808 if test "x$ax_pthread_ok" = "xno"; then
6809 for ax_pthread_try_flag in $ax_pthread_flags; do
6810
6811 case $ax_pthread_try_flag in
6812 none)
6813 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
6814 $as_echo_n "checking whether pthreads work without any flags... " >&6; }
6815 ;;
6816
6817 -mt,pthread)
6818 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with -mt -lpthread" >&5
6819 $as_echo_n "checking whether pthreads work with -mt -lpthread... " >&6; }
6820 PTHREAD_CFLAGS="-mt"
6821 PTHREAD_LIBS="-lpthread"
6822 ;;
6823
6824 -*)
6825 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $ax_pthread_try_flag" >&5
6826 $as_echo_n "checking whether pthreads work with $ax_pthread_try_flag... " >&6; }
6827 PTHREAD_CFLAGS="$ax_pthread_try_flag"
6828 ;;
6829
6830 pthread-config)
6831 # Extract the first word of "pthread-config", so it can be a program name with args.
6832 set dummy pthread-config; ac_word=$2
6833 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
6834 $as_echo_n "checking for $ac_word... " >&6; }
6835 if ${ac_cv_prog_ax_pthread_config+:} false; then :
6836 $as_echo_n "(cached) " >&6
6837 else
6838 if test -n "$ax_pthread_config"; then
6839 ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test.
6840 else
6841 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
6842 for as_dir in $PATH
6843 do
6844 IFS=$as_save_IFS
6845 test -z "$as_dir" && as_dir=.
6846 for ac_exec_ext in '' $ac_executable_extensions; do
6847 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
6848 ac_cv_prog_ax_pthread_config="yes"
6849 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
6850 break 2
6851 fi
6852 done
6853 done
6854 IFS=$as_save_IFS
6855
6856 test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no"
6857 fi
6858 fi
6859 ax_pthread_config=$ac_cv_prog_ax_pthread_config
6860 if test -n "$ax_pthread_config"; then
6861 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5
6862 $as_echo "$ax_pthread_config" >&6; }
6863 else
6864 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
6865 $as_echo "no" >&6; }
6866 fi
6867
6868
6869 if test "x$ax_pthread_config" = "xno"; then :
6870 continue
6871 fi
6872 PTHREAD_CFLAGS="`pthread-config --cflags`"
6873 PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
6874 ;;
6875
6876 *)
6877 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$ax_pthread_try_flag" >&5
6878 $as_echo_n "checking for the pthreads library -l$ax_pthread_try_flag... " >&6; }
6879 PTHREAD_LIBS="-l$ax_pthread_try_flag"
6880 ;;
6881 esac
6882
6883 ax_pthread_save_CFLAGS="$CFLAGS"
6884 ax_pthread_save_LIBS="$LIBS"
6885 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
6886 LIBS="$PTHREAD_LIBS $LIBS"
6887
6888 # Check for various functions. We must include pthread.h,
6889 # since some functions may be macros. (On the Sequent, we
6890 # need a special flag -Kthread to make this header compile.)
6891 # We check for pthread_join because it is in -lpthread on IRIX
6892 # while pthread_create is in libc. We check for pthread_attr_init
6893 # due to DEC craziness with -lpthreads. We check for
6894 # pthread_cleanup_push because it is one of the few pthread
6895 # functions on Solaris that doesn't have a non-functional libc stub.
6896 # We try pthread_create on general principles.
6897
6898 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6899 /* end confdefs.h. */
6900 #include <pthread.h>
6901 # if $ax_pthread_check_cond
6902 # error "$ax_pthread_check_macro must be defined"
6903 # endif
6904 static void routine(void *a) { a = 0; }
6905 static void *start_routine(void *a) { return a; }
6906 int
6907 main ()
6908 {
6909 pthread_t th; pthread_attr_t attr;
6910 pthread_create(&th, 0, start_routine, 0);
6911 pthread_join(th, 0);
6912 pthread_attr_init(&attr);
6913 pthread_cleanup_push(routine, 0);
6914 pthread_cleanup_pop(0) /* ; */
6915 ;
6916 return 0;
6917 }
6918 _ACEOF
6919 if ac_fn_c_try_link "$LINENO"; then :
6920 ax_pthread_ok=yes
6921 fi
6922 rm -f core conftest.err conftest.$ac_objext \
6923 conftest$ac_exeext conftest.$ac_ext
6924
6925 CFLAGS="$ax_pthread_save_CFLAGS"
6926 LIBS="$ax_pthread_save_LIBS"
6927
6928 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5
6929 $as_echo "$ax_pthread_ok" >&6; }
6930 if test "x$ax_pthread_ok" = "xyes"; then :
6931 break
6932 fi
6933
6934 PTHREAD_LIBS=""
6935 PTHREAD_CFLAGS=""
6936 done
6937 fi
6938
6939 # Various other checks:
6940 if test "x$ax_pthread_ok" = "xyes"; then
6941 ax_pthread_save_CFLAGS="$CFLAGS"
6942 ax_pthread_save_LIBS="$LIBS"
6943 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
6944 LIBS="$PTHREAD_LIBS $LIBS"
6945
6946 # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
6947 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
6948 $as_echo_n "checking for joinable pthread attribute... " >&6; }
6949 if ${ax_cv_PTHREAD_JOINABLE_ATTR+:} false; then :
6950 $as_echo_n "(cached) " >&6
6951 else
6952 ax_cv_PTHREAD_JOINABLE_ATTR=unknown
6953 for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
6954 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
6955 /* end confdefs.h. */
6956 #include <pthread.h>
6957 int
6958 main ()
6959 {
6960 int attr = $ax_pthread_attr; return attr /* ; */
6961 ;
6962 return 0;
6963 }
6964 _ACEOF
6965 if ac_fn_c_try_link "$LINENO"; then :
6966 ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break
6967 fi
6968 rm -f core conftest.err conftest.$ac_objext \
6969 conftest$ac_exeext conftest.$ac_ext
6970 done
6971
6972 fi
6973 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5
6974 $as_echo "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; }
6975 if test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
6976 test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
6977 test "x$ax_pthread_joinable_attr_defined" != "xyes"; then :
6978
6979 cat >>confdefs.h <<_ACEOF
6980 #define PTHREAD_CREATE_JOINABLE $ax_cv_PTHREAD_JOINABLE_ATTR
6981 _ACEOF
6982
6983 ax_pthread_joinable_attr_defined=yes
6984
6985 fi
6986
6987 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether more special flags are required for pthreads" >&5
6988 $as_echo_n "checking whether more special flags are required for pthreads... " >&6; }
6989 if ${ax_cv_PTHREAD_SPECIAL_FLAGS+:} false; then :
6990 $as_echo_n "(cached) " >&6
6991 else
6992 ax_cv_PTHREAD_SPECIAL_FLAGS=no
6993 case $host_os in
6994 solaris*)
6995 ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
6996 ;;
6997 esac
6998
6999 fi
7000 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5
7001 $as_echo "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; }
7002 if test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
7003 test "x$ax_pthread_special_flags_added" != "xyes"; then :
7004 PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
7005 ax_pthread_special_flags_added=yes
7006 fi
7007
7008 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5
7009 $as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; }
7010 if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then :
7011 $as_echo_n "(cached) " >&6
7012 else
7013 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
7014 /* end confdefs.h. */
7015 #include <pthread.h>
7016 int
7017 main ()
7018 {
7019 int i = PTHREAD_PRIO_INHERIT;
7020 ;
7021 return 0;
7022 }
7023 _ACEOF
7024 if ac_fn_c_try_link "$LINENO"; then :
7025 ax_cv_PTHREAD_PRIO_INHERIT=yes
7026 else
7027 ax_cv_PTHREAD_PRIO_INHERIT=no
7028 fi
7029 rm -f core conftest.err conftest.$ac_objext \
7030 conftest$ac_exeext conftest.$ac_ext
7031
7032 fi
7033 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5
7034 $as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; }
7035 if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
7036 test "x$ax_pthread_prio_inherit_defined" != "xyes"; then :
7037
7038 $as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h
7039
7040 ax_pthread_prio_inherit_defined=yes
7041
7042 fi
7043
7044 CFLAGS="$ax_pthread_save_CFLAGS"
7045 LIBS="$ax_pthread_save_LIBS"
7046
7047 # More AIX lossage: compile with *_r variant
7048 if test "x$GCC" != "xyes"; then
7049 case $host_os in
7050 aix*)
7051 case "x/$CC" in #(
7052 x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) :
7053 #handle absolute path differently from PATH based program lookup
7054 case "x$CC" in #(
7055 x/*) :
7056 if as_fn_executable_p ${CC}_r; then :
7057 PTHREAD_CC="${CC}_r"
7058 fi ;; #(
7059 *) :
7060 for ac_prog in ${CC}_r
7061 do
7062 # Extract the first word of "$ac_prog", so it can be a program name with args.
7063 set dummy $ac_prog; ac_word=$2
7064 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
7065 $as_echo_n "checking for $ac_word... " >&6; }
7066 if ${ac_cv_prog_PTHREAD_CC+:} false; then :
7067 $as_echo_n "(cached) " >&6
7068 else
7069 if test -n "$PTHREAD_CC"; then
7070 ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
7071 else
7072 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
7073 for as_dir in $PATH
7074 do
7075 IFS=$as_save_IFS
7076 test -z "$as_dir" && as_dir=.
7077 for ac_exec_ext in '' $ac_executable_extensions; do
7078 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
7079 ac_cv_prog_PTHREAD_CC="$ac_prog"
7080 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
7081 break 2
7082 fi
7083 done
7084 done
7085 IFS=$as_save_IFS
7086
7087 fi
7088 fi
7089 PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
7090 if test -n "$PTHREAD_CC"; then
7091 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5
7092 $as_echo "$PTHREAD_CC" >&6; }
7093 else
7094 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7095 $as_echo "no" >&6; }
7096 fi
7097
7098
7099 test -n "$PTHREAD_CC" && break
7100 done
7101 test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
7102 ;;
7103 esac ;; #(
7104 *) :
7105 ;;
7106 esac
7107 ;;
7108 esac
7109 fi
7110 fi
7111
7112 test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
7113
7114
7115
7116
7117
7118 # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
7119 if test "x$ax_pthread_ok" = "xyes"; then
7120
7121 $as_echo "#define HAVE_PTHREAD 1" >>confdefs.h
7122
7123 :
7124 else
7125 ax_pthread_ok=no
7126
7127 fi
7128 ac_ext=c
7129 ac_cpp='$CPP $CPPFLAGS'
7130 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
7131 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
7132 ac_compiler_gnu=$ac_cv_c_compiler_gnu
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143 if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
7144 if test -n "$ac_tool_prefix"; then
7145 # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
7146 set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
7147 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
7148 $as_echo_n "checking for $ac_word... " >&6; }
7149 if ${ac_cv_path_PKG_CONFIG+:} false; then :
7150 $as_echo_n "(cached) " >&6
7151 else
7152 case $PKG_CONFIG in
7153 [\\/]* | ?:[\\/]*)
7154 ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
7155 ;;
7156 *)
7157 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
7158 for as_dir in $PATH
7159 do
7160 IFS=$as_save_IFS
7161 test -z "$as_dir" && as_dir=.
7162 for ac_exec_ext in '' $ac_executable_extensions; do
7163 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
7164 ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
7165 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
7166 break 2
7167 fi
7168 done
7169 done
7170 IFS=$as_save_IFS
7171
7172 ;;
7173 esac
7174 fi
7175 PKG_CONFIG=$ac_cv_path_PKG_CONFIG
7176 if test -n "$PKG_CONFIG"; then
7177 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
7178 $as_echo "$PKG_CONFIG" >&6; }
7179 else
7180 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7181 $as_echo "no" >&6; }
7182 fi
7183
7184
7185 fi
7186 if test -z "$ac_cv_path_PKG_CONFIG"; then
7187 ac_pt_PKG_CONFIG=$PKG_CONFIG
7188 # Extract the first word of "pkg-config", so it can be a program name with args.
7189 set dummy pkg-config; ac_word=$2
7190 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
7191 $as_echo_n "checking for $ac_word... " >&6; }
7192 if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
7193 $as_echo_n "(cached) " >&6
7194 else
7195 case $ac_pt_PKG_CONFIG in
7196 [\\/]* | ?:[\\/]*)
7197 ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
7198 ;;
7199 *)
7200 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
7201 for as_dir in $PATH
7202 do
7203 IFS=$as_save_IFS
7204 test -z "$as_dir" && as_dir=.
7205 for ac_exec_ext in '' $ac_executable_extensions; do
7206 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
7207 ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
7208 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
7209 break 2
7210 fi
7211 done
7212 done
7213 IFS=$as_save_IFS
7214
7215 ;;
7216 esac
7217 fi
7218 ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
7219 if test -n "$ac_pt_PKG_CONFIG"; then
7220 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
7221 $as_echo "$ac_pt_PKG_CONFIG" >&6; }
7222 else
7223 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7224 $as_echo "no" >&6; }
7225 fi
7226
7227 if test "x$ac_pt_PKG_CONFIG" = x; then
7228 PKG_CONFIG=""
7229 else
7230 case $cross_compiling:$ac_tool_warned in
7231 yes:)
7232 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
7233 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
7234 ac_tool_warned=yes ;;
7235 esac
7236 PKG_CONFIG=$ac_pt_PKG_CONFIG
7237 fi
7238 else
7239 PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
7240 fi
7241
7242 fi
7243 if test -n "$PKG_CONFIG"; then
7244 _pkg_min_version=0.9.0
7245 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
7246 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
7247 if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
7248 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7249 $as_echo "yes" >&6; }
7250 else
7251 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7252 $as_echo "no" >&6; }
7253 PKG_CONFIG=""
7254 fi
7255 fi
7256
7257 pkg_failed=no
7258 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBSN" >&5
7259 $as_echo_n "checking for LIBSN... " >&6; }
7260
7261 if test -n "$LIBSN_CFLAGS"; then
7262 pkg_cv_LIBSN_CFLAGS="$LIBSN_CFLAGS"
7263 elif test -n "$PKG_CONFIG"; then
7264 if test -n "$PKG_CONFIG" && \
7265 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libstartup-notification-1.0\""; } >&5
7266 ($PKG_CONFIG --exists --print-errors "libstartup-notification-1.0") 2>&5
7267 ac_status=$?
7268 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7269 test $ac_status = 0; }; then
7270 pkg_cv_LIBSN_CFLAGS=`$PKG_CONFIG --cflags "libstartup-notification-1.0" 2>/dev/null`
7271 test "x$?" != "x0" && pkg_failed=yes
7272 else
7273 pkg_failed=yes
7274 fi
7275 else
7276 pkg_failed=untried
7277 fi
7278 if test -n "$LIBSN_LIBS"; then
7279 pkg_cv_LIBSN_LIBS="$LIBSN_LIBS"
7280 elif test -n "$PKG_CONFIG"; then
7281 if test -n "$PKG_CONFIG" && \
7282 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libstartup-notification-1.0\""; } >&5
7283 ($PKG_CONFIG --exists --print-errors "libstartup-notification-1.0") 2>&5
7284 ac_status=$?
7285 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7286 test $ac_status = 0; }; then
7287 pkg_cv_LIBSN_LIBS=`$PKG_CONFIG --libs "libstartup-notification-1.0" 2>/dev/null`
7288 test "x$?" != "x0" && pkg_failed=yes
7289 else
7290 pkg_failed=yes
7291 fi
7292 else
7293 pkg_failed=untried
7294 fi
7295
7296
7297
7298 if test $pkg_failed = yes; then
7299 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7300 $as_echo "no" >&6; }
7301
7302 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7303 _pkg_short_errors_supported=yes
7304 else
7305 _pkg_short_errors_supported=no
7306 fi
7307 if test $_pkg_short_errors_supported = yes; then
7308 LIBSN_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libstartup-notification-1.0" 2>&1`
7309 else
7310 LIBSN_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libstartup-notification-1.0" 2>&1`
7311 fi
7312 # Put the nasty error message in config.log where it belongs
7313 echo "$LIBSN_PKG_ERRORS" >&5
7314
7315 as_fn_error $? "Package requirements (libstartup-notification-1.0) were not met:
7316
7317 $LIBSN_PKG_ERRORS
7318
7319 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7320 installed software in a non-standard prefix.
7321
7322 Alternatively, you may set the environment variables LIBSN_CFLAGS
7323 and LIBSN_LIBS to avoid the need to call pkg-config.
7324 See the pkg-config man page for more details." "$LINENO" 5
7325 elif test $pkg_failed = untried; then
7326 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7327 $as_echo "no" >&6; }
7328 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7329 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7330 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7331 is in your PATH or set the PKG_CONFIG environment variable to the full
7332 path to pkg-config.
7333
7334 Alternatively, you may set the environment variables LIBSN_CFLAGS
7335 and LIBSN_LIBS to avoid the need to call pkg-config.
7336 See the pkg-config man page for more details.
7337
7338 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7339 See \`config.log' for more details" "$LINENO" 5; }
7340 else
7341 LIBSN_CFLAGS=$pkg_cv_LIBSN_CFLAGS
7342 LIBSN_LIBS=$pkg_cv_LIBSN_LIBS
7343 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7344 $as_echo "yes" >&6; }
7345
7346 fi
7347
7348 pkg_failed=no
7349 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XCB" >&5
7350 $as_echo_n "checking for XCB... " >&6; }
7351
7352 if test -n "$XCB_CFLAGS"; then
7353 pkg_cv_XCB_CFLAGS="$XCB_CFLAGS"
7354 elif test -n "$PKG_CONFIG"; then
7355 if test -n "$PKG_CONFIG" && \
7356 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb xcb-xkb xcb-xinerama xcb-randr\""; } >&5
7357 ($PKG_CONFIG --exists --print-errors "xcb xcb-xkb xcb-xinerama xcb-randr") 2>&5
7358 ac_status=$?
7359 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7360 test $ac_status = 0; }; then
7361 pkg_cv_XCB_CFLAGS=`$PKG_CONFIG --cflags "xcb xcb-xkb xcb-xinerama xcb-randr" 2>/dev/null`
7362 test "x$?" != "x0" && pkg_failed=yes
7363 else
7364 pkg_failed=yes
7365 fi
7366 else
7367 pkg_failed=untried
7368 fi
7369 if test -n "$XCB_LIBS"; then
7370 pkg_cv_XCB_LIBS="$XCB_LIBS"
7371 elif test -n "$PKG_CONFIG"; then
7372 if test -n "$PKG_CONFIG" && \
7373 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb xcb-xkb xcb-xinerama xcb-randr\""; } >&5
7374 ($PKG_CONFIG --exists --print-errors "xcb xcb-xkb xcb-xinerama xcb-randr") 2>&5
7375 ac_status=$?
7376 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7377 test $ac_status = 0; }; then
7378 pkg_cv_XCB_LIBS=`$PKG_CONFIG --libs "xcb xcb-xkb xcb-xinerama xcb-randr" 2>/dev/null`
7379 test "x$?" != "x0" && pkg_failed=yes
7380 else
7381 pkg_failed=yes
7382 fi
7383 else
7384 pkg_failed=untried
7385 fi
7386
7387
7388
7389 if test $pkg_failed = yes; then
7390 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7391 $as_echo "no" >&6; }
7392
7393 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7394 _pkg_short_errors_supported=yes
7395 else
7396 _pkg_short_errors_supported=no
7397 fi
7398 if test $_pkg_short_errors_supported = yes; then
7399 XCB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xcb xcb-xkb xcb-xinerama xcb-randr" 2>&1`
7400 else
7401 XCB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xcb xcb-xkb xcb-xinerama xcb-randr" 2>&1`
7402 fi
7403 # Put the nasty error message in config.log where it belongs
7404 echo "$XCB_PKG_ERRORS" >&5
7405
7406 as_fn_error $? "Package requirements (xcb xcb-xkb xcb-xinerama xcb-randr) were not met:
7407
7408 $XCB_PKG_ERRORS
7409
7410 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7411 installed software in a non-standard prefix.
7412
7413 Alternatively, you may set the environment variables XCB_CFLAGS
7414 and XCB_LIBS to avoid the need to call pkg-config.
7415 See the pkg-config man page for more details." "$LINENO" 5
7416 elif test $pkg_failed = untried; then
7417 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7418 $as_echo "no" >&6; }
7419 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7420 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7421 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7422 is in your PATH or set the PKG_CONFIG environment variable to the full
7423 path to pkg-config.
7424
7425 Alternatively, you may set the environment variables XCB_CFLAGS
7426 and XCB_LIBS to avoid the need to call pkg-config.
7427 See the pkg-config man page for more details.
7428
7429 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7430 See \`config.log' for more details" "$LINENO" 5; }
7431 else
7432 XCB_CFLAGS=$pkg_cv_XCB_CFLAGS
7433 XCB_LIBS=$pkg_cv_XCB_LIBS
7434 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7435 $as_echo "yes" >&6; }
7436
7437 fi
7438
7439 pkg_failed=no
7440 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XCB_UTIL" >&5
7441 $as_echo_n "checking for XCB_UTIL... " >&6; }
7442
7443 if test -n "$XCB_UTIL_CFLAGS"; then
7444 pkg_cv_XCB_UTIL_CFLAGS="$XCB_UTIL_CFLAGS"
7445 elif test -n "$PKG_CONFIG"; then
7446 if test -n "$PKG_CONFIG" && \
7447 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-event xcb-util\""; } >&5
7448 ($PKG_CONFIG --exists --print-errors "xcb-event xcb-util") 2>&5
7449 ac_status=$?
7450 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7451 test $ac_status = 0; }; then
7452 pkg_cv_XCB_UTIL_CFLAGS=`$PKG_CONFIG --cflags "xcb-event xcb-util" 2>/dev/null`
7453 test "x$?" != "x0" && pkg_failed=yes
7454 else
7455 pkg_failed=yes
7456 fi
7457 else
7458 pkg_failed=untried
7459 fi
7460 if test -n "$XCB_UTIL_LIBS"; then
7461 pkg_cv_XCB_UTIL_LIBS="$XCB_UTIL_LIBS"
7462 elif test -n "$PKG_CONFIG"; then
7463 if test -n "$PKG_CONFIG" && \
7464 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-event xcb-util\""; } >&5
7465 ($PKG_CONFIG --exists --print-errors "xcb-event xcb-util") 2>&5
7466 ac_status=$?
7467 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7468 test $ac_status = 0; }; then
7469 pkg_cv_XCB_UTIL_LIBS=`$PKG_CONFIG --libs "xcb-event xcb-util" 2>/dev/null`
7470 test "x$?" != "x0" && pkg_failed=yes
7471 else
7472 pkg_failed=yes
7473 fi
7474 else
7475 pkg_failed=untried
7476 fi
7477
7478
7479
7480 if test $pkg_failed = yes; then
7481 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7482 $as_echo "no" >&6; }
7483
7484 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7485 _pkg_short_errors_supported=yes
7486 else
7487 _pkg_short_errors_supported=no
7488 fi
7489 if test $_pkg_short_errors_supported = yes; then
7490 XCB_UTIL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xcb-event xcb-util" 2>&1`
7491 else
7492 XCB_UTIL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xcb-event xcb-util" 2>&1`
7493 fi
7494 # Put the nasty error message in config.log where it belongs
7495 echo "$XCB_UTIL_PKG_ERRORS" >&5
7496
7497 as_fn_error $? "Package requirements (xcb-event xcb-util) were not met:
7498
7499 $XCB_UTIL_PKG_ERRORS
7500
7501 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7502 installed software in a non-standard prefix.
7503
7504 Alternatively, you may set the environment variables XCB_UTIL_CFLAGS
7505 and XCB_UTIL_LIBS to avoid the need to call pkg-config.
7506 See the pkg-config man page for more details." "$LINENO" 5
7507 elif test $pkg_failed = untried; then
7508 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7509 $as_echo "no" >&6; }
7510 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7511 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7512 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7513 is in your PATH or set the PKG_CONFIG environment variable to the full
7514 path to pkg-config.
7515
7516 Alternatively, you may set the environment variables XCB_UTIL_CFLAGS
7517 and XCB_UTIL_LIBS to avoid the need to call pkg-config.
7518 See the pkg-config man page for more details.
7519
7520 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7521 See \`config.log' for more details" "$LINENO" 5; }
7522 else
7523 XCB_UTIL_CFLAGS=$pkg_cv_XCB_UTIL_CFLAGS
7524 XCB_UTIL_LIBS=$pkg_cv_XCB_UTIL_LIBS
7525 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7526 $as_echo "yes" >&6; }
7527
7528 fi
7529
7530 pkg_failed=no
7531 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XCB_UTIL_CURSOR" >&5
7532 $as_echo_n "checking for XCB_UTIL_CURSOR... " >&6; }
7533
7534 if test -n "$XCB_UTIL_CURSOR_CFLAGS"; then
7535 pkg_cv_XCB_UTIL_CURSOR_CFLAGS="$XCB_UTIL_CURSOR_CFLAGS"
7536 elif test -n "$PKG_CONFIG"; then
7537 if test -n "$PKG_CONFIG" && \
7538 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-cursor\""; } >&5
7539 ($PKG_CONFIG --exists --print-errors "xcb-cursor") 2>&5
7540 ac_status=$?
7541 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7542 test $ac_status = 0; }; then
7543 pkg_cv_XCB_UTIL_CURSOR_CFLAGS=`$PKG_CONFIG --cflags "xcb-cursor" 2>/dev/null`
7544 test "x$?" != "x0" && pkg_failed=yes
7545 else
7546 pkg_failed=yes
7547 fi
7548 else
7549 pkg_failed=untried
7550 fi
7551 if test -n "$XCB_UTIL_CURSOR_LIBS"; then
7552 pkg_cv_XCB_UTIL_CURSOR_LIBS="$XCB_UTIL_CURSOR_LIBS"
7553 elif test -n "$PKG_CONFIG"; then
7554 if test -n "$PKG_CONFIG" && \
7555 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-cursor\""; } >&5
7556 ($PKG_CONFIG --exists --print-errors "xcb-cursor") 2>&5
7557 ac_status=$?
7558 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7559 test $ac_status = 0; }; then
7560 pkg_cv_XCB_UTIL_CURSOR_LIBS=`$PKG_CONFIG --libs "xcb-cursor" 2>/dev/null`
7561 test "x$?" != "x0" && pkg_failed=yes
7562 else
7563 pkg_failed=yes
7564 fi
7565 else
7566 pkg_failed=untried
7567 fi
7568
7569
7570
7571 if test $pkg_failed = yes; then
7572 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7573 $as_echo "no" >&6; }
7574
7575 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7576 _pkg_short_errors_supported=yes
7577 else
7578 _pkg_short_errors_supported=no
7579 fi
7580 if test $_pkg_short_errors_supported = yes; then
7581 XCB_UTIL_CURSOR_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xcb-cursor" 2>&1`
7582 else
7583 XCB_UTIL_CURSOR_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xcb-cursor" 2>&1`
7584 fi
7585 # Put the nasty error message in config.log where it belongs
7586 echo "$XCB_UTIL_CURSOR_PKG_ERRORS" >&5
7587
7588 as_fn_error $? "Package requirements (xcb-cursor) were not met:
7589
7590 $XCB_UTIL_CURSOR_PKG_ERRORS
7591
7592 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7593 installed software in a non-standard prefix.
7594
7595 Alternatively, you may set the environment variables XCB_UTIL_CURSOR_CFLAGS
7596 and XCB_UTIL_CURSOR_LIBS to avoid the need to call pkg-config.
7597 See the pkg-config man page for more details." "$LINENO" 5
7598 elif test $pkg_failed = untried; then
7599 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7600 $as_echo "no" >&6; }
7601 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7602 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7603 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7604 is in your PATH or set the PKG_CONFIG environment variable to the full
7605 path to pkg-config.
7606
7607 Alternatively, you may set the environment variables XCB_UTIL_CURSOR_CFLAGS
7608 and XCB_UTIL_CURSOR_LIBS to avoid the need to call pkg-config.
7609 See the pkg-config man page for more details.
7610
7611 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7612 See \`config.log' for more details" "$LINENO" 5; }
7613 else
7614 XCB_UTIL_CURSOR_CFLAGS=$pkg_cv_XCB_UTIL_CURSOR_CFLAGS
7615 XCB_UTIL_CURSOR_LIBS=$pkg_cv_XCB_UTIL_CURSOR_LIBS
7616 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7617 $as_echo "yes" >&6; }
7618
7619 fi
7620
7621 pkg_failed=no
7622 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XCB_UTIL_KEYSYMS" >&5
7623 $as_echo_n "checking for XCB_UTIL_KEYSYMS... " >&6; }
7624
7625 if test -n "$XCB_UTIL_KEYSYMS_CFLAGS"; then
7626 pkg_cv_XCB_UTIL_KEYSYMS_CFLAGS="$XCB_UTIL_KEYSYMS_CFLAGS"
7627 elif test -n "$PKG_CONFIG"; then
7628 if test -n "$PKG_CONFIG" && \
7629 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-keysyms\""; } >&5
7630 ($PKG_CONFIG --exists --print-errors "xcb-keysyms") 2>&5
7631 ac_status=$?
7632 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7633 test $ac_status = 0; }; then
7634 pkg_cv_XCB_UTIL_KEYSYMS_CFLAGS=`$PKG_CONFIG --cflags "xcb-keysyms" 2>/dev/null`
7635 test "x$?" != "x0" && pkg_failed=yes
7636 else
7637 pkg_failed=yes
7638 fi
7639 else
7640 pkg_failed=untried
7641 fi
7642 if test -n "$XCB_UTIL_KEYSYMS_LIBS"; then
7643 pkg_cv_XCB_UTIL_KEYSYMS_LIBS="$XCB_UTIL_KEYSYMS_LIBS"
7644 elif test -n "$PKG_CONFIG"; then
7645 if test -n "$PKG_CONFIG" && \
7646 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-keysyms\""; } >&5
7647 ($PKG_CONFIG --exists --print-errors "xcb-keysyms") 2>&5
7648 ac_status=$?
7649 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7650 test $ac_status = 0; }; then
7651 pkg_cv_XCB_UTIL_KEYSYMS_LIBS=`$PKG_CONFIG --libs "xcb-keysyms" 2>/dev/null`
7652 test "x$?" != "x0" && pkg_failed=yes
7653 else
7654 pkg_failed=yes
7655 fi
7656 else
7657 pkg_failed=untried
7658 fi
7659
7660
7661
7662 if test $pkg_failed = yes; then
7663 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7664 $as_echo "no" >&6; }
7665
7666 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7667 _pkg_short_errors_supported=yes
7668 else
7669 _pkg_short_errors_supported=no
7670 fi
7671 if test $_pkg_short_errors_supported = yes; then
7672 XCB_UTIL_KEYSYMS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xcb-keysyms" 2>&1`
7673 else
7674 XCB_UTIL_KEYSYMS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xcb-keysyms" 2>&1`
7675 fi
7676 # Put the nasty error message in config.log where it belongs
7677 echo "$XCB_UTIL_KEYSYMS_PKG_ERRORS" >&5
7678
7679 as_fn_error $? "Package requirements (xcb-keysyms) were not met:
7680
7681 $XCB_UTIL_KEYSYMS_PKG_ERRORS
7682
7683 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7684 installed software in a non-standard prefix.
7685
7686 Alternatively, you may set the environment variables XCB_UTIL_KEYSYMS_CFLAGS
7687 and XCB_UTIL_KEYSYMS_LIBS to avoid the need to call pkg-config.
7688 See the pkg-config man page for more details." "$LINENO" 5
7689 elif test $pkg_failed = untried; then
7690 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7691 $as_echo "no" >&6; }
7692 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7693 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7694 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7695 is in your PATH or set the PKG_CONFIG environment variable to the full
7696 path to pkg-config.
7697
7698 Alternatively, you may set the environment variables XCB_UTIL_KEYSYMS_CFLAGS
7699 and XCB_UTIL_KEYSYMS_LIBS to avoid the need to call pkg-config.
7700 See the pkg-config man page for more details.
7701
7702 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7703 See \`config.log' for more details" "$LINENO" 5; }
7704 else
7705 XCB_UTIL_KEYSYMS_CFLAGS=$pkg_cv_XCB_UTIL_KEYSYMS_CFLAGS
7706 XCB_UTIL_KEYSYMS_LIBS=$pkg_cv_XCB_UTIL_KEYSYMS_LIBS
7707 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7708 $as_echo "yes" >&6; }
7709
7710 fi
7711
7712 pkg_failed=no
7713 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XCB_UTIL_WM" >&5
7714 $as_echo_n "checking for XCB_UTIL_WM... " >&6; }
7715
7716 if test -n "$XCB_UTIL_WM_CFLAGS"; then
7717 pkg_cv_XCB_UTIL_WM_CFLAGS="$XCB_UTIL_WM_CFLAGS"
7718 elif test -n "$PKG_CONFIG"; then
7719 if test -n "$PKG_CONFIG" && \
7720 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-icccm\""; } >&5
7721 ($PKG_CONFIG --exists --print-errors "xcb-icccm") 2>&5
7722 ac_status=$?
7723 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7724 test $ac_status = 0; }; then
7725 pkg_cv_XCB_UTIL_WM_CFLAGS=`$PKG_CONFIG --cflags "xcb-icccm" 2>/dev/null`
7726 test "x$?" != "x0" && pkg_failed=yes
7727 else
7728 pkg_failed=yes
7729 fi
7730 else
7731 pkg_failed=untried
7732 fi
7733 if test -n "$XCB_UTIL_WM_LIBS"; then
7734 pkg_cv_XCB_UTIL_WM_LIBS="$XCB_UTIL_WM_LIBS"
7735 elif test -n "$PKG_CONFIG"; then
7736 if test -n "$PKG_CONFIG" && \
7737 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-icccm\""; } >&5
7738 ($PKG_CONFIG --exists --print-errors "xcb-icccm") 2>&5
7739 ac_status=$?
7740 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7741 test $ac_status = 0; }; then
7742 pkg_cv_XCB_UTIL_WM_LIBS=`$PKG_CONFIG --libs "xcb-icccm" 2>/dev/null`
7743 test "x$?" != "x0" && pkg_failed=yes
7744 else
7745 pkg_failed=yes
7746 fi
7747 else
7748 pkg_failed=untried
7749 fi
7750
7751
7752
7753 if test $pkg_failed = yes; then
7754 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7755 $as_echo "no" >&6; }
7756
7757 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7758 _pkg_short_errors_supported=yes
7759 else
7760 _pkg_short_errors_supported=no
7761 fi
7762 if test $_pkg_short_errors_supported = yes; then
7763 XCB_UTIL_WM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xcb-icccm" 2>&1`
7764 else
7765 XCB_UTIL_WM_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xcb-icccm" 2>&1`
7766 fi
7767 # Put the nasty error message in config.log where it belongs
7768 echo "$XCB_UTIL_WM_PKG_ERRORS" >&5
7769
7770 as_fn_error $? "Package requirements (xcb-icccm) were not met:
7771
7772 $XCB_UTIL_WM_PKG_ERRORS
7773
7774 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7775 installed software in a non-standard prefix.
7776
7777 Alternatively, you may set the environment variables XCB_UTIL_WM_CFLAGS
7778 and XCB_UTIL_WM_LIBS to avoid the need to call pkg-config.
7779 See the pkg-config man page for more details." "$LINENO" 5
7780 elif test $pkg_failed = untried; then
7781 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7782 $as_echo "no" >&6; }
7783 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7784 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7785 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7786 is in your PATH or set the PKG_CONFIG environment variable to the full
7787 path to pkg-config.
7788
7789 Alternatively, you may set the environment variables XCB_UTIL_WM_CFLAGS
7790 and XCB_UTIL_WM_LIBS to avoid the need to call pkg-config.
7791 See the pkg-config man page for more details.
7792
7793 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7794 See \`config.log' for more details" "$LINENO" 5; }
7795 else
7796 XCB_UTIL_WM_CFLAGS=$pkg_cv_XCB_UTIL_WM_CFLAGS
7797 XCB_UTIL_WM_LIBS=$pkg_cv_XCB_UTIL_WM_LIBS
7798 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7799 $as_echo "yes" >&6; }
7800
7801 fi
7802
7803 pkg_failed=no
7804 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XCB_UTIL_XRM" >&5
7805 $as_echo_n "checking for XCB_UTIL_XRM... " >&6; }
7806
7807 if test -n "$XCB_UTIL_XRM_CFLAGS"; then
7808 pkg_cv_XCB_UTIL_XRM_CFLAGS="$XCB_UTIL_XRM_CFLAGS"
7809 elif test -n "$PKG_CONFIG"; then
7810 if test -n "$PKG_CONFIG" && \
7811 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-xrm\""; } >&5
7812 ($PKG_CONFIG --exists --print-errors "xcb-xrm") 2>&5
7813 ac_status=$?
7814 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7815 test $ac_status = 0; }; then
7816 pkg_cv_XCB_UTIL_XRM_CFLAGS=`$PKG_CONFIG --cflags "xcb-xrm" 2>/dev/null`
7817 test "x$?" != "x0" && pkg_failed=yes
7818 else
7819 pkg_failed=yes
7820 fi
7821 else
7822 pkg_failed=untried
7823 fi
7824 if test -n "$XCB_UTIL_XRM_LIBS"; then
7825 pkg_cv_XCB_UTIL_XRM_LIBS="$XCB_UTIL_XRM_LIBS"
7826 elif test -n "$PKG_CONFIG"; then
7827 if test -n "$PKG_CONFIG" && \
7828 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xcb-xrm\""; } >&5
7829 ($PKG_CONFIG --exists --print-errors "xcb-xrm") 2>&5
7830 ac_status=$?
7831 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7832 test $ac_status = 0; }; then
7833 pkg_cv_XCB_UTIL_XRM_LIBS=`$PKG_CONFIG --libs "xcb-xrm" 2>/dev/null`
7834 test "x$?" != "x0" && pkg_failed=yes
7835 else
7836 pkg_failed=yes
7837 fi
7838 else
7839 pkg_failed=untried
7840 fi
7841
7842
7843
7844 if test $pkg_failed = yes; then
7845 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7846 $as_echo "no" >&6; }
7847
7848 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7849 _pkg_short_errors_supported=yes
7850 else
7851 _pkg_short_errors_supported=no
7852 fi
7853 if test $_pkg_short_errors_supported = yes; then
7854 XCB_UTIL_XRM_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xcb-xrm" 2>&1`
7855 else
7856 XCB_UTIL_XRM_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xcb-xrm" 2>&1`
7857 fi
7858 # Put the nasty error message in config.log where it belongs
7859 echo "$XCB_UTIL_XRM_PKG_ERRORS" >&5
7860
7861 as_fn_error $? "Package requirements (xcb-xrm) were not met:
7862
7863 $XCB_UTIL_XRM_PKG_ERRORS
7864
7865 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7866 installed software in a non-standard prefix.
7867
7868 Alternatively, you may set the environment variables XCB_UTIL_XRM_CFLAGS
7869 and XCB_UTIL_XRM_LIBS to avoid the need to call pkg-config.
7870 See the pkg-config man page for more details." "$LINENO" 5
7871 elif test $pkg_failed = untried; then
7872 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7873 $as_echo "no" >&6; }
7874 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7875 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7876 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7877 is in your PATH or set the PKG_CONFIG environment variable to the full
7878 path to pkg-config.
7879
7880 Alternatively, you may set the environment variables XCB_UTIL_XRM_CFLAGS
7881 and XCB_UTIL_XRM_LIBS to avoid the need to call pkg-config.
7882 See the pkg-config man page for more details.
7883
7884 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7885 See \`config.log' for more details" "$LINENO" 5; }
7886 else
7887 XCB_UTIL_XRM_CFLAGS=$pkg_cv_XCB_UTIL_XRM_CFLAGS
7888 XCB_UTIL_XRM_LIBS=$pkg_cv_XCB_UTIL_XRM_LIBS
7889 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7890 $as_echo "yes" >&6; }
7891
7892 fi
7893
7894 pkg_failed=no
7895 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XKBCOMMON" >&5
7896 $as_echo_n "checking for XKBCOMMON... " >&6; }
7897
7898 if test -n "$XKBCOMMON_CFLAGS"; then
7899 pkg_cv_XKBCOMMON_CFLAGS="$XKBCOMMON_CFLAGS"
7900 elif test -n "$PKG_CONFIG"; then
7901 if test -n "$PKG_CONFIG" && \
7902 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xkbcommon xkbcommon-x11\""; } >&5
7903 ($PKG_CONFIG --exists --print-errors "xkbcommon xkbcommon-x11") 2>&5
7904 ac_status=$?
7905 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7906 test $ac_status = 0; }; then
7907 pkg_cv_XKBCOMMON_CFLAGS=`$PKG_CONFIG --cflags "xkbcommon xkbcommon-x11" 2>/dev/null`
7908 test "x$?" != "x0" && pkg_failed=yes
7909 else
7910 pkg_failed=yes
7911 fi
7912 else
7913 pkg_failed=untried
7914 fi
7915 if test -n "$XKBCOMMON_LIBS"; then
7916 pkg_cv_XKBCOMMON_LIBS="$XKBCOMMON_LIBS"
7917 elif test -n "$PKG_CONFIG"; then
7918 if test -n "$PKG_CONFIG" && \
7919 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"xkbcommon xkbcommon-x11\""; } >&5
7920 ($PKG_CONFIG --exists --print-errors "xkbcommon xkbcommon-x11") 2>&5
7921 ac_status=$?
7922 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7923 test $ac_status = 0; }; then
7924 pkg_cv_XKBCOMMON_LIBS=`$PKG_CONFIG --libs "xkbcommon xkbcommon-x11" 2>/dev/null`
7925 test "x$?" != "x0" && pkg_failed=yes
7926 else
7927 pkg_failed=yes
7928 fi
7929 else
7930 pkg_failed=untried
7931 fi
7932
7933
7934
7935 if test $pkg_failed = yes; then
7936 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7937 $as_echo "no" >&6; }
7938
7939 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
7940 _pkg_short_errors_supported=yes
7941 else
7942 _pkg_short_errors_supported=no
7943 fi
7944 if test $_pkg_short_errors_supported = yes; then
7945 XKBCOMMON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "xkbcommon xkbcommon-x11" 2>&1`
7946 else
7947 XKBCOMMON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "xkbcommon xkbcommon-x11" 2>&1`
7948 fi
7949 # Put the nasty error message in config.log where it belongs
7950 echo "$XKBCOMMON_PKG_ERRORS" >&5
7951
7952 as_fn_error $? "Package requirements (xkbcommon xkbcommon-x11) were not met:
7953
7954 $XKBCOMMON_PKG_ERRORS
7955
7956 Consider adjusting the PKG_CONFIG_PATH environment variable if you
7957 installed software in a non-standard prefix.
7958
7959 Alternatively, you may set the environment variables XKBCOMMON_CFLAGS
7960 and XKBCOMMON_LIBS to avoid the need to call pkg-config.
7961 See the pkg-config man page for more details." "$LINENO" 5
7962 elif test $pkg_failed = untried; then
7963 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
7964 $as_echo "no" >&6; }
7965 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
7966 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
7967 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
7968 is in your PATH or set the PKG_CONFIG environment variable to the full
7969 path to pkg-config.
7970
7971 Alternatively, you may set the environment variables XKBCOMMON_CFLAGS
7972 and XKBCOMMON_LIBS to avoid the need to call pkg-config.
7973 See the pkg-config man page for more details.
7974
7975 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
7976 See \`config.log' for more details" "$LINENO" 5; }
7977 else
7978 XKBCOMMON_CFLAGS=$pkg_cv_XKBCOMMON_CFLAGS
7979 XKBCOMMON_LIBS=$pkg_cv_XKBCOMMON_LIBS
7980 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
7981 $as_echo "yes" >&6; }
7982
7983 fi
7984
7985 pkg_failed=no
7986 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for YAJL" >&5
7987 $as_echo_n "checking for YAJL... " >&6; }
7988
7989 if test -n "$YAJL_CFLAGS"; then
7990 pkg_cv_YAJL_CFLAGS="$YAJL_CFLAGS"
7991 elif test -n "$PKG_CONFIG"; then
7992 if test -n "$PKG_CONFIG" && \
7993 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"yajl\""; } >&5
7994 ($PKG_CONFIG --exists --print-errors "yajl") 2>&5
7995 ac_status=$?
7996 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
7997 test $ac_status = 0; }; then
7998 pkg_cv_YAJL_CFLAGS=`$PKG_CONFIG --cflags "yajl" 2>/dev/null`
7999 test "x$?" != "x0" && pkg_failed=yes
8000 else
8001 pkg_failed=yes
8002 fi
8003 else
8004 pkg_failed=untried
8005 fi
8006 if test -n "$YAJL_LIBS"; then
8007 pkg_cv_YAJL_LIBS="$YAJL_LIBS"
8008 elif test -n "$PKG_CONFIG"; then
8009 if test -n "$PKG_CONFIG" && \
8010 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"yajl\""; } >&5
8011 ($PKG_CONFIG --exists --print-errors "yajl") 2>&5
8012 ac_status=$?
8013 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8014 test $ac_status = 0; }; then
8015 pkg_cv_YAJL_LIBS=`$PKG_CONFIG --libs "yajl" 2>/dev/null`
8016 test "x$?" != "x0" && pkg_failed=yes
8017 else
8018 pkg_failed=yes
8019 fi
8020 else
8021 pkg_failed=untried
8022 fi
8023
8024
8025
8026 if test $pkg_failed = yes; then
8027 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8028 $as_echo "no" >&6; }
8029
8030 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
8031 _pkg_short_errors_supported=yes
8032 else
8033 _pkg_short_errors_supported=no
8034 fi
8035 if test $_pkg_short_errors_supported = yes; then
8036 YAJL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "yajl" 2>&1`
8037 else
8038 YAJL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "yajl" 2>&1`
8039 fi
8040 # Put the nasty error message in config.log where it belongs
8041 echo "$YAJL_PKG_ERRORS" >&5
8042
8043 as_fn_error $? "Package requirements (yajl) were not met:
8044
8045 $YAJL_PKG_ERRORS
8046
8047 Consider adjusting the PKG_CONFIG_PATH environment variable if you
8048 installed software in a non-standard prefix.
8049
8050 Alternatively, you may set the environment variables YAJL_CFLAGS
8051 and YAJL_LIBS to avoid the need to call pkg-config.
8052 See the pkg-config man page for more details." "$LINENO" 5
8053 elif test $pkg_failed = untried; then
8054 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8055 $as_echo "no" >&6; }
8056 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
8057 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
8058 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
8059 is in your PATH or set the PKG_CONFIG environment variable to the full
8060 path to pkg-config.
8061
8062 Alternatively, you may set the environment variables YAJL_CFLAGS
8063 and YAJL_LIBS to avoid the need to call pkg-config.
8064 See the pkg-config man page for more details.
8065
8066 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
8067 See \`config.log' for more details" "$LINENO" 5; }
8068 else
8069 YAJL_CFLAGS=$pkg_cv_YAJL_CFLAGS
8070 YAJL_LIBS=$pkg_cv_YAJL_LIBS
8071 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
8072 $as_echo "yes" >&6; }
8073
8074 fi
8075
8076 pkg_failed=no
8077 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBPCRE" >&5
8078 $as_echo_n "checking for LIBPCRE... " >&6; }
8079
8080 if test -n "$LIBPCRE_CFLAGS"; then
8081 pkg_cv_LIBPCRE_CFLAGS="$LIBPCRE_CFLAGS"
8082 elif test -n "$PKG_CONFIG"; then
8083 if test -n "$PKG_CONFIG" && \
8084 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpcre >= 8.10\""; } >&5
8085 ($PKG_CONFIG --exists --print-errors "libpcre >= 8.10") 2>&5
8086 ac_status=$?
8087 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8088 test $ac_status = 0; }; then
8089 pkg_cv_LIBPCRE_CFLAGS=`$PKG_CONFIG --cflags "libpcre >= 8.10" 2>/dev/null`
8090 test "x$?" != "x0" && pkg_failed=yes
8091 else
8092 pkg_failed=yes
8093 fi
8094 else
8095 pkg_failed=untried
8096 fi
8097 if test -n "$LIBPCRE_LIBS"; then
8098 pkg_cv_LIBPCRE_LIBS="$LIBPCRE_LIBS"
8099 elif test -n "$PKG_CONFIG"; then
8100 if test -n "$PKG_CONFIG" && \
8101 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpcre >= 8.10\""; } >&5
8102 ($PKG_CONFIG --exists --print-errors "libpcre >= 8.10") 2>&5
8103 ac_status=$?
8104 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8105 test $ac_status = 0; }; then
8106 pkg_cv_LIBPCRE_LIBS=`$PKG_CONFIG --libs "libpcre >= 8.10" 2>/dev/null`
8107 test "x$?" != "x0" && pkg_failed=yes
8108 else
8109 pkg_failed=yes
8110 fi
8111 else
8112 pkg_failed=untried
8113 fi
8114
8115
8116
8117 if test $pkg_failed = yes; then
8118 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8119 $as_echo "no" >&6; }
8120
8121 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
8122 _pkg_short_errors_supported=yes
8123 else
8124 _pkg_short_errors_supported=no
8125 fi
8126 if test $_pkg_short_errors_supported = yes; then
8127 LIBPCRE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpcre >= 8.10" 2>&1`
8128 else
8129 LIBPCRE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpcre >= 8.10" 2>&1`
8130 fi
8131 # Put the nasty error message in config.log where it belongs
8132 echo "$LIBPCRE_PKG_ERRORS" >&5
8133
8134 as_fn_error $? "Package requirements (libpcre >= 8.10) were not met:
8135
8136 $LIBPCRE_PKG_ERRORS
8137
8138 Consider adjusting the PKG_CONFIG_PATH environment variable if you
8139 installed software in a non-standard prefix.
8140
8141 Alternatively, you may set the environment variables LIBPCRE_CFLAGS
8142 and LIBPCRE_LIBS to avoid the need to call pkg-config.
8143 See the pkg-config man page for more details." "$LINENO" 5
8144 elif test $pkg_failed = untried; then
8145 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8146 $as_echo "no" >&6; }
8147 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
8148 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
8149 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
8150 is in your PATH or set the PKG_CONFIG environment variable to the full
8151 path to pkg-config.
8152
8153 Alternatively, you may set the environment variables LIBPCRE_CFLAGS
8154 and LIBPCRE_LIBS to avoid the need to call pkg-config.
8155 See the pkg-config man page for more details.
8156
8157 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
8158 See \`config.log' for more details" "$LINENO" 5; }
8159 else
8160 LIBPCRE_CFLAGS=$pkg_cv_LIBPCRE_CFLAGS
8161 LIBPCRE_LIBS=$pkg_cv_LIBPCRE_LIBS
8162 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
8163 $as_echo "yes" >&6; }
8164
8165 fi
8166
8167 pkg_failed=no
8168 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PANGOCAIRO" >&5
8169 $as_echo_n "checking for PANGOCAIRO... " >&6; }
8170
8171 if test -n "$PANGOCAIRO_CFLAGS"; then
8172 pkg_cv_PANGOCAIRO_CFLAGS="$PANGOCAIRO_CFLAGS"
8173 elif test -n "$PKG_CONFIG"; then
8174 if test -n "$PKG_CONFIG" && \
8175 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cairo >= 1.14.4 pangocairo\""; } >&5
8176 ($PKG_CONFIG --exists --print-errors "cairo >= 1.14.4 pangocairo") 2>&5
8177 ac_status=$?
8178 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8179 test $ac_status = 0; }; then
8180 pkg_cv_PANGOCAIRO_CFLAGS=`$PKG_CONFIG --cflags "cairo >= 1.14.4 pangocairo" 2>/dev/null`
8181 test "x$?" != "x0" && pkg_failed=yes
8182 else
8183 pkg_failed=yes
8184 fi
8185 else
8186 pkg_failed=untried
8187 fi
8188 if test -n "$PANGOCAIRO_LIBS"; then
8189 pkg_cv_PANGOCAIRO_LIBS="$PANGOCAIRO_LIBS"
8190 elif test -n "$PKG_CONFIG"; then
8191 if test -n "$PKG_CONFIG" && \
8192 { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cairo >= 1.14.4 pangocairo\""; } >&5
8193 ($PKG_CONFIG --exists --print-errors "cairo >= 1.14.4 pangocairo") 2>&5
8194 ac_status=$?
8195 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8196 test $ac_status = 0; }; then
8197 pkg_cv_PANGOCAIRO_LIBS=`$PKG_CONFIG --libs "cairo >= 1.14.4 pangocairo" 2>/dev/null`
8198 test "x$?" != "x0" && pkg_failed=yes
8199 else
8200 pkg_failed=yes
8201 fi
8202 else
8203 pkg_failed=untried
8204 fi
8205
8206
8207
8208 if test $pkg_failed = yes; then
8209 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8210 $as_echo "no" >&6; }
8211
8212 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
8213 _pkg_short_errors_supported=yes
8214 else
8215 _pkg_short_errors_supported=no
8216 fi
8217 if test $_pkg_short_errors_supported = yes; then
8218 PANGOCAIRO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cairo >= 1.14.4 pangocairo" 2>&1`
8219 else
8220 PANGOCAIRO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cairo >= 1.14.4 pangocairo" 2>&1`
8221 fi
8222 # Put the nasty error message in config.log where it belongs
8223 echo "$PANGOCAIRO_PKG_ERRORS" >&5
8224
8225 as_fn_error $? "Package requirements (cairo >= 1.14.4 pangocairo) were not met:
8226
8227 $PANGOCAIRO_PKG_ERRORS
8228
8229 Consider adjusting the PKG_CONFIG_PATH environment variable if you
8230 installed software in a non-standard prefix.
8231
8232 Alternatively, you may set the environment variables PANGOCAIRO_CFLAGS
8233 and PANGOCAIRO_LIBS to avoid the need to call pkg-config.
8234 See the pkg-config man page for more details." "$LINENO" 5
8235 elif test $pkg_failed = untried; then
8236 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8237 $as_echo "no" >&6; }
8238 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
8239 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
8240 as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
8241 is in your PATH or set the PKG_CONFIG environment variable to the full
8242 path to pkg-config.
8243
8244 Alternatively, you may set the environment variables PANGOCAIRO_CFLAGS
8245 and PANGOCAIRO_LIBS to avoid the need to call pkg-config.
8246 See the pkg-config man page for more details.
8247
8248 To get pkg-config, see <http://pkg-config.freedesktop.org/>.
8249 See \`config.log' for more details" "$LINENO" 5; }
8250 else
8251 PANGOCAIRO_CFLAGS=$pkg_cv_PANGOCAIRO_CFLAGS
8252 PANGOCAIRO_LIBS=$pkg_cv_PANGOCAIRO_LIBS
8253 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
8254 $as_echo "yes" >&6; }
8255
8256 fi
8257
8258 # Checks for programs.
8259 for ac_prog in gawk mawk nawk awk
8260 do
8261 # Extract the first word of "$ac_prog", so it can be a program name with args.
8262 set dummy $ac_prog; ac_word=$2
8263 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8264 $as_echo_n "checking for $ac_word... " >&6; }
8265 if ${ac_cv_prog_AWK+:} false; then :
8266 $as_echo_n "(cached) " >&6
8267 else
8268 if test -n "$AWK"; then
8269 ac_cv_prog_AWK="$AWK" # Let the user override the test.
8270 else
8271 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8272 for as_dir in $PATH
8273 do
8274 IFS=$as_save_IFS
8275 test -z "$as_dir" && as_dir=.
8276 for ac_exec_ext in '' $ac_executable_extensions; do
8277 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8278 ac_cv_prog_AWK="$ac_prog"
8279 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8280 break 2
8281 fi
8282 done
8283 done
8284 IFS=$as_save_IFS
8285
8286 fi
8287 fi
8288 AWK=$ac_cv_prog_AWK
8289 if test -n "$AWK"; then
8290 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
8291 $as_echo "$AWK" >&6; }
8292 else
8293 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8294 $as_echo "no" >&6; }
8295 fi
8296
8297
8298 test -n "$AWK" && break
8299 done
8300
8301 ac_ext=c
8302 ac_cpp='$CPP $CPPFLAGS'
8303 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
8304 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
8305 ac_compiler_gnu=$ac_cv_c_compiler_gnu
8306 { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
8307 $as_echo_n "checking how to run the C preprocessor... " >&6; }
8308 # On Suns, sometimes $CPP names a directory.
8309 if test -n "$CPP" && test -d "$CPP"; then
8310 CPP=
8311 fi
8312 if test -z "$CPP"; then
8313 if ${ac_cv_prog_CPP+:} false; then :
8314 $as_echo_n "(cached) " >&6
8315 else
8316 # Double quotes because CPP needs to be expanded
8317 for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
8318 do
8319 ac_preproc_ok=false
8320 for ac_c_preproc_warn_flag in '' yes
8321 do
8322 # Use a header file that comes with gcc, so configuring glibc
8323 # with a fresh cross-compiler works.
8324 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
8325 # <limits.h> exists even on freestanding compilers.
8326 # On the NeXT, cc -E runs the code through the compiler's parser,
8327 # not just through cpp. "Syntax error" is here to catch this case.
8328 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8329 /* end confdefs.h. */
8330 #ifdef __STDC__
8331 # include <limits.h>
8332 #else
8333 # include <assert.h>
8334 #endif
8335 Syntax error
8336 _ACEOF
8337 if ac_fn_c_try_cpp "$LINENO"; then :
8338
8339 else
8340 # Broken: fails on valid input.
8341 continue
8342 fi
8343 rm -f conftest.err conftest.i conftest.$ac_ext
8344
8345 # OK, works on sane cases. Now check whether nonexistent headers
8346 # can be detected and how.
8347 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8348 /* end confdefs.h. */
8349 #include <ac_nonexistent.h>
8350 _ACEOF
8351 if ac_fn_c_try_cpp "$LINENO"; then :
8352 # Broken: success on invalid input.
8353 continue
8354 else
8355 # Passes both tests.
8356 ac_preproc_ok=:
8357 break
8358 fi
8359 rm -f conftest.err conftest.i conftest.$ac_ext
8360
8361 done
8362 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
8363 rm -f conftest.i conftest.err conftest.$ac_ext
8364 if $ac_preproc_ok; then :
8365 break
8366 fi
8367
8368 done
8369 ac_cv_prog_CPP=$CPP
8370
8371 fi
8372 CPP=$ac_cv_prog_CPP
8373 else
8374 ac_cv_prog_CPP=$CPP
8375 fi
8376 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
8377 $as_echo "$CPP" >&6; }
8378 ac_preproc_ok=false
8379 for ac_c_preproc_warn_flag in '' yes
8380 do
8381 # Use a header file that comes with gcc, so configuring glibc
8382 # with a fresh cross-compiler works.
8383 # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
8384 # <limits.h> exists even on freestanding compilers.
8385 # On the NeXT, cc -E runs the code through the compiler's parser,
8386 # not just through cpp. "Syntax error" is here to catch this case.
8387 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8388 /* end confdefs.h. */
8389 #ifdef __STDC__
8390 # include <limits.h>
8391 #else
8392 # include <assert.h>
8393 #endif
8394 Syntax error
8395 _ACEOF
8396 if ac_fn_c_try_cpp "$LINENO"; then :
8397
8398 else
8399 # Broken: fails on valid input.
8400 continue
8401 fi
8402 rm -f conftest.err conftest.i conftest.$ac_ext
8403
8404 # OK, works on sane cases. Now check whether nonexistent headers
8405 # can be detected and how.
8406 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8407 /* end confdefs.h. */
8408 #include <ac_nonexistent.h>
8409 _ACEOF
8410 if ac_fn_c_try_cpp "$LINENO"; then :
8411 # Broken: success on invalid input.
8412 continue
8413 else
8414 # Passes both tests.
8415 ac_preproc_ok=:
8416 break
8417 fi
8418 rm -f conftest.err conftest.i conftest.$ac_ext
8419
8420 done
8421 # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
8422 rm -f conftest.i conftest.err conftest.$ac_ext
8423 if $ac_preproc_ok; then :
8424
8425 else
8426 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
8427 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
8428 as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
8429 See \`config.log' for more details" "$LINENO" 5; }
8430 fi
8431
8432 ac_ext=c
8433 ac_cpp='$CPP $CPPFLAGS'
8434 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
8435 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
8436 ac_compiler_gnu=$ac_cv_c_compiler_gnu
8437
8438
8439 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
8440 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
8441 set x ${MAKE-make}
8442 ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
8443 if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
8444 $as_echo_n "(cached) " >&6
8445 else
8446 cat >conftest.make <<\_ACEOF
8447 SHELL = /bin/sh
8448 all:
8449 @echo '@@@%%%=$(MAKE)=@@@%%%'
8450 _ACEOF
8451 # GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
8452 case `${MAKE-make} -f conftest.make 2>/dev/null` in
8453 *@@@%%%=?*=@@@%%%*)
8454 eval ac_cv_prog_make_${ac_make}_set=yes;;
8455 *)
8456 eval ac_cv_prog_make_${ac_make}_set=no;;
8457 esac
8458 rm -f conftest.make
8459 fi
8460 if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
8461 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
8462 $as_echo "yes" >&6; }
8463 SET_MAKE=
8464 else
8465 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8466 $as_echo "no" >&6; }
8467 SET_MAKE="MAKE=${MAKE-make}"
8468 fi
8469
8470 if test -n "$ac_tool_prefix"; then
8471 # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
8472 set dummy ${ac_tool_prefix}ranlib; ac_word=$2
8473 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8474 $as_echo_n "checking for $ac_word... " >&6; }
8475 if ${ac_cv_prog_RANLIB+:} false; then :
8476 $as_echo_n "(cached) " >&6
8477 else
8478 if test -n "$RANLIB"; then
8479 ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
8480 else
8481 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8482 for as_dir in $PATH
8483 do
8484 IFS=$as_save_IFS
8485 test -z "$as_dir" && as_dir=.
8486 for ac_exec_ext in '' $ac_executable_extensions; do
8487 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8488 ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
8489 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8490 break 2
8491 fi
8492 done
8493 done
8494 IFS=$as_save_IFS
8495
8496 fi
8497 fi
8498 RANLIB=$ac_cv_prog_RANLIB
8499 if test -n "$RANLIB"; then
8500 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
8501 $as_echo "$RANLIB" >&6; }
8502 else
8503 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8504 $as_echo "no" >&6; }
8505 fi
8506
8507
8508 fi
8509 if test -z "$ac_cv_prog_RANLIB"; then
8510 ac_ct_RANLIB=$RANLIB
8511 # Extract the first word of "ranlib", so it can be a program name with args.
8512 set dummy ranlib; ac_word=$2
8513 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8514 $as_echo_n "checking for $ac_word... " >&6; }
8515 if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
8516 $as_echo_n "(cached) " >&6
8517 else
8518 if test -n "$ac_ct_RANLIB"; then
8519 ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
8520 else
8521 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8522 for as_dir in $PATH
8523 do
8524 IFS=$as_save_IFS
8525 test -z "$as_dir" && as_dir=.
8526 for ac_exec_ext in '' $ac_executable_extensions; do
8527 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8528 ac_cv_prog_ac_ct_RANLIB="ranlib"
8529 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8530 break 2
8531 fi
8532 done
8533 done
8534 IFS=$as_save_IFS
8535
8536 fi
8537 fi
8538 ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
8539 if test -n "$ac_ct_RANLIB"; then
8540 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
8541 $as_echo "$ac_ct_RANLIB" >&6; }
8542 else
8543 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8544 $as_echo "no" >&6; }
8545 fi
8546
8547 if test "x$ac_ct_RANLIB" = x; then
8548 RANLIB=":"
8549 else
8550 case $cross_compiling:$ac_tool_warned in
8551 yes:)
8552 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
8553 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
8554 ac_tool_warned=yes ;;
8555 esac
8556 RANLIB=$ac_ct_RANLIB
8557 fi
8558 else
8559 RANLIB="$ac_cv_prog_RANLIB"
8560 fi
8561
8562 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
8563 $as_echo_n "checking whether ln -s works... " >&6; }
8564 LN_S=$as_ln_s
8565 if test "$LN_S" = "ln -s"; then
8566 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
8567 $as_echo "yes" >&6; }
8568 else
8569 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
8570 $as_echo "no, using $LN_S" >&6; }
8571 fi
8572
8573
8574 # Check whether --enable-docs was given.
8575 if test "${enable_docs+set}" = set; then :
8576 enableval=$enable_docs; ax_docs=$enableval
8577 else
8578 ax_docs=yes
8579 fi
8580
8581 # Check whether --enable-mans was given.
8582 if test "${enable_mans+set}" = set; then :
8583 enableval=$enable_mans; ax_mans=$enableval
8584 else
8585 ax_mans=yes
8586 fi
8587
8588 if test x$ax_docs = xyes || test x$ax_mans = xyes; then :
8589
8590 # Extract the first word of "asciidoc", so it can be a program name with args.
8591 set dummy asciidoc; ac_word=$2
8592 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8593 $as_echo_n "checking for $ac_word... " >&6; }
8594 if ${ac_cv_path_PATH_ASCIIDOC+:} false; then :
8595 $as_echo_n "(cached) " >&6
8596 else
8597 case $PATH_ASCIIDOC in
8598 [\\/]* | ?:[\\/]*)
8599 ac_cv_path_PATH_ASCIIDOC="$PATH_ASCIIDOC" # Let the user override the test with a path.
8600 ;;
8601 *)
8602 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8603 for as_dir in $PATH
8604 do
8605 IFS=$as_save_IFS
8606 test -z "$as_dir" && as_dir=.
8607 for ac_exec_ext in '' $ac_executable_extensions; do
8608 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8609 ac_cv_path_PATH_ASCIIDOC="$as_dir/$ac_word$ac_exec_ext"
8610 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8611 break 2
8612 fi
8613 done
8614 done
8615 IFS=$as_save_IFS
8616
8617 ;;
8618 esac
8619 fi
8620 PATH_ASCIIDOC=$ac_cv_path_PATH_ASCIIDOC
8621 if test -n "$PATH_ASCIIDOC"; then
8622 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PATH_ASCIIDOC" >&5
8623 $as_echo "$PATH_ASCIIDOC" >&6; }
8624 else
8625 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8626 $as_echo "no" >&6; }
8627 fi
8628
8629
8630
8631 fi
8632 if test x$ax_mans = xyes; then :
8633
8634 # Extract the first word of "xmlto", so it can be a program name with args.
8635 set dummy xmlto; ac_word=$2
8636 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8637 $as_echo_n "checking for $ac_word... " >&6; }
8638 if ${ac_cv_path_PATH_XMLTO+:} false; then :
8639 $as_echo_n "(cached) " >&6
8640 else
8641 case $PATH_XMLTO in
8642 [\\/]* | ?:[\\/]*)
8643 ac_cv_path_PATH_XMLTO="$PATH_XMLTO" # Let the user override the test with a path.
8644 ;;
8645 *)
8646 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8647 for as_dir in $PATH
8648 do
8649 IFS=$as_save_IFS
8650 test -z "$as_dir" && as_dir=.
8651 for ac_exec_ext in '' $ac_executable_extensions; do
8652 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8653 ac_cv_path_PATH_XMLTO="$as_dir/$ac_word$ac_exec_ext"
8654 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8655 break 2
8656 fi
8657 done
8658 done
8659 IFS=$as_save_IFS
8660
8661 ;;
8662 esac
8663 fi
8664 PATH_XMLTO=$ac_cv_path_PATH_XMLTO
8665 if test -n "$PATH_XMLTO"; then
8666 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PATH_XMLTO" >&5
8667 $as_echo "$PATH_XMLTO" >&6; }
8668 else
8669 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8670 $as_echo "no" >&6; }
8671 fi
8672
8673
8674 # Extract the first word of "pod2man", so it can be a program name with args.
8675 set dummy pod2man; ac_word=$2
8676 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8677 $as_echo_n "checking for $ac_word... " >&6; }
8678 if ${ac_cv_path_PATH_POD2MAN+:} false; then :
8679 $as_echo_n "(cached) " >&6
8680 else
8681 case $PATH_POD2MAN in
8682 [\\/]* | ?:[\\/]*)
8683 ac_cv_path_PATH_POD2MAN="$PATH_POD2MAN" # Let the user override the test with a path.
8684 ;;
8685 *)
8686 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8687 for as_dir in $PATH
8688 do
8689 IFS=$as_save_IFS
8690 test -z "$as_dir" && as_dir=.
8691 for ac_exec_ext in '' $ac_executable_extensions; do
8692 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8693 ac_cv_path_PATH_POD2MAN="$as_dir/$ac_word$ac_exec_ext"
8694 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8695 break 2
8696 fi
8697 done
8698 done
8699 IFS=$as_save_IFS
8700
8701 ;;
8702 esac
8703 fi
8704 PATH_POD2MAN=$ac_cv_path_PATH_POD2MAN
8705 if test -n "$PATH_POD2MAN"; then
8706 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PATH_POD2MAN" >&5
8707 $as_echo "$PATH_POD2MAN" >&6; }
8708 else
8709 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8710 $as_echo "no" >&6; }
8711 fi
8712
8713
8714
8715 fi
8716 if test x$ax_mans = xyes && test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x; then
8717 BUILD_MANS_TRUE=
8718 BUILD_MANS_FALSE='#'
8719 else
8720 BUILD_MANS_TRUE='#'
8721 BUILD_MANS_FALSE=
8722 fi
8723
8724 if test x$ax_docs = xyes && test x$PATH_ASCIIDOC != x; then
8725 BUILD_DOCS_TRUE=
8726 BUILD_DOCS_FALSE='#'
8727 else
8728 BUILD_DOCS_TRUE='#'
8729 BUILD_DOCS_FALSE=
8730 fi
8731
8732
8733 if test -n "$ac_tool_prefix"; then
8734 for ac_prog in ar lib "link -lib"
8735 do
8736 # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
8737 set dummy $ac_tool_prefix$ac_prog; ac_word=$2
8738 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8739 $as_echo_n "checking for $ac_word... " >&6; }
8740 if ${ac_cv_prog_AR+:} false; then :
8741 $as_echo_n "(cached) " >&6
8742 else
8743 if test -n "$AR"; then
8744 ac_cv_prog_AR="$AR" # Let the user override the test.
8745 else
8746 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8747 for as_dir in $PATH
8748 do
8749 IFS=$as_save_IFS
8750 test -z "$as_dir" && as_dir=.
8751 for ac_exec_ext in '' $ac_executable_extensions; do
8752 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8753 ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
8754 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8755 break 2
8756 fi
8757 done
8758 done
8759 IFS=$as_save_IFS
8760
8761 fi
8762 fi
8763 AR=$ac_cv_prog_AR
8764 if test -n "$AR"; then
8765 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
8766 $as_echo "$AR" >&6; }
8767 else
8768 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8769 $as_echo "no" >&6; }
8770 fi
8771
8772
8773 test -n "$AR" && break
8774 done
8775 fi
8776 if test -z "$AR"; then
8777 ac_ct_AR=$AR
8778 for ac_prog in ar lib "link -lib"
8779 do
8780 # Extract the first word of "$ac_prog", so it can be a program name with args.
8781 set dummy $ac_prog; ac_word=$2
8782 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
8783 $as_echo_n "checking for $ac_word... " >&6; }
8784 if ${ac_cv_prog_ac_ct_AR+:} false; then :
8785 $as_echo_n "(cached) " >&6
8786 else
8787 if test -n "$ac_ct_AR"; then
8788 ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
8789 else
8790 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
8791 for as_dir in $PATH
8792 do
8793 IFS=$as_save_IFS
8794 test -z "$as_dir" && as_dir=.
8795 for ac_exec_ext in '' $ac_executable_extensions; do
8796 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
8797 ac_cv_prog_ac_ct_AR="$ac_prog"
8798 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
8799 break 2
8800 fi
8801 done
8802 done
8803 IFS=$as_save_IFS
8804
8805 fi
8806 fi
8807 ac_ct_AR=$ac_cv_prog_ac_ct_AR
8808 if test -n "$ac_ct_AR"; then
8809 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
8810 $as_echo "$ac_ct_AR" >&6; }
8811 else
8812 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
8813 $as_echo "no" >&6; }
8814 fi
8815
8816
8817 test -n "$ac_ct_AR" && break
8818 done
8819
8820 if test "x$ac_ct_AR" = x; then
8821 AR="false"
8822 else
8823 case $cross_compiling:$ac_tool_warned in
8824 yes:)
8825 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
8826 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
8827 ac_tool_warned=yes ;;
8828 esac
8829 AR=$ac_ct_AR
8830 fi
8831 fi
8832
8833 : ${AR=ar}
8834
8835 { $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5
8836 $as_echo_n "checking the archiver ($AR) interface... " >&6; }
8837 if ${am_cv_ar_interface+:} false; then :
8838 $as_echo_n "(cached) " >&6
8839 else
8840 ac_ext=c
8841 ac_cpp='$CPP $CPPFLAGS'
8842 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
8843 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
8844 ac_compiler_gnu=$ac_cv_c_compiler_gnu
8845
8846 am_cv_ar_interface=ar
8847 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8848 /* end confdefs.h. */
8849 int some_variable = 0;
8850 _ACEOF
8851 if ac_fn_c_try_compile "$LINENO"; then :
8852 am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5'
8853 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5
8854 (eval $am_ar_try) 2>&5
8855 ac_status=$?
8856 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8857 test $ac_status = 0; }
8858 if test "$ac_status" -eq 0; then
8859 am_cv_ar_interface=ar
8860 else
8861 am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5'
8862 { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5
8863 (eval $am_ar_try) 2>&5
8864 ac_status=$?
8865 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8866 test $ac_status = 0; }
8867 if test "$ac_status" -eq 0; then
8868 am_cv_ar_interface=lib
8869 else
8870 am_cv_ar_interface=unknown
8871 fi
8872 fi
8873 rm -f conftest.lib libconftest.a
8874
8875 fi
8876 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
8877 ac_ext=c
8878 ac_cpp='$CPP $CPPFLAGS'
8879 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
8880 ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
8881 ac_compiler_gnu=$ac_cv_c_compiler_gnu
8882
8883 fi
8884 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5
8885 $as_echo "$am_cv_ar_interface" >&6; }
8886
8887 case $am_cv_ar_interface in
8888 ar)
8889 ;;
8890 lib)
8891 # Microsoft lib, so override with the ar-lib wrapper script.
8892 # FIXME: It is wrong to rewrite AR.
8893 # But if we don't then we get into trouble of one sort or another.
8894 # A longer-term fix would be to have automake use am__AR in this case,
8895 # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
8896 # similar.
8897 AR="$am_aux_dir/ar-lib $AR"
8898 ;;
8899 unknown)
8900 as_fn_error $? "could not determine $AR interface" "$LINENO" 5
8901 ;;
8902 esac
8903
8904
8905 { $as_echo "$as_me:${as_lineno-$LINENO}: checking CFLAGS for maximum warnings" >&5
8906 $as_echo_n "checking CFLAGS for maximum warnings... " >&6; }
8907 if ${ac_cv_cflags_warn_all+:} false; then :
8908 $as_echo_n "(cached) " >&6
8909 else
8910 ac_cv_cflags_warn_all="no, unknown"
8911 ac_save_CFLAGS="$CFLAGS"
8912 for ac_arg in "-warn all % -warn all" "-pedantic % -Wall" "-xstrconst % -v" "-std1 % -verbose -w0 -warnprotos" "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" "-ansi -ansiE % -fullwarn" "+ESlit % +w1" "-Xc % -pvctl,fullmsg" "-h conform % -h msglevel 2" #
8913 do CFLAGS="$ac_save_CFLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
8914 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8915 /* end confdefs.h. */
8916
8917 int
8918 main ()
8919 {
8920
8921 ;
8922 return 0;
8923 }
8924 _ACEOF
8925 if ac_fn_c_try_compile "$LINENO"; then :
8926 ac_cv_cflags_warn_all=`echo $ac_arg | sed -e 's,.*% *,,'` ; break
8927 fi
8928 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
8929 done
8930 CFLAGS="$ac_save_CFLAGS"
8931
8932 fi
8933 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags_warn_all" >&5
8934 $as_echo "$ac_cv_cflags_warn_all" >&6; }
8935
8936
8937 case ".$ac_cv_cflags_warn_all" in
8938 .ok|.ok,*) ;;
8939 .|.no|.no,*) ;;
8940 *)
8941 if ${CFLAGS+:} false; then :
8942
8943 case " $CFLAGS " in #(
8944 *" $ac_cv_cflags_warn_all "*) :
8945 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains \$ac_cv_cflags_warn_all"; } >&5
8946 (: CFLAGS already contains $ac_cv_cflags_warn_all) 2>&5
8947 ac_status=$?
8948 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8949 test $ac_status = 0; } ;; #(
8950 *) :
8951
8952 as_fn_append CFLAGS " $ac_cv_cflags_warn_all"
8953 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
8954 (: CFLAGS="$CFLAGS") 2>&5
8955 ac_status=$?
8956 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8957 test $ac_status = 0; }
8958 ;;
8959 esac
8960
8961 else
8962
8963 CFLAGS=$ac_cv_cflags_warn_all
8964 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
8965 (: CFLAGS="$CFLAGS") 2>&5
8966 ac_status=$?
8967 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
8968 test $ac_status = 0; }
8969
8970 fi
8971 ;;
8972 esac
8973
8974 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunused-value" >&5
8975 $as_echo_n "checking whether C compiler accepts -Wunused-value... " >&6; }
8976 if ${ax_cv_check_cflags___Wunused_value+:} false; then :
8977 $as_echo_n "(cached) " >&6
8978 else
8979
8980 ax_check_save_flags=$CFLAGS
8981 CFLAGS="$CFLAGS -Wunused-value"
8982 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
8983 /* end confdefs.h. */
8984
8985 int
8986 main ()
8987 {
8988
8989 ;
8990 return 0;
8991 }
8992 _ACEOF
8993 if ac_fn_c_try_compile "$LINENO"; then :
8994 ax_cv_check_cflags___Wunused_value=yes
8995 else
8996 ax_cv_check_cflags___Wunused_value=no
8997 fi
8998 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
8999 CFLAGS=$ax_check_save_flags
9000 fi
9001 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Wunused_value" >&5
9002 $as_echo "$ax_cv_check_cflags___Wunused_value" >&6; }
9003 if test "x$ax_cv_check_cflags___Wunused_value" = xyes; then :
9004
9005 if ${AM_CFLAGS+:} false; then :
9006
9007 case " $AM_CFLAGS " in #(
9008 *" -Wunused-value "*) :
9009 { { $as_echo "$as_me:${as_lineno-$LINENO}: : AM_CFLAGS already contains -Wunused-value"; } >&5
9010 (: AM_CFLAGS already contains -Wunused-value) 2>&5
9011 ac_status=$?
9012 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9013 test $ac_status = 0; } ;; #(
9014 *) :
9015
9016 as_fn_append AM_CFLAGS " -Wunused-value"
9017 { { $as_echo "$as_me:${as_lineno-$LINENO}: : AM_CFLAGS=\"\$AM_CFLAGS\""; } >&5
9018 (: AM_CFLAGS="$AM_CFLAGS") 2>&5
9019 ac_status=$?
9020 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9021 test $ac_status = 0; }
9022 ;;
9023 esac
9024
9025 else
9026
9027 AM_CFLAGS=-Wunused-value
9028 { { $as_echo "$as_me:${as_lineno-$LINENO}: : AM_CFLAGS=\"\$AM_CFLAGS\""; } >&5
9029 (: AM_CFLAGS="$AM_CFLAGS") 2>&5
9030 ac_status=$?
9031 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9032 test $ac_status = 0; }
9033
9034 fi
9035
9036 else
9037 :
9038 fi
9039
9040
9041
9042 # Checks for header files.
9043 for ac_header in fcntl.h float.h inttypes.h limits.h locale.h netinet/in.h paths.h stddef.h stdint.h stdlib.h string.h sys/param.h sys/socket.h sys/time.h unistd.h
9044 do :
9045 as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
9046 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
9047 if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
9048 cat >>confdefs.h <<_ACEOF
9049 #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
9050 _ACEOF
9051
9052 else
9053 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
9054 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
9055 as_fn_error $? "cannot find the $ac_header header, which i3 requires
9056 See \`config.log' for more details" "$LINENO" 5; }
9057 fi
9058
9059 done
9060
9061
9062 ac_config_files="$ac_config_files Makefile testcases/lib/i3test.pm man/asciidoc.conf"
9063
9064 ac_config_files="$ac_config_files testcases/complete-run.pl"
9065
9066
9067 # Enable address sanitizer for non-release builds. The performance hit is a
9068 # 50% increase of wallclock time for the testsuite on my machine.
9069 if test x$is_release = xyes; then
9070 default_sanitizers=
9071 else
9072 default_sanitizers=address
9073 fi
9074
9075
9076
9077
9078
9079
9080 # Check whether --enable-sanitizers was given.
9081 if test "${enable_sanitizers+set}" = set; then :
9082 enableval=$enable_sanitizers; ax_sanitizers_default=$enableval
9083 else
9084 ax_sanitizers_default=
9085 fi
9086
9087 ax_enabled_sanitizers=
9088
9089 if test "x$ax_sanitizers_default" = "x"; then :
9090 ax_sanitizer_default=
9091 for mycheck in $default_sanitizers; do
9092 if test "x$mycheck" = "xaddress"; then :
9093 ax_sanitizer_default=yes
9094 fi
9095 done
9096 if test "x$ax_sanitizer_default" = "x"; then :
9097 ax_sanitizer_default=no
9098 fi
9099
9100 else
9101 ax_sanitizer_default=$ax_sanitizers_default
9102 fi
9103 # Check whether --enable-address-sanitizer was given.
9104 if test "${enable_address_sanitizer+set}" = set; then :
9105 enableval=$enable_address_sanitizer; ax_sanitizer_enabled=$enableval
9106 else
9107 ax_sanitizer_enabled=$ax_sanitizer_default
9108 fi
9109
9110
9111 if test "x$ax_sanitizer_enabled" = "xyes"; then :
9112
9113 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fsanitize=address" >&5
9114 $as_echo_n "checking whether C compiler accepts -fsanitize=address... " >&6; }
9115 if ${ax_cv_check_cflags___fsanitize_address+:} false; then :
9116 $as_echo_n "(cached) " >&6
9117 else
9118
9119 ax_check_save_flags=$CFLAGS
9120 CFLAGS="$CFLAGS -fsanitize=address"
9121 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9122 /* end confdefs.h. */
9123
9124 int
9125 main ()
9126 {
9127
9128 ;
9129 return 0;
9130 }
9131 _ACEOF
9132 if ac_fn_c_try_compile "$LINENO"; then :
9133 ax_cv_check_cflags___fsanitize_address=yes
9134 else
9135 ax_cv_check_cflags___fsanitize_address=no
9136 fi
9137 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
9138 CFLAGS=$ax_check_save_flags
9139 fi
9140 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fsanitize_address" >&5
9141 $as_echo "$ax_cv_check_cflags___fsanitize_address" >&6; }
9142 if test "x$ax_cv_check_cflags___fsanitize_address" = xyes; then :
9143
9144 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker accepts -fsanitize=address" >&5
9145 $as_echo_n "checking whether the linker accepts -fsanitize=address... " >&6; }
9146 if ${ax_cv_check_ldflags___fsanitize_address+:} false; then :
9147 $as_echo_n "(cached) " >&6
9148 else
9149
9150 ax_check_save_flags=$LDFLAGS
9151 LDFLAGS="$LDFLAGS -fsanitize=address"
9152 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9153 /* end confdefs.h. */
9154
9155 int
9156 main ()
9157 {
9158
9159 ;
9160 return 0;
9161 }
9162 _ACEOF
9163 if ac_fn_c_try_link "$LINENO"; then :
9164 ax_cv_check_ldflags___fsanitize_address=yes
9165 else
9166 ax_cv_check_ldflags___fsanitize_address=no
9167 fi
9168 rm -f core conftest.err conftest.$ac_objext \
9169 conftest$ac_exeext conftest.$ac_ext
9170 LDFLAGS=$ax_check_save_flags
9171 fi
9172 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_ldflags___fsanitize_address" >&5
9173 $as_echo "$ax_cv_check_ldflags___fsanitize_address" >&6; }
9174 if test "x$ax_cv_check_ldflags___fsanitize_address" = xyes; then :
9175
9176
9177 if ${CFLAGS+:} false; then :
9178
9179 case " $CFLAGS " in #(
9180 *" -fsanitize=address "*) :
9181 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains -fsanitize=address"; } >&5
9182 (: CFLAGS already contains -fsanitize=address) 2>&5
9183 ac_status=$?
9184 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9185 test $ac_status = 0; } ;; #(
9186 *) :
9187
9188 as_fn_append CFLAGS " -fsanitize=address"
9189 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9190 (: CFLAGS="$CFLAGS") 2>&5
9191 ac_status=$?
9192 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9193 test $ac_status = 0; }
9194 ;;
9195 esac
9196
9197 else
9198
9199 CFLAGS=-fsanitize=address
9200 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9201 (: CFLAGS="$CFLAGS") 2>&5
9202 ac_status=$?
9203 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9204 test $ac_status = 0; }
9205
9206 fi
9207
9208
9209 if ${LDFLAGS+:} false; then :
9210
9211 case " $LDFLAGS " in #(
9212 *" -fsanitize=address "*) :
9213 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS already contains -fsanitize=address"; } >&5
9214 (: LDFLAGS already contains -fsanitize=address) 2>&5
9215 ac_status=$?
9216 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9217 test $ac_status = 0; } ;; #(
9218 *) :
9219
9220 as_fn_append LDFLAGS " -fsanitize=address"
9221 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS=\"\$LDFLAGS\""; } >&5
9222 (: LDFLAGS="$LDFLAGS") 2>&5
9223 ac_status=$?
9224 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9225 test $ac_status = 0; }
9226 ;;
9227 esac
9228
9229 else
9230
9231 LDFLAGS=-fsanitize=address
9232 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS=\"\$LDFLAGS\""; } >&5
9233 (: LDFLAGS="$LDFLAGS") 2>&5
9234 ac_status=$?
9235 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9236 test $ac_status = 0; }
9237
9238 fi
9239
9240 # -fno-omit-frame-pointer results in nicer stack traces in error
9241 # messages, see http://clang.llvm.org/docs/AddressSanitizer.html#usage
9242 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-omit-frame-pointer" >&5
9243 $as_echo_n "checking whether C compiler accepts -fno-omit-frame-pointer... " >&6; }
9244 if ${ax_cv_check_cflags___fno_omit_frame_pointer+:} false; then :
9245 $as_echo_n "(cached) " >&6
9246 else
9247
9248 ax_check_save_flags=$CFLAGS
9249 CFLAGS="$CFLAGS -fno-omit-frame-pointer"
9250 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9251 /* end confdefs.h. */
9252
9253 int
9254 main ()
9255 {
9256
9257 ;
9258 return 0;
9259 }
9260 _ACEOF
9261 if ac_fn_c_try_compile "$LINENO"; then :
9262 ax_cv_check_cflags___fno_omit_frame_pointer=yes
9263 else
9264 ax_cv_check_cflags___fno_omit_frame_pointer=no
9265 fi
9266 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
9267 CFLAGS=$ax_check_save_flags
9268 fi
9269 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fno_omit_frame_pointer" >&5
9270 $as_echo "$ax_cv_check_cflags___fno_omit_frame_pointer" >&6; }
9271 if test "x$ax_cv_check_cflags___fno_omit_frame_pointer" = xyes; then :
9272
9273
9274 if ${CFLAGS+:} false; then :
9275
9276 case " $CFLAGS " in #(
9277 *" -fno-omit-frame-pointer "*) :
9278 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains -fno-omit-frame-pointer"; } >&5
9279 (: CFLAGS already contains -fno-omit-frame-pointer) 2>&5
9280 ac_status=$?
9281 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9282 test $ac_status = 0; } ;; #(
9283 *) :
9284
9285 as_fn_append CFLAGS " -fno-omit-frame-pointer"
9286 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9287 (: CFLAGS="$CFLAGS") 2>&5
9288 ac_status=$?
9289 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9290 test $ac_status = 0; }
9291 ;;
9292 esac
9293
9294 else
9295
9296 CFLAGS=-fno-omit-frame-pointer
9297 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9298 (: CFLAGS="$CFLAGS") 2>&5
9299 ac_status=$?
9300 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9301 test $ac_status = 0; }
9302
9303 fi
9304
9305 else
9306 :
9307 fi
9308
9309
9310 $as_echo "#define I3_ASAN_ENABLED /**/" >>confdefs.h
9311
9312 ax_enabled_sanitizers="address $ax_enabled_sanitizers"
9313
9314 else
9315 :
9316 fi
9317
9318
9319 else
9320 :
9321 fi
9322
9323
9324 fi
9325
9326 if test "x$ax_sanitizers_default" = "x"; then :
9327 ax_sanitizer_default=
9328 for mycheck in $default_sanitizers; do
9329 if test "x$mycheck" = "xmemory"; then :
9330 ax_sanitizer_default=yes
9331 fi
9332 done
9333 if test "x$ax_sanitizer_default" = "x"; then :
9334 ax_sanitizer_default=no
9335 fi
9336
9337 else
9338 ax_sanitizer_default=$ax_sanitizers_default
9339 fi
9340 # Check whether --enable-memory-sanitizer was given.
9341 if test "${enable_memory_sanitizer+set}" = set; then :
9342 enableval=$enable_memory_sanitizer; ax_sanitizer_enabled=$enableval
9343 else
9344 ax_sanitizer_enabled=$ax_sanitizer_default
9345 fi
9346
9347
9348 if test "x$ax_sanitizer_enabled" = "xyes"; then :
9349
9350 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fsanitize=memory" >&5
9351 $as_echo_n "checking whether C compiler accepts -fsanitize=memory... " >&6; }
9352 if ${ax_cv_check_cflags___fsanitize_memory+:} false; then :
9353 $as_echo_n "(cached) " >&6
9354 else
9355
9356 ax_check_save_flags=$CFLAGS
9357 CFLAGS="$CFLAGS -fsanitize=memory"
9358 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9359 /* end confdefs.h. */
9360
9361 int
9362 main ()
9363 {
9364
9365 ;
9366 return 0;
9367 }
9368 _ACEOF
9369 if ac_fn_c_try_compile "$LINENO"; then :
9370 ax_cv_check_cflags___fsanitize_memory=yes
9371 else
9372 ax_cv_check_cflags___fsanitize_memory=no
9373 fi
9374 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
9375 CFLAGS=$ax_check_save_flags
9376 fi
9377 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fsanitize_memory" >&5
9378 $as_echo "$ax_cv_check_cflags___fsanitize_memory" >&6; }
9379 if test "x$ax_cv_check_cflags___fsanitize_memory" = xyes; then :
9380
9381 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker accepts -fsanitize=memory" >&5
9382 $as_echo_n "checking whether the linker accepts -fsanitize=memory... " >&6; }
9383 if ${ax_cv_check_ldflags___fsanitize_memory+:} false; then :
9384 $as_echo_n "(cached) " >&6
9385 else
9386
9387 ax_check_save_flags=$LDFLAGS
9388 LDFLAGS="$LDFLAGS -fsanitize=memory"
9389 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9390 /* end confdefs.h. */
9391
9392 int
9393 main ()
9394 {
9395
9396 ;
9397 return 0;
9398 }
9399 _ACEOF
9400 if ac_fn_c_try_link "$LINENO"; then :
9401 ax_cv_check_ldflags___fsanitize_memory=yes
9402 else
9403 ax_cv_check_ldflags___fsanitize_memory=no
9404 fi
9405 rm -f core conftest.err conftest.$ac_objext \
9406 conftest$ac_exeext conftest.$ac_ext
9407 LDFLAGS=$ax_check_save_flags
9408 fi
9409 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_ldflags___fsanitize_memory" >&5
9410 $as_echo "$ax_cv_check_ldflags___fsanitize_memory" >&6; }
9411 if test "x$ax_cv_check_ldflags___fsanitize_memory" = xyes; then :
9412
9413
9414 if ${CFLAGS+:} false; then :
9415
9416 case " $CFLAGS " in #(
9417 *" -fsanitize=memory "*) :
9418 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains -fsanitize=memory"; } >&5
9419 (: CFLAGS already contains -fsanitize=memory) 2>&5
9420 ac_status=$?
9421 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9422 test $ac_status = 0; } ;; #(
9423 *) :
9424
9425 as_fn_append CFLAGS " -fsanitize=memory"
9426 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9427 (: CFLAGS="$CFLAGS") 2>&5
9428 ac_status=$?
9429 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9430 test $ac_status = 0; }
9431 ;;
9432 esac
9433
9434 else
9435
9436 CFLAGS=-fsanitize=memory
9437 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9438 (: CFLAGS="$CFLAGS") 2>&5
9439 ac_status=$?
9440 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9441 test $ac_status = 0; }
9442
9443 fi
9444
9445
9446 if ${LDFLAGS+:} false; then :
9447
9448 case " $LDFLAGS " in #(
9449 *" -fsanitize=memory "*) :
9450 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS already contains -fsanitize=memory"; } >&5
9451 (: LDFLAGS already contains -fsanitize=memory) 2>&5
9452 ac_status=$?
9453 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9454 test $ac_status = 0; } ;; #(
9455 *) :
9456
9457 as_fn_append LDFLAGS " -fsanitize=memory"
9458 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS=\"\$LDFLAGS\""; } >&5
9459 (: LDFLAGS="$LDFLAGS") 2>&5
9460 ac_status=$?
9461 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9462 test $ac_status = 0; }
9463 ;;
9464 esac
9465
9466 else
9467
9468 LDFLAGS=-fsanitize=memory
9469 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS=\"\$LDFLAGS\""; } >&5
9470 (: LDFLAGS="$LDFLAGS") 2>&5
9471 ac_status=$?
9472 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9473 test $ac_status = 0; }
9474
9475 fi
9476
9477 # -fno-omit-frame-pointer results in nicer stack traces in error
9478 # messages, see http://clang.llvm.org/docs/AddressSanitizer.html#usage
9479 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-omit-frame-pointer" >&5
9480 $as_echo_n "checking whether C compiler accepts -fno-omit-frame-pointer... " >&6; }
9481 if ${ax_cv_check_cflags___fno_omit_frame_pointer+:} false; then :
9482 $as_echo_n "(cached) " >&6
9483 else
9484
9485 ax_check_save_flags=$CFLAGS
9486 CFLAGS="$CFLAGS -fno-omit-frame-pointer"
9487 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9488 /* end confdefs.h. */
9489
9490 int
9491 main ()
9492 {
9493
9494 ;
9495 return 0;
9496 }
9497 _ACEOF
9498 if ac_fn_c_try_compile "$LINENO"; then :
9499 ax_cv_check_cflags___fno_omit_frame_pointer=yes
9500 else
9501 ax_cv_check_cflags___fno_omit_frame_pointer=no
9502 fi
9503 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
9504 CFLAGS=$ax_check_save_flags
9505 fi
9506 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fno_omit_frame_pointer" >&5
9507 $as_echo "$ax_cv_check_cflags___fno_omit_frame_pointer" >&6; }
9508 if test "x$ax_cv_check_cflags___fno_omit_frame_pointer" = xyes; then :
9509
9510
9511 if ${CFLAGS+:} false; then :
9512
9513 case " $CFLAGS " in #(
9514 *" -fno-omit-frame-pointer "*) :
9515 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains -fno-omit-frame-pointer"; } >&5
9516 (: CFLAGS already contains -fno-omit-frame-pointer) 2>&5
9517 ac_status=$?
9518 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9519 test $ac_status = 0; } ;; #(
9520 *) :
9521
9522 as_fn_append CFLAGS " -fno-omit-frame-pointer"
9523 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9524 (: CFLAGS="$CFLAGS") 2>&5
9525 ac_status=$?
9526 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9527 test $ac_status = 0; }
9528 ;;
9529 esac
9530
9531 else
9532
9533 CFLAGS=-fno-omit-frame-pointer
9534 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9535 (: CFLAGS="$CFLAGS") 2>&5
9536 ac_status=$?
9537 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9538 test $ac_status = 0; }
9539
9540 fi
9541
9542 else
9543 :
9544 fi
9545
9546
9547 $as_echo "#define I3_ASAN_ENABLED /**/" >>confdefs.h
9548
9549 ax_enabled_sanitizers="memory $ax_enabled_sanitizers"
9550
9551 else
9552 :
9553 fi
9554
9555
9556 else
9557 :
9558 fi
9559
9560
9561 fi
9562
9563 if test "x$ax_sanitizers_default" = "x"; then :
9564 ax_sanitizer_default=
9565 for mycheck in $default_sanitizers; do
9566 if test "x$mycheck" = "xundefined"; then :
9567 ax_sanitizer_default=yes
9568 fi
9569 done
9570 if test "x$ax_sanitizer_default" = "x"; then :
9571 ax_sanitizer_default=no
9572 fi
9573
9574 else
9575 ax_sanitizer_default=$ax_sanitizers_default
9576 fi
9577 # Check whether --enable-undefined-sanitizer was given.
9578 if test "${enable_undefined_sanitizer+set}" = set; then :
9579 enableval=$enable_undefined_sanitizer; ax_sanitizer_enabled=$enableval
9580 else
9581 ax_sanitizer_enabled=$ax_sanitizer_default
9582 fi
9583
9584
9585 if test "x$ax_sanitizer_enabled" = "xyes"; then :
9586
9587 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fsanitize=undefined" >&5
9588 $as_echo_n "checking whether C compiler accepts -fsanitize=undefined... " >&6; }
9589 if ${ax_cv_check_cflags___fsanitize_undefined+:} false; then :
9590 $as_echo_n "(cached) " >&6
9591 else
9592
9593 ax_check_save_flags=$CFLAGS
9594 CFLAGS="$CFLAGS -fsanitize=undefined"
9595 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9596 /* end confdefs.h. */
9597
9598 int
9599 main ()
9600 {
9601
9602 ;
9603 return 0;
9604 }
9605 _ACEOF
9606 if ac_fn_c_try_compile "$LINENO"; then :
9607 ax_cv_check_cflags___fsanitize_undefined=yes
9608 else
9609 ax_cv_check_cflags___fsanitize_undefined=no
9610 fi
9611 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
9612 CFLAGS=$ax_check_save_flags
9613 fi
9614 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fsanitize_undefined" >&5
9615 $as_echo "$ax_cv_check_cflags___fsanitize_undefined" >&6; }
9616 if test "x$ax_cv_check_cflags___fsanitize_undefined" = xyes; then :
9617
9618 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker accepts -fsanitize=undefined" >&5
9619 $as_echo_n "checking whether the linker accepts -fsanitize=undefined... " >&6; }
9620 if ${ax_cv_check_ldflags___fsanitize_undefined+:} false; then :
9621 $as_echo_n "(cached) " >&6
9622 else
9623
9624 ax_check_save_flags=$LDFLAGS
9625 LDFLAGS="$LDFLAGS -fsanitize=undefined"
9626 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9627 /* end confdefs.h. */
9628
9629 int
9630 main ()
9631 {
9632
9633 ;
9634 return 0;
9635 }
9636 _ACEOF
9637 if ac_fn_c_try_link "$LINENO"; then :
9638 ax_cv_check_ldflags___fsanitize_undefined=yes
9639 else
9640 ax_cv_check_ldflags___fsanitize_undefined=no
9641 fi
9642 rm -f core conftest.err conftest.$ac_objext \
9643 conftest$ac_exeext conftest.$ac_ext
9644 LDFLAGS=$ax_check_save_flags
9645 fi
9646 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_ldflags___fsanitize_undefined" >&5
9647 $as_echo "$ax_cv_check_ldflags___fsanitize_undefined" >&6; }
9648 if test "x$ax_cv_check_ldflags___fsanitize_undefined" = xyes; then :
9649
9650
9651 if ${CFLAGS+:} false; then :
9652
9653 case " $CFLAGS " in #(
9654 *" -fsanitize=undefined "*) :
9655 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains -fsanitize=undefined"; } >&5
9656 (: CFLAGS already contains -fsanitize=undefined) 2>&5
9657 ac_status=$?
9658 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9659 test $ac_status = 0; } ;; #(
9660 *) :
9661
9662 as_fn_append CFLAGS " -fsanitize=undefined"
9663 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9664 (: CFLAGS="$CFLAGS") 2>&5
9665 ac_status=$?
9666 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9667 test $ac_status = 0; }
9668 ;;
9669 esac
9670
9671 else
9672
9673 CFLAGS=-fsanitize=undefined
9674 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9675 (: CFLAGS="$CFLAGS") 2>&5
9676 ac_status=$?
9677 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9678 test $ac_status = 0; }
9679
9680 fi
9681
9682
9683 if ${LDFLAGS+:} false; then :
9684
9685 case " $LDFLAGS " in #(
9686 *" -fsanitize=undefined "*) :
9687 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS already contains -fsanitize=undefined"; } >&5
9688 (: LDFLAGS already contains -fsanitize=undefined) 2>&5
9689 ac_status=$?
9690 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9691 test $ac_status = 0; } ;; #(
9692 *) :
9693
9694 as_fn_append LDFLAGS " -fsanitize=undefined"
9695 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS=\"\$LDFLAGS\""; } >&5
9696 (: LDFLAGS="$LDFLAGS") 2>&5
9697 ac_status=$?
9698 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9699 test $ac_status = 0; }
9700 ;;
9701 esac
9702
9703 else
9704
9705 LDFLAGS=-fsanitize=undefined
9706 { { $as_echo "$as_me:${as_lineno-$LINENO}: : LDFLAGS=\"\$LDFLAGS\""; } >&5
9707 (: LDFLAGS="$LDFLAGS") 2>&5
9708 ac_status=$?
9709 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9710 test $ac_status = 0; }
9711
9712 fi
9713
9714 # -fno-omit-frame-pointer results in nicer stack traces in error
9715 # messages, see http://clang.llvm.org/docs/AddressSanitizer.html#usage
9716 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fno-omit-frame-pointer" >&5
9717 $as_echo_n "checking whether C compiler accepts -fno-omit-frame-pointer... " >&6; }
9718 if ${ax_cv_check_cflags___fno_omit_frame_pointer+:} false; then :
9719 $as_echo_n "(cached) " >&6
9720 else
9721
9722 ax_check_save_flags=$CFLAGS
9723 CFLAGS="$CFLAGS -fno-omit-frame-pointer"
9724 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
9725 /* end confdefs.h. */
9726
9727 int
9728 main ()
9729 {
9730
9731 ;
9732 return 0;
9733 }
9734 _ACEOF
9735 if ac_fn_c_try_compile "$LINENO"; then :
9736 ax_cv_check_cflags___fno_omit_frame_pointer=yes
9737 else
9738 ax_cv_check_cflags___fno_omit_frame_pointer=no
9739 fi
9740 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
9741 CFLAGS=$ax_check_save_flags
9742 fi
9743 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___fno_omit_frame_pointer" >&5
9744 $as_echo "$ax_cv_check_cflags___fno_omit_frame_pointer" >&6; }
9745 if test "x$ax_cv_check_cflags___fno_omit_frame_pointer" = xyes; then :
9746
9747
9748 if ${CFLAGS+:} false; then :
9749
9750 case " $CFLAGS " in #(
9751 *" -fno-omit-frame-pointer "*) :
9752 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS already contains -fno-omit-frame-pointer"; } >&5
9753 (: CFLAGS already contains -fno-omit-frame-pointer) 2>&5
9754 ac_status=$?
9755 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9756 test $ac_status = 0; } ;; #(
9757 *) :
9758
9759 as_fn_append CFLAGS " -fno-omit-frame-pointer"
9760 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9761 (: CFLAGS="$CFLAGS") 2>&5
9762 ac_status=$?
9763 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9764 test $ac_status = 0; }
9765 ;;
9766 esac
9767
9768 else
9769
9770 CFLAGS=-fno-omit-frame-pointer
9771 { { $as_echo "$as_me:${as_lineno-$LINENO}: : CFLAGS=\"\$CFLAGS\""; } >&5
9772 (: CFLAGS="$CFLAGS") 2>&5
9773 ac_status=$?
9774 $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
9775 test $ac_status = 0; }
9776
9777 fi
9778
9779 else
9780 :
9781 fi
9782
9783
9784 $as_echo "#define I3_ASAN_ENABLED /**/" >>confdefs.h
9785
9786 ax_enabled_sanitizers="undefined $ax_enabled_sanitizers"
9787
9788 else
9789 :
9790 fi
9791
9792
9793 else
9794 :
9795 fi
9796
9797
9798 fi
9799
9800
9801 cat >confcache <<\_ACEOF
9802 # This file is a shell script that caches the results of configure
9803 # tests run on this system so they can be shared between configure
9804 # scripts and configure runs, see configure's option --config-cache.
9805 # It is not useful on other systems. If it contains results you don't
9806 # want to keep, you may remove or edit it.
9807 #
9808 # config.status only pays attention to the cache file if you give it
9809 # the --recheck option to rerun configure.
9810 #
9811 # `ac_cv_env_foo' variables (set or unset) will be overridden when
9812 # loading this file, other *unset* `ac_cv_foo' will be assigned the
9813 # following values.
9814
9815 _ACEOF
9816
9817 # The following way of writing the cache mishandles newlines in values,
9818 # but we know of no workaround that is simple, portable, and efficient.
9819 # So, we kill variables containing newlines.
9820 # Ultrix sh set writes to stderr and can't be redirected directly,
9821 # and sets the high bit in the cache file unless we assign to the vars.
9822 (
9823 for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
9824 eval ac_val=\$$ac_var
9825 case $ac_val in #(
9826 *${as_nl}*)
9827 case $ac_var in #(
9828 *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
9829 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
9830 esac
9831 case $ac_var in #(
9832 _ | IFS | as_nl) ;; #(
9833 BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
9834 *) { eval $ac_var=; unset $ac_var;} ;;
9835 esac ;;
9836 esac
9837 done
9838
9839 (set) 2>&1 |
9840 case $as_nl`(ac_space=' '; set) 2>&1` in #(
9841 *${as_nl}ac_space=\ *)
9842 # `set' does not quote correctly, so add quotes: double-quote
9843 # substitution turns \\\\ into \\, and sed turns \\ into \.
9844 sed -n \
9845 "s/'/'\\\\''/g;
9846 s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
9847 ;; #(
9848 *)
9849 # `set' quotes correctly as required by POSIX, so do not add quotes.
9850 sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
9851 ;;
9852 esac |
9853 sort
9854 ) |
9855 sed '
9856 /^ac_cv_env_/b end
9857 t clear
9858 :clear
9859 s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
9860 t end
9861 s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
9862 :end' >>confcache
9863 if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
9864 if test -w "$cache_file"; then
9865 if test "x$cache_file" != "x/dev/null"; then
9866 { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
9867 $as_echo "$as_me: updating cache $cache_file" >&6;}
9868 if test ! -f "$cache_file" || test -h "$cache_file"; then
9869 cat confcache >"$cache_file"
9870 else
9871 case $cache_file in #(
9872 */* | ?:*)
9873 mv -f confcache "$cache_file"$$ &&
9874 mv -f "$cache_file"$$ "$cache_file" ;; #(
9875 *)
9876 mv -f confcache "$cache_file" ;;
9877 esac
9878 fi
9879 fi
9880 else
9881 { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
9882 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
9883 fi
9884 fi
9885 rm -f confcache
9886
9887 test "x$prefix" = xNONE && prefix=$ac_default_prefix
9888 # Let make expand exec_prefix.
9889 test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
9890
9891 DEFS=-DHAVE_CONFIG_H
9892
9893 ac_libobjs=
9894 ac_ltlibobjs=
9895 U=
9896 for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
9897 # 1. Remove the extension, and $U if already installed.
9898 ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
9899 ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
9900 # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
9901 # will be set to the directory where LIBOBJS objects are built.
9902 as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
9903 as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
9904 done
9905 LIBOBJS=$ac_libobjs
9906
9907 LTLIBOBJS=$ac_ltlibobjs
9908
9909
9910 { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
9911 $as_echo_n "checking that generated files are newer than configure... " >&6; }
9912 if test -n "$am_sleep_pid"; then
9913 # Hide warnings about reused PIDs.
9914 wait $am_sleep_pid 2>/dev/null
9915 fi
9916 { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
9917 $as_echo "done" >&6; }
9918 if test -n "$EXEEXT"; then
9919 am__EXEEXT_TRUE=
9920 am__EXEEXT_FALSE='#'
9921 else
9922 am__EXEEXT_TRUE='#'
9923 am__EXEEXT_FALSE=
9924 fi
9925
9926 if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
9927 as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
9928 Usually this means the macro was only invoked conditionally." "$LINENO" 5
9929 fi
9930 if test -z "${CODE_COVERAGE_ENABLED_TRUE}" && test -z "${CODE_COVERAGE_ENABLED_FALSE}"; then
9931 as_fn_error $? "conditional \"CODE_COVERAGE_ENABLED\" was never defined.
9932 Usually this means the macro was only invoked conditionally." "$LINENO" 5
9933 fi
9934 if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
9935 as_fn_error $? "conditional \"AMDEP\" was never defined.
9936 Usually this means the macro was only invoked conditionally." "$LINENO" 5
9937 fi
9938 if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
9939 as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
9940 Usually this means the macro was only invoked conditionally." "$LINENO" 5
9941 fi
9942 if test -z "${BUILD_MANS_TRUE}" && test -z "${BUILD_MANS_FALSE}"; then
9943 as_fn_error $? "conditional \"BUILD_MANS\" was never defined.
9944 Usually this means the macro was only invoked conditionally." "$LINENO" 5
9945 fi
9946 if test -z "${BUILD_DOCS_TRUE}" && test -z "${BUILD_DOCS_FALSE}"; then
9947 as_fn_error $? "conditional \"BUILD_DOCS\" was never defined.
9948 Usually this means the macro was only invoked conditionally." "$LINENO" 5
9949 fi
9950
9951 : "${CONFIG_STATUS=./config.status}"
9952 ac_write_fail=0
9953 ac_clean_files_save=$ac_clean_files
9954 ac_clean_files="$ac_clean_files $CONFIG_STATUS"
9955 { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
9956 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
9957 as_write_fail=0
9958 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
9959 #! $SHELL
9960 # Generated by $as_me.
9961 # Run this file to recreate the current configuration.
9962 # Compiler output produced by configure, useful for debugging
9963 # configure, is in config.log if it exists.
9964
9965 debug=false
9966 ac_cs_recheck=false
9967 ac_cs_silent=false
9968
9969 SHELL=\${CONFIG_SHELL-$SHELL}
9970 export SHELL
9971 _ASEOF
9972 cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
9973 ## -------------------- ##
9974 ## M4sh Initialization. ##
9975 ## -------------------- ##
9976
9977 # Be more Bourne compatible
9978 DUALCASE=1; export DUALCASE # for MKS sh
9979 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
9980 emulate sh
9981 NULLCMD=:
9982 # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
9983 # is contrary to our usage. Disable this feature.
9984 alias -g '${1+"$@"}'='"$@"'
9985 setopt NO_GLOB_SUBST
9986 else
9987 case `(set -o) 2>/dev/null` in #(
9988 *posix*) :
9989 set -o posix ;; #(
9990 *) :
9991 ;;
9992 esac
9993 fi
9994
9995
9996 as_nl='
9997 '
9998 export as_nl
9999 # Printing a long string crashes Solaris 7 /usr/bin/printf.
10000 as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
10001 as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
10002 as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
10003 # Prefer a ksh shell builtin over an external printf program on Solaris,
10004 # but without wasting forks for bash or zsh.
10005 if test -z "$BASH_VERSION$ZSH_VERSION" \
10006 && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
10007 as_echo='print -r --'
10008 as_echo_n='print -rn --'
10009 elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
10010 as_echo='printf %s\n'
10011 as_echo_n='printf %s'
10012 else
10013 if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
10014 as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
10015 as_echo_n='/usr/ucb/echo -n'
10016 else
10017 as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
10018 as_echo_n_body='eval
10019 arg=$1;
10020 case $arg in #(
10021 *"$as_nl"*)
10022 expr "X$arg" : "X\\(.*\\)$as_nl";
10023 arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
10024 esac;
10025 expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
10026 '
10027 export as_echo_n_body
10028 as_echo_n='sh -c $as_echo_n_body as_echo'
10029 fi
10030 export as_echo_body
10031 as_echo='sh -c $as_echo_body as_echo'
10032 fi
10033
10034 # The user is always right.
10035 if test "${PATH_SEPARATOR+set}" != set; then
10036 PATH_SEPARATOR=:
10037 (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
10038 (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
10039 PATH_SEPARATOR=';'
10040 }
10041 fi
10042
10043
10044 # IFS
10045 # We need space, tab and new line, in precisely that order. Quoting is
10046 # there to prevent editors from complaining about space-tab.
10047 # (If _AS_PATH_WALK were called with IFS unset, it would disable word
10048 # splitting by setting IFS to empty value.)
10049 IFS=" "" $as_nl"
10050
10051 # Find who we are. Look in the path if we contain no directory separator.
10052 as_myself=
10053 case $0 in #((
10054 *[\\/]* ) as_myself=$0 ;;
10055 *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
10056 for as_dir in $PATH
10057 do
10058 IFS=$as_save_IFS
10059 test -z "$as_dir" && as_dir=.
10060 test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
10061 done
10062 IFS=$as_save_IFS
10063
10064 ;;
10065 esac
10066 # We did not find ourselves, most probably we were run as `sh COMMAND'
10067 # in which case we are not to be found in the path.
10068 if test "x$as_myself" = x; then
10069 as_myself=$0
10070 fi
10071 if test ! -f "$as_myself"; then
10072 $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
10073 exit 1
10074 fi
10075
10076 # Unset variables that we do not need and which cause bugs (e.g. in
10077 # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
10078 # suppresses any "Segmentation fault" message there. '((' could
10079 # trigger a bug in pdksh 5.2.14.
10080 for as_var in BASH_ENV ENV MAIL MAILPATH
10081 do eval test x\${$as_var+set} = xset \
10082 && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
10083 done
10084 PS1='$ '
10085 PS2='> '
10086 PS4='+ '
10087
10088 # NLS nuisances.
10089 LC_ALL=C
10090 export LC_ALL
10091 LANGUAGE=C
10092 export LANGUAGE
10093
10094 # CDPATH.
10095 (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
10096
10097
10098 # as_fn_error STATUS ERROR [LINENO LOG_FD]
10099 # ----------------------------------------
10100 # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
10101 # provided, also output the error to LOG_FD, referencing LINENO. Then exit the
10102 # script with STATUS, using 1 if that was 0.
10103 as_fn_error ()
10104 {
10105 as_status=$1; test $as_status -eq 0 && as_status=1
10106 if test "$4"; then
10107 as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
10108 $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
10109 fi
10110 $as_echo "$as_me: error: $2" >&2
10111 as_fn_exit $as_status
10112 } # as_fn_error
10113
10114
10115 # as_fn_set_status STATUS
10116 # -----------------------
10117 # Set $? to STATUS, without forking.
10118 as_fn_set_status ()
10119 {
10120 return $1
10121 } # as_fn_set_status
10122
10123 # as_fn_exit STATUS
10124 # -----------------
10125 # Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
10126 as_fn_exit ()
10127 {
10128 set +e
10129 as_fn_set_status $1
10130 exit $1
10131 } # as_fn_exit
10132
10133 # as_fn_unset VAR
10134 # ---------------
10135 # Portably unset VAR.
10136 as_fn_unset ()
10137 {
10138 { eval $1=; unset $1;}
10139 }
10140 as_unset=as_fn_unset
10141 # as_fn_append VAR VALUE
10142 # ----------------------
10143 # Append the text in VALUE to the end of the definition contained in VAR. Take
10144 # advantage of any shell optimizations that allow amortized linear growth over
10145 # repeated appends, instead of the typical quadratic growth present in naive
10146 # implementations.
10147 if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
10148 eval 'as_fn_append ()
10149 {
10150 eval $1+=\$2
10151 }'
10152 else
10153 as_fn_append ()
10154 {
10155 eval $1=\$$1\$2
10156 }
10157 fi # as_fn_append
10158
10159 # as_fn_arith ARG...
10160 # ------------------
10161 # Perform arithmetic evaluation on the ARGs, and store the result in the
10162 # global $as_val. Take advantage of shells that can avoid forks. The arguments
10163 # must be portable across $(()) and expr.
10164 if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
10165 eval 'as_fn_arith ()
10166 {
10167 as_val=$(( $* ))
10168 }'
10169 else
10170 as_fn_arith ()
10171 {
10172 as_val=`expr "$@" || test $? -eq 1`
10173 }
10174 fi # as_fn_arith
10175
10176
10177 if expr a : '\(a\)' >/dev/null 2>&1 &&
10178 test "X`expr 00001 : '.*\(...\)'`" = X001; then
10179 as_expr=expr
10180 else
10181 as_expr=false
10182 fi
10183
10184 if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
10185 as_basename=basename
10186 else
10187 as_basename=false
10188 fi
10189
10190 if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
10191 as_dirname=dirname
10192 else
10193 as_dirname=false
10194 fi
10195
10196 as_me=`$as_basename -- "$0" ||
10197 $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
10198 X"$0" : 'X\(//\)$' \| \
10199 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
10200 $as_echo X/"$0" |
10201 sed '/^.*\/\([^/][^/]*\)\/*$/{
10202 s//\1/
10203 q
10204 }
10205 /^X\/\(\/\/\)$/{
10206 s//\1/
10207 q
10208 }
10209 /^X\/\(\/\).*/{
10210 s//\1/
10211 q
10212 }
10213 s/.*/./; q'`
10214
10215 # Avoid depending upon Character Ranges.
10216 as_cr_letters='abcdefghijklmnopqrstuvwxyz'
10217 as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
10218 as_cr_Letters=$as_cr_letters$as_cr_LETTERS
10219 as_cr_digits='0123456789'
10220 as_cr_alnum=$as_cr_Letters$as_cr_digits
10221
10222 ECHO_C= ECHO_N= ECHO_T=
10223 case `echo -n x` in #(((((
10224 -n*)
10225 case `echo 'xy\c'` in
10226 *c*) ECHO_T=' ';; # ECHO_T is single tab character.
10227 xy) ECHO_C='\c';;
10228 *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
10229 ECHO_T=' ';;
10230 esac;;
10231 *)
10232 ECHO_N='-n';;
10233 esac
10234
10235 rm -f conf$$ conf$$.exe conf$$.file
10236 if test -d conf$$.dir; then
10237 rm -f conf$$.dir/conf$$.file
10238 else
10239 rm -f conf$$.dir
10240 mkdir conf$$.dir 2>/dev/null
10241 fi
10242 if (echo >conf$$.file) 2>/dev/null; then
10243 if ln -s conf$$.file conf$$ 2>/dev/null; then
10244 as_ln_s='ln -s'
10245 # ... but there are two gotchas:
10246 # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
10247 # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
10248 # In both cases, we have to default to `cp -pR'.
10249 ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
10250 as_ln_s='cp -pR'
10251 elif ln conf$$.file conf$$ 2>/dev/null; then
10252 as_ln_s=ln
10253 else
10254 as_ln_s='cp -pR'
10255 fi
10256 else
10257 as_ln_s='cp -pR'
10258 fi
10259 rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
10260 rmdir conf$$.dir 2>/dev/null
10261
10262
10263 # as_fn_mkdir_p
10264 # -------------
10265 # Create "$as_dir" as a directory, including parents if necessary.
10266 as_fn_mkdir_p ()
10267 {
10268
10269 case $as_dir in #(
10270 -*) as_dir=./$as_dir;;
10271 esac
10272 test -d "$as_dir" || eval $as_mkdir_p || {
10273 as_dirs=
10274 while :; do
10275 case $as_dir in #(
10276 *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
10277 *) as_qdir=$as_dir;;
10278 esac
10279 as_dirs="'$as_qdir' $as_dirs"
10280 as_dir=`$as_dirname -- "$as_dir" ||
10281 $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
10282 X"$as_dir" : 'X\(//\)[^/]' \| \
10283 X"$as_dir" : 'X\(//\)$' \| \
10284 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
10285 $as_echo X"$as_dir" |
10286 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
10287 s//\1/
10288 q
10289 }
10290 /^X\(\/\/\)[^/].*/{
10291 s//\1/
10292 q
10293 }
10294 /^X\(\/\/\)$/{
10295 s//\1/
10296 q
10297 }
10298 /^X\(\/\).*/{
10299 s//\1/
10300 q
10301 }
10302 s/.*/./; q'`
10303 test -d "$as_dir" && break
10304 done
10305 test -z "$as_dirs" || eval "mkdir $as_dirs"
10306 } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
10307
10308
10309 } # as_fn_mkdir_p
10310 if mkdir -p . 2>/dev/null; then
10311 as_mkdir_p='mkdir -p "$as_dir"'
10312 else
10313 test -d ./-p && rmdir ./-p
10314 as_mkdir_p=false
10315 fi
10316
10317
10318 # as_fn_executable_p FILE
10319 # -----------------------
10320 # Test if FILE is an executable regular file.
10321 as_fn_executable_p ()
10322 {
10323 test -f "$1" && test -x "$1"
10324 } # as_fn_executable_p
10325 as_test_x='test -x'
10326 as_executable_p=as_fn_executable_p
10327
10328 # Sed expression to map a string onto a valid CPP name.
10329 as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
10330
10331 # Sed expression to map a string onto a valid variable name.
10332 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
10333
10334
10335 exec 6>&1
10336 ## ----------------------------------- ##
10337 ## Main body of $CONFIG_STATUS script. ##
10338 ## ----------------------------------- ##
10339 _ASEOF
10340 test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
10341
10342 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10343 # Save the log message, to keep $0 and so on meaningful, and to
10344 # report actual input values of CONFIG_FILES etc. instead of their
10345 # values after options handling.
10346 ac_log="
10347 This file was extended by i3 $as_me 4.16.1, which was
10348 generated by GNU Autoconf 2.69. Invocation command line was
10349
10350 CONFIG_FILES = $CONFIG_FILES
10351 CONFIG_HEADERS = $CONFIG_HEADERS
10352 CONFIG_LINKS = $CONFIG_LINKS
10353 CONFIG_COMMANDS = $CONFIG_COMMANDS
10354 $ $0 $@
10355
10356 on `(hostname || uname -n) 2>/dev/null | sed 1q`
10357 "
10358
10359 _ACEOF
10360
10361 case $ac_config_files in *"
10362 "*) set x $ac_config_files; shift; ac_config_files=$*;;
10363 esac
10364
10365 case $ac_config_headers in *"
10366 "*) set x $ac_config_headers; shift; ac_config_headers=$*;;
10367 esac
10368
10369
10370 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10371 # Files that config.status was made for.
10372 config_files="$ac_config_files"
10373 config_headers="$ac_config_headers"
10374 config_commands="$ac_config_commands"
10375
10376 _ACEOF
10377
10378 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10379 ac_cs_usage="\
10380 \`$as_me' instantiates files and other configuration actions
10381 from templates according to the current configuration. Unless the files
10382 and actions are specified as TAGs, all are instantiated by default.
10383
10384 Usage: $0 [OPTION]... [TAG]...
10385
10386 -h, --help print this help, then exit
10387 -V, --version print version number and configuration settings, then exit
10388 --config print configuration, then exit
10389 -q, --quiet, --silent
10390 do not print progress messages
10391 -d, --debug don't remove temporary files
10392 --recheck update $as_me by reconfiguring in the same conditions
10393 --file=FILE[:TEMPLATE]
10394 instantiate the configuration file FILE
10395 --header=FILE[:TEMPLATE]
10396 instantiate the configuration header FILE
10397
10398 Configuration files:
10399 $config_files
10400
10401 Configuration headers:
10402 $config_headers
10403
10404 Configuration commands:
10405 $config_commands
10406
10407 Report bugs to <https://github.com/i3/i3/issues>."
10408
10409 _ACEOF
10410 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10411 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
10412 ac_cs_version="\\
10413 i3 config.status 4.16.1
10414 configured by $0, generated by GNU Autoconf 2.69,
10415 with options \\"\$ac_cs_config\\"
10416
10417 Copyright (C) 2012 Free Software Foundation, Inc.
10418 This config.status script is free software; the Free Software Foundation
10419 gives unlimited permission to copy, distribute and modify it."
10420
10421 ac_pwd='$ac_pwd'
10422 srcdir='$srcdir'
10423 INSTALL='$INSTALL'
10424 MKDIR_P='$MKDIR_P'
10425 AWK='$AWK'
10426 test -n "\$AWK" || AWK=awk
10427 _ACEOF
10428
10429 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10430 # The default lists apply if the user does not specify any file.
10431 ac_need_defaults=:
10432 while test $# != 0
10433 do
10434 case $1 in
10435 --*=?*)
10436 ac_option=`expr "X$1" : 'X\([^=]*\)='`
10437 ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
10438 ac_shift=:
10439 ;;
10440 --*=)
10441 ac_option=`expr "X$1" : 'X\([^=]*\)='`
10442 ac_optarg=
10443 ac_shift=:
10444 ;;
10445 *)
10446 ac_option=$1
10447 ac_optarg=$2
10448 ac_shift=shift
10449 ;;
10450 esac
10451
10452 case $ac_option in
10453 # Handling of the options.
10454 -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
10455 ac_cs_recheck=: ;;
10456 --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
10457 $as_echo "$ac_cs_version"; exit ;;
10458 --config | --confi | --conf | --con | --co | --c )
10459 $as_echo "$ac_cs_config"; exit ;;
10460 --debug | --debu | --deb | --de | --d | -d )
10461 debug=: ;;
10462 --file | --fil | --fi | --f )
10463 $ac_shift
10464 case $ac_optarg in
10465 *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
10466 '') as_fn_error $? "missing file argument" ;;
10467 esac
10468 as_fn_append CONFIG_FILES " '$ac_optarg'"
10469 ac_need_defaults=false;;
10470 --header | --heade | --head | --hea )
10471 $ac_shift
10472 case $ac_optarg in
10473 *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
10474 esac
10475 as_fn_append CONFIG_HEADERS " '$ac_optarg'"
10476 ac_need_defaults=false;;
10477 --he | --h)
10478 # Conflict between --help and --header
10479 as_fn_error $? "ambiguous option: \`$1'
10480 Try \`$0 --help' for more information.";;
10481 --help | --hel | -h )
10482 $as_echo "$ac_cs_usage"; exit ;;
10483 -q | -quiet | --quiet | --quie | --qui | --qu | --q \
10484 | -silent | --silent | --silen | --sile | --sil | --si | --s)
10485 ac_cs_silent=: ;;
10486
10487 # This is an error.
10488 -*) as_fn_error $? "unrecognized option: \`$1'
10489 Try \`$0 --help' for more information." ;;
10490
10491 *) as_fn_append ac_config_targets " $1"
10492 ac_need_defaults=false ;;
10493
10494 esac
10495 shift
10496 done
10497
10498 ac_configure_extra_args=
10499
10500 if $ac_cs_silent; then
10501 exec 6>/dev/null
10502 ac_configure_extra_args="$ac_configure_extra_args --silent"
10503 fi
10504
10505 _ACEOF
10506 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10507 if \$ac_cs_recheck; then
10508 set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
10509 shift
10510 \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
10511 CONFIG_SHELL='$SHELL'
10512 export CONFIG_SHELL
10513 exec "\$@"
10514 fi
10515
10516 _ACEOF
10517 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10518 exec 5>>config.log
10519 {
10520 echo
10521 sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
10522 ## Running $as_me. ##
10523 _ASBOX
10524 $as_echo "$ac_log"
10525 } >&5
10526
10527 _ACEOF
10528 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10529 #
10530 # INIT-COMMANDS
10531 #
10532 ax_enable_builddir_srcdir="$srcdir" # $srcdir
10533 ax_enable_builddir_host="$HOST" # $HOST / $host
10534 ax_enable_builddir_version="$VERSION" # $VERSION
10535 ax_enable_builddir_package="$PACKAGE" # $PACKAGE
10536 ax_enable_builddir_auxdir="$ax_enable_builddir_auxdir" # $AUX
10537 ax_enable_builddir_sed="$ax_enable_builddir_sed" # $SED
10538 ax_enable_builddir="$ax_enable_builddir" # $SUB
10539
10540 AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
10541
10542 _ACEOF
10543
10544 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10545
10546 # Handling of arguments.
10547 for ac_config_target in $ac_config_targets
10548 do
10549 case $ac_config_target in
10550 "buildir") CONFIG_COMMANDS="$CONFIG_COMMANDS buildir" ;;
10551 "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
10552 "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
10553 "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
10554 "testcases/lib/i3test.pm") CONFIG_FILES="$CONFIG_FILES testcases/lib/i3test.pm" ;;
10555 "man/asciidoc.conf") CONFIG_FILES="$CONFIG_FILES man/asciidoc.conf" ;;
10556 "testcases/complete-run.pl") CONFIG_FILES="$CONFIG_FILES testcases/complete-run.pl" ;;
10557
10558 *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
10559 esac
10560 done
10561
10562
10563 # If the user did not use the arguments to specify the items to instantiate,
10564 # then the envvar interface is used. Set only those that are not.
10565 # We use the long form for the default assignment because of an extremely
10566 # bizarre bug on SunOS 4.1.3.
10567 if $ac_need_defaults; then
10568 test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
10569 test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
10570 test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
10571 fi
10572
10573 # Have a temporary directory for convenience. Make it in the build tree
10574 # simply because there is no reason against having it here, and in addition,
10575 # creating and moving files from /tmp can sometimes cause problems.
10576 # Hook for its removal unless debugging.
10577 # Note that there is a small window in which the directory will not be cleaned:
10578 # after its creation but before its name has been assigned to `$tmp'.
10579 $debug ||
10580 {
10581 tmp= ac_tmp=
10582 trap 'exit_status=$?
10583 : "${ac_tmp:=$tmp}"
10584 { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
10585 ' 0
10586 trap 'as_fn_exit 1' 1 2 13 15
10587 }
10588 # Create a (secure) tmp directory for tmp files.
10589
10590 {
10591 tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
10592 test -d "$tmp"
10593 } ||
10594 {
10595 tmp=./conf$$-$RANDOM
10596 (umask 077 && mkdir "$tmp")
10597 } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
10598 ac_tmp=$tmp
10599
10600 # Set up the scripts for CONFIG_FILES section.
10601 # No need to generate them if there are no CONFIG_FILES.
10602 # This happens for instance with `./config.status config.h'.
10603 if test -n "$CONFIG_FILES"; then
10604
10605
10606 ac_cr=`echo X | tr X '\015'`
10607 # On cygwin, bash can eat \r inside `` if the user requested igncr.
10608 # But we know of no other shell where ac_cr would be empty at this
10609 # point, so we can use a bashism as a fallback.
10610 if test "x$ac_cr" = x; then
10611 eval ac_cr=\$\'\\r\'
10612 fi
10613 ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
10614 if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
10615 ac_cs_awk_cr='\\r'
10616 else
10617 ac_cs_awk_cr=$ac_cr
10618 fi
10619
10620 echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
10621 _ACEOF
10622
10623
10624 {
10625 echo "cat >conf$$subs.awk <<_ACEOF" &&
10626 echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
10627 echo "_ACEOF"
10628 } >conf$$subs.sh ||
10629 as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
10630 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
10631 ac_delim='%!_!# '
10632 for ac_last_try in false false false false false :; do
10633 . ./conf$$subs.sh ||
10634 as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
10635
10636 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
10637 if test $ac_delim_n = $ac_delim_num; then
10638 break
10639 elif $ac_last_try; then
10640 as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
10641 else
10642 ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
10643 fi
10644 done
10645 rm -f conf$$subs.sh
10646
10647 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10648 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
10649 _ACEOF
10650 sed -n '
10651 h
10652 s/^/S["/; s/!.*/"]=/
10653 p
10654 g
10655 s/^[^!]*!//
10656 :repl
10657 t repl
10658 s/'"$ac_delim"'$//
10659 t delim
10660 :nl
10661 h
10662 s/\(.\{148\}\)..*/\1/
10663 t more1
10664 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
10665 p
10666 n
10667 b repl
10668 :more1
10669 s/["\\]/\\&/g; s/^/"/; s/$/"\\/
10670 p
10671 g
10672 s/.\{148\}//
10673 t nl
10674 :delim
10675 h
10676 s/\(.\{148\}\)..*/\1/
10677 t more2
10678 s/["\\]/\\&/g; s/^/"/; s/$/"/
10679 p
10680 b
10681 :more2
10682 s/["\\]/\\&/g; s/^/"/; s/$/"\\/
10683 p
10684 g
10685 s/.\{148\}//
10686 t delim
10687 ' <conf$$subs.awk | sed '
10688 /^[^""]/{
10689 N
10690 s/\n//
10691 }
10692 ' >>$CONFIG_STATUS || ac_write_fail=1
10693 rm -f conf$$subs.awk
10694 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10695 _ACAWK
10696 cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
10697 for (key in S) S_is_set[key] = 1
10698 FS = ""
10699
10700 }
10701 {
10702 line = $ 0
10703 nfields = split(line, field, "@")
10704 substed = 0
10705 len = length(field[1])
10706 for (i = 2; i < nfields; i++) {
10707 key = field[i]
10708 keylen = length(key)
10709 if (S_is_set[key]) {
10710 value = S[key]
10711 line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
10712 len += length(value) + length(field[++i])
10713 substed = 1
10714 } else
10715 len += 1 + keylen
10716 }
10717
10718 print line
10719 }
10720
10721 _ACAWK
10722 _ACEOF
10723 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10724 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
10725 sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
10726 else
10727 cat
10728 fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
10729 || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
10730 _ACEOF
10731
10732 # VPATH may cause trouble with some makes, so we remove sole $(srcdir),
10733 # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
10734 # trailing colons and then remove the whole line if VPATH becomes empty
10735 # (actually we leave an empty line to preserve line numbers).
10736 if test "x$srcdir" = x.; then
10737 ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
10738 h
10739 s///
10740 s/^/:/
10741 s/[ ]*$/:/
10742 s/:\$(srcdir):/:/g
10743 s/:\${srcdir}:/:/g
10744 s/:@srcdir@:/:/g
10745 s/^:*//
10746 s/:*$//
10747 x
10748 s/\(=[ ]*\).*/\1/
10749 G
10750 s/\n//
10751 s/^[^=]*=[ ]*$//
10752 }'
10753 fi
10754
10755 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10756 fi # test -n "$CONFIG_FILES"
10757
10758 # Set up the scripts for CONFIG_HEADERS section.
10759 # No need to generate them if there are no CONFIG_HEADERS.
10760 # This happens for instance with `./config.status Makefile'.
10761 if test -n "$CONFIG_HEADERS"; then
10762 cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
10763 BEGIN {
10764 _ACEOF
10765
10766 # Transform confdefs.h into an awk script `defines.awk', embedded as
10767 # here-document in config.status, that substitutes the proper values into
10768 # config.h.in to produce config.h.
10769
10770 # Create a delimiter string that does not exist in confdefs.h, to ease
10771 # handling of long lines.
10772 ac_delim='%!_!# '
10773 for ac_last_try in false false :; do
10774 ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
10775 if test -z "$ac_tt"; then
10776 break
10777 elif $ac_last_try; then
10778 as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
10779 else
10780 ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
10781 fi
10782 done
10783
10784 # For the awk script, D is an array of macro values keyed by name,
10785 # likewise P contains macro parameters if any. Preserve backslash
10786 # newline sequences.
10787
10788 ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
10789 sed -n '
10790 s/.\{148\}/&'"$ac_delim"'/g
10791 t rset
10792 :rset
10793 s/^[ ]*#[ ]*define[ ][ ]*/ /
10794 t def
10795 d
10796 :def
10797 s/\\$//
10798 t bsnl
10799 s/["\\]/\\&/g
10800 s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
10801 D["\1"]=" \3"/p
10802 s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
10803 d
10804 :bsnl
10805 s/["\\]/\\&/g
10806 s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
10807 D["\1"]=" \3\\\\\\n"\\/p
10808 t cont
10809 s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
10810 t cont
10811 d
10812 :cont
10813 n
10814 s/.\{148\}/&'"$ac_delim"'/g
10815 t clear
10816 :clear
10817 s/\\$//
10818 t bsnlc
10819 s/["\\]/\\&/g; s/^/"/; s/$/"/p
10820 d
10821 :bsnlc
10822 s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
10823 b cont
10824 ' <confdefs.h | sed '
10825 s/'"$ac_delim"'/"\\\
10826 "/g' >>$CONFIG_STATUS || ac_write_fail=1
10827
10828 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
10829 for (key in D) D_is_set[key] = 1
10830 FS = ""
10831 }
10832 /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
10833 line = \$ 0
10834 split(line, arg, " ")
10835 if (arg[1] == "#") {
10836 defundef = arg[2]
10837 mac1 = arg[3]
10838 } else {
10839 defundef = substr(arg[1], 2)
10840 mac1 = arg[2]
10841 }
10842 split(mac1, mac2, "(") #)
10843 macro = mac2[1]
10844 prefix = substr(line, 1, index(line, defundef) - 1)
10845 if (D_is_set[macro]) {
10846 # Preserve the white space surrounding the "#".
10847 print prefix "define", macro P[macro] D[macro]
10848 next
10849 } else {
10850 # Replace #undef with comments. This is necessary, for example,
10851 # in the case of _POSIX_SOURCE, which is predefined and required
10852 # on some systems where configure will not decide to define it.
10853 if (defundef == "undef") {
10854 print "/*", prefix defundef, macro, "*/"
10855 next
10856 }
10857 }
10858 }
10859 { print }
10860 _ACAWK
10861 _ACEOF
10862 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
10863 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
10864 fi # test -n "$CONFIG_HEADERS"
10865
10866
10867 eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
10868 shift
10869 for ac_tag
10870 do
10871 case $ac_tag in
10872 :[FHLC]) ac_mode=$ac_tag; continue;;
10873 esac
10874 case $ac_mode$ac_tag in
10875 :[FHL]*:*);;
10876 :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
10877 :[FH]-) ac_tag=-:-;;
10878 :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
10879 esac
10880 ac_save_IFS=$IFS
10881 IFS=:
10882 set x $ac_tag
10883 IFS=$ac_save_IFS
10884 shift
10885 ac_file=$1
10886 shift
10887
10888 case $ac_mode in
10889 :L) ac_source=$1;;
10890 :[FH])
10891 ac_file_inputs=
10892 for ac_f
10893 do
10894 case $ac_f in
10895 -) ac_f="$ac_tmp/stdin";;
10896 *) # Look for the file first in the build tree, then in the source tree
10897 # (if the path is not absolute). The absolute path cannot be DOS-style,
10898 # because $ac_f cannot contain `:'.
10899 test -f "$ac_f" ||
10900 case $ac_f in
10901 [\\/$]*) false;;
10902 *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
10903 esac ||
10904 as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
10905 esac
10906 case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
10907 as_fn_append ac_file_inputs " '$ac_f'"
10908 done
10909
10910 # Let's still pretend it is `configure' which instantiates (i.e., don't
10911 # use $as_me), people would be surprised to read:
10912 # /* config.h. Generated by config.status. */
10913 configure_input='Generated from '`
10914 $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
10915 `' by configure.'
10916 if test x"$ac_file" != x-; then
10917 configure_input="$ac_file. $configure_input"
10918 { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
10919 $as_echo "$as_me: creating $ac_file" >&6;}
10920 fi
10921 # Neutralize special characters interpreted by sed in replacement strings.
10922 case $configure_input in #(
10923 *\&* | *\|* | *\\* )
10924 ac_sed_conf_input=`$as_echo "$configure_input" |
10925 sed 's/[\\\\&|]/\\\\&/g'`;; #(
10926 *) ac_sed_conf_input=$configure_input;;
10927 esac
10928
10929 case $ac_tag in
10930 *:-:* | *:-) cat >"$ac_tmp/stdin" \
10931 || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
10932 esac
10933 ;;
10934 esac
10935
10936 ac_dir=`$as_dirname -- "$ac_file" ||
10937 $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
10938 X"$ac_file" : 'X\(//\)[^/]' \| \
10939 X"$ac_file" : 'X\(//\)$' \| \
10940 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
10941 $as_echo X"$ac_file" |
10942 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
10943 s//\1/
10944 q
10945 }
10946 /^X\(\/\/\)[^/].*/{
10947 s//\1/
10948 q
10949 }
10950 /^X\(\/\/\)$/{
10951 s//\1/
10952 q
10953 }
10954 /^X\(\/\).*/{
10955 s//\1/
10956 q
10957 }
10958 s/.*/./; q'`
10959 as_dir="$ac_dir"; as_fn_mkdir_p
10960 ac_builddir=.
10961
10962 case "$ac_dir" in
10963 .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
10964 *)
10965 ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
10966 # A ".." for each directory in $ac_dir_suffix.
10967 ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
10968 case $ac_top_builddir_sub in
10969 "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
10970 *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
10971 esac ;;
10972 esac
10973 ac_abs_top_builddir=$ac_pwd
10974 ac_abs_builddir=$ac_pwd$ac_dir_suffix
10975 # for backward compatibility:
10976 ac_top_builddir=$ac_top_build_prefix
10977
10978 case $srcdir in
10979 .) # We are building in place.
10980 ac_srcdir=.
10981 ac_top_srcdir=$ac_top_builddir_sub
10982 ac_abs_top_srcdir=$ac_pwd ;;
10983 [\\/]* | ?:[\\/]* ) # Absolute name.
10984 ac_srcdir=$srcdir$ac_dir_suffix;
10985 ac_top_srcdir=$srcdir
10986 ac_abs_top_srcdir=$srcdir ;;
10987 *) # Relative name.
10988 ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
10989 ac_top_srcdir=$ac_top_build_prefix$srcdir
10990 ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
10991 esac
10992 ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
10993
10994
10995 case $ac_mode in
10996 :F)
10997 #
10998 # CONFIG_FILE
10999 #
11000
11001 case $INSTALL in
11002 [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
11003 *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
11004 esac
11005 ac_MKDIR_P=$MKDIR_P
11006 case $MKDIR_P in
11007 [\\/$]* | ?:[\\/]* ) ;;
11008 */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
11009 esac
11010 _ACEOF
11011
11012 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
11013 # If the template does not know about datarootdir, expand it.
11014 # FIXME: This hack should be removed a few years after 2.60.
11015 ac_datarootdir_hack=; ac_datarootdir_seen=
11016 ac_sed_dataroot='
11017 /datarootdir/ {
11018 p
11019 q
11020 }
11021 /@datadir@/p
11022 /@docdir@/p
11023 /@infodir@/p
11024 /@localedir@/p
11025 /@mandir@/p'
11026 case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
11027 *datarootdir*) ac_datarootdir_seen=yes;;
11028 *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
11029 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
11030 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
11031 _ACEOF
11032 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
11033 ac_datarootdir_hack='
11034 s&@datadir@&$datadir&g
11035 s&@docdir@&$docdir&g
11036 s&@infodir@&$infodir&g
11037 s&@localedir@&$localedir&g
11038 s&@mandir@&$mandir&g
11039 s&\\\${datarootdir}&$datarootdir&g' ;;
11040 esac
11041 _ACEOF
11042
11043 # Neutralize VPATH when `$srcdir' = `.'.
11044 # Shell code in configure.ac might set extrasub.
11045 # FIXME: do we really want to maintain this feature?
11046 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
11047 ac_sed_extra="$ac_vpsub
11048 $extrasub
11049 _ACEOF
11050 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
11051 :t
11052 /@[a-zA-Z_][a-zA-Z_0-9]*@/!b
11053 s|@configure_input@|$ac_sed_conf_input|;t t
11054 s&@top_builddir@&$ac_top_builddir_sub&;t t
11055 s&@top_build_prefix@&$ac_top_build_prefix&;t t
11056 s&@srcdir@&$ac_srcdir&;t t
11057 s&@abs_srcdir@&$ac_abs_srcdir&;t t
11058 s&@top_srcdir@&$ac_top_srcdir&;t t
11059 s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
11060 s&@builddir@&$ac_builddir&;t t
11061 s&@abs_builddir@&$ac_abs_builddir&;t t
11062 s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
11063 s&@INSTALL@&$ac_INSTALL&;t t
11064 s&@MKDIR_P@&$ac_MKDIR_P&;t t
11065 $ac_datarootdir_hack
11066 "
11067 eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
11068 >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
11069
11070 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
11071 { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
11072 { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
11073 "$ac_tmp/out"`; test -z "$ac_out"; } &&
11074 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
11075 which seems to be undefined. Please make sure it is defined" >&5
11076 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
11077 which seems to be undefined. Please make sure it is defined" >&2;}
11078
11079 rm -f "$ac_tmp/stdin"
11080 case $ac_file in
11081 -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
11082 *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
11083 esac \
11084 || as_fn_error $? "could not create $ac_file" "$LINENO" 5
11085 ;;
11086 :H)
11087 #
11088 # CONFIG_HEADER
11089 #
11090 if test x"$ac_file" != x-; then
11091 {
11092 $as_echo "/* $configure_input */" \
11093 && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
11094 } >"$ac_tmp/config.h" \
11095 || as_fn_error $? "could not create $ac_file" "$LINENO" 5
11096 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
11097 { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
11098 $as_echo "$as_me: $ac_file is unchanged" >&6;}
11099 else
11100 rm -f "$ac_file"
11101 mv "$ac_tmp/config.h" "$ac_file" \
11102 || as_fn_error $? "could not create $ac_file" "$LINENO" 5
11103 fi
11104 else
11105 $as_echo "/* $configure_input */" \
11106 && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
11107 || as_fn_error $? "could not create -" "$LINENO" 5
11108 fi
11109 # Compute "$ac_file"'s index in $config_headers.
11110 _am_arg="$ac_file"
11111 _am_stamp_count=1
11112 for _am_header in $config_headers :; do
11113 case $_am_header in
11114 $_am_arg | $_am_arg:* )
11115 break ;;
11116 * )
11117 _am_stamp_count=`expr $_am_stamp_count + 1` ;;
11118 esac
11119 done
11120 echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
11121 $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
11122 X"$_am_arg" : 'X\(//\)[^/]' \| \
11123 X"$_am_arg" : 'X\(//\)$' \| \
11124 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
11125 $as_echo X"$_am_arg" |
11126 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
11127 s//\1/
11128 q
11129 }
11130 /^X\(\/\/\)[^/].*/{
11131 s//\1/
11132 q
11133 }
11134 /^X\(\/\/\)$/{
11135 s//\1/
11136 q
11137 }
11138 /^X\(\/\).*/{
11139 s//\1/
11140 q
11141 }
11142 s/.*/./; q'`/stamp-h$_am_stamp_count
11143 ;;
11144
11145 :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
11146 $as_echo "$as_me: executing $ac_file commands" >&6;}
11147 ;;
11148 esac
11149
11150
11151 case $ac_file$ac_mode in
11152 "buildir":C) ac_top_srcdir="$ax_enable_builddir_srcdir"
11153 if test ".$ax_enable_builddir" = ".." ; then
11154 if test -f "$top_srcdir/Makefile" ; then
11155 { $as_echo "$as_me:${as_lineno-$LINENO}: skipping top_srcdir/Makefile - left untouched" >&5
11156 $as_echo "$as_me: skipping top_srcdir/Makefile - left untouched" >&6;}
11157 else
11158 { $as_echo "$as_me:${as_lineno-$LINENO}: skipping top_srcdir/Makefile - not created" >&5
11159 $as_echo "$as_me: skipping top_srcdir/Makefile - not created" >&6;}
11160 fi
11161 else
11162 if test -f "$ac_top_srcdir/Makefile" ; then
11163 a=`grep "^VERSION " "$ac_top_srcdir/Makefile"` ; b=`grep "^VERSION " Makefile`
11164 test "$a" != "$b" && rm "$ac_top_srcdir/Makefile"
11165 fi
11166 if test -f "$ac_top_srcdir/Makefile" ; then
11167 echo "$ac_top_srcdir/Makefile : $ac_top_srcdir/Makefile.in" > $tmp/conftemp.mk
11168 echo " @ echo 'REMOVED,,,' >\$@" >> $tmp/conftemp.mk
11169 eval "${MAKE-make} -f $tmp/conftemp.mk 2>/dev/null >/dev/null"
11170 if grep '^REMOVED,,,' "$ac_top_srcdir/Makefile" >/dev/null
11171 then rm $ac_top_srcdir/Makefile ; fi
11172 cp $tmp/conftemp.mk $ac_top_srcdir/makefiles.mk~ ## DEBUGGING
11173 fi
11174 if test ! -f "$ac_top_srcdir/Makefile" ; then
11175 { $as_echo "$as_me:${as_lineno-$LINENO}: create top_srcdir/Makefile guessed from local Makefile" >&5
11176 $as_echo "$as_me: create top_srcdir/Makefile guessed from local Makefile" >&6;}
11177 x='`' ; cat >$tmp/conftemp.sed <<_EOF
11178 /^\$/n
11179 x
11180 /^\$/bS
11181 x
11182 /\\\\\$/{H;d;}
11183 {H;s/.*//;x;}
11184 bM
11185 :S
11186 x
11187 /\\\\\$/{h;d;}
11188 {h;s/.*//;x;}
11189 :M
11190 s/\\(\\n\\) /\\1 /g
11191 /^ /d
11192 /^[ ]*[\\#]/d
11193 /^VPATH *=/d
11194 s/^srcdir *=.*/srcdir = ./
11195 s/^top_srcdir *=.*/top_srcdir = ./
11196 /[:=]/!d
11197 /^\\./d
11198 / = /b
11199 / .= /b
11200 /:/!b
11201 s/:.*/:/
11202 s/ / /g
11203 s/ \\([a-z][a-z-]*[a-zA-Z0-9]\\)\\([ :]\\)/ \\1 \\1-all\\2/g
11204 s/^\\([a-z][a-z-]*[a-zA-Z0-9]\\)\\([ :]\\)/\\1 \\1-all\\2/
11205 s/ / /g
11206 /^all all-all[ :]/i\\
11207 all-configured : all-all
11208 s/ [a-zA-Z0-9-]*-all [a-zA-Z0-9-]*-all-all//g
11209 /-all-all/d
11210 a\\
11211 @ HOST="\$(HOST)\" \\\\\\
11212 ; test ".\$\$HOST" = "." && HOST=$x sh $ax_enable_builddir_auxdir/config.guess $x \\\\\\
11213 ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
11214 ; use=$x basename "\$\@" -all $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\
11215 ; echo "MAKE \$\$HOST : \$\$n * \$\@"; if test "\$\$n" -eq "0" ; then : \\\\\\
11216 ; BUILD=$x grep "^####.*|" Makefile |tail -1| sed -e 's/.*|//' $x ; fi \\\\\\
11217 ; test ".\$\$BUILD" = "." && BUILD="." \\\\\\
11218 ; test "\$\$use" = "\$\@" && BUILD=$x echo "\$\$BUILD" | tail -1 $x \\\\\\
11219 ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
11220 ; (cd "\$\$i" && test ! -f configure && \$(MAKE) \$\$use) || exit; done
11221 /dist-all *:/a\\
11222 @ HOST="\$(HOST)\" \\\\\\
11223 ; test ".\$\$HOST" = "." && HOST=$x sh $ax_enable_builddir_auxdir/config.guess $x \\\\\\
11224 ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
11225 ; found=$x echo \$\$BUILD | wc -w $x \\\\\\
11226 ; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).tar.*" \\\\\\
11227 ; if test "\$\$found" -eq "0" ; then : \\\\\\
11228 ; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\
11229 ; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
11230 ; for f in \$\$i/\$(PACKAGE)-\$(VERSION).tar.* \\\\\\
11231 ; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done
11232 /dist-[a-zA-Z0-9]*-all *:/a\\
11233 @ HOST="\$(HOST)\" \\\\\\
11234 ; test ".\$\$HOST" = "." && HOST=$x sh ./config.guess $x \\\\\\
11235 ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
11236 ; found=$x echo \$\$BUILD | wc -w $x \\\\\\
11237 ; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).*" \\\\\\
11238 ; if test "\$\$found" -eq "0" ; then : \\\\\\
11239 ; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\
11240 ; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
11241 ; for f in \$\$i/\$(PACKAGE)-\$(VERSION).* \\\\\\
11242 ; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done
11243 /distclean-all *:/a\\
11244 @ HOST="\$(HOST)\" \\\\\\
11245 ; test ".\$\$HOST" = "." && HOST=$x sh $ax_enable_builddir_auxdir/config.guess $x \\\\\\
11246 ; BUILD=$x grep "^#### .*|" Makefile | sed -e 's/.*|//' $x \\\\\\
11247 ; use=$x basename "\$\@" -all $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\
11248 ; echo "MAKE \$\$HOST : \$\$n * \$\@ (all local builds)" \\\\\\
11249 ; test ".\$\$BUILD" = "." && BUILD="." \\\\\\
11250 ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
11251 ; echo "# rm -r \$\$i"; done ; echo "# (sleep 3)" ; sleep 3 \\\\\\
11252 ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
11253 ; echo "\$\$i" | grep "^/" > /dev/null && continue \\\\\\
11254 ; echo "\$\$i" | grep "^../" > /dev/null && continue \\\\\\
11255 ; echo "rm -r \$\$i"; (rm -r "\$\$i") ; done ; rm Makefile
11256 _EOF
11257 cp "$tmp/conftemp.sed" "$ac_top_srcdir/makefile.sed~" ## DEBUGGING
11258 $ax_enable_builddir_sed -f $tmp/conftemp.sed Makefile >$ac_top_srcdir/Makefile
11259 if test -f "$ac_top_srcdir/Makefile.mk" ; then
11260 { $as_echo "$as_me:${as_lineno-$LINENO}: extend top_srcdir/Makefile with top_srcdir/Makefile.mk" >&5
11261 $as_echo "$as_me: extend top_srcdir/Makefile with top_srcdir/Makefile.mk" >&6;}
11262 cat $ac_top_srcdir/Makefile.mk >>$ac_top_srcdir/Makefile
11263 fi ; xxxx="####"
11264 echo "$xxxx CONFIGURATIONS FOR TOPLEVEL MAKEFILE: " >>$ac_top_srcdir/Makefile
11265 # sanity check
11266 if grep '^; echo "MAKE ' $ac_top_srcdir/Makefile >/dev/null ; then
11267 { $as_echo "$as_me:${as_lineno-$LINENO}: buggy sed found - it deletes tab in \"a\" text parts" >&5
11268 $as_echo "$as_me: buggy sed found - it deletes tab in \"a\" text parts" >&6;}
11269 $ax_enable_builddir_sed -e '/^@ HOST=/s/^/ /' -e '/^; /s/^/ /' $ac_top_srcdir/Makefile \
11270 >$ac_top_srcdir/Makefile~
11271 (test -s $ac_top_srcdir/Makefile~ && mv $ac_top_srcdir/Makefile~ $ac_top_srcdir/Makefile) 2>/dev/null
11272 fi
11273 else
11274 xxxx="\\#\\#\\#\\#"
11275 # echo "/^$xxxx *$ax_enable_builddir_host /d" >$tmp/conftemp.sed
11276 echo "s!^$xxxx [^|]* | *$ax_enable_builddir *\$!$xxxx ...... $ax_enable_builddir!" >$tmp/conftemp.sed
11277 $ax_enable_builddir_sed -f "$tmp/conftemp.sed" "$ac_top_srcdir/Makefile" >$tmp/mkfile.tmp
11278 cp "$tmp/conftemp.sed" "$ac_top_srcdir/makefiles.sed~" ## DEBUGGING
11279 cp "$tmp/mkfile.tmp" "$ac_top_srcdir/makefiles.out~" ## DEBUGGING
11280 if cmp -s "$ac_top_srcdir/Makefile" "$tmp/mkfile.tmp" 2>/dev/null ; then
11281 { $as_echo "$as_me:${as_lineno-$LINENO}: keeping top_srcdir/Makefile from earlier configure" >&5
11282 $as_echo "$as_me: keeping top_srcdir/Makefile from earlier configure" >&6;}
11283 rm "$tmp/mkfile.tmp"
11284 else
11285 { $as_echo "$as_me:${as_lineno-$LINENO}: reusing top_srcdir/Makefile from earlier configure" >&5
11286 $as_echo "$as_me: reusing top_srcdir/Makefile from earlier configure" >&6;}
11287 mv "$tmp/mkfile.tmp" "$ac_top_srcdir/Makefile"
11288 fi
11289 fi
11290 { $as_echo "$as_me:${as_lineno-$LINENO}: build in $ax_enable_builddir (HOST=$ax_enable_builddir_host)" >&5
11291 $as_echo "$as_me: build in $ax_enable_builddir (HOST=$ax_enable_builddir_host)" >&6;}
11292 xxxx="####"
11293 echo "$xxxx" "$ax_enable_builddir_host" "|$ax_enable_builddir" >>$ac_top_srcdir/Makefile
11294 fi
11295 ;;
11296 "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
11297 # Older Autoconf quotes --file arguments for eval, but not when files
11298 # are listed without --file. Let's play safe and only enable the eval
11299 # if we detect the quoting.
11300 # TODO: see whether this extra hack can be removed once we start
11301 # requiring Autoconf 2.70 or later.
11302 case $CONFIG_FILES in #(
11303 *\'*) :
11304 eval set x "$CONFIG_FILES" ;; #(
11305 *) :
11306 set x $CONFIG_FILES ;; #(
11307 *) :
11308 ;;
11309 esac
11310 shift
11311 # Used to flag and report bootstrapping failures.
11312 am_rc=0
11313 for am_mf
11314 do
11315 # Strip MF so we end up with the name of the file.
11316 am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
11317 # Check whether this is an Automake generated Makefile which includes
11318 # dependency-tracking related rules and includes.
11319 # Grep'ing the whole file directly is not great: AIX grep has a line
11320 # limit of 2048, but all sed's we know have understand at least 4000.
11321 sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
11322 || continue
11323 am_dirpart=`$as_dirname -- "$am_mf" ||
11324 $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
11325 X"$am_mf" : 'X\(//\)[^/]' \| \
11326 X"$am_mf" : 'X\(//\)$' \| \
11327 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
11328 $as_echo X"$am_mf" |
11329 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
11330 s//\1/
11331 q
11332 }
11333 /^X\(\/\/\)[^/].*/{
11334 s//\1/
11335 q
11336 }
11337 /^X\(\/\/\)$/{
11338 s//\1/
11339 q
11340 }
11341 /^X\(\/\).*/{
11342 s//\1/
11343 q
11344 }
11345 s/.*/./; q'`
11346 am_filepart=`$as_basename -- "$am_mf" ||
11347 $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
11348 X"$am_mf" : 'X\(//\)$' \| \
11349 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
11350 $as_echo X/"$am_mf" |
11351 sed '/^.*\/\([^/][^/]*\)\/*$/{
11352 s//\1/
11353 q
11354 }
11355 /^X\/\(\/\/\)$/{
11356 s//\1/
11357 q
11358 }
11359 /^X\/\(\/\).*/{
11360 s//\1/
11361 q
11362 }
11363 s/.*/./; q'`
11364 { echo "$as_me:$LINENO: cd "$am_dirpart" \
11365 && sed -e '/# am--include-marker/d' "$am_filepart" \
11366 | $MAKE -f - am--depfiles" >&5
11367 (cd "$am_dirpart" \
11368 && sed -e '/# am--include-marker/d' "$am_filepart" \
11369 | $MAKE -f - am--depfiles) >&5 2>&5
11370 ac_status=$?
11371 echo "$as_me:$LINENO: \$? = $ac_status" >&5
11372 (exit $ac_status); } || am_rc=$?
11373 done
11374 if test $am_rc -ne 0; then
11375 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
11376 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
11377 as_fn_error $? "Something went wrong bootstrapping makefile fragments
11378 for automatic dependency tracking. Try re-running configure with the
11379 '--disable-dependency-tracking' option to at least be able to build
11380 the package (albeit without support for automatic dependency tracking).
11381 See \`config.log' for more details" "$LINENO" 5; }
11382 fi
11383 { am_dirpart=; unset am_dirpart;}
11384 { am_filepart=; unset am_filepart;}
11385 { am_mf=; unset am_mf;}
11386 { am_rc=; unset am_rc;}
11387 rm -f conftest-deps.mk
11388 }
11389 ;;
11390 "testcases/complete-run.pl":F) chmod +x testcases/complete-run.pl ;;
11391
11392 esac
11393 done # for ac_tag
11394
11395
11396 as_fn_exit 0
11397 _ACEOF
11398 ac_clean_files=$ac_clean_files_save
11399
11400 test $ac_write_fail = 0 ||
11401 as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
11402
11403
11404 # configure is writing to config.log, and then calls config.status.
11405 # config.status does its own redirection, appending to config.log.
11406 # Unfortunately, on DOS this fails, as config.log is still kept open
11407 # by configure, so config.status won't be able to write to it; its
11408 # output is simply discarded. So we exec the FD to /dev/null,
11409 # effectively closing config.log, so it can be properly (re)opened and
11410 # appended to by config.status. When coming back to configure, we
11411 # need to make the FD available again.
11412 if test "$no_create" != yes; then
11413 ac_cs_success=:
11414 ac_config_status_args=
11415 test "$silent" = yes &&
11416 ac_config_status_args="$ac_config_status_args --quiet"
11417 exec 5>/dev/null
11418 $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
11419 exec 5>>config.log
11420 # Use ||, not &&, to avoid exiting from the if with $? = 1, which
11421 # would make configure fail if this is the last instruction.
11422 $ac_cs_success || as_fn_exit 1
11423 fi
11424 if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
11425 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
11426 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
11427 fi
11428
11429
11430 if test -z "${BUILD_DOCS_TRUE}"; then
11431 print_BUILD_DOCS=yes
11432 else
11433 print_BUILD_DOCS=no
11434 fi
11435
11436
11437 if test -z "${BUILD_MANS_TRUE}"; then
11438 print_BUILD_MANS=yes
11439 else
11440 print_BUILD_MANS=no
11441 fi
11442
11443 in_git_worktree=`git rev-parse --is-inside-work-tree 2>/dev/null`
11444 if [ "$in_git_worktree" = "true" ]; then
11445 git_dir=`git rev-parse --git-dir 2>/dev/null`
11446 srcdir=`dirname "$git_dir"`
11447 exclude_dir=`pwd | sed "s,^$srcdir,,g"`
11448 if ! grep -q "^$exclude_dir" "$git_dir/info/exclude"; then
11449 echo "$exclude_dir" >> "$git_dir/info/exclude"
11450 fi
11451 fi
11452
11453 echo \
11454 "--------------------------------------------------------------------------------
11455 build configured:
11456
11457 i3 version: `echo ${I3_VERSION} | sed 's,\\\\,,g'`
11458 is release version: ${is_release}
11459
11460 build manpages: ${print_BUILD_MANS}
11461 build docs: ${print_BUILD_DOCS}
11462 enable debug flags: ${ax_enable_debug}
11463 code coverage: ${CODE_COVERAGE_ENABLED}
11464 enabled sanitizers: ${ax_enabled_sanitizers}
11465
11466 To compile, run:
11467
11468 cd `pwd` && make -j8
11469 --------------------------------------------------------------------------------"
0 # -*- Autoconf -*-
1 # Run autoreconf -fi to generate a configure script from this file.
2
3 AC_PREREQ([2.69])
4 AC_INIT([i3], [4.16.1], [https://github.com/i3/i3/issues])
5 # For AX_EXTEND_SRCDIR
6 AX_ENABLE_BUILDDIR
7 AM_INIT_AUTOMAKE([foreign subdir-objects -Wall no-dist-gzip dist-bzip2])
8 # Default to silent rules, use V=1 to get verbose compilation output.
9 AM_SILENT_RULES([yes])
10 # Make it possible to disable maintainer mode to disable re-generation of build
11 # system files.
12 AM_MAINTAINER_MODE([enable])
13 AC_CONFIG_SRCDIR([libi3/ipc_recv_message.c])
14 AC_CONFIG_HEADERS([config.h])
15 AC_CONFIG_MACRO_DIR([m4])
16
17 dnl Verify macros defined in m4/ such as AX_SANITIZERS are not present in the
18 dnl output, i.e. are replaced as expected. This line results in a better error
19 dnl message when using aclocal < 1.13 (which does not understand
20 dnl AC_CONFIG_MACRO_DIR) without passing the -I m4 parameter.
21 m4_pattern_forbid([AX_SANITIZERS])
22
23 # Verify we are using GNU make because we use '%'-style pattern rules in
24 # Makefile.am, which are a GNU make extension. Pull requests to replace
25 # '%'-style pattern rules with a more portable alternative are welcome.
26 AX_CHECK_GNU_MAKE
27 AS_VAR_IF([_cv_gnu_make_command], [""], [AC_MSG_ERROR([the i3 Makefile.am requires GNU make])])
28
29 AX_EXTEND_SRCDIR
30
31 AS_IF([test -d ${srcdir}/.git],
32 [
33 VERSION="$(git -C ${srcdir} describe --tags --abbrev=0)"
34 I3_VERSION="$(git -C ${srcdir} describe --tags --always) ($(git -C ${srcdir} log --pretty=format:%cd --date=short -n1), branch \\\"$(git -C ${srcdir} describe --tags --always --all | sed s:heads/::)\\\")"
35 # Mirrors what libi3/is_debug_build.c does:
36 is_release=$(test $(echo "${I3_VERSION}" | cut -d '(' -f 1 | wc -m) -lt 10 && echo yes || echo no)
37 ],
38 [
39 VERSION="$(cut -d '-' -f 1 ${srcdir}/I3_VERSION | cut -d ' ' -f 1)"
40 I3_VERSION="$(sed -e 's/@<:@\"?\\@:>@/\\&/g' ${srcdir}/I3_VERSION)"
41 is_release="$(grep -q non-git ${srcdir}/I3_VERSION && echo no || echo yes)"
42 ])
43 AC_SUBST([I3_VERSION], [$I3_VERSION])
44 MAJOR_VERSION="$(echo ${VERSION} | cut -d '.' -f 1)"
45 MINOR_VERSION="$(echo ${VERSION} | cut -d '.' -f 2)"
46 PATCH_VERSION="$(echo ${VERSION} | cut -d '.' -f 3)"
47 AS_IF([test "x${PATCH_VERSION}" = x], [PATCH_VERSION=0])
48 AC_DEFINE_UNQUOTED([I3_VERSION], ["${I3_VERSION}"], [i3 version])
49 AC_DEFINE_UNQUOTED([MAJOR_VERSION], [${MAJOR_VERSION}], [i3 major version])
50 AC_DEFINE_UNQUOTED([MINOR_VERSION], [${MINOR_VERSION}], [i3 minor version])
51 AC_DEFINE_UNQUOTED([PATCH_VERSION], [${PATCH_VERSION}], [i3 patch version])
52
53 AX_CODE_COVERAGE
54
55 dnl is_release must be lowercase because AX_CHECK_ENABLE_DEBUG calls m4_tolower
56 dnl on its fourth argument.
57 AX_CHECK_ENABLE_DEBUG([yes], , [UNUSED_NDEBUG], [$is_release])
58
59 AC_PROG_CC_C99
60
61 # For strnlen() and vasprintf().
62 AC_USE_SYSTEM_EXTENSIONS
63
64 # Checks for typedefs, structures, and compiler characteristics.
65 AC_CHECK_HEADER_STDBOOL
66 dnl The error message should include the specific type which could not be
67 dnl found, but I do not see a way to achieve that.
68 AC_CHECK_TYPES([mode_t, off_t, pid_t, size_t, ssize_t], , [AC_MSG_FAILURE([cannot find required type])])
69
70 # Checks for library functions.
71 AC_FUNC_FORK
72 AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
73 AC_FUNC_STRNLEN
74 AC_CHECK_FUNCS([atexit dup2 ftruncate getcwd gettimeofday localtime_r memchr memset mkdir rmdir setlocale socket strcasecmp strchr strdup strerror strncasecmp strndup strrchr strspn strstr strtol strtoul], , [AC_MSG_FAILURE([cannot find the $ac_func function, which i3 requires])])
75
76 # Checks for libraries.
77
78 AC_SEARCH_LIBS([floor], [m], , [AC_MSG_FAILURE([cannot find the required floor() function despite trying to link with -lm])])
79
80 # libev does not ship with a pkg-config file :(.
81 AC_SEARCH_LIBS([ev_run], [ev], , [AC_MSG_FAILURE([cannot find the required ev_run() function despite trying to link with -lev])])
82
83 AC_SEARCH_LIBS([shm_open], [rt], [], [], [-pthread])
84
85 AC_SEARCH_LIBS([iconv_open], [iconv], ,
86 AC_SEARCH_LIBS([libiconv_open], [iconv], , [AC_MSG_FAILURE([cannot find the required iconv_open() function despite trying to link with -liconv])]))
87
88 AX_PTHREAD
89
90 dnl Each prefix corresponds to a source tarball which users might have
91 dnl downloaded in a newer version and would like to overwrite.
92 PKG_CHECK_MODULES([LIBSN], [libstartup-notification-1.0])
93 PKG_CHECK_MODULES([XCB], [xcb xcb-xkb xcb-xinerama xcb-randr])
94 PKG_CHECK_MODULES([XCB_UTIL], [xcb-event xcb-util])
95 PKG_CHECK_MODULES([XCB_UTIL_CURSOR], [xcb-cursor])
96 PKG_CHECK_MODULES([XCB_UTIL_KEYSYMS], [xcb-keysyms])
97 PKG_CHECK_MODULES([XCB_UTIL_WM], [xcb-icccm])
98 PKG_CHECK_MODULES([XCB_UTIL_XRM], [xcb-xrm])
99 PKG_CHECK_MODULES([XKBCOMMON], [xkbcommon xkbcommon-x11])
100 PKG_CHECK_MODULES([YAJL], [yajl])
101 PKG_CHECK_MODULES([LIBPCRE], [libpcre >= 8.10])
102 PKG_CHECK_MODULES([PANGOCAIRO], [cairo >= 1.14.4 pangocairo])
103
104 # Checks for programs.
105 AC_PROG_AWK
106 AC_PROG_CPP
107 AC_PROG_INSTALL
108 AC_PROG_MAKE_SET
109 AC_PROG_RANLIB
110 AC_PROG_LN_S
111
112 AC_ARG_ENABLE(docs,
113 AS_HELP_STRING(
114 [--disable-docs],
115 [disable building documentation]),
116 [ax_docs=$enableval],
117 [ax_docs=yes])
118 AC_ARG_ENABLE(mans,
119 AS_HELP_STRING(
120 [--disable-mans],
121 [disable building manual pages]),
122 [ax_mans=$enableval],
123 [ax_mans=yes])
124 AS_IF([test x$ax_docs = xyes || test x$ax_mans = xyes], [
125 AC_PATH_PROG([PATH_ASCIIDOC], [asciidoc])
126 ])
127 AS_IF([test x$ax_mans = xyes], [
128 AC_PATH_PROG([PATH_XMLTO], [xmlto])
129 AC_PATH_PROG([PATH_POD2MAN], [pod2man])
130 ])
131 AM_CONDITIONAL([BUILD_MANS], [test x$ax_mans = xyes && test x$PATH_ASCIIDOC != x && test x$PATH_XMLTO != x && test x$PATH_POD2MAN != x])
132 AM_CONDITIONAL([BUILD_DOCS], [test x$ax_docs = xyes && test x$PATH_ASCIIDOC != x])
133
134 AM_PROG_AR
135
136 AX_FLAGS_WARN_ALL
137 AX_CHECK_COMPILE_FLAG([-Wunused-value], [AX_APPEND_FLAG([-Wunused-value], [AM_CFLAGS])])
138 AC_SUBST(AM_CFLAGS)
139
140 # Checks for header files.
141 AC_CHECK_HEADERS([fcntl.h float.h inttypes.h limits.h locale.h netinet/in.h paths.h stddef.h stdint.h stdlib.h string.h sys/param.h sys/socket.h sys/time.h unistd.h], , [AC_MSG_FAILURE([cannot find the $ac_header header, which i3 requires])])
142
143 AC_CONFIG_FILES([Makefile testcases/lib/i3test.pm man/asciidoc.conf])
144 AC_CONFIG_FILES([testcases/complete-run.pl], [chmod +x testcases/complete-run.pl])
145
146 # Enable address sanitizer for non-release builds. The performance hit is a
147 # 50% increase of wallclock time for the testsuite on my machine.
148 if test x$is_release = xyes; then
149 default_sanitizers=
150 else
151 default_sanitizers=address
152 fi
153 AX_SANITIZERS(, [$default_sanitizers], [AC_DEFINE([I3_ASAN_ENABLED], [], [Enable ASAN])])
154
155 AC_OUTPUT
156
157 if test -z "${BUILD_DOCS_TRUE}"; then
158 print_BUILD_DOCS=yes
159 else
160 print_BUILD_DOCS=no
161 fi
162
163
164 if test -z "${BUILD_MANS_TRUE}"; then
165 print_BUILD_MANS=yes
166 else
167 print_BUILD_MANS=no
168 fi
169
170 in_git_worktree=`git rev-parse --is-inside-work-tree 2>/dev/null`
171 if [[ "$in_git_worktree" = "true" ]]; then
172 git_dir=`git rev-parse --git-dir 2>/dev/null`
173 srcdir=`dirname "$git_dir"`
174 exclude_dir=`pwd | sed "s,^$srcdir,,g"`
175 if ! grep -q "^$exclude_dir" "$git_dir/info/exclude"; then
176 echo "$exclude_dir" >> "$git_dir/info/exclude"
177 fi
178 fi
179
180 echo \
181 "--------------------------------------------------------------------------------
182 build configured:
183
184 AS_HELP_STRING([i3 version:], [`echo ${I3_VERSION} | sed 's,\\\\,,g'`])
185 AS_HELP_STRING([is release version:], [${is_release}])
186
187 AS_HELP_STRING([build manpages:], [${print_BUILD_MANS}])
188 AS_HELP_STRING([build docs:], [${print_BUILD_DOCS}])
189 AS_HELP_STRING([enable debug flags:], [${ax_enable_debug}])
190 AS_HELP_STRING([code coverage:], [${CODE_COVERAGE_ENABLED}])
191 AS_HELP_STRING([enabled sanitizers:], [${ax_enabled_sanitizers}])
192
193 To compile, run:
194
195 cd `pwd` && make -j8
196 --------------------------------------------------------------------------------"
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use Data::Dumper;
6 use Getopt::Long;
7 use Pod::Usage;
8 use AnyEvent::I3;
9 use File::Temp;
10 use File::Spec;
11 use File::Basename;
12 use v5.10;
13 use IPC::Cmd qw[can_run];
14
15 my %options = (
16 gv => 1,
17 save => undef,
18 help => 0,
19 );
20 my $result = GetOptions(
21 "gv!" => \$options{gv},
22 "save=s" => \$options{save},
23 "help|?" => \$options{help},
24 );
25
26 pod2usage(-verbose => 2, -exitcode => 0) if $options{help};
27
28 # prerequisites check so we can be specific about failures caused
29 # by not having these tools in the path
30 can_run('asy') or die 'Please install asymptote';
31 can_run('gv') or die 'Please install gv' unless !$options{gv};
32
33 my $i3 = i3();
34
35 my $tree = $i3->get_tree->recv;
36
37 my $tmp = File::Temp->new(UNLINK => 0, SUFFIX => '.asy');
38
39 say $tmp "import drawtree;";
40
41 say $tmp "treeLevelStep = 2cm;";
42
43 sub dump_node {
44 my ($n, $parent) = @_;
45
46 my $o = ($n->{orientation} eq 'none' ? "u" : ($n->{orientation} eq 'horizontal' ? "h" : "v"));
47 my $w = (defined($n->{window}) ? $n->{window} : "N");
48 my $na = ($n->{name} or ($n->{type} eq "floating_con" ? "[Floating con]" : "[Empty]"));
49 $na =~ s/#/\\#/g;
50 $na =~ s/\$/\\\$/g;
51 $na =~ s/&/\\&/g;
52 $na =~ s/_/\\_/g;
53 $na =~ s/~/\\textasciitilde{}/g;
54 my $type = 'leaf';
55 if (!defined($n->{window})) {
56 $type = $n->{layout};
57 }
58 my $marks = $n->{marks} ? ' [' . join('][', @{$n->{marks}}) . ']' : '';
59 my $name = qq|``$na'' ($type)$marks|;
60
61 print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
62
63 print $tmp "n" . $parent->{id} . ", " if defined($parent);
64 print $tmp "\"" . $name . "\");\n";
65
66 dump_node($_, $n) for @{$n->{nodes}};
67 dump_node($_, $n) for @{$n->{floating_nodes}};
68 }
69
70 sub find_node_with_name {
71 my ($node, $name) = @_;
72
73 return $node if ($node->{name} eq $name);
74 for my $child (@{$node->{nodes}}) {
75 my $res = find_node_with_name($child, $name);
76 return $res if defined($res);
77 }
78 return undef;
79 }
80
81 my $start = shift;
82 my $root;
83 if ($start) {
84 # Find the specified node in the tree
85 $root = find_node_with_name($tree, $start);
86 } else {
87 $root = $tree;
88 }
89 dump_node($root);
90 say $tmp "draw(n" . $root->{id} . ", (0, 0));";
91
92 close($tmp);
93 my $rep = "$tmp";
94 $rep =~ s/asy$/eps/;
95 if ($options{gv}) {
96 my $tmp_dir = dirname($rep);
97 $options{save} = File::Spec->rel2abs($options{save}) if $options{save};
98 chdir($tmp_dir);
99 } else {
100 $rep = basename($rep); # Output in current dir.
101 }
102 system("asy $tmp"); # Create the .eps file.
103 system("gv --scale=-1000 --noresize --widgetless $rep") if $options{gv};
104 if ($options{save}) {
105 system("mv $rep ${options{save}}");
106 } elsif ($options{gv}) {
107 system("rm $rep");
108 }
109 system("rm $tmp");
110
111 __END__
112
113 =head1 NAME
114
115 dump-asy.pl - Render the layout tree using asymptote
116
117 =head1 SYNOPSIS
118
119 dump-asy.pl [workspace]
120
121 =head1 EXAMPLE
122
123 Render the entire tree, run:
124
125 ./dump-asy.pl
126
127 Render the tree starting from the node with the specified name, run:
128
129 ./dump-asy.pl 'name'
130
131 Render the entire tree, save in file 'file.eps' and show using gv, run:
132
133 ./dump-asy.pl --save file.eps
134
135 =head1 OPTIONS
136
137 =over 8
138
139 =item B<--gv>
140
141 =item B<--no-gv>
142
143 Enable or disable showing the result through gv. If disabled, an .eps file will
144 be saved in the current working directory. Enabled by default.
145
146 =item B<--save>
147
148 Save result using the specified file-name. If omitted, no file will be saved
149 when using '--gv' or a random name will be used when using '--no-gv'.
150
151 =back
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 # renders the layout tree using asymptote
3
4 use strict;
5 use warnings;
6
7 use JSON::XS;
8 use Data::Dumper;
9 use AnyEvent::I3;
10 use v5.10;
11
12 use Gtk2 '-init';
13 use Gtk2::SimpleMenu;
14 use Glib qw/TRUE FALSE/;
15
16 my $window = Gtk2::Window->new('toplevel');
17 $window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
18
19 my $tree_store = Gtk2::TreeStore->new(qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/);
20
21 my $i3 = i3();
22
23 my $tree_view = Gtk2::TreeView->new($tree_store);
24
25 my $layout_box = undef;
26
27 sub copy_node {
28 my ($n, $parent, $piter, $pbox) = @_;
29
30 my $o = ($n->{orientation} == 0 ? "u" : ($n->{orientation} == 1 ? "h" : "v"));
31 my $w = (defined($n->{window}) ? $n->{window} : "N");
32
33 # convert a rectangle struct to X11 notation (WxH+X+Y)
34 my $r = $n->{rect};
35 my $x = $r->{x};
36 my $y = $r->{y};
37 my $dim = $r->{width}."x".$r->{height}.($x<0?$x:"+$x").($y<0?$y:"+$y");
38
39 # add node to the tree with all known properties
40 my $iter = $tree_store->append($piter);
41 $tree_store->set($iter, 0 => $n->{name}, 1 => $w, 2 => $o, 3 => sprintf("0x%08x", $n->{id}), 4 => $n->{urgent}, 5 => $n->{focused}, 6 => $n->{layout}, 7 => $dim);
42
43 # also create a box for the node, each node has a vbox
44 # for combining the title (and properties) with the
45 # container itself, the container will be empty in case
46 # of no children, a vbox or hbox
47 my $box;
48 if($n->{orientation} == 1) {
49 $box = Gtk2::HBox->new(1, 5);
50 } else {
51 $box = Gtk2::VBox->new(1, 5);
52 }
53
54 # combine label and container
55 my $node = Gtk2::Frame->new($n->{name}.",".$o.",".$w);
56 $node->set_shadow_type('etched-out');
57 $node->add($box);
58
59 # the parent is added onto a scrolled window, so add it with a viewport
60 if(defined($pbox)) {
61 $pbox->pack_start($node, 1, 1, 0);
62 } else {
63 $layout_box = $node;
64 }
65
66 # recurse into children
67 copy_node($_, $n, $iter, $box) for @{$n->{nodes}};
68
69 # if it is a window draw a nice color
70 if(defined($n->{window})) {
71 # use a drawing area to fill a colored rectangle
72 my $area = Gtk2::DrawingArea->new();
73
74 # the color is stored as hex in the name
75 $area->{"user-data"} = $n->{name};
76
77 $area->signal_connect(expose_event => sub {
78 my ($widget, $event) = @_;
79
80 # fetch a cairo context and it width/height to start drawing nodes
81 my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
82
83 my $w = $widget->allocation->width;
84 my $h = $widget->allocation->height;
85
86 my $hc = $widget->{"user-data"};
87 my $r = hex(substr($hc, 1, 2)) / 255.0;
88 my $g = hex(substr($hc, 3, 2)) / 255.0;
89 my $b = hex(substr($hc, 5, 2)) / 255.0;
90
91 $cr->set_source_rgb($r, $g, $b);
92 $cr->rectangle(0, 0, $w, $h);
93 $cr->fill();
94
95 return FALSE;
96 });
97
98 $box->pack_end($area, 1, 1, 0);
99 }
100 }
101
102 # Replaced by Gtk2 Boxes:
103 #sub draw_node {
104 # my ($n, $cr, $x, $y, $w, $h) = @_;
105 #
106 # $cr->set_source_rgb(1.0, 1.0, 1.0);
107 # $cr->rectangle($x, $y, $w/2, $h/2);
108 # $cr->fill();
109 #}
110
111 my $json_prev = "";
112
113 my $layout_sw = Gtk2::ScrolledWindow->new(undef, undef);
114 my $layout_container = Gtk2::HBox->new(0, 0);
115 $layout_sw->add_with_viewport($layout_container);
116
117 sub copy_tree {
118 my $tree = $i3->get_tree->recv;
119
120 # convert the tree back to json so we only rebuild/redraw when the tree is changed
121 my $json = encode_json($tree);
122 if ($json ne $json_prev) {
123 $json_prev = $json;
124
125 # rebuild the tree and the layout
126 $tree_store->clear();
127 if(defined($layout_box)) {
128 $layout_container->remove($layout_box);
129 }
130 copy_node($tree);
131 $layout_container->add($layout_box);
132 $layout_container->show_all();
133
134 # keep things expanded, otherwise the tree collapses every reload which is more annoying then this :-)
135 $tree_view->expand_all();
136 }
137
138 return(TRUE);
139 }
140
141 sub new_column {
142 my $tree_column = Gtk2::TreeViewColumn->new();
143 $tree_column->set_title(shift);
144
145 my $renderer = Gtk2::CellRendererText->new();
146 $tree_column->pack_start($renderer, FALSE);
147 $tree_column->add_attribute($renderer, text => shift);
148
149 return($tree_column);
150 }
151
152 my $col = 0;
153 $tree_view->append_column(new_column("Name", $col++));
154 $tree_view->append_column(new_column("Window", $col++));
155 $tree_view->append_column(new_column("Orientation", $col++));
156 $tree_view->append_column(new_column("ID", $col++));
157 $tree_view->append_column(new_column("Urgent", $col++));
158 $tree_view->append_column(new_column("Focused", $col++));
159 $tree_view->append_column(new_column("Layout", $col++));
160 $tree_view->append_column(new_column("Rect", $col++));
161
162 $tree_view->set_grid_lines("both");
163
164 my $tree_sw = Gtk2::ScrolledWindow->new(undef, undef);
165 $tree_sw->add($tree_view);
166
167 # Replaced by Gtk2 Boxes:
168 #my $area = Gtk2::DrawingArea->new();
169 #$area->signal_connect(expose_event => sub {
170 # my ($widget, $event) = @_;
171 #
172 # # fetch a cairo context and it width/height to start drawing nodes
173 # my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window());
174 #
175 # my $w = $widget->allocation->width;
176 # my $h = $widget->allocation->height;
177 #
178 # draw_node($gtree, $cr, 0, 0, $w, $h);
179 #
180 # return FALSE;
181 #});
182
183 sub menu_export {
184 print("TODO: EXPORT\n");
185 }
186
187 my $menu_tree = [
188 _File => {
189 item_type => '<Branch>',
190 children => [
191 _Export => {
192 callback => \&menu_export,
193 accelerator => '<ctrl>E',
194 },
195 _Quit => {
196 callback => sub { Gtk2->main_quit; },
197 accelerator => '<ctrl>Q',
198 },
199 ],
200 },
201 ];
202
203 my $menu = Gtk2::SimpleMenu->new(menu_tree => $menu_tree);
204
205 my $vbox = Gtk2::VBox->new(0, 0);
206 $vbox->pack_start($menu->{widget}, 0, 0, 0);
207 $vbox->pack_end($tree_sw, 1, 1, 0);
208 $vbox->pack_end($layout_sw, 1, 1, 0);
209
210 $window->add($vbox);
211 $window->show_all();
212 $window->set_size_request(500,500);
213
214 Glib::Timeout->add(1000, "copy_tree", undef, Glib::G_PRIORITY_DEFAULT);
215 copy_tree();
216
217 Gtk2->main();
218
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab:ft=perl
2 # © 2010 Michael Stapelberg, see LICENSE for license information
3
4 use strict;
5 use warnings;
6 use Getopt::Long;
7 use Pod::Usage;
8 use IPC::Run qw(start pump);
9 use Try::Tiny;
10 use AnyEvent::I3;
11 use AnyEvent;
12 use v5.10;
13
14 my $stdin;
15 my $socket_path = undef;
16 my ($workspaces, $outputs) = ([], {});
17 my $last_line = "";
18 my $w = AnyEvent->timer(
19 after => 2,
20 cb => sub {
21 say "Connection to i3 timed out. Verify socket path ($socket_path)";
22 exit 1;
23 }
24 );
25
26 my $command = "";
27 my $input_on = "";
28 my $output_on = "";
29 my $show_all = 0;
30
31 my $result = GetOptions(
32 'command=s' => \$command,
33 'socket=s' => \$socket_path,
34 'input-on=s' => \$input_on,
35 'output-on=s' => \$output_on,
36 'show-all' => \$show_all,
37 'help' => sub { pod2usage(1); exit 0 },
38 );
39
40 if ($command eq '') {
41 say "i3-wsbar is only useful in combination with dzen2.";
42 say "Please specify -c (command)";
43 exit 1;
44 }
45
46 my $i3 = i3($socket_path);
47
48 my @input_on = split(/,/, $input_on);
49 my @output_on = split(/,/, $output_on);
50
51 # Disable buffering
52 $| = 1;
53
54 # Wait a short amount of time and try to connect to i3 again
55 sub reconnect {
56 my $timer;
57 if (!defined($w)) {
58 $w = AnyEvent->timer(
59 after => 2,
60 cb => sub {
61 say "Connection to i3 timed out. Verify socket path ($socket_path)";
62 exit 1;
63 }
64 );
65 }
66
67 my $c = sub {
68 $timer = AnyEvent->timer(
69 after => 0.01,
70 cb => sub { $i3->connect->cb(\&connected) }
71 );
72 };
73 $c->();
74 }
75
76 # Connection attempt succeeded or failed
77 sub connected {
78 my ($cv) = @_;
79
80 if (!$cv->recv) {
81 reconnect();
82 return;
83 }
84
85 $w = undef;
86
87 $i3->subscribe({
88 workspace => \&ws_change,
89 output => \&output_change,
90 _error => sub { reconnect() }
91 });
92 ws_change();
93 output_change();
94 }
95
96 # Called when a ws changes
97 sub ws_change {
98 # Request the current workspaces and update the output afterwards
99 $i3->get_workspaces->cb(
100 sub {
101 my ($cv) = @_;
102 $workspaces = $cv->recv;
103 update_output();
104 });
105 }
106
107 # Called when the reply to the GET_OUTPUTS message arrives
108 # Compares old outputs with new outputs and starts/kills
109 # $command for each output (if specified)
110 sub got_outputs {
111 my $reply = shift->recv;
112 my %old = %{$outputs};
113 my %new = map { ($_->{name}, $_) } grep { $_->{active} } @{$reply};
114
115 # If no command was given, we do not need to compare outputs
116 if ($command eq '') {
117 update_output();
118 return;
119 }
120
121 # Handle new outputs
122 for my $name (keys %new) {
123 next if @output_on and !($name ~~ @output_on);
124
125 if (defined($old{$name})) {
126 # Check if the mode changed (by reversing the hashes so
127 # that we can check for equality using the smartmatch op)
128 my %oldrect = reverse %{$old{$name}->{rect}};
129 my %newrect = reverse %{$new{$name}->{rect}};
130 next if (%oldrect ~~ %newrect);
131
132 # On mode changes, we re-start the command
133 $outputs->{$name}->{cmd}->finish;
134 delete $outputs->{$name};
135 }
136
137 my $x = $new{$name}->{rect}->{x};
138 my $w = $new{$name}->{rect}->{width};
139 my $launch = $command;
140 $launch =~ s/([^%])%x/$1$x/g;
141 $launch =~ s/([^%])%w/$1$w/g;
142 $launch =~ s/%%x/%x/g;
143 $launch =~ s/%%w/%w/g;
144
145 $new{$name}->{cmd_input} = '';
146 my @cmd = ('/bin/sh', '-c', $launch);
147 $new{$name}->{cmd} = start \@cmd, \$new{$name}->{cmd_input};
148 $outputs->{$name} = $new{$name};
149 }
150
151 # Handle old outputs
152 for my $name (keys %old) {
153 next if defined($new{$name});
154
155 $outputs->{$name}->{cmd}->finish;
156 delete $outputs->{$name};
157 }
158
159 update_output();
160 }
161
162 sub output_change {
163 $i3->get_outputs->cb(\&got_outputs)
164 }
165
166 sub update_output {
167 my $dzen_bg = "#111111";
168 my $out;
169 my $previous_output;
170
171 for my $name (keys %{$outputs}) {
172 my $width = $outputs->{$name}->{rect}->{width};
173
174 $previous_output = undef;
175 $out = qq|^pa(;2)|;
176 for my $ws (@{$workspaces}) {
177 next if $ws->{output} ne $name and !$show_all;
178
179 # Display a separator if we are on a different output now
180 if (defined($previous_output) and
181 ($ws->{output} ne $previous_output)) {
182 $out .= qq|^fg(#900000)^ib(1)\|^ib(0)^p(+4)|;
183 }
184 $previous_output = $ws->{output};
185
186 my ($bg, $fg) = qw(333333 888888);
187 ($bg, $fg) = qw(4c7899 ffffff) if $ws->{visible};
188 ($bg, $fg) = qw(900000 ffffff) if $ws->{urgent};
189
190 my $cmd = q|i3-msg "workspace | . $ws->{name} . q|"|;
191 my $name = $ws->{name};
192
193 # Begin the clickable area
194 $out .= qq|^ca(1,$cmd)|;
195
196 # Draw the rest of the bar in the background color, but
197 # don’t move the "cursor"
198 $out .= qq|^p(_LOCK_X)^fg(#$bg)^r(${width}x17)^p(_UNLOCK_X)|;
199 # Draw the name of the workspace without overwriting the
200 # background color
201 $out .= qq|^p(+3)^fg(#$fg)^ib(1)$name^ib(0)^p(+5)|;
202 # Draw the rest of the bar in the normal background color
203 # without moving the "cursor"
204 $out .= qq|^p(_LOCK_X)^fg($dzen_bg)^r(${width}x17)^p(_UNLOCK_X)|;
205
206 # End the clickable area
207 $out .= qq|^ca()|;
208
209 # Move to the next rect, reset Y coordinate
210 $out .= qq|^p(2)^pa(;2)|;
211 }
212
213 $out .= qq|^p(_LOCK_X)^fg($dzen_bg)^r(${width}x17)^p(_UNLOCK_X)^fg()|;
214 $out .= qq|^p(+5)|;
215 $out .= $last_line if (!@input_on or $name ~~ @input_on);
216 $out .= "\n";
217
218 $outputs->{$name}->{cmd_input} = $out;
219 try {
220 pump $outputs->{$name}->{cmd} while length $outputs->{$name}->{cmd_input};
221 } catch {
222 warn "Could not write to dzen2";
223 exit 1;
224 }
225 }
226 }
227
228 $i3->connect->cb(\&connected);
229
230 $stdin = AnyEvent->io(
231 fh => \*STDIN,
232 poll => 'r',
233 cb => sub {
234 my $line = <STDIN>;
235 if (!defined($line)) {
236 undef $stdin;
237 return;
238 }
239 chomp($line);
240 $last_line = $line;
241 update_output();
242 });
243
244 # let AnyEvent do the rest ("endless loop")
245 AnyEvent->condvar->recv
246
247 __END__
248
249 =head1 NAME
250
251 i3-wsbar - sample implementation of a standalone workspace bar
252
253 =head1 SYNOPSIS
254
255 i3-wsbar -c <dzen2-commandline> [options]
256
257 =head1 OPTIONS
258
259 =over 4
260
261 =item B<--command> <command>
262
263 This command (at the moment only dzen2 is supported) will be started for each
264 output. C<%x> will be replaced with the X coordinate of the output, C<%w> will
265 be replaced with the width of the output.
266
267 Example:
268 --command "dzen2 -dock -x %x -w %w"
269
270 =item B<--input-on> <list-of-RandR-outputs>
271
272 Specifies on which outputs the contents of stdin should be appended to the
273 workspace bar.
274
275 Example:
276 --input-on "LVDS1"
277
278 =item B<--output-on> <list-of-RandR-outputs>
279
280 Specifies for which outputs i3-wsbar should start C<command>.
281
282 =item B<--show-all>
283
284 If enabled, all workspaces are shown (not only those of the current output).
285 Handy to use with C<--output-on>.
286
287 =back
288
289 =cut
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 # © 2012 Michael Stapelberg
3 # Licensed under BSD license, see https://github.com/i3/i3/blob/next/LICENSE
4 #
5 # Append this line to your i3 config file:
6 # exec_always ~/per-workspace-layout.pl
7 #
8 # Then, change the %layouts hash like you want your workspaces to be set up.
9 # This script requires i3 >= v4.4 for the extended workspace event.
10
11 use strict;
12 use warnings;
13 use AnyEvent;
14 use AnyEvent::I3;
15 use v5.10;
16 use utf8;
17
18 my %layouts = (
19 '4' => 'tabbed',
20 '5' => 'stacked',
21 );
22
23 my $i3 = i3();
24
25 die "Could not connect to i3: $!" unless $i3->connect->recv();
26
27 die "Could not subscribe to the workspace event: $!" unless
28 $i3->subscribe({
29 workspace => sub {
30 my ($msg) = @_;
31 return unless $msg->{change} eq 'focus';
32 die "Your version of i3 is too old. You need >= v4.4"
33 unless exists($msg->{current});
34 my $ws = $msg->{current};
35
36 # If the workspace already has children, don’t change the layout.
37 return unless scalar @{$ws->{nodes}} == 0;
38
39 my $name = $ws->{name};
40 my $con_id = $ws->{id};
41
42 return unless exists $layouts{$name};
43
44 $i3->command(qq|[con_id="$con_id"] layout | . $layouts{$name});
45 },
46 _error => sub {
47 my ($msg) = @_;
48 say "AnyEvent::I3 error: $msg";
49 say "Exiting.";
50 exit 1;
51 },
52 })->recv->{success};
53
54 # Run forever.
55 AnyEvent->condvar->recv
0 #!/bin/sh
1 # vim:ts=4:sw=4:expandtab
2 # © 2012 Michael Stapelberg, Public Domain
3
4 # This script is a trivial shell script to send your own output to i3bar while
5 # using the JSON protocol.
6 #
7 # It is ugly and that is inherent to using JSON with shell scripts. You
8 # _really_ should not do that. See i3status or i3status’s contrib/ directory
9 # for examples of how to handle the output in higher-level languages.
10 #
11 # This example is purely for illustration of the protocol. DO NOT USE IT IN THE
12 # REAL WORLD.
13
14 # Send the header so that i3bar knows we want to use JSON:
15 echo '{ "version": 1 }'
16
17 # Begin the endless array.
18 echo '['
19
20 # We send an empty first array of blocks to make the loop simpler:
21 echo '[]'
22
23 # Now send blocks with information forever:
24 while :;
25 do
26 echo ",[{\"name\":\"time\",\"full_text\":\"$(date)\"}]"
27 sleep 1
28 done
0 #! /bin/sh
1 # depcomp - compile a program generating dependencies as side-effects
2
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 1999-2018 Free Software Foundation, Inc.
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
20 # As a special exception to the GNU General Public License, if you
21 # distribute this file as part of a program that contains a
22 # configuration script generated by Autoconf, you may include it under
23 # the same distribution terms that you use for the rest of that program.
24
25 # Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
26
27 case $1 in
28 '')
29 echo "$0: No command. Try '$0 --help' for more information." 1>&2
30 exit 1;
31 ;;
32 -h | --h*)
33 cat <<\EOF
34 Usage: depcomp [--help] [--version] PROGRAM [ARGS]
35
36 Run PROGRAMS ARGS to compile a file, generating dependencies
37 as side-effects.
38
39 Environment variables:
40 depmode Dependency tracking mode.
41 source Source file read by 'PROGRAMS ARGS'.
42 object Object file output by 'PROGRAMS ARGS'.
43 DEPDIR directory where to store dependencies.
44 depfile Dependency file to output.
45 tmpdepfile Temporary file to use when outputting dependencies.
46 libtool Whether libtool is used (yes/no).
47
48 Report bugs to <bug-automake@gnu.org>.
49 EOF
50 exit $?
51 ;;
52 -v | --v*)
53 echo "depcomp $scriptversion"
54 exit $?
55 ;;
56 esac
57
58 # Get the directory component of the given path, and save it in the
59 # global variables '$dir'. Note that this directory component will
60 # be either empty or ending with a '/' character. This is deliberate.
61 set_dir_from ()
62 {
63 case $1 in
64 */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
65 *) dir=;;
66 esac
67 }
68
69 # Get the suffix-stripped basename of the given path, and save it the
70 # global variable '$base'.
71 set_base_from ()
72 {
73 base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
74 }
75
76 # If no dependency file was actually created by the compiler invocation,
77 # we still have to create a dummy depfile, to avoid errors with the
78 # Makefile "include basename.Plo" scheme.
79 make_dummy_depfile ()
80 {
81 echo "#dummy" > "$depfile"
82 }
83
84 # Factor out some common post-processing of the generated depfile.
85 # Requires the auxiliary global variable '$tmpdepfile' to be set.
86 aix_post_process_depfile ()
87 {
88 # If the compiler actually managed to produce a dependency file,
89 # post-process it.
90 if test -f "$tmpdepfile"; then
91 # Each line is of the form 'foo.o: dependency.h'.
92 # Do two passes, one to just change these to
93 # $object: dependency.h
94 # and one to simply output
95 # dependency.h:
96 # which is needed to avoid the deleted-header problem.
97 { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
98 sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
99 } > "$depfile"
100 rm -f "$tmpdepfile"
101 else
102 make_dummy_depfile
103 fi
104 }
105
106 # A tabulation character.
107 tab=' '
108 # A newline character.
109 nl='
110 '
111 # Character ranges might be problematic outside the C locale.
112 # These definitions help.
113 upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
114 lower=abcdefghijklmnopqrstuvwxyz
115 digits=0123456789
116 alpha=${upper}${lower}
117
118 if test -z "$depmode" || test -z "$source" || test -z "$object"; then
119 echo "depcomp: Variables source, object and depmode must be set" 1>&2
120 exit 1
121 fi
122
123 # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
124 depfile=${depfile-`echo "$object" |
125 sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
126 tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
127
128 rm -f "$tmpdepfile"
129
130 # Avoid interferences from the environment.
131 gccflag= dashmflag=
132
133 # Some modes work just like other modes, but use different flags. We
134 # parameterize here, but still list the modes in the big case below,
135 # to make depend.m4 easier to write. Note that we *cannot* use a case
136 # here, because this file can only contain one case statement.
137 if test "$depmode" = hp; then
138 # HP compiler uses -M and no extra arg.
139 gccflag=-M
140 depmode=gcc
141 fi
142
143 if test "$depmode" = dashXmstdout; then
144 # This is just like dashmstdout with a different argument.
145 dashmflag=-xM
146 depmode=dashmstdout
147 fi
148
149 cygpath_u="cygpath -u -f -"
150 if test "$depmode" = msvcmsys; then
151 # This is just like msvisualcpp but w/o cygpath translation.
152 # Just convert the backslash-escaped backslashes to single forward
153 # slashes to satisfy depend.m4
154 cygpath_u='sed s,\\\\,/,g'
155 depmode=msvisualcpp
156 fi
157
158 if test "$depmode" = msvc7msys; then
159 # This is just like msvc7 but w/o cygpath translation.
160 # Just convert the backslash-escaped backslashes to single forward
161 # slashes to satisfy depend.m4
162 cygpath_u='sed s,\\\\,/,g'
163 depmode=msvc7
164 fi
165
166 if test "$depmode" = xlc; then
167 # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
168 gccflag=-qmakedep=gcc,-MF
169 depmode=gcc
170 fi
171
172 case "$depmode" in
173 gcc3)
174 ## gcc 3 implements dependency tracking that does exactly what
175 ## we want. Yay! Note: for some reason libtool 1.4 doesn't like
176 ## it if -MD -MP comes after the -MF stuff. Hmm.
177 ## Unfortunately, FreeBSD c89 acceptance of flags depends upon
178 ## the command line argument order; so add the flags where they
179 ## appear in depend2.am. Note that the slowdown incurred here
180 ## affects only configure: in makefiles, %FASTDEP% shortcuts this.
181 for arg
182 do
183 case $arg in
184 -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
185 *) set fnord "$@" "$arg" ;;
186 esac
187 shift # fnord
188 shift # $arg
189 done
190 "$@"
191 stat=$?
192 if test $stat -ne 0; then
193 rm -f "$tmpdepfile"
194 exit $stat
195 fi
196 mv "$tmpdepfile" "$depfile"
197 ;;
198
199 gcc)
200 ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
201 ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
202 ## (see the conditional assignment to $gccflag above).
203 ## There are various ways to get dependency output from gcc. Here's
204 ## why we pick this rather obscure method:
205 ## - Don't want to use -MD because we'd like the dependencies to end
206 ## up in a subdir. Having to rename by hand is ugly.
207 ## (We might end up doing this anyway to support other compilers.)
208 ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
209 ## -MM, not -M (despite what the docs say). Also, it might not be
210 ## supported by the other compilers which use the 'gcc' depmode.
211 ## - Using -M directly means running the compiler twice (even worse
212 ## than renaming).
213 if test -z "$gccflag"; then
214 gccflag=-MD,
215 fi
216 "$@" -Wp,"$gccflag$tmpdepfile"
217 stat=$?
218 if test $stat -ne 0; then
219 rm -f "$tmpdepfile"
220 exit $stat
221 fi
222 rm -f "$depfile"
223 echo "$object : \\" > "$depfile"
224 # The second -e expression handles DOS-style file names with drive
225 # letters.
226 sed -e 's/^[^:]*: / /' \
227 -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
228 ## This next piece of magic avoids the "deleted header file" problem.
229 ## The problem is that when a header file which appears in a .P file
230 ## is deleted, the dependency causes make to die (because there is
231 ## typically no way to rebuild the header). We avoid this by adding
232 ## dummy dependencies for each header file. Too bad gcc doesn't do
233 ## this for us directly.
234 ## Some versions of gcc put a space before the ':'. On the theory
235 ## that the space means something, we add a space to the output as
236 ## well. hp depmode also adds that space, but also prefixes the VPATH
237 ## to the object. Take care to not repeat it in the output.
238 ## Some versions of the HPUX 10.20 sed can't process this invocation
239 ## correctly. Breaking it into two sed invocations is a workaround.
240 tr ' ' "$nl" < "$tmpdepfile" \
241 | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
242 | sed -e 's/$/ :/' >> "$depfile"
243 rm -f "$tmpdepfile"
244 ;;
245
246 hp)
247 # This case exists only to let depend.m4 do its work. It works by
248 # looking at the text of this script. This case will never be run,
249 # since it is checked for above.
250 exit 1
251 ;;
252
253 sgi)
254 if test "$libtool" = yes; then
255 "$@" "-Wp,-MDupdate,$tmpdepfile"
256 else
257 "$@" -MDupdate "$tmpdepfile"
258 fi
259 stat=$?
260 if test $stat -ne 0; then
261 rm -f "$tmpdepfile"
262 exit $stat
263 fi
264 rm -f "$depfile"
265
266 if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
267 echo "$object : \\" > "$depfile"
268 # Clip off the initial element (the dependent). Don't try to be
269 # clever and replace this with sed code, as IRIX sed won't handle
270 # lines with more than a fixed number of characters (4096 in
271 # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
272 # the IRIX cc adds comments like '#:fec' to the end of the
273 # dependency line.
274 tr ' ' "$nl" < "$tmpdepfile" \
275 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
276 | tr "$nl" ' ' >> "$depfile"
277 echo >> "$depfile"
278 # The second pass generates a dummy entry for each header file.
279 tr ' ' "$nl" < "$tmpdepfile" \
280 | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
281 >> "$depfile"
282 else
283 make_dummy_depfile
284 fi
285 rm -f "$tmpdepfile"
286 ;;
287
288 xlc)
289 # This case exists only to let depend.m4 do its work. It works by
290 # looking at the text of this script. This case will never be run,
291 # since it is checked for above.
292 exit 1
293 ;;
294
295 aix)
296 # The C for AIX Compiler uses -M and outputs the dependencies
297 # in a .u file. In older versions, this file always lives in the
298 # current directory. Also, the AIX compiler puts '$object:' at the
299 # start of each line; $object doesn't have directory information.
300 # Version 6 uses the directory in both cases.
301 set_dir_from "$object"
302 set_base_from "$object"
303 if test "$libtool" = yes; then
304 tmpdepfile1=$dir$base.u
305 tmpdepfile2=$base.u
306 tmpdepfile3=$dir.libs/$base.u
307 "$@" -Wc,-M
308 else
309 tmpdepfile1=$dir$base.u
310 tmpdepfile2=$dir$base.u
311 tmpdepfile3=$dir$base.u
312 "$@" -M
313 fi
314 stat=$?
315 if test $stat -ne 0; then
316 rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
317 exit $stat
318 fi
319
320 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
321 do
322 test -f "$tmpdepfile" && break
323 done
324 aix_post_process_depfile
325 ;;
326
327 tcc)
328 # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
329 # FIXME: That version still under development at the moment of writing.
330 # Make that this statement remains true also for stable, released
331 # versions.
332 # It will wrap lines (doesn't matter whether long or short) with a
333 # trailing '\', as in:
334 #
335 # foo.o : \
336 # foo.c \
337 # foo.h \
338 #
339 # It will put a trailing '\' even on the last line, and will use leading
340 # spaces rather than leading tabs (at least since its commit 0394caf7
341 # "Emit spaces for -MD").
342 "$@" -MD -MF "$tmpdepfile"
343 stat=$?
344 if test $stat -ne 0; then
345 rm -f "$tmpdepfile"
346 exit $stat
347 fi
348 rm -f "$depfile"
349 # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
350 # We have to change lines of the first kind to '$object: \'.
351 sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
352 # And for each line of the second kind, we have to emit a 'dep.h:'
353 # dummy dependency, to avoid the deleted-header problem.
354 sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
355 rm -f "$tmpdepfile"
356 ;;
357
358 ## The order of this option in the case statement is important, since the
359 ## shell code in configure will try each of these formats in the order
360 ## listed in this file. A plain '-MD' option would be understood by many
361 ## compilers, so we must ensure this comes after the gcc and icc options.
362 pgcc)
363 # Portland's C compiler understands '-MD'.
364 # Will always output deps to 'file.d' where file is the root name of the
365 # source file under compilation, even if file resides in a subdirectory.
366 # The object file name does not affect the name of the '.d' file.
367 # pgcc 10.2 will output
368 # foo.o: sub/foo.c sub/foo.h
369 # and will wrap long lines using '\' :
370 # foo.o: sub/foo.c ... \
371 # sub/foo.h ... \
372 # ...
373 set_dir_from "$object"
374 # Use the source, not the object, to determine the base name, since
375 # that's sadly what pgcc will do too.
376 set_base_from "$source"
377 tmpdepfile=$base.d
378
379 # For projects that build the same source file twice into different object
380 # files, the pgcc approach of using the *source* file root name can cause
381 # problems in parallel builds. Use a locking strategy to avoid stomping on
382 # the same $tmpdepfile.
383 lockdir=$base.d-lock
384 trap "
385 echo '$0: caught signal, cleaning up...' >&2
386 rmdir '$lockdir'
387 exit 1
388 " 1 2 13 15
389 numtries=100
390 i=$numtries
391 while test $i -gt 0; do
392 # mkdir is a portable test-and-set.
393 if mkdir "$lockdir" 2>/dev/null; then
394 # This process acquired the lock.
395 "$@" -MD
396 stat=$?
397 # Release the lock.
398 rmdir "$lockdir"
399 break
400 else
401 # If the lock is being held by a different process, wait
402 # until the winning process is done or we timeout.
403 while test -d "$lockdir" && test $i -gt 0; do
404 sleep 1
405 i=`expr $i - 1`
406 done
407 fi
408 i=`expr $i - 1`
409 done
410 trap - 1 2 13 15
411 if test $i -le 0; then
412 echo "$0: failed to acquire lock after $numtries attempts" >&2
413 echo "$0: check lockdir '$lockdir'" >&2
414 exit 1
415 fi
416
417 if test $stat -ne 0; then
418 rm -f "$tmpdepfile"
419 exit $stat
420 fi
421 rm -f "$depfile"
422 # Each line is of the form `foo.o: dependent.h',
423 # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
424 # Do two passes, one to just change these to
425 # `$object: dependent.h' and one to simply `dependent.h:'.
426 sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
427 # Some versions of the HPUX 10.20 sed can't process this invocation
428 # correctly. Breaking it into two sed invocations is a workaround.
429 sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
430 | sed -e 's/$/ :/' >> "$depfile"
431 rm -f "$tmpdepfile"
432 ;;
433
434 hp2)
435 # The "hp" stanza above does not work with aCC (C++) and HP's ia64
436 # compilers, which have integrated preprocessors. The correct option
437 # to use with these is +Maked; it writes dependencies to a file named
438 # 'foo.d', which lands next to the object file, wherever that
439 # happens to be.
440 # Much of this is similar to the tru64 case; see comments there.
441 set_dir_from "$object"
442 set_base_from "$object"
443 if test "$libtool" = yes; then
444 tmpdepfile1=$dir$base.d
445 tmpdepfile2=$dir.libs/$base.d
446 "$@" -Wc,+Maked
447 else
448 tmpdepfile1=$dir$base.d
449 tmpdepfile2=$dir$base.d
450 "$@" +Maked
451 fi
452 stat=$?
453 if test $stat -ne 0; then
454 rm -f "$tmpdepfile1" "$tmpdepfile2"
455 exit $stat
456 fi
457
458 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
459 do
460 test -f "$tmpdepfile" && break
461 done
462 if test -f "$tmpdepfile"; then
463 sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
464 # Add 'dependent.h:' lines.
465 sed -ne '2,${
466 s/^ *//
467 s/ \\*$//
468 s/$/:/
469 p
470 }' "$tmpdepfile" >> "$depfile"
471 else
472 make_dummy_depfile
473 fi
474 rm -f "$tmpdepfile" "$tmpdepfile2"
475 ;;
476
477 tru64)
478 # The Tru64 compiler uses -MD to generate dependencies as a side
479 # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
480 # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
481 # dependencies in 'foo.d' instead, so we check for that too.
482 # Subdirectories are respected.
483 set_dir_from "$object"
484 set_base_from "$object"
485
486 if test "$libtool" = yes; then
487 # Libtool generates 2 separate objects for the 2 libraries. These
488 # two compilations output dependencies in $dir.libs/$base.o.d and
489 # in $dir$base.o.d. We have to check for both files, because
490 # one of the two compilations can be disabled. We should prefer
491 # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
492 # automatically cleaned when .libs/ is deleted, while ignoring
493 # the former would cause a distcleancheck panic.
494 tmpdepfile1=$dir$base.o.d # libtool 1.5
495 tmpdepfile2=$dir.libs/$base.o.d # Likewise.
496 tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
497 "$@" -Wc,-MD
498 else
499 tmpdepfile1=$dir$base.d
500 tmpdepfile2=$dir$base.d
501 tmpdepfile3=$dir$base.d
502 "$@" -MD
503 fi
504
505 stat=$?
506 if test $stat -ne 0; then
507 rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
508 exit $stat
509 fi
510
511 for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
512 do
513 test -f "$tmpdepfile" && break
514 done
515 # Same post-processing that is required for AIX mode.
516 aix_post_process_depfile
517 ;;
518
519 msvc7)
520 if test "$libtool" = yes; then
521 showIncludes=-Wc,-showIncludes
522 else
523 showIncludes=-showIncludes
524 fi
525 "$@" $showIncludes > "$tmpdepfile"
526 stat=$?
527 grep -v '^Note: including file: ' "$tmpdepfile"
528 if test $stat -ne 0; then
529 rm -f "$tmpdepfile"
530 exit $stat
531 fi
532 rm -f "$depfile"
533 echo "$object : \\" > "$depfile"
534 # The first sed program below extracts the file names and escapes
535 # backslashes for cygpath. The second sed program outputs the file
536 # name when reading, but also accumulates all include files in the
537 # hold buffer in order to output them again at the end. This only
538 # works with sed implementations that can handle large buffers.
539 sed < "$tmpdepfile" -n '
540 /^Note: including file: *\(.*\)/ {
541 s//\1/
542 s/\\/\\\\/g
543 p
544 }' | $cygpath_u | sort -u | sed -n '
545 s/ /\\ /g
546 s/\(.*\)/'"$tab"'\1 \\/p
547 s/.\(.*\) \\/\1:/
548 H
549 $ {
550 s/.*/'"$tab"'/
551 G
552 p
553 }' >> "$depfile"
554 echo >> "$depfile" # make sure the fragment doesn't end with a backslash
555 rm -f "$tmpdepfile"
556 ;;
557
558 msvc7msys)
559 # This case exists only to let depend.m4 do its work. It works by
560 # looking at the text of this script. This case will never be run,
561 # since it is checked for above.
562 exit 1
563 ;;
564
565 #nosideeffect)
566 # This comment above is used by automake to tell side-effect
567 # dependency tracking mechanisms from slower ones.
568
569 dashmstdout)
570 # Important note: in order to support this mode, a compiler *must*
571 # always write the preprocessed file to stdout, regardless of -o.
572 "$@" || exit $?
573
574 # Remove the call to Libtool.
575 if test "$libtool" = yes; then
576 while test "X$1" != 'X--mode=compile'; do
577 shift
578 done
579 shift
580 fi
581
582 # Remove '-o $object'.
583 IFS=" "
584 for arg
585 do
586 case $arg in
587 -o)
588 shift
589 ;;
590 $object)
591 shift
592 ;;
593 *)
594 set fnord "$@" "$arg"
595 shift # fnord
596 shift # $arg
597 ;;
598 esac
599 done
600
601 test -z "$dashmflag" && dashmflag=-M
602 # Require at least two characters before searching for ':'
603 # in the target name. This is to cope with DOS-style filenames:
604 # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
605 "$@" $dashmflag |
606 sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
607 rm -f "$depfile"
608 cat < "$tmpdepfile" > "$depfile"
609 # Some versions of the HPUX 10.20 sed can't process this sed invocation
610 # correctly. Breaking it into two sed invocations is a workaround.
611 tr ' ' "$nl" < "$tmpdepfile" \
612 | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
613 | sed -e 's/$/ :/' >> "$depfile"
614 rm -f "$tmpdepfile"
615 ;;
616
617 dashXmstdout)
618 # This case only exists to satisfy depend.m4. It is never actually
619 # run, as this mode is specially recognized in the preamble.
620 exit 1
621 ;;
622
623 makedepend)
624 "$@" || exit $?
625 # Remove any Libtool call
626 if test "$libtool" = yes; then
627 while test "X$1" != 'X--mode=compile'; do
628 shift
629 done
630 shift
631 fi
632 # X makedepend
633 shift
634 cleared=no eat=no
635 for arg
636 do
637 case $cleared in
638 no)
639 set ""; shift
640 cleared=yes ;;
641 esac
642 if test $eat = yes; then
643 eat=no
644 continue
645 fi
646 case "$arg" in
647 -D*|-I*)
648 set fnord "$@" "$arg"; shift ;;
649 # Strip any option that makedepend may not understand. Remove
650 # the object too, otherwise makedepend will parse it as a source file.
651 -arch)
652 eat=yes ;;
653 -*|$object)
654 ;;
655 *)
656 set fnord "$@" "$arg"; shift ;;
657 esac
658 done
659 obj_suffix=`echo "$object" | sed 's/^.*\././'`
660 touch "$tmpdepfile"
661 ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
662 rm -f "$depfile"
663 # makedepend may prepend the VPATH from the source file name to the object.
664 # No need to regex-escape $object, excess matching of '.' is harmless.
665 sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
666 # Some versions of the HPUX 10.20 sed can't process the last invocation
667 # correctly. Breaking it into two sed invocations is a workaround.
668 sed '1,2d' "$tmpdepfile" \
669 | tr ' ' "$nl" \
670 | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
671 | sed -e 's/$/ :/' >> "$depfile"
672 rm -f "$tmpdepfile" "$tmpdepfile".bak
673 ;;
674
675 cpp)
676 # Important note: in order to support this mode, a compiler *must*
677 # always write the preprocessed file to stdout.
678 "$@" || exit $?
679
680 # Remove the call to Libtool.
681 if test "$libtool" = yes; then
682 while test "X$1" != 'X--mode=compile'; do
683 shift
684 done
685 shift
686 fi
687
688 # Remove '-o $object'.
689 IFS=" "
690 for arg
691 do
692 case $arg in
693 -o)
694 shift
695 ;;
696 $object)
697 shift
698 ;;
699 *)
700 set fnord "$@" "$arg"
701 shift # fnord
702 shift # $arg
703 ;;
704 esac
705 done
706
707 "$@" -E \
708 | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
709 -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
710 | sed '$ s: \\$::' > "$tmpdepfile"
711 rm -f "$depfile"
712 echo "$object : \\" > "$depfile"
713 cat < "$tmpdepfile" >> "$depfile"
714 sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
715 rm -f "$tmpdepfile"
716 ;;
717
718 msvisualcpp)
719 # Important note: in order to support this mode, a compiler *must*
720 # always write the preprocessed file to stdout.
721 "$@" || exit $?
722
723 # Remove the call to Libtool.
724 if test "$libtool" = yes; then
725 while test "X$1" != 'X--mode=compile'; do
726 shift
727 done
728 shift
729 fi
730
731 IFS=" "
732 for arg
733 do
734 case "$arg" in
735 -o)
736 shift
737 ;;
738 $object)
739 shift
740 ;;
741 "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
742 set fnord "$@"
743 shift
744 shift
745 ;;
746 *)
747 set fnord "$@" "$arg"
748 shift
749 shift
750 ;;
751 esac
752 done
753 "$@" -E 2>/dev/null |
754 sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
755 rm -f "$depfile"
756 echo "$object : \\" > "$depfile"
757 sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
758 echo "$tab" >> "$depfile"
759 sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
760 rm -f "$tmpdepfile"
761 ;;
762
763 msvcmsys)
764 # This case exists only to let depend.m4 do its work. It works by
765 # looking at the text of this script. This case will never be run,
766 # since it is checked for above.
767 exit 1
768 ;;
769
770 none)
771 exec "$@"
772 ;;
773
774 *)
775 echo "Unknown depmode $depmode" 1>&2
776 exit 1
777 ;;
778 esac
779
780 exit 0
781
782 # Local Variables:
783 # mode: shell-script
784 # sh-indentation: 2
785 # eval: (add-hook 'before-save-hook 'time-stamp)
786 # time-stamp-start: "scriptversion="
787 # time-stamp-format: "%:y-%02m-%02d.%02H"
788 # time-stamp-time-zone: "UTC0"
789 # time-stamp-end: "; # UTC"
790 # End:
0 #
1 # xhtml11.conf
2 #
3 # Asciidoc configuration file.
4 # xhtml11 backend, generates XHTML 1.1 conformant markup.
5 #
6
7 [miscellaneous]
8 outfilesuffix=.html
9
10 [attributes]
11 basebackend=html
12 basebackend-html=
13 basebackend-xhtml11=
14
15 [replacements2]
16 # Line break.
17 (?m)^(.*)\s\+$=\1<br />
18
19 [replacements]
20 ifdef::asciidoc7compatible[]
21 # Superscripts.
22 \^(.+?)\^=<sup>\1</sup>
23 # Subscripts.
24 ~(.+?)~=<sub>\1</sub>
25 endif::asciidoc7compatible[]
26
27 [ruler-blockmacro]
28 <hr />
29
30 [pagebreak-blockmacro]
31 <div style="page-break-after:always"></div>
32
33 [blockdef-pass]
34 asciimath-style=template="asciimathblock",subs=[]
35 latexmath-style=template="latexmathblock",subs=[]
36
37 [macros]
38 # math macros.
39 # Special characters are escaped in HTML math markup.
40 (?su)[\\]?(?P<name>asciimath|latexmath):(?P<subslist>\S*?)\[(?P<passtext>.*?)(?<!\\)\]=[specialcharacters]
41 (?u)^(?P<name>asciimath|latexmath)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$=#[specialcharacters]
42
43 [asciimath-inlinemacro]
44 `{passtext}`
45
46 [asciimath-blockmacro]
47 <div class="mathblock{role? {role}}"{id? id="{id}"}>
48 <div class="content">
49 <div class="title">{title}</div>
50 `{passtext}`
51 </div></div>
52
53 [asciimathblock]
54 <div class="mathblock{role? {role}}"{id? id="{id}"}>
55 <div class="content">
56 <div class="title">{title}</div>
57 `|`
58 </div></div>
59
60 [latexmath-inlinemacro]
61 {passtext}
62
63 [latexmath-blockmacro]
64 <div class="mathblock{role? {role}}"{id? id="{id}"}>
65 <div class="content">
66 <div class="title">{title}</div>
67 {passtext}
68 </div></div>
69
70 [latexmathblock]
71 <div class="mathblock{role? {role}}"{id? id="{id}"}>
72 <div class="content">
73 <div class="title">{title}</div>
74 |
75 </div></div>
76
77 [image-inlinemacro]
78 <span class="image{role? {role}}">
79 <a class="image" href="{link}">
80 {data-uri%}<img src="{imagesdir=}{imagesdir?/}{target}" alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}{title? title="{title}"} />
81 {data-uri#}<img alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"}{title? title="{title}"} src="data:image/{eval:os.path.splitext('{target}')[1][1:]};base64,
82 {data-uri#}{sys3:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{imagesdir=}","{target}")}"}" />
83 {link#}</a>
84 </span>
85
86 [image-blockmacro]
87 <div class="imageblock{style? {style}}{role? {role}}"{id? id="{id}"}{align? style="text-align:{align};"}{float? style="float:{float};"}>
88 <div class="content">
89 <a class="image" href="{link}">
90 {data-uri%}<img src="{imagesdir=}{imagesdir?/}{target}" alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"} />
91 {data-uri#}<img alt="{alt={target}}"{width? width="{width}"}{height? height="{height}"} src="data:image/{eval:os.path.splitext('{target}')[1][1:]};base64,
92 {data-uri#}{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{imagesdir=}","{target}")}"}" />
93 {link#}</a>
94 </div>
95 <div class="title">{caption={figure-caption} {counter:figure-number}. }{title}</div>
96 </div>
97
98 [unfloat-blockmacro]
99 <div style="clear:both;"></div>
100
101 [indexterm-inlinemacro]
102 # Index term.
103 {empty}
104
105 [indexterm2-inlinemacro]
106 # Index term.
107 # Single entry index term that is visible in the primary text flow.
108 {1}
109
110 [footnote-inlinemacro]
111 # footnote:[<text>].
112 <span class="footnote"><br />[{0}]<br /></span>
113
114 [footnoteref-inlinemacro]
115 # footnoteref:[<id>], create reference to footnote.
116 {2%}<span class="footnoteref"><br /><a href="#_footnote_{1}">[{1}]</a><br /></span>
117 # footnoteref:[<id>,<text>], create footnote with ID.
118 {2#}<span class="footnote" id="_footnote_{1}"><br />[{2}]<br /></span>
119
120 [callout-inlinemacro]
121 ifndef::icons[]
122 <b>&lt;{index}&gt;</b>
123 endif::icons[]
124 ifdef::icons[]
125 ifndef::data-uri[]
126 <img src="{icon={iconsdir}/callouts/{index}.png}" alt="{index}" />
127 endif::data-uri[]
128 ifdef::data-uri[]
129 <img alt="{index}" src="data:image/png;base64,
130 {sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/callouts/{index}.png}")}"}" />
131 endif::data-uri[]
132 endif::icons[]
133
134 # Comment line macros.
135 [comment-inlinemacro]
136 {showcomments#}<br /><span class="comment">{passtext}</span><br />
137
138 [comment-blockmacro]
139 {showcomments#}<p><span class="comment">{passtext}</span></p>
140
141 [literal-inlinemacro]
142 # Inline literal.
143 <tt>{passtext}</tt>
144
145 # List tags.
146 [listtags-bulleted]
147 list=<div class="ulist{style? {style}}{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ul>|</ul></div>
148 item=<li>|</li>
149 text=<p>|</p>
150
151 [listtags-numbered]
152 # The start attribute is not valid XHTML 1.1 but all browsers support it.
153 list=<div class="olist{style? {style}}{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol class="{style}"{start? start="{start}"}>|</ol></div>
154 item=<li>|</li>
155 text=<p>|</p>
156
157 [listtags-labeled]
158 list=<div class="dlist{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<dl>|</dl></div>
159 entry=
160 label=
161 term=<dt class="hdlist1{strong-option? strong}">|</dt>
162 item=<dd>|</dd>
163 text=<p>|</p>
164
165 [listtags-horizontal]
166 list=<div class="hdlist{compact-option? compact}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<table>{labelwidth?<col width="{labelwidth}%" />}{itemwidth?<col width="{itemwidth}%" />}|</table></div>
167 label=<td class="hdlist1{strong-option? strong}">|</td>
168 term=|<br />
169 entry=<tr>|</tr>
170 item=<td class="hdlist2">|</td>
171 text=<p style="margin-top: 0;">|</p>
172
173 [listtags-qanda]
174 list=<div class="qlist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol>|</ol></div>
175 entry=<li>|</li>
176 label=
177 term=<p><em>|</em></p>
178 item=
179 text=<p>|</p>
180
181 [listtags-callout]
182 ifndef::icons[]
183 list=<div class="colist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ol>|</ol></div>
184 item=<li>|</li>
185 text=<p>|</p>
186 endif::icons[]
187 ifdef::icons[]
188 list=<div class="colist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<table>|</table></div>
189 ifndef::data-uri[]
190 item=<tr><td><img src="{iconsdir}/callouts/{listindex}.png" alt="{listindex}" /></td><td>|</td></tr>
191 endif::data-uri[]
192 ifdef::data-uri[]
193 item=<tr><td><img alt="{listindex}" src="data:image/png;base64, {sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/callouts/{listindex}.png}")}"}" /></td><td>|</td></tr>
194 endif::data-uri[]
195 text=|
196 endif::icons[]
197
198 [listtags-glossary]
199 list=<div class="dlist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<dl>|</dl></div>
200 label=
201 entry=
202 term=<dt>|</dt>
203 item=<dd>|</dd>
204 text=<p>|</p>
205
206 [listtags-bibliography]
207 list=<div class="ulist{style? {style}}{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<ul>|</ul></div>
208 item=<li>|</li>
209 text=<p>|</p>
210
211 [tags]
212 # Quoted text.
213 emphasis=<em>{1?<span class="{1}">}|{1?</span>}</em>
214 strong=<strong>{1?<span class="{1}">}|{1?</span>}</strong>
215 monospaced=<tt>{1?<span class="{1}">}|{1?</span>}</tt>
216 singlequoted={lsquo}{1?<span class="{1}">}|{1?</span>}{rsquo}
217 doublequoted={ldquo}{1?<span class="{1}">}|{1?</span>}{rdquo}
218 unquoted={1?<span class="{1}">}|{1?</span>}
219 superscript=<sup>{1?<span class="{1}">}|{1?</span>}</sup>
220 subscript=<sub>{1?<span class="{1}">}|{1?</span>}</sub>
221
222 ifdef::deprecated-quotes[]
223 # Override with deprecated quote attributes.
224 emphasis={role?<span class="{role}">}<em{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</em>{role?</span>}
225 strong={role?<span class="{role}">}<strong{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</strong>{role?</span>}
226 monospaced={role?<span class="{role}">}<tt{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</tt>{role?</span>}
227 singlequoted={role?<span class="{role}">}{1,2,3?<span style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?">}{amp}#8216;|{amp}#8217;{1,2,3?</span>}{role?</span>}
228 doublequoted={role?<span class="{role}">}{1,2,3?<span style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?">}{amp}#8220;|{amp}#8221;{1,2,3?</span>}{role?</span>}
229 unquoted={role?<span class="{role}">}{1,2,3?<span style="{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}">}|{1,2,3?</span>}{role?</span>}
230 superscript={role?<span class="{role}">}<sup{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</sup>{role?</span>}
231 subscript={role?<span class="{role}">}<sub{1,2,3? style="}{1?color:{1};}{2?background-color:{2};}{3?font-size:{3}em;}{1,2,3?"}>|</sub>{role?</span>}
232 endif::deprecated-quotes[]
233
234 # Inline macros
235 [http-inlinemacro]
236 <a href="{name}:{target}">{0={name}:{target}}</a>
237 [https-inlinemacro]
238 <a href="{name}:{target}">{0={name}:{target}}</a>
239 [ftp-inlinemacro]
240 <a href="{name}:{target}">{0={name}:{target}}</a>
241 [file-inlinemacro]
242 <a href="{name}:{target}">{0={name}:{target}}</a>
243 [irc-inlinemacro]
244 <a href="{name}:{target}">{0={name}:{target}}</a>
245 [mailto-inlinemacro]
246 <a href="mailto:{target}">{0={target}}</a>
247 [link-inlinemacro]
248 <a href="{target}">{0={target}}</a>
249 [callto-inlinemacro]
250 <a href="{name}:{target}">{0={target}}</a>
251 # anchor:id[text]
252 [anchor-inlinemacro]
253 <a id="{target}"></a>
254 # [[id,text]]
255 [anchor2-inlinemacro]
256 <a id="{1}"></a>
257 # [[[id]]]
258 [anchor3-inlinemacro]
259 <a id="{1}"></a>[{1}]
260 # xref:id[text]
261 [xref-inlinemacro]
262 <a href="#{target}">{0=[{target}]}</a>
263 # <<id,text>>
264 [xref2-inlinemacro]
265 <a href="#{1}">{2=[{1}]}</a>
266
267 # Special word substitution.
268 [emphasizedwords]
269 <em>{words}</em>
270 [monospacedwords]
271 <tt>{words}</tt>
272 [strongwords]
273 <strong>{words}</strong>
274
275 # Paragraph substitution.
276 [paragraph]
277 <div class="paragraph{role? {role}}"{id? id="{id}"}>{title?<div class="title">{title}</div>}<p>
278 |
279 </p></div>
280
281 [admonitionparagraph]
282 template::[admonitionblock]
283
284 # Delimited blocks.
285 [listingblock]
286 <div class="listingblock{role? {role}}"{id? id="{id}"}>
287 <div class="title">{caption=}{title}</div>
288 <div class="content">
289 <pre><tt>
290 |
291 </tt></pre>
292 </div></div>
293
294 [literalblock]
295 <div class="literalblock{role? {role}}"{id? id="{id}"}>
296 <div class="title">{title}</div>
297 <div class="content">
298 <pre><tt>
299 |
300 </tt></pre>
301 </div></div>
302
303 [sidebarblock]
304 <div class="sidebarblock{role? {role}}"{id? id="{id}"}>
305 <div class="content">
306 <div class="title">{title}</div>
307 |
308 </div></div>
309
310 [openblock]
311 <div class="openblock{role? {role}}"{id? id="{id}"}>
312 <div class="title">{title}</div>
313 <div class="content">
314 |
315 </div></div>
316
317 [partintroblock]
318 template::[openblock]
319
320 [abstractblock]
321 template::[quoteblock]
322
323 [quoteblock]
324 <div class="quoteblock{role? {role}}"{id? id="{id}"}>
325 <div class="title">{title}</div>
326 <div class="content">
327 |
328 </div>
329 <div class="attribution">
330 <em>{citetitle}</em>{attribution?<br />}
331 &#8212; {attribution}
332 </div></div>
333
334 [verseblock]
335 <div class="verseblock{role? {role}}"{id? id="{id}"}>
336 <div class="title">{title}</div>
337 <pre class="content">
338 |
339 </pre>
340 <div class="attribution">
341 <em>{citetitle}</em>{attribution?<br />}
342 &#8212; {attribution}
343 </div></div>
344
345 [exampleblock]
346 <div class="exampleblock{role? {role}}"{id? id="{id}"}>
347 <div class="title">{caption={example-caption} {counter:example-number}. }{title}</div>
348 <div class="content">
349 |
350 </div></div>
351
352 [admonitionblock]
353 <div class="admonitionblock{role? {role}}"{id? id="{id}"}>
354 <table><tr>
355 <td class="icon">
356 {data-uri%}{icons#}<img src="{icon={iconsdir}/{name}.png}" alt="{caption}" />
357 {data-uri#}{icons#}<img alt="{caption}" src="data:image/png;base64,
358 {data-uri#}{icons#}{sys:python -uc "import base64,sys; base64.encode(sys.stdin,sys.stdout)" < "{eval:os.path.join("{indir={outdir}}","{icon={iconsdir}/{name}.png}")}"}" />
359 {icons%}<div class="title">{caption}</div>
360 </td>
361 <td class="content">
362 <div class="title">{title}</div>
363 |
364 </td>
365 </tr></table>
366 </div>
367
368 # Tables.
369 [tabletags-default]
370 colspec=<col{autowidth-option! width="{colpcwidth}%"} />
371 bodyrow=<tr>|</tr>
372 headdata=<th {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }align="{halign}" valign="{valign}">|</th>
373 bodydata=<td {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }align="{halign}" valign="{valign}">|</td>
374 paragraph=<p class="table">|</p>
375
376 [tabletags-header]
377 paragraph=<p class="table header">|</p>
378
379 [tabletags-emphasis]
380 paragraph=<p class="table"><em>|</em></p>
381
382 [tabletags-strong]
383 paragraph=<p class="table"><strong>|</strong></p>
384
385 [tabletags-monospaced]
386 paragraph=<p class="table"><tt>|</tt></p>
387
388 [tabletags-verse]
389 bodydata=<td {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }align="{halign}" valign="{valign}"><div class="verse">|</div></td>
390 paragraph=
391
392 [tabletags-literal]
393 bodydata=<td {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }align="{halign}" valign="{valign}"><div class="literal"><pre><tt>|</tt></pre></div></td>
394 paragraph=
395
396 [tabletags-asciidoc]
397 bodydata=<td {colspan@1::colspan="{colspan}" }{rowspan@1::rowspan="{rowspan}" }align="{halign}" valign="{valign}"><div>|</div></td>
398 paragraph=
399
400 [table]
401 <div class="tableblock{role? {role}}"{id? id="{id}"}>
402 <table rules="{grid=all}"
403 style="margin-left:{align@left:0}{align@center|right:auto}; margin-right:{align@left|center:auto}{align@right:0};"
404 style="float:{float};"
405 {autowidth-option%}width="{tablepcwidth}%"
406 {autowidth-option#}{width#width="{tablepcwidth}%"}
407 frame="{frame%border}"
408 frame="{frame@topbot:hsides}{frame@all:border}{frame@none:void}{frame@sides:vsides}"
409 cellspacing="0" cellpadding="4">
410 <caption class="title">{caption={table-caption} {counter:table-number}. }{title}</caption>
411 {colspecs}
412 {headrows#}<thead>
413 {headrows}
414 {headrows#}</thead>
415 {footrows#}<tfoot>
416 {footrows}
417 {footrows#}</tfoot>
418 <tbody>
419 {bodyrows}
420 </tbody>
421 </table>
422 </div>
423
424 #--------------------------------------------------------------------
425 # Deprecated old table definitions.
426 #
427
428 [miscellaneous]
429 # Screen width in pixels.
430 pagewidth=800
431 pageunits=
432
433 [old_tabledef-default]
434 template=old_table
435 colspec=<col width="{colwidth}{pageunits}" />
436 bodyrow=<tr>|</tr>
437 headdata=<th align="{colalign}">|</th>
438 footdata=<td align="{colalign}">|</td>
439 bodydata=<td align="{colalign}">|</td>
440
441 [old_table]
442 <div class="tableblock"{id? id="{id}"}>
443 <table rules="{grid=none}"
444 frame="{frame%hsides}"
445 frame="{frame@topbot:hsides}{frame@all:border}{frame@none:void}{frame@sides:vsides}"
446 cellspacing="0" cellpadding="4">
447 <caption class="title">{caption={table-caption}}{title}</caption>
448 {colspecs}
449 {headrows#}<thead>
450 {headrows}
451 {headrows#}</thead>
452 {footrows#}<tfoot>
453 {footrows}
454 {footrows#}</tfoot>
455 <tbody valign="top">
456 {bodyrows}
457 </tbody>
458 </table>
459 </div>
460
461 # End of deprecated old table definitions.
462 #--------------------------------------------------------------------
463
464 [floatingtitle]
465 <h{level@0:1}{level@1:2}{level@2:3}{level@3:4}{level@4:5}{id? id="{id}"} class="float">{title}</h{level@0:1}{level@1:2}{level@2:3}{level@3:4}{level@4:5}>
466
467 [preamble]
468 # Untitled elements between header and first section title.
469 <div id="preamble">
470 <div class="sectionbody">
471 |
472 </div>
473 </div>
474
475 # Document sections.
476 [sect0]
477 <h1{id? id="{id}"}>{title}</h1>
478 |
479
480 [sect1]
481 <div class="sect1{style? {style}}{role? {role}}">
482 <h2{id? id="{id}"}>{numbered?{sectnum} }{title}</h2>
483 <div class="sectionbody">
484 |
485 </div>
486 </div>
487
488 [sect2]
489 <div class="sect2{style? {style}}{role? {role}}">
490 <h3{id? id="{id}"}>{numbered?{sectnum} }{title}</h3>
491 |
492 </div>
493
494 [sect3]
495 <div class="sect3{style? {style}}{role? {role}}">
496 <h4{id? id="{id}"}>{numbered?{sectnum} }{title}</h4>
497 |
498 </div>
499
500 [sect4]
501 <div class="sect4{style? {style}}{role? {role}}">
502 <h5{id? id="{id}"}>{title}</h5>
503 |
504 </div>
505
506 [appendix]
507 <div class="sect1{style? {style}}{role? {role}}">
508 <h2{id? id="{id}"}>{numbered?{sectnum} }{appendix-caption} {counter:appendix-number:A}: {title}</h2>
509 <div class="sectionbody">
510 |
511 </div>
512 </div>
513
514 [toc]
515 <div id="toc">
516 <div id="toctitle">{toc-title}</div>
517 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
518 </div>
519
520 [header]
521 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
522 "https://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
523 <html xmlns="https://www.w3.org/1999/xhtml" xml:lang="{lang=en}">
524 <head>
525 <link rel="icon" type="image/png" href="/favicon.png">
526 <meta http-equiv="Content-Type" content="{quirks=application/xhtml+xml}{quirks?text/html}; charset={encoding}" />
527 <meta name="generator" content="AsciiDoc {asciidoc-version}" />
528 <meta name="description" content="{description}" />
529 <meta name="keywords" content="{keywords}" />
530 <title>i3: {title}</title>
531 {title%}<title>i3: {doctitle=}</title>
532 <link rel="stylesheet" href="{stylesdir=.}/style.css" type="text/css" />
533 ifdef::linkcss[]
534 <link rel="stylesheet" href="{stylesdir=.}/{theme={backend}}.css" type="text/css" />
535 {doctype-manpage}<link rel="stylesheet" href="{stylesdir=.}/{theme={backend}}-manpage.css" type="text/css" />
536 ifdef::quirks[]
537 <link rel="stylesheet" href="{stylesdir=.}/{theme={backend}}-quirks.css" type="text/css" />
538 endif::quirks[]
539 <link rel="stylesheet" href="{stylesdir=.}/{stylesheet}" type="text/css" />
540 ifdef::pygments[<link rel="stylesheet" href="{stylesdir=.}/pygments.css" type="text/css" />]
541 endif::linkcss[]
542 ifndef::linkcss[]
543 <style type="text/css">
544 include1::{stylesdir=./stylesheets}/{theme={backend}}.css[]
545 ifdef::doctype-manpage[]
546 include1::{stylesdir=./stylesheets}/{theme={backend}}-manpage.css[]
547 endif::doctype-manpage[]
548 ifdef::quirks[]
549 include1::{stylesdir=./stylesheets}/{theme={backend}}-quirks.css[]
550 endif::quirks[]
551 include1::{stylesheet}[]
552 ifdef::pygments[]
553 include1::{stylesdir=./stylesheets}/pygments.css[]
554 endif::pygments[]
555 </style>
556 endif::linkcss[]
557 ifndef::disable-javascript[]
558 ifdef::linkcss[]
559 <script type="text/javascript">
560 # Escape as CDATA to pass validators.
561 /*<![CDATA[*/
562 document.addEventListener("DOMContentLoaded", function()\{asciidoc.footnotes();{toc? asciidoc.toc({toclevels});}\}, false);
563 /*]]>*/
564 </script>
565 <script type="text/javascript" src="{scriptsdir=.}/asciidoc-xhtml11.js"></script>
566 endif::linkcss[]
567 ifndef::linkcss[]
568 <script type="text/javascript">
569 # Escape as CDATA to pass validators.
570 /*<![CDATA[*/
571 document.addEventListener("DOMContentLoaded", function()\{asciidoc.footnotes();{toc? asciidoc.toc({toclevels});}\}, false);
572 include1::{scriptsdir=./javascripts}/asciidoc-xhtml11.js[]
573 /*]]>*/
574 </script>
575 endif::linkcss[]
576 endif::disable-javascript[]
577 ifdef::asciimath[]
578 ifdef::linkcss[]
579 <script type="text/javascript" src="{scriptsdir=.}/ASCIIMathML.js"></script>
580 endif::linkcss[]
581 ifndef::linkcss[]
582 <script type="text/javascript">
583 # Escape as CDATA to pass validators.
584 /*<![CDATA[*/
585 include1::{scriptsdir=./javascripts}/ASCIIMathML.js[]
586 /*]]>*/
587 </script>
588 endif::linkcss[]
589 endif::asciimath[]
590 ifdef::latexmath[]
591 ifdef::linkcss[]
592 <script type="text/javascript" src="{scriptsdir=.}/LaTeXMathML.js"></script>
593 endif::linkcss[]
594 ifndef::linkcss[]
595 <script type="text/javascript">
596 # Escape as CDATA to pass validators.
597 /*<![CDATA[*/
598 include1::{scriptsdir=./javascripts}/LaTeXMathML.js[]
599 /*]]>*/
600 </script>
601 endif::linkcss[]
602 endif::latexmath[]
603 {docinfo1,docinfo2#}{include:{docdir}/docinfo.html}
604 {docinfo,docinfo2#}{include:{docdir}/{docname}-docinfo.html}
605 </head>
606 <body class="{doctype}"{max-width? style="max-width:{max-width}"}>
607
608 <div id="main">
609 <a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>
610 <ul id="nav">
611 <li style=" background-color: #FFD000; font-size: 2em;padding: 0.25em;-webkit-border-radius: 0.25em;border: 4px dashed black;color: #000000;">latest git docs</li>
612 </ul>
613 <br style="clear: both">
614 <div id="content">
615 # Article, book header.
616 ifndef::doctype-manpage[]
617 <div id="header">
618 ifndef::notitle[<h1>{doctitle}</h1>]
619 ifdef::doctitle[]
620 <span id="author">{author}</span><br />
621 <span id="email"><tt>&lt;<a href="mailto:{email}">{email}</a>&gt;</tt></span><br />
622 <span id="revnumber">version {revnumber}{revdate?,}</span>
623 <span id="revdate">{revdate}</span>
624 <br /><span id="revremark">{revremark}</span>
625 endif::doctitle[]
626 ifdef::toc[{template:toc}]
627 </div>
628 endif::doctype-manpage[]
629 # Man page header.
630 ifdef::doctype-manpage[]
631 <div id="header">
632 <h1>
633 {doctitle} Manual Page
634 </h1>
635 ifdef::toc[{template:toc}]
636 <h2>{manname-title}</h2>
637 <div class="sectionbody">
638 <p>{manname} -
639 {manpurpose}
640 </p>
641 </div>
642 </div>
643 endif::doctype-manpage[]
644
645 [footer]
646 </div>
647 {disable-javascript%<div id="footnotes"><hr /></div>}
648 <div id="footer" lang="de">
649 © 2009 Michael Stapelberg, <a href="https://i3wm.org/impress.html">Impressum</a>
650 </div>
651 </body>
652 </html>
653
654 ifdef::doctype-manpage[]
655 [synopsis]
656 template::[sect1]
657 endif::doctype-manpage[]
658
659 ifdef::quirks[]
660 include::{backend}-quirks.conf[]
661 endif::quirks[]
Binary diff not shown
0 Debugging i3: How To
1 ====================
2 Michael Stapelberg <michael@i3wm.org>
3 January 2014
4
5 This document describes how to debug i3 to send us useful bug
6 reports, even if you have no knowledge of C programming.
7
8 Thank you for being interested in debugging i3. It really means
9 something to us to get your bug fixed. If you have any questions about the
10 process and/or need further help, do not hesitate to contact us!
11
12 == Verify you are using i3 ≥ 4.10
13
14 Only the latest major version of i3 is supported. To verify which version
15 you are running, use:
16
17 ---------------
18 $ i3 --moreversion 2>&- || i3 --version
19 Binary i3 version: 4.7 (2013-12-22, branch "tags/4.7")
20 Running i3 version: 4.7-84-gac74a63 (2014-01-01, branch "next") (pid 1995)
21 ---------------
22
23 Your version can look like this:
24
25 4.7 (release version)::
26 You are using a release version. In many cases, bugs are already
27 fixed in the development version of i3. Even if the bug is not a known fixed
28 one, we will still ask you to reproduce your error with the most recent
29 development version of i3. Therefore, please upgrade to a development version
30 if you can.
31
32 4.7-85-g9c15b95 (development version)::
33 Your version is 85 commits newer than 4.7, and the git revision of your
34 version is +9c15b95+. Go to https://github.com/i3/i3/commits/next and see if
35 the most recent commit starts with the same revision. If so, you are using the
36 latest version.
37
38 Development versions of i3 have logging enabled by default and are compiled
39 with debug symbols.
40
41 == Enabling logging
42
43 If you are using a development version (see previous section), you don’t need
44 to do anything -- skip to section 3.
45
46 If you are using a release version with a custom +~/.xsession+ (or xinitrc)
47 file, execute i3 with a line like this:
48
49 ----------------------------------
50 # Use 25 MiB of RAM for debug logs
51 exec i3 --shmlog-size=26214400
52 ----------------------------------
53
54 If you are *NOT* using an +~/.xsession+ file but you just chose "i3" from the
55 list of sessions in your desktop manager (gdm, lxdm, …), edit
56 +/usr/share/xsessions/i3.desktop+ and replace the +Exec=i3+ line with:
57
58 ------------------------------
59 Exec=i3 --shmlog-size=26214400
60 ------------------------------
61
62 If you cannot restart i3 for some reason, you can enable debug logging on the
63 fly:
64
65 ---------------------------------------
66 i3-msg 'debuglog on; shmlog on; reload'
67 ---------------------------------------
68
69 == Reproducing the problem
70
71 Before submitting an issue, please make sure to close down on the problem as
72 much as you can yourself. Here are some steps you should consider:
73
74 * Find a deterministic, reliable way to reproduce the problem and provide it
75 with your bug report.
76 * Try using the default i3 config to reproduce the problem. If the issue does
77 not appear with the default config, gradually adapt it to track down what
78 change(s) to the config introduce the problem.
79 * Reproduce the problem with a minimal setup, i.e., only use as few applications,
80 windows and steps as necessary.
81 * In addition, try to stick to applications that are common and, even more
82 importantly, free / open source.
83 * Before obtaining the log file, restart i3 in-place, execute the steps to
84 reproduce the problem and then save the logs. This keeps the log file as
85 small as possible and necessary.
86
87 Please be aware that we cannot support compatibility issues with closed-source
88 software, as digging into compatibility problems without having access to the
89 source code is too time-consuming. Additionally, experience has shown that
90 often, the software in question is responsible for the issue. Please raise an
91 issue with the software in question, not i3.
92
93 == Obtaining the debug logfile
94
95 [CAUTION]
96 ================================================================================
97 Logs may contain sensitive information, so please inspect the log before
98 submitting it. Logs may be viewed by anyone, once posted. If you choose to
99 redact the log, make an effort not to discard information which may be relevant
100 to the issue you are reporting.
101
102 The best way to avoid submitting such information is to only run the necessary
103 steps to reproduce the behavior when saving the log file. This will also make
104 analyzing the log file easier.
105 ================================================================================
106
107 No matter whether i3 misbehaved in some way without crashing or whether it just
108 crashed, the logfile provides all information necessary to debug the problem.
109
110 To upload a compressed version of the logfile (for a bugreport), use:
111 -------------------------------------------------------------------------------
112 DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- https://logs.i3wm.org
113 -------------------------------------------------------------------------------
114
115 This command does not depend on i3 (it also works while i3 displays
116 the crash dialog), but it requires a working X11 connection.
117
118 After running it, you will get a URL to the logfile. Please include that URL in
119 your bug report.
120
121 == On crashes: Obtaining a backtrace
122
123 When i3 crashes, it will display a dialog stating “i3 just crashed”, offering
124 you to save a backtrace to a text file.
125
126 To actually get useful backtraces, you should make sure that your version of i3
127 is compiled with debug symbols:
128
129 ------------------------------------------------------------------------------
130 $ file `which i3`
131 /usr/bin/i3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
132 linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
133 ------------------------------------------------------------------------------
134
135 Notice the +not stripped+, which is the important part. If you have a version
136 which is stripped, please check whether your distribution provides debug
137 symbols (package +i3-wm-dbg+ on Debian for example) or if you can turn off
138 stripping. If nothing helps, please build i3 from source.
139
140 Once you have made sure that your i3 is compiled with debug symbols and the C
141 debugger +gdb+ is installed on your machine, you can let i3 generate a
142 backtrace in the crash dialog.
143
144 After pressing "b" in the crash dialog, you will get a file called
145 +/tmp/i3-backtrace.%d.%d.txt+ where the first +%d+ is replaced by i3’s process
146 id (PID) and the second one is incremented each time you generate a backtrace,
147 starting at 0.
148
149 == Sending bug reports/debugging on IRC
150
151 When sending bug reports, please attach the *whole* log file. Even if you think
152 you found the section which clearly highlights the problem, additional
153 information might be necessary to completely diagnose the problem.
154
155 When debugging with us in IRC, be prepared to use a so-called nopaste service
156 such as https://pastebin.com because pasting large amounts of text in IRC
157 sometimes leads to incomplete lines (servers have line length limitations) or
158 flood kicks.
159
160 == Debugging i3bar
161
162 To debug i3bar problems, use the +--verbose+ commandline parameter,
163 or add +verbose yes+ to all +bar {}+ blocks in your i3
164 config, reload your config and then restart all i3bar instances like this:
165
166 ---------------------------------------------------------------------
167 $ i3 reload
168 $ killall i3bar
169 $ for c in $(i3-msg -t get_bar_config | python -c \
170 'import json,sys;print("\n".join(json.load(sys.stdin)))'); do \
171 (i3bar --bar_id=$c >i3bar.$c.log 2>&1) & \
172 done;
173 ---------------------------------------------------------------------
174
175 There will now be +i3bar.*.log+ files in your current directory that you can provide
176 in your bug report.
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>Debugging i3: How To</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install();
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>Debugging i3: How To</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">January 2014</span>
740 </div>
741 <div id="content">
742 <div id="preamble">
743 <div class="sectionbody">
744 <div class="paragraph"><p>This document describes how to debug i3 to send us useful bug
745 reports, even if you have no knowledge of C programming.</p></div>
746 <div class="paragraph"><p>Thank you for being interested in debugging i3. It really means
747 something to us to get your bug fixed. If you have any questions about the
748 process and/or need further help, do not hesitate to contact us!</p></div>
749 </div>
750 </div>
751 <div class="sect1">
752 <h2 id="_verify_you_are_using_i3_4_10">1. Verify you are using i3 ≥ 4.10</h2>
753 <div class="sectionbody">
754 <div class="paragraph"><p>Only the latest major version of i3 is supported. To verify which version
755 you are running, use:</p></div>
756 <div class="listingblock">
757 <div class="content">
758 <pre><code>$ i3 --moreversion 2&gt;&amp;- || i3 --version
759 Binary i3 version: 4.7 (2013-12-22, branch "tags/4.7")
760 Running i3 version: 4.7-84-gac74a63 (2014-01-01, branch "next") (pid 1995)</code></pre>
761 </div></div>
762 <div class="paragraph"><p>Your version can look like this:</p></div>
763 <div class="dlist"><dl>
764 <dt class="hdlist1">
765 4.7 (release version)
766 </dt>
767 <dd>
768 <p>
769 You are using a release version. In many cases, bugs are already
770 fixed in the development version of i3. Even if the bug is not a known fixed
771 one, we will still ask you to reproduce your error with the most recent
772 development version of i3. Therefore, please upgrade to a development version
773 if you can.
774 </p>
775 </dd>
776 <dt class="hdlist1">
777 4.7-85-g9c15b95 (development version)
778 </dt>
779 <dd>
780 <p>
781 Your version is 85 commits newer than 4.7, and the git revision of your
782 version is <code>9c15b95</code>. Go to <a href="https://github.com/i3/i3/commits/next">https://github.com/i3/i3/commits/next</a> and see if
783 the most recent commit starts with the same revision. If so, you are using the
784 latest version.
785 </p>
786 </dd>
787 </dl></div>
788 <div class="paragraph"><p>Development versions of i3 have logging enabled by default and are compiled
789 with debug symbols.</p></div>
790 </div>
791 </div>
792 <div class="sect1">
793 <h2 id="_enabling_logging">2. Enabling logging</h2>
794 <div class="sectionbody">
795 <div class="paragraph"><p>If you are using a development version (see previous section), you don’t need
796 to do anything&#8201;&#8212;&#8201;skip to section 3.</p></div>
797 <div class="paragraph"><p>If you are using a release version with a custom <code>~/.xsession</code> (or xinitrc)
798 file, execute i3 with a line like this:</p></div>
799 <div class="listingblock">
800 <div class="content">
801 <pre><code># Use 25 MiB of RAM for debug logs
802 exec i3 --shmlog-size=26214400</code></pre>
803 </div></div>
804 <div class="paragraph"><p>If you are <strong>NOT</strong> using an <code>~/.xsession</code> file but you just chose "i3" from the
805 list of sessions in your desktop manager (gdm, lxdm, …), edit
806 <code>/usr/share/xsessions/i3.desktop</code> and replace the <code>Exec=i3</code> line with:</p></div>
807 <div class="listingblock">
808 <div class="content">
809 <pre><code>Exec=i3 --shmlog-size=26214400</code></pre>
810 </div></div>
811 <div class="paragraph"><p>If you cannot restart i3 for some reason, you can enable debug logging on the
812 fly:</p></div>
813 <div class="listingblock">
814 <div class="content">
815 <pre><code>i3-msg 'debuglog on; shmlog on; reload'</code></pre>
816 </div></div>
817 </div>
818 </div>
819 <div class="sect1">
820 <h2 id="_reproducing_the_problem">3. Reproducing the problem</h2>
821 <div class="sectionbody">
822 <div class="paragraph"><p>Before submitting an issue, please make sure to close down on the problem as
823 much as you can yourself. Here are some steps you should consider:</p></div>
824 <div class="ulist"><ul>
825 <li>
826 <p>
827 Find a deterministic, reliable way to reproduce the problem and provide it
828 with your bug report.
829 </p>
830 </li>
831 <li>
832 <p>
833 Try using the default i3 config to reproduce the problem. If the issue does
834 not appear with the default config, gradually adapt it to track down what
835 change(s) to the config introduce the problem.
836 </p>
837 </li>
838 <li>
839 <p>
840 Reproduce the problem with a minimal setup, i.e., only use as few applications,
841 windows and steps as necessary.
842 </p>
843 </li>
844 <li>
845 <p>
846 In addition, try to stick to applications that are common and, even more
847 importantly, free / open source.
848 </p>
849 </li>
850 <li>
851 <p>
852 Before obtaining the log file, restart i3 in-place, execute the steps to
853 reproduce the problem and then save the logs. This keeps the log file as
854 small as possible and necessary.
855 </p>
856 </li>
857 </ul></div>
858 <div class="paragraph"><p>Please be aware that we cannot support compatibility issues with closed-source
859 software, as digging into compatibility problems without having access to the
860 source code is too time-consuming. Additionally, experience has shown that
861 often, the software in question is responsible for the issue. Please raise an
862 issue with the software in question, not i3.</p></div>
863 </div>
864 </div>
865 <div class="sect1">
866 <h2 id="_obtaining_the_debug_logfile">4. Obtaining the debug logfile</h2>
867 <div class="sectionbody">
868 <div class="admonitionblock">
869 <table><tr>
870 <td class="icon">
871 <div class="title">Caution</div>
872 </td>
873 <td class="content">
874 <div class="paragraph"><p>Logs may contain sensitive information, so please inspect the log before
875 submitting it. Logs may be viewed by anyone, once posted. If you choose to
876 redact the log, make an effort not to discard information which may be relevant
877 to the issue you are reporting.</p></div>
878 <div class="paragraph"><p>The best way to avoid submitting such information is to only run the necessary
879 steps to reproduce the behavior when saving the log file. This will also make
880 analyzing the log file easier.</p></div>
881 </td>
882 </tr></table>
883 </div>
884 <div class="paragraph"><p>No matter whether i3 misbehaved in some way without crashing or whether it just
885 crashed, the logfile provides all information necessary to debug the problem.</p></div>
886 <div class="paragraph"><p>To upload a compressed version of the logfile (for a bugreport), use:</p></div>
887 <div class="listingblock">
888 <div class="content">
889 <pre><code>DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- https://logs.i3wm.org</code></pre>
890 </div></div>
891 <div class="paragraph"><p>This command does not depend on i3 (it also works while i3 displays
892 the crash dialog), but it requires a working X11 connection.</p></div>
893 <div class="paragraph"><p>After running it, you will get a URL to the logfile. Please include that URL in
894 your bug report.</p></div>
895 </div>
896 </div>
897 <div class="sect1">
898 <h2 id="_on_crashes_obtaining_a_backtrace">5. On crashes: Obtaining a backtrace</h2>
899 <div class="sectionbody">
900 <div class="paragraph"><p>When i3 crashes, it will display a dialog stating “i3 just crashed”, offering
901 you to save a backtrace to a text file.</p></div>
902 <div class="paragraph"><p>To actually get useful backtraces, you should make sure that your version of i3
903 is compiled with debug symbols:</p></div>
904 <div class="listingblock">
905 <div class="content">
906 <pre><code>$ file `which i3`
907 /usr/bin/i3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
908 linked (uses shared libs), for GNU/Linux 2.6.18, not stripped</code></pre>
909 </div></div>
910 <div class="paragraph"><p>Notice the <code>not stripped</code>, which is the important part. If you have a version
911 which is stripped, please check whether your distribution provides debug
912 symbols (package <code>i3-wm-dbg</code> on Debian for example) or if you can turn off
913 stripping. If nothing helps, please build i3 from source.</p></div>
914 <div class="paragraph"><p>Once you have made sure that your i3 is compiled with debug symbols and the C
915 debugger <code>gdb</code> is installed on your machine, you can let i3 generate a
916 backtrace in the crash dialog.</p></div>
917 <div class="paragraph"><p>After pressing "b" in the crash dialog, you will get a file called
918 <code>/tmp/i3-backtrace.%d.%d.txt</code> where the first <code>%d</code> is replaced by i3’s process
919 id (PID) and the second one is incremented each time you generate a backtrace,
920 starting at 0.</p></div>
921 </div>
922 </div>
923 <div class="sect1">
924 <h2 id="_sending_bug_reports_debugging_on_irc">6. Sending bug reports/debugging on IRC</h2>
925 <div class="sectionbody">
926 <div class="paragraph"><p>When sending bug reports, please attach the <strong>whole</strong> log file. Even if you think
927 you found the section which clearly highlights the problem, additional
928 information might be necessary to completely diagnose the problem.</p></div>
929 <div class="paragraph"><p>When debugging with us in IRC, be prepared to use a so-called nopaste service
930 such as <a href="https://pastebin.com">https://pastebin.com</a> because pasting large amounts of text in IRC
931 sometimes leads to incomplete lines (servers have line length limitations) or
932 flood kicks.</p></div>
933 </div>
934 </div>
935 <div class="sect1">
936 <h2 id="_debugging_i3bar">7. Debugging i3bar</h2>
937 <div class="sectionbody">
938 <div class="paragraph"><p>To debug i3bar problems, use the <code>--verbose</code> commandline parameter,
939 or add <code>verbose yes</code> to all <code>bar {}</code> blocks in your i3
940 config, reload your config and then restart all i3bar instances like this:</p></div>
941 <div class="listingblock">
942 <div class="content">
943 <pre><code>$ i3 reload
944 $ killall i3bar
945 $ for c in $(i3-msg -t get_bar_config | python -c \
946 'import json,sys;print("\n".join(json.load(sys.stdin)))'); do \
947 (i3bar --bar_id=$c &gt;i3bar.$c.log 2&gt;&amp;1) &amp; \
948 done;</code></pre>
949 </div></div>
950 <div class="paragraph"><p>There will now be <code>i3bar.*.log</code> files in your current directory that you can provide
951 in your bug report.</p></div>
952 </div>
953 </div>
954 </div>
955 <div id="footnotes"><hr /></div>
956 <div id="footer">
957 <div id="footer-text">
958 Last updated
959 2019-01-27 16:45:19 CET
960 </div>
961 </div>
962 </body>
963 </html>
0 Hacking i3: How To
1 ==================
2 Michael Stapelberg <michael@i3wm.org>
3 February 2013
4
5 This document is intended to be the first thing you read before looking and/or
6 touching i3’s source code. It should contain all important information to help
7 you understand why things are like they are. If it does not mention something
8 you find necessary, please do not hesitate to contact me.
9
10 == Building i3
11
12 You can build i3 like you build any other software package which uses autotools.
13 Here’s a memory refresher:
14
15 $ autoreconf -fi
16 $ mkdir -p build && cd build
17 $ ../configure
18 $ make -j8
19
20 (The autoreconf -fi step is unnecessary if you are building from a release tarball,
21 but shouldn’t hurt either.)
22
23 === Build system features
24
25 * We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate
26 directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building
27 in a separate directory is common practice anyway. In case this causes any
28 trouble when packaging i3 for your distribution, please open an issue.
29
30 * “make check” runs the i3 testsuite. See docs/testsuite for details.
31
32 * “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
33 feedback cycle than waiting for the travis build to catch the issue).
34
35 * “make uninstall” (occasionally requested by users who compile from source)
36
37 * “make” will build manpages/docs by default if the tools are installed.
38 Conversely, manpages/docs are not tried to be built for users who don’t want
39 to install all these dependencies to get started hacking on i3.
40
41 * non-release builds will enable address sanitizer by default. Use the
42 --disable-sanitizers configure option to turn off all sanitizers, and see
43 --help for available sanitizers.
44
45 * Support for pre-compiled headers (PCH) has been dropped for now in the
46 interest of simplicity. If you need support for PCH, please open an issue.
47
48 * Coverage reports are now generated using “make check-code-coverage”, which
49 requires specifying --enable-code-coverage when calling configure.
50
51 == Using git / sending patches
52
53 For a short introduction into using git, see
54 https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
55 or, for more documentation, see https://git-scm.com/documentation
56
57 Please talk to us before working on new features to see whether they will be
58 accepted. A good way for this is to open an issue and asking for opinions on it.
59 Even for accepted features, this can be a good way to refine an idea upfront. However,
60 we don't want to see certain features in i3, e.g., switching window focus in an
61 Alt+Tab like way.
62
63 When working on bugfixes, please make sure you mention that you are working on
64 it in the corresponding bug report at https://github.com/i3/i3/issues. In case
65 there is no bug report yet, please create one.
66
67 After you are done, please submit your work for review as a pull request at
68 https://github.com/i3/i3.
69
70 Do not send emails to the mailing list or any author directly, and don’t submit
71 them in the bugtracker, since all reviews should be done in public at
72 https://github.com/i3/i3. In order to make your review go as fast as possible, you
73 could have a look at previous reviews and see what the common mistakes are.
74
75 === Which branch to use?
76
77 Work on i3 generally happens in two branches: “master” and “next” (the latter
78 being the default branch, the one that people get when they check out the git
79 repository).
80
81 The contents of “master” are always stable. That is, it contains the source code
82 of the latest release, plus any bugfixes that were applied since that release.
83
84 New features are only found in the “next” branch. Therefore, if you are working
85 on a new feature, use the “next” branch. If you are working on a bugfix, use the
86 “next” branch, too, but make sure your code also works on “master”.
87
88 == Window Managers
89
90 A window manager is not necessarily needed to run X, but it is usually used in
91 combination with X to facilitate some things. The window manager's job is to
92 take care of the placement of windows, to provide the user with some mechanisms
93 to change the position/size of windows and to communicate with clients to a
94 certain extent (for example handle fullscreen requests of clients such as
95 MPlayer).
96
97 There are no different contexts in which X11 clients run, so a window manager
98 is just another client, like all other X11 applications. However, it handles
99 some events which normal clients usually don’t handle.
100
101 In the case of i3, the tasks (and order of them) are the following:
102
103 . Grab the key bindings (events will be sent upon keypress/keyrelease)
104 . Iterate through all existing windows (if the window manager is not started as
105 the first client of X) and manage them (reparent them, create window
106 decorations, etc.)
107 . When new windows are created, manage them
108 . Handle the client’s `_WM_STATE` property, but only `_WM_STATE_FULLSCREEN` and
109 `_NET_WM_STATE_DEMANDS_ATTENTION`
110 . Handle the client’s `WM_NAME` property
111 . Handle the client’s size hints to display them proportionally
112 . Handle the client’s urgency hint
113 . Handle enter notifications (focus follows mouse)
114 . Handle button (as in mouse buttons) presses for focus/raise on click
115 . Handle expose events to re-draw own windows such as decorations
116 . React to the user’s commands: Change focus, Move windows, Switch workspaces,
117 Change the layout mode of a container (default/stacking/tabbed), start a new
118 application, restart the window manager
119
120 In the following chapters, each of these tasks and their implementation details
121 will be discussed.
122
123 === Tiling window managers
124
125 Traditionally, there are two approaches to managing windows: The most common
126 one nowadays is floating, which means the user can freely move/resize the
127 windows. The other approach is called tiling, which means that your window
128 manager distributes windows to use as much space as possible while not
129 overlapping each other.
130
131 The idea behind tiling is that you should not need to waste your time
132 moving/resizing windows while you usually want to get some work done. After
133 all, most users sooner or later tend to lay out their windows in a way which
134 corresponds to tiling or stacking mode in i3. Therefore, why not let i3 do this
135 for you? Certainly, it’s faster than you could ever do it.
136
137 The problem with most tiling window managers is that they are too inflexible.
138 In my opinion, a window manager is just another tool, and similar to vim which
139 can edit all kinds of text files (like source code, HTML, …) and is not limited
140 to a specific file type, a window manager should not limit itself to a certain
141 layout (like dwm, awesome, …) but provide mechanisms for you to easily create
142 the layout you need at the moment.
143
144 === The layout tree
145
146 The data structure which i3 uses to keep track of your windows is a tree. Every
147 node in the tree is a container (type +Con+). Some containers represent actual
148 windows (every container with a +window != NULL+), some represent split
149 containers and a few have special purposes: they represent workspaces, outputs
150 (like VGA1, LVDS1, …) or the X11 root window.
151
152 So, when you open a terminal and immediately open another one, they reside in
153 the same split container, which uses the default layout. In case of an empty
154 workspace, the split container we are talking about is the workspace.
155
156 To get an impression of how different layouts are represented, just play around
157 and look at the data structures -- they are exposed as a JSON hash. See
158 https://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an
159 example.
160
161 == Files
162
163 include/atoms.xmacro::
164 A file containing all X11 atoms which i3 uses. This file will be included
165 various times (for defining, requesting and receiving the atoms), each time
166 with a different definition of xmacro().
167
168 include/data.h::
169 Contains data definitions used by nearly all files. You really need to read
170 this first.
171
172 include/*.h::
173 Contains forward definitions for all public functions, as well as
174 doxygen-compatible comments (so if you want to get a bit more of the big
175 picture, either browse all header files or use doxygen if you prefer that).
176
177 src/config_parser.c::
178 Contains a custom configuration parser. See src/command_parser.c for rationale
179 on why we use a custom parser.
180
181 src/click.c::
182 Contains all functions which handle mouse button clicks (right mouse button
183 clicks initiate resizing and thus are relatively complex).
184
185 src/command_parser.c::
186 Contains a hand-written parser to parse commands (commands are what
187 you bind on keys and what you can send to i3 using the IPC interface, like
188 'move left' or 'workspace 4').
189
190 src/con.c::
191 Contains all functions which deal with containers directly (creating
192 containers, searching containers, getting specific properties from containers,
193 …).
194
195 src/config.c::
196 Contains all functions handling the configuration file (calling the parser
197 src/config_parser.c) with the correct path, switching key bindings mode).
198
199 src/ewmh.c::
200 Functions to get/set certain EWMH properties easily.
201
202 src/floating.c::
203 Contains functions for floating mode (mostly resizing/dragging).
204
205 src/handlers.c::
206 Contains all handlers for all kinds of X events (new window title, new hints,
207 unmapping, key presses, button presses, …).
208
209 src/ipc.c::
210 Contains code for the IPC interface.
211
212 src/load_layout.c::
213 Contains code for loading layouts from JSON files.
214
215 src/log.c::
216 Contains the logging functions.
217
218 src/main.c::
219 Initializes the window manager.
220
221 src/manage.c::
222 Looks at existing or new windows and decides whether to manage them. If so, it
223 reparents the window and inserts it into our data structures.
224
225 src/match.c::
226 A "match" is a data structure which acts like a mask or expression to match
227 certain windows or not. For example, when using commands, you can specify a
228 command like this: [title="*Firefox*"] kill. The title member of the match
229 data structure will then be filled and i3 will check each window using
230 match_matches_window() to find the windows affected by this command.
231
232 src/move.c::
233 Contains code to move a container in a specific direction.
234
235 src/output.c::
236 Functions to handle CT_OUTPUT cons.
237
238 src/randr.c::
239 The RandR API is used to get (and re-query) the configured outputs (monitors,
240 …).
241
242 src/render.c::
243 Renders the tree data structure by assigning coordinates to every node. These
244 values will later be pushed to X11 in +src/x.c+.
245
246 src/resize.c::
247 Contains the functions to resize containers.
248
249 src/restore_layout.c::
250 Everything for restored containers that is not pure state parsing (which can be
251 found in load_layout.c).
252
253 src/sighandler.c::
254 Handles +SIGSEGV+, +SIGABRT+ and +SIGFPE+ by showing a dialog that i3 crashed.
255 You can chose to let it dump core, to restart it in-place or to restart it
256 in-place but forget about the layout.
257
258 src/tree.c::
259 Contains functions which open or close containers in the tree, change focus or
260 cleanup ("flatten") the tree. See also +src/move.c+ for another similar
261 function, which was moved into its own file because it is so long.
262
263 src/util.c::
264 Contains useful functions which are not really dependent on anything.
265
266 src/window.c::
267 Handlers to update X11 window properties like +WM_CLASS+, +_NET_WM_NAME+,
268 +CLIENT_LEADER+, etc.
269
270 src/workspace.c::
271 Contains all functions related to workspaces (displaying, hiding, renaming…)
272
273 src/x.c::
274 Transfers our in-memory tree (see +src/render.c+) to X11.
275
276 src/xcb.c::
277 Contains wrappers to use xcb more easily.
278
279 src/xcursor.c::
280 XCursor functions (for cursor themes).
281
282 src/xinerama.c::
283 Legacy support for Xinerama. See +src/randr.c+ for the preferred API.
284
285 == Data structures
286
287
288 See include/data.h for documented data structures. The most important ones are
289 explained right here.
290
291 /////////////////////////////////////////////////////////////////////////////////
292 // TODO: update image
293
294 image:bigpicture.png[The Big Picture]
295
296 /////////////////////////////////////////////////////////////////////////////////
297
298 So, the hierarchy is:
299
300 . *X11 root window*, the root container
301 . *Output container* (LVDS1 in this example)
302 . *Content container* (there are also containers for dock windows)
303 . *Workspaces* (Workspace 1 in this example, with horizontal orientation)
304 . *Split container* (vertically split)
305 . *X11 window containers*
306
307 The data type is +Con+, in all cases.
308
309 === X11 root window
310
311 The X11 root window is a single window per X11 display (a display is identified
312 by +:0+ or +:1+ etc.). The root window is what you draw your background image
313 on. It spans all the available outputs, e.g. +VGA1+ is a specific part of the
314 root window and +LVDS1+ is a specific part of the root window.
315
316 === Output container
317
318 Every active output obtained through RandR is represented by one output
319 container. Outputs are considered active when a mode is configured (meaning
320 something is actually displayed on the output) and the output is not a clone.
321
322 For example, if your notebook has a screen resolution of 1280x800 px and you
323 connect a video projector with a resolution of 1024x768 px, set it up in clone
324 mode (+xrandr \--output VGA1 \--mode 1024x768 \--same-as LVDS1+), i3 will
325 reduce the resolution to the lowest common resolution and disable one of the
326 cloned outputs afterwards.
327
328 However, if you configure it using +xrandr \--output VGA1 \--mode 1024x768
329 \--right-of LVDS1+, i3 will set both outputs active. For each output, a new
330 workspace will be assigned. New workspaces are created on the output you are
331 currently on.
332
333 === Content container
334
335 Each output has multiple children. Two of them are dock containers which hold
336 dock clients. The other one is the content container, which holds the actual
337 content (workspaces) of this output.
338
339 === Workspace
340
341 A workspace is identified by its name. Basically, you could think of
342 workspaces as different desks in your office, if you like the desktop
343 metaphor. They just contain different sets of windows and are completely
344 separate of each other. Other window managers also call this ``Virtual
345 desktops''.
346
347 === Split container
348
349 A split container is a container which holds an arbitrary amount of split
350 containers or X11 window containers. It has an orientation (horizontal or
351 vertical) and a layout.
352
353 Split containers (and X11 window containers, which are a subtype of split
354 containers) can have different border styles.
355
356 === X11 window container
357
358 An X11 window container holds exactly one X11 window. These are the leaf nodes
359 of the layout tree, they cannot have any children.
360
361 == List/queue macros
362
363 i3 makes heavy use of the list macros defined in BSD operating systems. To
364 ensure that the operating system on which i3 is compiled has all the expected
365 features, i3 comes with `include/queue.h`. On BSD systems, you can use man
366 `queue(3)`. On Linux, you have to use google (or read the source).
367
368 The lists used are +SLIST+ (single linked lists), +CIRCLEQ+ (circular
369 queues) and +TAILQ+ (tail queues). Usually, only forward traversal is necessary,
370 so an `SLIST` works fine. If inserting elements at arbitrary positions or at
371 the end of a list is necessary, a +TAILQ+ is used instead. However, for the
372 windows inside a container, a +CIRCLEQ+ is necessary to go from the currently
373 selected window to the window above/below.
374
375 == Naming conventions
376
377 There is a row of standard variables used in many events. The following names
378 should be chosen for those:
379
380 * ``conn'' is the xcb_connection_t
381 * ``event'' is the event of the particular type
382 * ``con'' names a container
383 * ``current'' is a loop variable when using +TAILQ_FOREACH+ etc.
384
385 == Startup (src/mainx.c, main())
386
387 * Establish the xcb connection
388 * Check for XKB extension on the separate X connection, load Xcursor
389 * Check for RandR screens (with a fall-back to Xinerama)
390 * Grab the keycodes for which bindings exist
391 * Manage all existing windows
392 * Enter the event loop
393
394 == Keybindings
395
396 === Grabbing the bindings
397
398 Grabbing the bindings is quite straight-forward. You pass X your combination of
399 modifiers and the keycode you want to grab and whether you want to grab them
400 actively or passively. Most bindings (everything except for bindings using
401 Mode_switch) are grabbed passively, that is, just the window manager gets the
402 event and cannot replay it.
403
404 We need to grab bindings that use Mode_switch actively because of a bug in X.
405 When the window manager receives the keypress/keyrelease event for an actively
406 grabbed keycode, it has to decide what to do with this event: It can either
407 replay it so that other applications get it or it can prevent other
408 applications from receiving it.
409
410 So, why do we need to grab keycodes actively? Because X does not set the
411 state-property of keypress/keyrelease events properly. The Mode_switch bit is
412 not set and we need to get it using XkbGetState. This means we cannot pass X
413 our combination of modifiers containing Mode_switch when grabbing the key and
414 therefore need to grab the keycode itself without any modifiers. This means,
415 if you bind Mode_switch + keycode 38 ("a"), i3 will grab keycode 38 ("a") and
416 check on each press of "a" if the Mode_switch bit is set using XKB. If yes, it
417 will handle the event, if not, it will replay the event.
418
419 === Handling a keypress
420
421 As mentioned in "Grabbing the bindings", upon a keypress event, i3 first gets
422 the correct state.
423
424 Then, it looks through all bindings and gets the one which matches the received
425 event.
426
427 The bound command is parsed by the cmdparse lexer/parser, see +parse_cmd+ in
428 +src/cmdparse.y+.
429
430 == Manage windows (src/main.c, manage_window() and reparent_window())
431
432 `manage_window()` does some checks to decide whether the window should be
433 managed at all:
434
435 * Windows have to be mapped, that is, visible on screen
436 * The override_redirect must not be set. Windows with override_redirect shall
437 not be managed by a window manager
438
439 Afterwards, i3 gets the initial geometry and reparents the window (see
440 `reparent_window()`) if it wasn’t already managed.
441
442 Reparenting means that for each window which is reparented, a new window,
443 slightly larger than the original one, is created. The original window is then
444 reparented to the bigger one (called "frame").
445
446 After reparenting, the window type (`_NET_WM_WINDOW_TYPE`) is checked to see
447 whether this window is a dock (`_NET_WM_WINDOW_TYPE_DOCK`), like dzen2 for
448 example. Docks are handled differently, they don’t have decorations and are not
449 assigned to a specific container. Instead, they are positioned at the bottom
450 or top of the screen (in the appropriate dock area containers). To get the
451 height which needs to be reserved for the window, the `_NET_WM_STRUT_PARTIAL`
452 property is used.
453
454 Furthermore, the list of assignments (to other workspaces, which may be on
455 other screens) is checked. If the window matches one of the user’s criteria,
456 it may either be put in floating mode or moved to a different workspace. If the
457 target workspace is not visible, the window will not be mapped.
458
459 == What happens when an application is started?
460
461 i3 does not care about applications. All it notices is when new windows are
462 mapped (see `src/handlers.c`, `handle_map_request()`). The window is then
463 reparented (see section "Manage windows").
464
465 After reparenting the window, `render_tree()` is called which renders the
466 internal layout table. The new window has been placed in the currently focused
467 container and therefore the new window and the old windows (if any) need to be
468 moved/resized so that the currently active layout (default/stacking/tabbed mode)
469 is rendered correctly. To move/resize windows, a window is ``configured'' in
470 X11-speak.
471
472 Some applications, such as MPlayer obviously assume the window manager is
473 stupid and try to configure their windows by themselves. This generates an
474 event called configurerequest. i3 handles these events and tells the window the
475 size it had before the configurerequest (with the exception of not yet mapped
476 windows, which get configured like they want to, and floating windows, which
477 can reconfigure themselves).
478
479 == _NET_WM_STATE
480
481 Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
482 are handled.
483
484 The former calls ``toggle_fullscreen()'' for the specific client which just
485 configures the client to use the whole screen on which it currently is.
486 Also, it is set as fullscreen_client for the i3Screen.
487
488 The latter is used to set, read and display urgency hints.
489
490 == WM_NAME
491
492 When the WM_NAME property of a window changes, its decoration (containing the
493 title) is re-rendered. Note that WM_NAME is in COMPOUND_TEXT encoding which is
494 totally uncommon and cumbersome. Therefore, the _NET_WM_NAME atom will be used
495 if present.
496
497 == _NET_WM_NAME
498
499 Like WM_NAME, this atom contains the title of a window. However, _NET_WM_NAME
500 is encoded in UTF-8. i3 will recode it to UCS-2 in order to be able to pass it
501 to X. Using an appropriate font (ISO-10646), you can see most special
502 characters (every special character contained in your font).
503
504 == Size hints
505
506 Size hints specify the minimum/maximum size for a given window as well as its
507 aspect ratio. This is important for clients like mplayer, who only set the
508 aspect ratio and resize their window to be as small as possible (but only with
509 some video outputs, for example in Xv, while when using x11, mplayer does the
510 necessary centering for itself).
511
512 So, when an aspect ratio was specified, i3 adjusts the height of the window
513 until the size maintains the correct aspect ratio. For the code to do this, see
514 src/layout.c, function resize_client().
515
516 == Rendering (src/layout.c, render_layout() and render_container())
517
518 Rendering in i3 version 4 is the step which assigns the correct sizes for
519 borders, decoration windows, child windows and the stacking order of all
520 windows. In a separate step (+x_push_changes()+), these changes are pushed to
521 X11.
522
523 Keep in mind that all these properties (+rect+, +window_rect+ and +deco_rect+)
524 are temporary, meaning they will be overwritten by calling +render_con+.
525 Persistent position/size information is kept in +geometry+.
526
527 The entry point for every rendering operation (except for the case of moving
528 floating windows around) currently is +tree_render()+ which will re-render
529 everything that’s necessary (for every output, only the currently displayed
530 workspace is rendered). This behavior is expected to change in the future,
531 since for a lot of updates, re-rendering everything is not actually necessary.
532 Focus was on getting it working correct, not getting it work very fast.
533
534 What +tree_render()+ actually does is calling +render_con()+ on the root
535 container and then pushing the changes to X11. The following sections talk
536 about the different rendering steps, in the order of "top of the tree" (root
537 container) to the bottom.
538
539 === Rendering the root container
540
541 The i3 root container (`con->type == CT_ROOT`) represents the X11 root window.
542 It contains one child container for every output (like LVDS1, VGA1, …), which
543 is available on your computer.
544
545 Rendering the root will first render all tiling windows and then all floating
546 windows. This is necessary because a floating window can be positioned in such
547 a way that it is visible on two different outputs. Therefore, by first
548 rendering all the tiling windows (of all outputs), we make sure that floating
549 windows can never be obscured by tiling windows.
550
551 Essentially, though, this code path will just call +render_con()+ for every
552 output and +x_raise_con(); render_con()+ for every floating window.
553
554 In the special case of having a "global fullscreen" window (fullscreen mode
555 spanning all outputs), a shortcut is taken and +x_raise_con(); render_con()+ is
556 only called for the global fullscreen window.
557
558 === Rendering an output
559
560 Output containers (`con->layout == L_OUTPUT`) represent a hardware output like
561 LVDS1, VGA1, etc. An output container has three children (at the moment): One
562 content container (having workspaces as children) and the top/bottom dock area
563 containers.
564
565 The rendering happens in the function +render_l_output()+ in the following
566 steps:
567
568 1. Find the content container (`con->type == CT_CON`)
569 2. Get the currently visible workspace (+con_get_fullscreen_con(content,
570 CF_OUTPUT)+).
571 3. If there is a fullscreened window on that workspace, directly render it and
572 return, thus ignoring the dock areas.
573 4. Sum up the space used by all the dock windows (they have a variable height
574 only).
575 5. Set the workspace rects (x/y/width/height) based on the position of the
576 output (stored in `con->rect`) and the usable space
577 (`con->rect.{width,height}` without the space used for dock windows).
578 6. Recursively raise and render the output’s child containers (meaning dock
579 area containers and the content container).
580
581 === Rendering a workspace or split container
582
583 From here on, there really is no difference anymore. All containers are of
584 `con->type == CT_CON` (whether workspace or split container) and some of them
585 have a `con->window`, meaning they represent an actual window instead of a
586 split container.
587
588 ==== Default layout
589
590 In default layout, containers are placed horizontally or vertically next to
591 each other (depending on the `con->orientation`). If a child is a leaf node (as
592 opposed to a split container) and has border style "normal", appropriate space
593 will be reserved for its window decoration.
594
595 ==== Stacked layout
596
597 In stacked layout, only the focused window is actually shown (this is achieved
598 by calling +x_raise_con()+ in reverse focus order at the end of +render_con()+).
599
600 The available space for the focused window is the size of the container minus
601 the height of the window decoration for all windows inside this stacked
602 container.
603
604 If border style is "1pixel" or "none", no window decoration height will be
605 reserved (or displayed later on), unless there is more than one window inside
606 the stacked container.
607
608 ==== Tabbed layout
609
610 Tabbed layout works precisely like stacked layout, but the window decoration
611 position/size is different: They are placed next to each other on a single line
612 (fixed height).
613
614 ==== Dock area layout
615
616 This is a special case. Users cannot choose the dock area layout, but it will be
617 set for the dock area containers. In the dockarea layout (at the moment!),
618 windows will be placed above each other.
619
620 === Rendering a window
621
622 A window’s size and position will be determined in the following way:
623
624 1. Subtract the border if border style is not "none" (but "normal" or "1pixel").
625 2. Subtract the X11 border, if the window has an X11 border > 0.
626 3. Obey the aspect ratio of the window (think MPlayer).
627 4. Obey the height- and width-increments of the window (think terminal emulator
628 which can only be resized in one-line or one-character steps).
629
630 == Pushing updates to X11 / Drawing
631
632 A big problem with i3 before version 4 was that we just sent requests to X11
633 anywhere in the source code. This was bad because nobody could understand the
634 entirety of our interaction with X11, it lead to subtle bugs and a lot of edge
635 cases which we had to consider all over again.
636
637 Therefore, since version 4, we have a single file, +src/x.c+, which is
638 responsible for repeatedly transferring parts of our tree datastructure to X11.
639
640 +src/x.c+ consists of multiple parts:
641
642 1. The state pushing: +x_push_changes()+, which calls +x_push_node()+.
643 2. State modification functions: +x_con_init+, +x_reinit+,
644 +x_reparent_child+, +x_move_win+, +x_con_kill+, +x_raise_con+, +x_set_name+
645 and +x_set_warp_to+.
646 3. Expose event handling (drawing decorations): +x_deco_recurse()+ and
647 +x_draw_decoration()+.
648
649 === Pushing state to X11
650
651 In general, the function +x_push_changes+ should be called to push state
652 changes. Only when the scope of the state change is clearly defined (for
653 example only the title of a window) and its impact is known beforehand, one can
654 optimize this and call +x_push_node+ on the appropriate con directly.
655
656 +x_push_changes+ works in the following steps:
657
658 1. Clear the eventmask for all mapped windows. This leads to not getting
659 useless ConfigureNotify or EnterNotify events which are caused by our
660 requests. In general, we only want to handle user input.
661 2. Stack windows above each other, in reverse stack order (starting with the
662 most obscured/bottom window). This is relevant for floating windows which
663 can overlap each other, but also for tiling windows in stacked or tabbed
664 containers. We also update the +_NET_CLIENT_LIST_STACKING+ hint which is
665 necessary for tab drag and drop in Chromium.
666 3. +x_push_node+ will be called for the root container, recursively calling
667 itself for the container’s children. This function actually pushes the
668 state, see the next paragraph.
669 4. If the pointer needs to be warped to a different position (for example when
670 changing focus to a different output), it will be warped now.
671 5. The eventmask is restored for all mapped windows.
672 6. Window decorations will be rendered by calling +x_deco_recurse+ on the root
673 container, which then recursively calls itself for the children.
674 7. If the input focus needs to be changed (because the user focused a different
675 window), it will be updated now.
676 8. +x_push_node_unmaps+ will be called for the root container. This function
677 only pushes UnmapWindow requests. Separating the state pushing is necessary
678 to handle fullscreen windows (and workspace switches) in a smooth fashion:
679 The newly visible windows should be visible before the old windows are
680 unmapped.
681
682 +x_push_node+ works in the following steps:
683
684 1. Update the window’s +WM_NAME+, if changed (the +WM_NAME+ is set on i3
685 containers mainly for debugging purposes).
686 2. Reparents a child window into the i3 container if the container was created
687 for a specific managed window.
688 3. If the size/position of the i3 container changed (due to opening a new
689 window or switching layouts for example), the window will be reconfigured.
690 Also, the pixmap which is used to draw the window decoration/border on is
691 reconfigured (pixmaps are size-dependent).
692 4. Size/position for the child window is adjusted.
693 5. The i3 container is mapped if it should be visible and was not yet mapped.
694 When mapping, +WM_STATE+ is set to +WM_STATE_NORMAL+. Also, the eventmask of
695 the child window is updated and the i3 container’s contents are copied from
696 the pixmap.
697 6. +x_push_node+ is called recursively for all children of the current
698 container.
699
700 +x_push_node_unmaps+ handles the remaining case of an i3 container being
701 unmapped if it should not be visible anymore. +WM_STATE+ will be set to
702 +WM_STATE_WITHDRAWN+.
703
704
705 === Drawing window decorations/borders/backgrounds
706
707 +x_draw_decoration+ draws window decorations. It is run for every leaf
708 container (representing an actual X11 window) and for every non-leaf container
709 which is in a stacked/tabbed container (because stacked/tabbed containers
710 display a window decoration for split containers, which consists of a representation
711 of the child container's names.
712
713 Then, parameters are collected to be able to determine whether this decoration
714 drawing is actually necessary or was already done. This saves a substantial
715 number of redraws (depending on your workload, but far over 50%).
716
717 Assuming that we need to draw this decoration, we start by filling the empty
718 space around the child window (think of MPlayer with a specific aspect ratio)
719 in the user-configured client background color.
720
721 Afterwards, we draw the appropriate border (in case of border styles "normal"
722 and "1pixel") and the top bar (in case of border style "normal").
723
724 The last step is drawing the window title on the top bar.
725
726
727 /////////////////////////////////////////////////////////////////////////////////
728
729 == Resizing containers
730
731 By clicking and dragging the border of a container, you can resize the whole
732 column (respectively row) which this container is in. This is necessary to keep
733 the table layout working and consistent.
734
735 The resizing works similarly to the resizing of floating windows or movement of
736 floating windows:
737
738 * A new, invisible window with the size of the root window is created
739 (+grabwin+)
740 * Another window, 2px width and as high as your screen (or vice versa for
741 horizontal resizing) is created. Its background color is the border color and
742 it is only there to inform the user how big the container will be (it
743 creates the impression of dragging the border out of the container).
744 * The +drag_pointer+ function of +src/floating.c+ is called to grab the pointer
745 and enter its own event loop which will pass all events (expose events) but
746 motion notify events. This function then calls the specified callback
747 (+resize_callback+) which does some boundary checking and moves the helper
748 window. As soon as the mouse button is released, this loop will be
749 terminated.
750 * The new width_factor for each involved column (respectively row) will be
751 calculated.
752
753 /////////////////////////////////////////////////////////////////////////////////
754
755 == User commands (parser-specs/commands.spec)
756
757 In the configuration file and when using i3 interactively (with +i3-msg+, for
758 example), you use commands to make i3 do things, like focus a different window,
759 set a window to fullscreen, and so on. An example command is +floating enable+,
760 which enables floating mode for the currently focused window. See the
761 appropriate section in the link:userguide.html[User’s Guide] for a reference of
762 all commands.
763
764 In earlier versions of i3, interpreting these commands was done using lex and
765 yacc, but experience has shown that lex and yacc are not well suited for our
766 command language. Therefore, starting from version 4.2, we use a custom parser
767 for user commands and the configuration file.
768 The input specification for this parser can be found in the file
769 +parser-specs/*.spec+. Should you happen to use Vim as an editor, use
770 :source parser-specs/highlighting.vim to get syntax highlighting for this file
771 (highlighting files for other editors are welcome).
772
773 .Excerpt from commands.spec
774 -----------------------------------------------------------------------
775 state INITIAL:
776 '[' -> call cmd_criteria_init(); CRITERIA
777 'move' -> MOVE
778 'exec' -> EXEC
779 'workspace' -> WORKSPACE
780 'exit' -> call cmd_exit()
781 'restart' -> call cmd_restart()
782 'reload' -> call cmd_reload()
783 -----------------------------------------------------------------------
784
785 The input specification is written in an extremely simple format. The
786 specification is then converted into C code by the Perl script
787 generate-commands-parser.pl (the output file names begin with GENERATED and the
788 files are stored in the +include+ directory). The parser implementation
789 +src/commands_parser.c+ includes the generated C code at compile-time.
790
791 The above excerpt from commands.spec illustrates nearly all features of our
792 specification format: You describe different states and what can happen within
793 each state. State names are all-caps; the state in the above excerpt is called
794 INITIAL. A list of tokens and their actions (separated by an ASCII arrow)
795 follows. In the excerpt, all tokens are literals, that is, simple text strings
796 which will be compared with the input. An action is either the name of a state
797 in which the parser will transition into, or the keyword 'call', followed by
798 the name of a function (and optionally a state).
799
800 === Example: The WORKSPACE state
801
802 Let’s have a look at the WORKSPACE state, which is a good example of all
803 features. This is its definition:
804
805 .WORKSPACE state (commands.spec)
806 ----------------------------------------------------------------
807 # workspace next|prev|next_on_output|prev_on_output
808 # workspace back_and_forth
809 # workspace <name>
810 # workspace number <number>
811 state WORKSPACE:
812 direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
813 -> call cmd_workspace($direction)
814 'back_and_forth'
815 -> call cmd_workspace_back_and_forth()
816 'number'
817 -> WORKSPACE_NUMBER
818 workspace = string
819 -> call cmd_workspace_name($workspace)
820 ----------------------------------------------------------------
821
822 As you can see from the commands, there are multiple different valid variants
823 of the workspace command:
824
825 workspace <direction>::
826 The word 'workspace' can be followed by any of the tokens 'next',
827 'prev', 'next_on_output' or 'prev_on_output'. This command will
828 switch to the next or previous workspace (optionally on the same
829 output). +
830 There is one function called +cmd_workspace+, which is defined
831 in +src/commands.c+. It will handle this kind of command. To know which
832 direction was specified, the direction token is stored on the stack
833 with the name "direction", which is what the "direction = " means in
834 the beginning. +
835
836 NOTE: Note that you can specify multiple literals in the same line. This has
837 exactly the same effect as if you specified `direction =
838 'next_on_output' -> call cmd_workspace($direction)` and so forth. +
839
840 NOTE: Also note that the order of literals is important here: If 'next' were
841 ordered before 'next_on_output', then 'next_on_output' would never
842 match.
843
844 workspace back_and_forth::
845 This is a very simple case: When the literal 'back_and_forth' is found
846 in the input, the function +cmd_workspace_back_and_forth+ will be
847 called without parameters and the parser will return to the INITIAL
848 state (since no other state was specified).
849 workspace <name>::
850 In this case, the workspace command is followed by an arbitrary string,
851 possibly in quotes, for example "workspace 3" or "workspace bleh". +
852 This is the first time that the token is actually not a literal (not in
853 single quotes), but just called string. Other possible tokens are word
854 (the same as string, but stops matching at a whitespace) and end
855 (matches the end of the input).
856 workspace number <number>::
857 The workspace command has to be followed by the keyword +number+. It
858 then transitions into the state +WORKSPACE_NUMBER+, where the actual
859 parameter will be read.
860
861 === Introducing a new command
862
863 The following steps have to be taken in order to properly introduce a new
864 command (or possibly extend an existing command):
865
866 1. Define a function beginning with +cmd_+ in the file +src/commands.c+. Copy
867 the prototype of an existing function.
868 2. After adding a comment on what the function does, copy the comment and
869 function definition to +include/commands.h+. Make the comment in the header
870 file use double asterisks to make doxygen pick it up.
871 3. Write a test case (or extend an existing test case) for your feature, see
872 link:testsuite.html[i3 testsuite]. For now, it is sufficient to simply call
873 your command in all the various possible ways.
874 4. Extend the parser specification in +parser-specs/commands.spec+. Run the
875 testsuite and see if your new function gets called with the appropriate
876 arguments for the appropriate input.
877 5. Actually implement the feature.
878 6. Document the feature in the link:userguide.html[User’s Guide].
879
880 == Moving containers
881
882 The movement code is pretty delicate. You need to consider all cases before
883 making any changes or before being able to fully understand how it works.
884
885 === Case 1: Moving inside the same container
886
887 The reference layout for this case is a single workspace in horizontal
888 orientation with two containers on it. Focus is on the left container (1).
889
890
891 [width="15%",cols="^,^"]
892 |========
893 | 1 | 2
894 |========
895
896 When moving the left window to the right (command +move right+), tree_move will
897 look for a container with horizontal orientation and finds the parent of the
898 left container, that is, the workspace. Afterwards, it runs the code branch
899 commented with "the easy case": it calls TAILQ_NEXT to get the container right
900 of the current one and swaps both containers.
901
902 === Case 2: Move a container into a split container
903
904 The reference layout for this case is a horizontal workspace with two
905 containers. The right container is a v-split with two containers. Focus is on
906 the left container (1).
907
908 [width="15%",cols="^,^"]
909 |========
910 1.2+^.^| 1 | 2
911 | 3
912 |========
913
914 When moving to the right (command +move right+), i3 will work like in case 1
915 ("the easy case"). However, as the right container is not a leaf container, but
916 a v-split, the left container (1) will be inserted at the right position (below
917 2, assuming that 2 is focused inside the v-split) by calling +insert_con_into+.
918
919 +insert_con_into+ detaches the container from its parent and inserts it
920 before/after the given target container. Afterwards, the on_remove_child
921 callback is called on the old parent container which will then be closed, if
922 empty.
923
924 Afterwards, +con_focus+ will be called to fix the focus stack and the tree will
925 be flattened.
926
927 === Case 3: Moving to non-existent top/bottom
928
929 Like in case 1, the reference layout for this case is a single workspace in
930 horizontal orientation with two containers on it. Focus is on the left
931 container:
932
933 [width="15%",cols="^,^"]
934 |========
935 | 1 | 2
936 |========
937
938 This time however, the command is +move up+ or +move down+. tree_move will look
939 for a container with vertical orientation. As it will not find any,
940 +same_orientation+ is NULL and therefore i3 will perform a forced orientation
941 change on the workspace by creating a new h-split container, moving the
942 workspace contents into it and then changing the workspace orientation to
943 vertical. Now it will again search for parent containers with vertical
944 orientation and it will find the workspace.
945
946 This time, the easy case code path will not be run as we are not moving inside
947 the same container. Instead, +insert_con_into+ will be called with the focused
948 container and the container above/below the current one (on the level of
949 +same_orientation+).
950
951 Now, +con_focus+ will be called to fix the focus stack and the tree will be
952 flattened.
953
954 === Case 4: Moving to existent top/bottom
955
956 The reference layout for this case is a vertical workspace with two containers.
957 The bottom one is a h-split containing two containers (1 and 2). Focus is on
958 the bottom left container (1).
959
960 [width="15%",cols="^,^"]
961 |========
962 2+| 3
963 | 1 | 2
964 |========
965
966 This case is very much like case 3, only this time the forced workspace
967 orientation change does not need to be performed because the workspace already
968 is in vertical orientation.
969
970 === Case 5: Moving in one-child h-split
971
972 The reference layout for this case is a horizontal workspace with two
973 containers having a v-split on the left side with a one-child h-split on the
974 bottom. Focus is on the bottom left container (2(h)):
975
976 [width="15%",cols="^,^"]
977 |========
978 | 1 1.2+^.^| 3
979 | 2(h)
980 |========
981
982 In this case, +same_orientation+ will be set to the h-split container around
983 the focused container. However, when trying the easy case, the next/previous
984 container +swap+ will be NULL. Therefore, i3 will search again for a
985 +same_orientation+ container, this time starting from the parent of the h-split
986 container.
987
988 After determining a new +same_orientation+ container (if it is NULL, the
989 orientation will be force-changed), this case is equivalent to case 2 or case
990 4.
991
992
993 === Case 6: Floating containers
994
995 The reference layout for this case is a horizontal workspace with two
996 containers plus one floating h-split container. Focus is on the floating
997 container.
998
999 TODO: nice illustration. table not possible?
1000
1001 When moving up/down, the container needs to leave the floating container and it
1002 needs to be placed on the workspace (at workspace level). This is accomplished
1003 by calling the function +attach_to_workspace+.
1004
1005 == Click handling
1006
1007 Without much ado, here is the list of cases which need to be considered:
1008
1009 * click to focus (tiling + floating) and raise (floating)
1010 * click to focus/raise when in stacked/tabbed mode
1011 * floating_modifier + left mouse button to drag a floating con
1012 * floating_modifier + right mouse button to resize a floating con
1013 * click on decoration in a floating con to either initiate a resize (if there
1014 is more than one child in the floating con) or to drag the
1015 floating con (if it’s the one at the top).
1016 * click on border in a floating con to resize the floating con
1017 * floating_modifier + right mouse button to resize a tiling con
1018 * click on border/decoration to resize a tiling con
1019
1020 == Gotchas
1021
1022 * Forgetting to call `xcb_flush(conn);` after sending a request. This usually
1023 leads to code which looks like it works fine but which does not work under
1024 certain conditions.
1025
1026 * Forgetting to call `floating_fix_coordinates(con, old_rect, new_rect)` after
1027 moving workspaces across outputs. Coordinates for floating containers are
1028 not relative to workspace boundaries, so you must correct their coordinates
1029 or those containers will show up in the wrong workspace or not at all.
1030
1031 == Thought experiments
1032
1033 In this section, we collect thought experiments, so that we don’t forget our
1034 thoughts about specific topics. They are not necessary to get into hacking i3,
1035 but if you are interested in one of the topics they cover, you should read them
1036 before asking us why things are the way they are or why we don’t implement
1037 things.
1038
1039 === Using cgroups per workspace
1040
1041 cgroups (control groups) are a linux-only feature which provides the ability to
1042 group multiple processes. For each group, you can individually set resource
1043 limits, like allowed memory usage. Furthermore, and more importantly for our
1044 purposes, they serve as a namespace, a label which you can attach to processes
1045 and their children.
1046
1047 One interesting use for cgroups is having one cgroup per workspace (or
1048 container, doesn’t really matter). That way, you could set different priorities
1049 and have a workspace for important stuff (say, writing a LaTeX document or
1050 programming) and a workspace for unimportant background stuff (say,
1051 JDownloader). Both tasks can obviously consume a lot of I/O resources, but in
1052 this example it doesn’t really matter if JDownloader unpacks the download a
1053 minute earlier or not. However, your compiler should work as fast as possible.
1054 Having one cgroup per workspace, you would assign more resources to the
1055 programming workspace.
1056
1057 Another interesting feature is that an inherent problem of the workspace
1058 concept could be solved by using cgroups: When starting an application on
1059 workspace 1, then switching to workspace 2, you will get the application’s
1060 window(s) on workspace 2 instead of the one you started it on. This is because
1061 the window manager does not have any mapping between the process it starts (or
1062 gets started in any way) and the window(s) which appear.
1063
1064 Imagine for example using dmenu: The user starts dmenu by pressing Mod+d, dmenu
1065 gets started with PID 3390. The user then decides to launch Firefox, which
1066 takes a long time. So they enter firefox into dmenu and press enter. Firefox
1067 gets started with PID 4001. When it finally finishes loading, it creates an X11
1068 window and uses MapWindow to make it visible. This is the first time i3
1069 actually gets in touch with Firefox. It decides to map the window, but it has
1070 no way of knowing that this window (even though it has the _NET_WM_PID property
1071 set to 4001) belongs to the dmenu the user started before.
1072
1073 How do cgroups help with this? Well, when pressing Mod+d to launch dmenu, i3
1074 would create a new cgroup, let’s call it i3-3390-1. It launches dmenu in that
1075 cgroup, which gets PID 3390. As before, the user enters firefox and Firefox
1076 gets launched with PID 4001. This time, though, the Firefox process with PID
1077 4001 is *also* member of the cgroup i3-3390-1 (because fork()ing in a cgroup
1078 retains the cgroup property). Therefore, when mapping the window, i3 can look
1079 up in which cgroup the process is and can establish a mapping between the
1080 workspace and the window.
1081
1082 There are multiple problems with this approach:
1083
1084 . Every application has to properly set +_NET_WM_PID+. This is acceptable and
1085 patches can be written for the few applications which don’t set the hint yet.
1086 . It does only work on Linux, since cgroups are a Linux-only feature. Again,
1087 this is acceptable.
1088 . The main problem is that some applications create X11 windows completely
1089 independent of UNIX processes. An example for this is Chromium (or
1090 gnome-terminal), which, when being started a second time, communicates with
1091 the first process and lets the first process open a new window. Therefore, if
1092 you have a Chromium window on workspace 2 and you are currently working on
1093 workspace 3, starting +chromium+ does not lead to the desired result (the
1094 window will open on workspace 2).
1095
1096 Therefore, my conclusion is that the only proper way of fixing the "window gets
1097 opened on the wrong workspace" problem is in the application itself. Most
1098 modern applications support freedesktop startup-notifications which can be
1099 used for this.
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>Hacking i3: How To</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>Hacking i3: How To</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">February 2013</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>This document is intended to be the first thing you read before looking and/or
749 touching i3’s source code. It should contain all important information to help
750 you understand why things are like they are. If it does not mention something
751 you find necessary, please do not hesitate to contact me.</p></div>
752 </div>
753 </div>
754 <div class="sect1">
755 <h2 id="_building_i3">1. Building i3</h2>
756 <div class="sectionbody">
757 <div class="paragraph"><p>You can build i3 like you build any other software package which uses autotools.
758 Here’s a memory refresher:</p></div>
759 <div class="literalblock">
760 <div class="content">
761 <pre><code>$ autoreconf -fi
762 $ mkdir -p build &amp;&amp; cd build
763 $ ../configure
764 $ make -j8</code></pre>
765 </div></div>
766 <div class="paragraph"><p>(The autoreconf -fi step is unnecessary if you are building from a release tarball,
767 but shouldn’t hurt either.)</p></div>
768 <div class="sect2">
769 <h3 id="_build_system_features">1.1. Build system features</h3>
770 <div class="ulist"><ul>
771 <li>
772 <p>
773 We use the AX_ENABLE_BUILDDIR macro to enforce builds happening in a separate
774 directory. This is a prerequisite for the AX_EXTEND_SRCDIR macro and building
775 in a separate directory is common practice anyway. In case this causes any
776 trouble when packaging i3 for your distribution, please open an issue.
777 </p>
778 </li>
779 <li>
780 <p>
781 “make check” runs the i3 testsuite. See docs/testsuite for details.
782 </p>
783 </li>
784 <li>
785 <p>
786 “make distcheck” (runs testsuite on “make dist” result, tiny bit quicker
787 feedback cycle than waiting for the travis build to catch the issue).
788 </p>
789 </li>
790 <li>
791 <p>
792 “make uninstall” (occasionally requested by users who compile from source)
793 </p>
794 </li>
795 <li>
796 <p>
797 “make” will build manpages/docs by default if the tools are installed.
798 Conversely, manpages/docs are not tried to be built for users who don’t want
799 to install all these dependencies to get started hacking on i3.
800 </p>
801 </li>
802 <li>
803 <p>
804 non-release builds will enable address sanitizer by default. Use the
805 --disable-sanitizers configure option to turn off all sanitizers, and see
806 --help for available sanitizers.
807 </p>
808 </li>
809 <li>
810 <p>
811 Support for pre-compiled headers (PCH) has been dropped for now in the
812 interest of simplicity. If you need support for PCH, please open an issue.
813 </p>
814 </li>
815 <li>
816 <p>
817 Coverage reports are now generated using “make check-code-coverage”, which
818 requires specifying --enable-code-coverage when calling configure.
819 </p>
820 </li>
821 </ul></div>
822 </div>
823 </div>
824 </div>
825 <div class="sect1">
826 <h2 id="_using_git_sending_patches">2. Using git / sending patches</h2>
827 <div class="sectionbody">
828 <div class="paragraph"><p>For a short introduction into using git, see
829 <a href="https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy">https://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy</a>
830 or, for more documentation, see <a href="https://git-scm.com/documentation">https://git-scm.com/documentation</a></p></div>
831 <div class="paragraph"><p>Please talk to us before working on new features to see whether they will be
832 accepted. A good way for this is to open an issue and asking for opinions on it.
833 Even for accepted features, this can be a good way to refine an idea upfront. However,
834 we don&#8217;t want to see certain features in i3, e.g., switching window focus in an
835 Alt+Tab like way.</p></div>
836 <div class="paragraph"><p>When working on bugfixes, please make sure you mention that you are working on
837 it in the corresponding bug report at <a href="https://github.com/i3/i3/issues">https://github.com/i3/i3/issues</a>. In case
838 there is no bug report yet, please create one.</p></div>
839 <div class="paragraph"><p>After you are done, please submit your work for review as a pull request at
840 <a href="https://github.com/i3/i3">https://github.com/i3/i3</a>.</p></div>
841 <div class="paragraph"><p>Do not send emails to the mailing list or any author directly, and don’t submit
842 them in the bugtracker, since all reviews should be done in public at
843 <a href="https://github.com/i3/i3">https://github.com/i3/i3</a>. In order to make your review go as fast as possible, you
844 could have a look at previous reviews and see what the common mistakes are.</p></div>
845 <div class="sect2">
846 <h3 id="_which_branch_to_use">2.1. Which branch to use?</h3>
847 <div class="paragraph"><p>Work on i3 generally happens in two branches: “master” and “next” (the latter
848 being the default branch, the one that people get when they check out the git
849 repository).</p></div>
850 <div class="paragraph"><p>The contents of “master” are always stable. That is, it contains the source code
851 of the latest release, plus any bugfixes that were applied since that release.</p></div>
852 <div class="paragraph"><p>New features are only found in the “next” branch. Therefore, if you are working
853 on a new feature, use the “next” branch. If you are working on a bugfix, use the
854 “next” branch, too, but make sure your code also works on “master”.</p></div>
855 </div>
856 </div>
857 </div>
858 <div class="sect1">
859 <h2 id="_window_managers">3. Window Managers</h2>
860 <div class="sectionbody">
861 <div class="paragraph"><p>A window manager is not necessarily needed to run X, but it is usually used in
862 combination with X to facilitate some things. The window manager&#8217;s job is to
863 take care of the placement of windows, to provide the user with some mechanisms
864 to change the position/size of windows and to communicate with clients to a
865 certain extent (for example handle fullscreen requests of clients such as
866 MPlayer).</p></div>
867 <div class="paragraph"><p>There are no different contexts in which X11 clients run, so a window manager
868 is just another client, like all other X11 applications. However, it handles
869 some events which normal clients usually don’t handle.</p></div>
870 <div class="paragraph"><p>In the case of i3, the tasks (and order of them) are the following:</p></div>
871 <div class="olist arabic"><ol class="arabic">
872 <li>
873 <p>
874 Grab the key bindings (events will be sent upon keypress/keyrelease)
875 </p>
876 </li>
877 <li>
878 <p>
879 Iterate through all existing windows (if the window manager is not started as
880 the first client of X) and manage them (reparent them, create window
881 decorations, etc.)
882 </p>
883 </li>
884 <li>
885 <p>
886 When new windows are created, manage them
887 </p>
888 </li>
889 <li>
890 <p>
891 Handle the client’s <code>_WM_STATE</code> property, but only <code>_WM_STATE_FULLSCREEN</code> and
892 <code>_NET_WM_STATE_DEMANDS_ATTENTION</code>
893 </p>
894 </li>
895 <li>
896 <p>
897 Handle the client’s <code>WM_NAME</code> property
898 </p>
899 </li>
900 <li>
901 <p>
902 Handle the client’s size hints to display them proportionally
903 </p>
904 </li>
905 <li>
906 <p>
907 Handle the client’s urgency hint
908 </p>
909 </li>
910 <li>
911 <p>
912 Handle enter notifications (focus follows mouse)
913 </p>
914 </li>
915 <li>
916 <p>
917 Handle button (as in mouse buttons) presses for focus/raise on click
918 </p>
919 </li>
920 <li>
921 <p>
922 Handle expose events to re-draw own windows such as decorations
923 </p>
924 </li>
925 <li>
926 <p>
927 React to the user’s commands: Change focus, Move windows, Switch workspaces,
928 Change the layout mode of a container (default/stacking/tabbed), start a new
929 application, restart the window manager
930 </p>
931 </li>
932 </ol></div>
933 <div class="paragraph"><p>In the following chapters, each of these tasks and their implementation details
934 will be discussed.</p></div>
935 <div class="sect2">
936 <h3 id="_tiling_window_managers">3.1. Tiling window managers</h3>
937 <div class="paragraph"><p>Traditionally, there are two approaches to managing windows: The most common
938 one nowadays is floating, which means the user can freely move/resize the
939 windows. The other approach is called tiling, which means that your window
940 manager distributes windows to use as much space as possible while not
941 overlapping each other.</p></div>
942 <div class="paragraph"><p>The idea behind tiling is that you should not need to waste your time
943 moving/resizing windows while you usually want to get some work done. After
944 all, most users sooner or later tend to lay out their windows in a way which
945 corresponds to tiling or stacking mode in i3. Therefore, why not let i3 do this
946 for you? Certainly, it’s faster than you could ever do it.</p></div>
947 <div class="paragraph"><p>The problem with most tiling window managers is that they are too inflexible.
948 In my opinion, a window manager is just another tool, and similar to vim which
949 can edit all kinds of text files (like source code, HTML, …) and is not limited
950 to a specific file type, a window manager should not limit itself to a certain
951 layout (like dwm, awesome, …) but provide mechanisms for you to easily create
952 the layout you need at the moment.</p></div>
953 </div>
954 <div class="sect2">
955 <h3 id="_the_layout_tree">3.2. The layout tree</h3>
956 <div class="paragraph"><p>The data structure which i3 uses to keep track of your windows is a tree. Every
957 node in the tree is a container (type <code>Con</code>). Some containers represent actual
958 windows (every container with a <code>window != NULL</code>), some represent split
959 containers and a few have special purposes: they represent workspaces, outputs
960 (like VGA1, LVDS1, …) or the X11 root window.</p></div>
961 <div class="paragraph"><p>So, when you open a terminal and immediately open another one, they reside in
962 the same split container, which uses the default layout. In case of an empty
963 workspace, the split container we are talking about is the workspace.</p></div>
964 <div class="paragraph"><p>To get an impression of how different layouts are represented, just play around
965 and look at the data structures&#8201;&#8212;&#8201;they are exposed as a JSON hash. See
966 <a href="https://i3wm.org/docs/ipc.html#_tree_reply">https://i3wm.org/docs/ipc.html#_tree_reply</a> for documentation on that and an
967 example.</p></div>
968 </div>
969 </div>
970 </div>
971 <div class="sect1">
972 <h2 id="_files">4. Files</h2>
973 <div class="sectionbody">
974 <div class="dlist"><dl>
975 <dt class="hdlist1">
976 include/atoms.xmacro
977 </dt>
978 <dd>
979 <p>
980 A file containing all X11 atoms which i3 uses. This file will be included
981 various times (for defining, requesting and receiving the atoms), each time
982 with a different definition of xmacro().
983 </p>
984 </dd>
985 <dt class="hdlist1">
986 include/data.h
987 </dt>
988 <dd>
989 <p>
990 Contains data definitions used by nearly all files. You really need to read
991 this first.
992 </p>
993 </dd>
994 <dt class="hdlist1">
995 include/*.h
996 </dt>
997 <dd>
998 <p>
999 Contains forward definitions for all public functions, as well as
1000 doxygen-compatible comments (so if you want to get a bit more of the big
1001 picture, either browse all header files or use doxygen if you prefer that).
1002 </p>
1003 </dd>
1004 <dt class="hdlist1">
1005 src/config_parser.c
1006 </dt>
1007 <dd>
1008 <p>
1009 Contains a custom configuration parser. See src/command_parser.c for rationale
1010 on why we use a custom parser.
1011 </p>
1012 </dd>
1013 <dt class="hdlist1">
1014 src/click.c
1015 </dt>
1016 <dd>
1017 <p>
1018 Contains all functions which handle mouse button clicks (right mouse button
1019 clicks initiate resizing and thus are relatively complex).
1020 </p>
1021 </dd>
1022 <dt class="hdlist1">
1023 src/command_parser.c
1024 </dt>
1025 <dd>
1026 <p>
1027 Contains a hand-written parser to parse commands (commands are what
1028 you bind on keys and what you can send to i3 using the IPC interface, like
1029 <em>move left</em> or <em>workspace 4</em>).
1030 </p>
1031 </dd>
1032 <dt class="hdlist1">
1033 src/con.c
1034 </dt>
1035 <dd>
1036 <p>
1037 Contains all functions which deal with containers directly (creating
1038 containers, searching containers, getting specific properties from containers,
1039 …).
1040 </p>
1041 </dd>
1042 <dt class="hdlist1">
1043 src/config.c
1044 </dt>
1045 <dd>
1046 <p>
1047 Contains all functions handling the configuration file (calling the parser
1048 src/config_parser.c) with the correct path, switching key bindings mode).
1049 </p>
1050 </dd>
1051 <dt class="hdlist1">
1052 src/ewmh.c
1053 </dt>
1054 <dd>
1055 <p>
1056 Functions to get/set certain EWMH properties easily.
1057 </p>
1058 </dd>
1059 <dt class="hdlist1">
1060 src/floating.c
1061 </dt>
1062 <dd>
1063 <p>
1064 Contains functions for floating mode (mostly resizing/dragging).
1065 </p>
1066 </dd>
1067 <dt class="hdlist1">
1068 src/handlers.c
1069 </dt>
1070 <dd>
1071 <p>
1072 Contains all handlers for all kinds of X events (new window title, new hints,
1073 unmapping, key presses, button presses, …).
1074 </p>
1075 </dd>
1076 <dt class="hdlist1">
1077 src/ipc.c
1078 </dt>
1079 <dd>
1080 <p>
1081 Contains code for the IPC interface.
1082 </p>
1083 </dd>
1084 <dt class="hdlist1">
1085 src/load_layout.c
1086 </dt>
1087 <dd>
1088 <p>
1089 Contains code for loading layouts from JSON files.
1090 </p>
1091 </dd>
1092 <dt class="hdlist1">
1093 src/log.c
1094 </dt>
1095 <dd>
1096 <p>
1097 Contains the logging functions.
1098 </p>
1099 </dd>
1100 <dt class="hdlist1">
1101 src/main.c
1102 </dt>
1103 <dd>
1104 <p>
1105 Initializes the window manager.
1106 </p>
1107 </dd>
1108 <dt class="hdlist1">
1109 src/manage.c
1110 </dt>
1111 <dd>
1112 <p>
1113 Looks at existing or new windows and decides whether to manage them. If so, it
1114 reparents the window and inserts it into our data structures.
1115 </p>
1116 </dd>
1117 <dt class="hdlist1">
1118 src/match.c
1119 </dt>
1120 <dd>
1121 <p>
1122 A "match" is a data structure which acts like a mask or expression to match
1123 certain windows or not. For example, when using commands, you can specify a
1124 command like this: [title="<strong>Firefox</strong>"] kill. The title member of the match
1125 data structure will then be filled and i3 will check each window using
1126 match_matches_window() to find the windows affected by this command.
1127 </p>
1128 </dd>
1129 <dt class="hdlist1">
1130 src/move.c
1131 </dt>
1132 <dd>
1133 <p>
1134 Contains code to move a container in a specific direction.
1135 </p>
1136 </dd>
1137 <dt class="hdlist1">
1138 src/output.c
1139 </dt>
1140 <dd>
1141 <p>
1142 Functions to handle CT_OUTPUT cons.
1143 </p>
1144 </dd>
1145 <dt class="hdlist1">
1146 src/randr.c
1147 </dt>
1148 <dd>
1149 <p>
1150 The RandR API is used to get (and re-query) the configured outputs (monitors,
1151 …).
1152 </p>
1153 </dd>
1154 <dt class="hdlist1">
1155 src/render.c
1156 </dt>
1157 <dd>
1158 <p>
1159 Renders the tree data structure by assigning coordinates to every node. These
1160 values will later be pushed to X11 in <code>src/x.c</code>.
1161 </p>
1162 </dd>
1163 <dt class="hdlist1">
1164 src/resize.c
1165 </dt>
1166 <dd>
1167 <p>
1168 Contains the functions to resize containers.
1169 </p>
1170 </dd>
1171 <dt class="hdlist1">
1172 src/restore_layout.c
1173 </dt>
1174 <dd>
1175 <p>
1176 Everything for restored containers that is not pure state parsing (which can be
1177 found in load_layout.c).
1178 </p>
1179 </dd>
1180 <dt class="hdlist1">
1181 src/sighandler.c
1182 </dt>
1183 <dd>
1184 <p>
1185 Handles <code>SIGSEGV</code>, <code>SIGABRT</code> and <code>SIGFPE</code> by showing a dialog that i3 crashed.
1186 You can chose to let it dump core, to restart it in-place or to restart it
1187 in-place but forget about the layout.
1188 </p>
1189 </dd>
1190 <dt class="hdlist1">
1191 src/tree.c
1192 </dt>
1193 <dd>
1194 <p>
1195 Contains functions which open or close containers in the tree, change focus or
1196 cleanup ("flatten") the tree. See also <code>src/move.c</code> for another similar
1197 function, which was moved into its own file because it is so long.
1198 </p>
1199 </dd>
1200 <dt class="hdlist1">
1201 src/util.c
1202 </dt>
1203 <dd>
1204 <p>
1205 Contains useful functions which are not really dependent on anything.
1206 </p>
1207 </dd>
1208 <dt class="hdlist1">
1209 src/window.c
1210 </dt>
1211 <dd>
1212 <p>
1213 Handlers to update X11 window properties like <code>WM_CLASS</code>, <code>_NET_WM_NAME</code>,
1214 <code>CLIENT_LEADER</code>, etc.
1215 </p>
1216 </dd>
1217 <dt class="hdlist1">
1218 src/workspace.c
1219 </dt>
1220 <dd>
1221 <p>
1222 Contains all functions related to workspaces (displaying, hiding, renaming…)
1223 </p>
1224 </dd>
1225 <dt class="hdlist1">
1226 src/x.c
1227 </dt>
1228 <dd>
1229 <p>
1230 Transfers our in-memory tree (see <code>src/render.c</code>) to X11.
1231 </p>
1232 </dd>
1233 <dt class="hdlist1">
1234 src/xcb.c
1235 </dt>
1236 <dd>
1237 <p>
1238 Contains wrappers to use xcb more easily.
1239 </p>
1240 </dd>
1241 <dt class="hdlist1">
1242 src/xcursor.c
1243 </dt>
1244 <dd>
1245 <p>
1246 XCursor functions (for cursor themes).
1247 </p>
1248 </dd>
1249 <dt class="hdlist1">
1250 src/xinerama.c
1251 </dt>
1252 <dd>
1253 <p>
1254 Legacy support for Xinerama. See <code>src/randr.c</code> for the preferred API.
1255 </p>
1256 </dd>
1257 </dl></div>
1258 </div>
1259 </div>
1260 <div class="sect1">
1261 <h2 id="_data_structures">5. Data structures</h2>
1262 <div class="sectionbody">
1263 <div class="paragraph"><p>See include/data.h for documented data structures. The most important ones are
1264 explained right here.</p></div>
1265 <div class="paragraph"><p>So, the hierarchy is:</p></div>
1266 <div class="olist arabic"><ol class="arabic">
1267 <li>
1268 <p>
1269 <strong>X11 root window</strong>, the root container
1270 </p>
1271 </li>
1272 <li>
1273 <p>
1274 <strong>Output container</strong> (LVDS1 in this example)
1275 </p>
1276 </li>
1277 <li>
1278 <p>
1279 <strong>Content container</strong> (there are also containers for dock windows)
1280 </p>
1281 </li>
1282 <li>
1283 <p>
1284 <strong>Workspaces</strong> (Workspace 1 in this example, with horizontal orientation)
1285 </p>
1286 </li>
1287 <li>
1288 <p>
1289 <strong>Split container</strong> (vertically split)
1290 </p>
1291 </li>
1292 <li>
1293 <p>
1294 <strong>X11 window containers</strong>
1295 </p>
1296 </li>
1297 </ol></div>
1298 <div class="paragraph"><p>The data type is <code>Con</code>, in all cases.</p></div>
1299 <div class="sect2">
1300 <h3 id="_x11_root_window">5.1. X11 root window</h3>
1301 <div class="paragraph"><p>The X11 root window is a single window per X11 display (a display is identified
1302 by <code>:0</code> or <code>:1</code> etc.). The root window is what you draw your background image
1303 on. It spans all the available outputs, e.g. <code>VGA1</code> is a specific part of the
1304 root window and <code>LVDS1</code> is a specific part of the root window.</p></div>
1305 </div>
1306 <div class="sect2">
1307 <h3 id="_output_container">5.2. Output container</h3>
1308 <div class="paragraph"><p>Every active output obtained through RandR is represented by one output
1309 container. Outputs are considered active when a mode is configured (meaning
1310 something is actually displayed on the output) and the output is not a clone.</p></div>
1311 <div class="paragraph"><p>For example, if your notebook has a screen resolution of 1280x800 px and you
1312 connect a video projector with a resolution of 1024x768 px, set it up in clone
1313 mode (<code>xrandr --output VGA1 --mode 1024x768 --same-as LVDS1</code>), i3 will
1314 reduce the resolution to the lowest common resolution and disable one of the
1315 cloned outputs afterwards.</p></div>
1316 <div class="paragraph"><p>However, if you configure it using <code>xrandr --output VGA1 --mode 1024x768
1317 --right-of LVDS1</code>, i3 will set both outputs active. For each output, a new
1318 workspace will be assigned. New workspaces are created on the output you are
1319 currently on.</p></div>
1320 </div>
1321 <div class="sect2">
1322 <h3 id="_content_container">5.3. Content container</h3>
1323 <div class="paragraph"><p>Each output has multiple children. Two of them are dock containers which hold
1324 dock clients. The other one is the content container, which holds the actual
1325 content (workspaces) of this output.</p></div>
1326 </div>
1327 <div class="sect2">
1328 <h3 id="_workspace">5.4. Workspace</h3>
1329 <div class="paragraph"><p>A workspace is identified by its name. Basically, you could think of
1330 workspaces as different desks in your office, if you like the desktop
1331 metaphor. They just contain different sets of windows and are completely
1332 separate of each other. Other window managers also call this &#8220;Virtual
1333 desktops&#8221;.</p></div>
1334 </div>
1335 <div class="sect2">
1336 <h3 id="_split_container">5.5. Split container</h3>
1337 <div class="paragraph"><p>A split container is a container which holds an arbitrary amount of split
1338 containers or X11 window containers. It has an orientation (horizontal or
1339 vertical) and a layout.</p></div>
1340 <div class="paragraph"><p>Split containers (and X11 window containers, which are a subtype of split
1341 containers) can have different border styles.</p></div>
1342 </div>
1343 <div class="sect2">
1344 <h3 id="_x11_window_container">5.6. X11 window container</h3>
1345 <div class="paragraph"><p>An X11 window container holds exactly one X11 window. These are the leaf nodes
1346 of the layout tree, they cannot have any children.</p></div>
1347 </div>
1348 </div>
1349 </div>
1350 <div class="sect1">
1351 <h2 id="_list_queue_macros">6. List/queue macros</h2>
1352 <div class="sectionbody">
1353 <div class="paragraph"><p>i3 makes heavy use of the list macros defined in BSD operating systems. To
1354 ensure that the operating system on which i3 is compiled has all the expected
1355 features, i3 comes with <code>include/queue.h</code>. On BSD systems, you can use man
1356 <code>queue(3)</code>. On Linux, you have to use google (or read the source).</p></div>
1357 <div class="paragraph"><p>The lists used are <code>SLIST</code> (single linked lists), <code>CIRCLEQ</code> (circular
1358 queues) and <code>TAILQ</code> (tail queues). Usually, only forward traversal is necessary,
1359 so an <code>SLIST</code> works fine. If inserting elements at arbitrary positions or at
1360 the end of a list is necessary, a <code>TAILQ</code> is used instead. However, for the
1361 windows inside a container, a <code>CIRCLEQ</code> is necessary to go from the currently
1362 selected window to the window above/below.</p></div>
1363 </div>
1364 </div>
1365 <div class="sect1">
1366 <h2 id="_naming_conventions">7. Naming conventions</h2>
1367 <div class="sectionbody">
1368 <div class="paragraph"><p>There is a row of standard variables used in many events. The following names
1369 should be chosen for those:</p></div>
1370 <div class="ulist"><ul>
1371 <li>
1372 <p>
1373 &#8220;conn&#8221; is the xcb_connection_t
1374 </p>
1375 </li>
1376 <li>
1377 <p>
1378 &#8220;event&#8221; is the event of the particular type
1379 </p>
1380 </li>
1381 <li>
1382 <p>
1383 &#8220;con&#8221; names a container
1384 </p>
1385 </li>
1386 <li>
1387 <p>
1388 &#8220;current&#8221; is a loop variable when using <code>TAILQ_FOREACH</code> etc.
1389 </p>
1390 </li>
1391 </ul></div>
1392 </div>
1393 </div>
1394 <div class="sect1">
1395 <h2 id="_startup_src_mainx_c_main">8. Startup (src/mainx.c, main())</h2>
1396 <div class="sectionbody">
1397 <div class="ulist"><ul>
1398 <li>
1399 <p>
1400 Establish the xcb connection
1401 </p>
1402 </li>
1403 <li>
1404 <p>
1405 Check for XKB extension on the separate X connection, load Xcursor
1406 </p>
1407 </li>
1408 <li>
1409 <p>
1410 Check for RandR screens (with a fall-back to Xinerama)
1411 </p>
1412 </li>
1413 <li>
1414 <p>
1415 Grab the keycodes for which bindings exist
1416 </p>
1417 </li>
1418 <li>
1419 <p>
1420 Manage all existing windows
1421 </p>
1422 </li>
1423 <li>
1424 <p>
1425 Enter the event loop
1426 </p>
1427 </li>
1428 </ul></div>
1429 </div>
1430 </div>
1431 <div class="sect1">
1432 <h2 id="_keybindings">9. Keybindings</h2>
1433 <div class="sectionbody">
1434 <div class="sect2">
1435 <h3 id="_grabbing_the_bindings">9.1. Grabbing the bindings</h3>
1436 <div class="paragraph"><p>Grabbing the bindings is quite straight-forward. You pass X your combination of
1437 modifiers and the keycode you want to grab and whether you want to grab them
1438 actively or passively. Most bindings (everything except for bindings using
1439 Mode_switch) are grabbed passively, that is, just the window manager gets the
1440 event and cannot replay it.</p></div>
1441 <div class="paragraph"><p>We need to grab bindings that use Mode_switch actively because of a bug in X.
1442 When the window manager receives the keypress/keyrelease event for an actively
1443 grabbed keycode, it has to decide what to do with this event: It can either
1444 replay it so that other applications get it or it can prevent other
1445 applications from receiving it.</p></div>
1446 <div class="paragraph"><p>So, why do we need to grab keycodes actively? Because X does not set the
1447 state-property of keypress/keyrelease events properly. The Mode_switch bit is
1448 not set and we need to get it using XkbGetState. This means we cannot pass X
1449 our combination of modifiers containing Mode_switch when grabbing the key and
1450 therefore need to grab the keycode itself without any modifiers. This means,
1451 if you bind Mode_switch + keycode 38 ("a"), i3 will grab keycode 38 ("a") and
1452 check on each press of "a" if the Mode_switch bit is set using XKB. If yes, it
1453 will handle the event, if not, it will replay the event.</p></div>
1454 </div>
1455 <div class="sect2">
1456 <h3 id="_handling_a_keypress">9.2. Handling a keypress</h3>
1457 <div class="paragraph"><p>As mentioned in "Grabbing the bindings", upon a keypress event, i3 first gets
1458 the correct state.</p></div>
1459 <div class="paragraph"><p>Then, it looks through all bindings and gets the one which matches the received
1460 event.</p></div>
1461 <div class="paragraph"><p>The bound command is parsed by the cmdparse lexer/parser, see <code>parse_cmd</code> in
1462 <code>src/cmdparse.y</code>.</p></div>
1463 </div>
1464 </div>
1465 </div>
1466 <div class="sect1">
1467 <h2 id="_manage_windows_src_main_c_manage_window_and_reparent_window">10. Manage windows (src/main.c, manage_window() and reparent_window())</h2>
1468 <div class="sectionbody">
1469 <div class="paragraph"><p><code>manage_window()</code> does some checks to decide whether the window should be
1470 managed at all:</p></div>
1471 <div class="ulist"><ul>
1472 <li>
1473 <p>
1474 Windows have to be mapped, that is, visible on screen
1475 </p>
1476 </li>
1477 <li>
1478 <p>
1479 The override_redirect must not be set. Windows with override_redirect shall
1480 not be managed by a window manager
1481 </p>
1482 </li>
1483 </ul></div>
1484 <div class="paragraph"><p>Afterwards, i3 gets the initial geometry and reparents the window (see
1485 <code>reparent_window()</code>) if it wasn’t already managed.</p></div>
1486 <div class="paragraph"><p>Reparenting means that for each window which is reparented, a new window,
1487 slightly larger than the original one, is created. The original window is then
1488 reparented to the bigger one (called "frame").</p></div>
1489 <div class="paragraph"><p>After reparenting, the window type (<code>_NET_WM_WINDOW_TYPE</code>) is checked to see
1490 whether this window is a dock (<code>_NET_WM_WINDOW_TYPE_DOCK</code>), like dzen2 for
1491 example. Docks are handled differently, they don’t have decorations and are not
1492 assigned to a specific container. Instead, they are positioned at the bottom
1493 or top of the screen (in the appropriate dock area containers). To get the
1494 height which needs to be reserved for the window, the <code>_NET_WM_STRUT_PARTIAL</code>
1495 property is used.</p></div>
1496 <div class="paragraph"><p>Furthermore, the list of assignments (to other workspaces, which may be on
1497 other screens) is checked. If the window matches one of the user’s criteria,
1498 it may either be put in floating mode or moved to a different workspace. If the
1499 target workspace is not visible, the window will not be mapped.</p></div>
1500 </div>
1501 </div>
1502 <div class="sect1">
1503 <h2 id="_what_happens_when_an_application_is_started">11. What happens when an application is started?</h2>
1504 <div class="sectionbody">
1505 <div class="paragraph"><p>i3 does not care about applications. All it notices is when new windows are
1506 mapped (see <code>src/handlers.c</code>, <code>handle_map_request()</code>). The window is then
1507 reparented (see section "Manage windows").</p></div>
1508 <div class="paragraph"><p>After reparenting the window, <code>render_tree()</code> is called which renders the
1509 internal layout table. The new window has been placed in the currently focused
1510 container and therefore the new window and the old windows (if any) need to be
1511 moved/resized so that the currently active layout (default/stacking/tabbed mode)
1512 is rendered correctly. To move/resize windows, a window is &#8220;configured&#8221; in
1513 X11-speak.</p></div>
1514 <div class="paragraph"><p>Some applications, such as MPlayer obviously assume the window manager is
1515 stupid and try to configure their windows by themselves. This generates an
1516 event called configurerequest. i3 handles these events and tells the window the
1517 size it had before the configurerequest (with the exception of not yet mapped
1518 windows, which get configured like they want to, and floating windows, which
1519 can reconfigure themselves).</p></div>
1520 </div>
1521 </div>
1522 <div class="sect1">
1523 <h2 id="_net_wm_state">12. _NET_WM_STATE</h2>
1524 <div class="sectionbody">
1525 <div class="paragraph"><p>Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
1526 are handled.</p></div>
1527 <div class="paragraph"><p>The former calls &#8220;toggle_fullscreen()&#8221; for the specific client which just
1528 configures the client to use the whole screen on which it currently is.
1529 Also, it is set as fullscreen_client for the i3Screen.</p></div>
1530 <div class="paragraph"><p>The latter is used to set, read and display urgency hints.</p></div>
1531 </div>
1532 </div>
1533 <div class="sect1">
1534 <h2 id="_wm_name">13. WM_NAME</h2>
1535 <div class="sectionbody">
1536 <div class="paragraph"><p>When the WM_NAME property of a window changes, its decoration (containing the
1537 title) is re-rendered. Note that WM_NAME is in COMPOUND_TEXT encoding which is
1538 totally uncommon and cumbersome. Therefore, the _NET_WM_NAME atom will be used
1539 if present.</p></div>
1540 </div>
1541 </div>
1542 <div class="sect1">
1543 <h2 id="_net_wm_name">14. _NET_WM_NAME</h2>
1544 <div class="sectionbody">
1545 <div class="paragraph"><p>Like WM_NAME, this atom contains the title of a window. However, _NET_WM_NAME
1546 is encoded in UTF-8. i3 will recode it to UCS-2 in order to be able to pass it
1547 to X. Using an appropriate font (ISO-10646), you can see most special
1548 characters (every special character contained in your font).</p></div>
1549 </div>
1550 </div>
1551 <div class="sect1">
1552 <h2 id="_size_hints">15. Size hints</h2>
1553 <div class="sectionbody">
1554 <div class="paragraph"><p>Size hints specify the minimum/maximum size for a given window as well as its
1555 aspect ratio. This is important for clients like mplayer, who only set the
1556 aspect ratio and resize their window to be as small as possible (but only with
1557 some video outputs, for example in Xv, while when using x11, mplayer does the
1558 necessary centering for itself).</p></div>
1559 <div class="paragraph"><p>So, when an aspect ratio was specified, i3 adjusts the height of the window
1560 until the size maintains the correct aspect ratio. For the code to do this, see
1561 src/layout.c, function resize_client().</p></div>
1562 </div>
1563 </div>
1564 <div class="sect1">
1565 <h2 id="_rendering_src_layout_c_render_layout_and_render_container">16. Rendering (src/layout.c, render_layout() and render_container())</h2>
1566 <div class="sectionbody">
1567 <div class="paragraph"><p>Rendering in i3 version 4 is the step which assigns the correct sizes for
1568 borders, decoration windows, child windows and the stacking order of all
1569 windows. In a separate step (<code>x_push_changes()</code>), these changes are pushed to
1570 X11.</p></div>
1571 <div class="paragraph"><p>Keep in mind that all these properties (<code>rect</code>, <code>window_rect</code> and <code>deco_rect</code>)
1572 are temporary, meaning they will be overwritten by calling <code>render_con</code>.
1573 Persistent position/size information is kept in <code>geometry</code>.</p></div>
1574 <div class="paragraph"><p>The entry point for every rendering operation (except for the case of moving
1575 floating windows around) currently is <code>tree_render()</code> which will re-render
1576 everything that’s necessary (for every output, only the currently displayed
1577 workspace is rendered). This behavior is expected to change in the future,
1578 since for a lot of updates, re-rendering everything is not actually necessary.
1579 Focus was on getting it working correct, not getting it work very fast.</p></div>
1580 <div class="paragraph"><p>What <code>tree_render()</code> actually does is calling <code>render_con()</code> on the root
1581 container and then pushing the changes to X11. The following sections talk
1582 about the different rendering steps, in the order of "top of the tree" (root
1583 container) to the bottom.</p></div>
1584 <div class="sect2">
1585 <h3 id="_rendering_the_root_container">16.1. Rendering the root container</h3>
1586 <div class="paragraph"><p>The i3 root container (<code>con-&gt;type == CT_ROOT</code>) represents the X11 root window.
1587 It contains one child container for every output (like LVDS1, VGA1, …), which
1588 is available on your computer.</p></div>
1589 <div class="paragraph"><p>Rendering the root will first render all tiling windows and then all floating
1590 windows. This is necessary because a floating window can be positioned in such
1591 a way that it is visible on two different outputs. Therefore, by first
1592 rendering all the tiling windows (of all outputs), we make sure that floating
1593 windows can never be obscured by tiling windows.</p></div>
1594 <div class="paragraph"><p>Essentially, though, this code path will just call <code>render_con()</code> for every
1595 output and <code>x_raise_con(); render_con()</code> for every floating window.</p></div>
1596 <div class="paragraph"><p>In the special case of having a "global fullscreen" window (fullscreen mode
1597 spanning all outputs), a shortcut is taken and <code>x_raise_con(); render_con()</code> is
1598 only called for the global fullscreen window.</p></div>
1599 </div>
1600 <div class="sect2">
1601 <h3 id="_rendering_an_output">16.2. Rendering an output</h3>
1602 <div class="paragraph"><p>Output containers (<code>con-&gt;layout == L_OUTPUT</code>) represent a hardware output like
1603 LVDS1, VGA1, etc. An output container has three children (at the moment): One
1604 content container (having workspaces as children) and the top/bottom dock area
1605 containers.</p></div>
1606 <div class="paragraph"><p>The rendering happens in the function <code>render_l_output()</code> in the following
1607 steps:</p></div>
1608 <div class="olist arabic"><ol class="arabic">
1609 <li>
1610 <p>
1611 Find the content container (<code>con-&gt;type == CT_CON</code>)
1612 </p>
1613 </li>
1614 <li>
1615 <p>
1616 Get the currently visible workspace (<code>con_get_fullscreen_con(content,
1617 CF_OUTPUT)</code>).
1618 </p>
1619 </li>
1620 <li>
1621 <p>
1622 If there is a fullscreened window on that workspace, directly render it and
1623 return, thus ignoring the dock areas.
1624 </p>
1625 </li>
1626 <li>
1627 <p>
1628 Sum up the space used by all the dock windows (they have a variable height
1629 only).
1630 </p>
1631 </li>
1632 <li>
1633 <p>
1634 Set the workspace rects (x/y/width/height) based on the position of the
1635 output (stored in <code>con-&gt;rect</code>) and the usable space
1636 (<code>con-&gt;rect.{width,height}</code> without the space used for dock windows).
1637 </p>
1638 </li>
1639 <li>
1640 <p>
1641 Recursively raise and render the output’s child containers (meaning dock
1642 area containers and the content container).
1643 </p>
1644 </li>
1645 </ol></div>
1646 </div>
1647 <div class="sect2">
1648 <h3 id="_rendering_a_workspace_or_split_container">16.3. Rendering a workspace or split container</h3>
1649 <div class="paragraph"><p>From here on, there really is no difference anymore. All containers are of
1650 <code>con-&gt;type == CT_CON</code> (whether workspace or split container) and some of them
1651 have a <code>con-&gt;window</code>, meaning they represent an actual window instead of a
1652 split container.</p></div>
1653 <div class="sect3">
1654 <h4 id="_default_layout">16.3.1. Default layout</h4>
1655 <div class="paragraph"><p>In default layout, containers are placed horizontally or vertically next to
1656 each other (depending on the <code>con-&gt;orientation</code>). If a child is a leaf node (as
1657 opposed to a split container) and has border style "normal", appropriate space
1658 will be reserved for its window decoration.</p></div>
1659 </div>
1660 <div class="sect3">
1661 <h4 id="_stacked_layout">16.3.2. Stacked layout</h4>
1662 <div class="paragraph"><p>In stacked layout, only the focused window is actually shown (this is achieved
1663 by calling <code>x_raise_con()</code> in reverse focus order at the end of <code>render_con()</code>).</p></div>
1664 <div class="paragraph"><p>The available space for the focused window is the size of the container minus
1665 the height of the window decoration for all windows inside this stacked
1666 container.</p></div>
1667 <div class="paragraph"><p>If border style is "1pixel" or "none", no window decoration height will be
1668 reserved (or displayed later on), unless there is more than one window inside
1669 the stacked container.</p></div>
1670 </div>
1671 <div class="sect3">
1672 <h4 id="_tabbed_layout">16.3.3. Tabbed layout</h4>
1673 <div class="paragraph"><p>Tabbed layout works precisely like stacked layout, but the window decoration
1674 position/size is different: They are placed next to each other on a single line
1675 (fixed height).</p></div>
1676 </div>
1677 <div class="sect3">
1678 <h4 id="_dock_area_layout">16.3.4. Dock area layout</h4>
1679 <div class="paragraph"><p>This is a special case. Users cannot choose the dock area layout, but it will be
1680 set for the dock area containers. In the dockarea layout (at the moment!),
1681 windows will be placed above each other.</p></div>
1682 </div>
1683 </div>
1684 <div class="sect2">
1685 <h3 id="_rendering_a_window">16.4. Rendering a window</h3>
1686 <div class="paragraph"><p>A window’s size and position will be determined in the following way:</p></div>
1687 <div class="olist arabic"><ol class="arabic">
1688 <li>
1689 <p>
1690 Subtract the border if border style is not "none" (but "normal" or "1pixel").
1691 </p>
1692 </li>
1693 <li>
1694 <p>
1695 Subtract the X11 border, if the window has an X11 border &gt; 0.
1696 </p>
1697 </li>
1698 <li>
1699 <p>
1700 Obey the aspect ratio of the window (think MPlayer).
1701 </p>
1702 </li>
1703 <li>
1704 <p>
1705 Obey the height- and width-increments of the window (think terminal emulator
1706 which can only be resized in one-line or one-character steps).
1707 </p>
1708 </li>
1709 </ol></div>
1710 </div>
1711 </div>
1712 </div>
1713 <div class="sect1">
1714 <h2 id="_pushing_updates_to_x11_drawing">17. Pushing updates to X11 / Drawing</h2>
1715 <div class="sectionbody">
1716 <div class="paragraph"><p>A big problem with i3 before version 4 was that we just sent requests to X11
1717 anywhere in the source code. This was bad because nobody could understand the
1718 entirety of our interaction with X11, it lead to subtle bugs and a lot of edge
1719 cases which we had to consider all over again.</p></div>
1720 <div class="paragraph"><p>Therefore, since version 4, we have a single file, <code>src/x.c</code>, which is
1721 responsible for repeatedly transferring parts of our tree datastructure to X11.</p></div>
1722 <div class="paragraph"><p><code>src/x.c</code> consists of multiple parts:</p></div>
1723 <div class="olist arabic"><ol class="arabic">
1724 <li>
1725 <p>
1726 The state pushing: <code>x_push_changes()</code>, which calls <code>x_push_node()</code>.
1727 </p>
1728 </li>
1729 <li>
1730 <p>
1731 State modification functions: <code>x_con_init</code>, <code>x_reinit</code>,
1732 <code>x_reparent_child</code>, <code>x_move_win</code>, <code>x_con_kill</code>, <code>x_raise_con</code>, <code>x_set_name</code>
1733 and <code>x_set_warp_to</code>.
1734 </p>
1735 </li>
1736 <li>
1737 <p>
1738 Expose event handling (drawing decorations): <code>x_deco_recurse()</code> and
1739 <code>x_draw_decoration()</code>.
1740 </p>
1741 </li>
1742 </ol></div>
1743 <div class="sect2">
1744 <h3 id="_pushing_state_to_x11">17.1. Pushing state to X11</h3>
1745 <div class="paragraph"><p>In general, the function <code>x_push_changes</code> should be called to push state
1746 changes. Only when the scope of the state change is clearly defined (for
1747 example only the title of a window) and its impact is known beforehand, one can
1748 optimize this and call <code>x_push_node</code> on the appropriate con directly.</p></div>
1749 <div class="paragraph"><p><code>x_push_changes</code> works in the following steps:</p></div>
1750 <div class="olist arabic"><ol class="arabic">
1751 <li>
1752 <p>
1753 Clear the eventmask for all mapped windows. This leads to not getting
1754 useless ConfigureNotify or EnterNotify events which are caused by our
1755 requests. In general, we only want to handle user input.
1756 </p>
1757 </li>
1758 <li>
1759 <p>
1760 Stack windows above each other, in reverse stack order (starting with the
1761 most obscured/bottom window). This is relevant for floating windows which
1762 can overlap each other, but also for tiling windows in stacked or tabbed
1763 containers. We also update the <code>_NET_CLIENT_LIST_STACKING</code> hint which is
1764 necessary for tab drag and drop in Chromium.
1765 </p>
1766 </li>
1767 <li>
1768 <p>
1769 <code>x_push_node</code> will be called for the root container, recursively calling
1770 itself for the container’s children. This function actually pushes the
1771 state, see the next paragraph.
1772 </p>
1773 </li>
1774 <li>
1775 <p>
1776 If the pointer needs to be warped to a different position (for example when
1777 changing focus to a different output), it will be warped now.
1778 </p>
1779 </li>
1780 <li>
1781 <p>
1782 The eventmask is restored for all mapped windows.
1783 </p>
1784 </li>
1785 <li>
1786 <p>
1787 Window decorations will be rendered by calling <code>x_deco_recurse</code> on the root
1788 container, which then recursively calls itself for the children.
1789 </p>
1790 </li>
1791 <li>
1792 <p>
1793 If the input focus needs to be changed (because the user focused a different
1794 window), it will be updated now.
1795 </p>
1796 </li>
1797 <li>
1798 <p>
1799 <code>x_push_node_unmaps</code> will be called for the root container. This function
1800 only pushes UnmapWindow requests. Separating the state pushing is necessary
1801 to handle fullscreen windows (and workspace switches) in a smooth fashion:
1802 The newly visible windows should be visible before the old windows are
1803 unmapped.
1804 </p>
1805 </li>
1806 </ol></div>
1807 <div class="paragraph"><p><code>x_push_node</code> works in the following steps:</p></div>
1808 <div class="olist arabic"><ol class="arabic">
1809 <li>
1810 <p>
1811 Update the window’s <code>WM_NAME</code>, if changed (the <code>WM_NAME</code> is set on i3
1812 containers mainly for debugging purposes).
1813 </p>
1814 </li>
1815 <li>
1816 <p>
1817 Reparents a child window into the i3 container if the container was created
1818 for a specific managed window.
1819 </p>
1820 </li>
1821 <li>
1822 <p>
1823 If the size/position of the i3 container changed (due to opening a new
1824 window or switching layouts for example), the window will be reconfigured.
1825 Also, the pixmap which is used to draw the window decoration/border on is
1826 reconfigured (pixmaps are size-dependent).
1827 </p>
1828 </li>
1829 <li>
1830 <p>
1831 Size/position for the child window is adjusted.
1832 </p>
1833 </li>
1834 <li>
1835 <p>
1836 The i3 container is mapped if it should be visible and was not yet mapped.
1837 When mapping, <code>WM_STATE</code> is set to <code>WM_STATE_NORMAL</code>. Also, the eventmask of
1838 the child window is updated and the i3 container’s contents are copied from
1839 the pixmap.
1840 </p>
1841 </li>
1842 <li>
1843 <p>
1844 <code>x_push_node</code> is called recursively for all children of the current
1845 container.
1846 </p>
1847 </li>
1848 </ol></div>
1849 <div class="paragraph"><p><code>x_push_node_unmaps</code> handles the remaining case of an i3 container being
1850 unmapped if it should not be visible anymore. <code>WM_STATE</code> will be set to
1851 <code>WM_STATE_WITHDRAWN</code>.</p></div>
1852 </div>
1853 <div class="sect2">
1854 <h3 id="_drawing_window_decorations_borders_backgrounds">17.2. Drawing window decorations/borders/backgrounds</h3>
1855 <div class="paragraph"><p><code>x_draw_decoration</code> draws window decorations. It is run for every leaf
1856 container (representing an actual X11 window) and for every non-leaf container
1857 which is in a stacked/tabbed container (because stacked/tabbed containers
1858 display a window decoration for split containers, which consists of a representation
1859 of the child container&#8217;s names.</p></div>
1860 <div class="paragraph"><p>Then, parameters are collected to be able to determine whether this decoration
1861 drawing is actually necessary or was already done. This saves a substantial
1862 number of redraws (depending on your workload, but far over 50%).</p></div>
1863 <div class="paragraph"><p>Assuming that we need to draw this decoration, we start by filling the empty
1864 space around the child window (think of MPlayer with a specific aspect ratio)
1865 in the user-configured client background color.</p></div>
1866 <div class="paragraph"><p>Afterwards, we draw the appropriate border (in case of border styles "normal"
1867 and "1pixel") and the top bar (in case of border style "normal").</p></div>
1868 <div class="paragraph"><p>The last step is drawing the window title on the top bar.</p></div>
1869 </div>
1870 </div>
1871 </div>
1872 <div class="sect1">
1873 <h2 id="_user_commands_parser_specs_commands_spec">18. User commands (parser-specs/commands.spec)</h2>
1874 <div class="sectionbody">
1875 <div class="paragraph"><p>In the configuration file and when using i3 interactively (with <code>i3-msg</code>, for
1876 example), you use commands to make i3 do things, like focus a different window,
1877 set a window to fullscreen, and so on. An example command is <code>floating enable</code>,
1878 which enables floating mode for the currently focused window. See the
1879 appropriate section in the <a href="userguide.html">User’s Guide</a> for a reference of
1880 all commands.</p></div>
1881 <div class="paragraph"><p>In earlier versions of i3, interpreting these commands was done using lex and
1882 yacc, but experience has shown that lex and yacc are not well suited for our
1883 command language. Therefore, starting from version 4.2, we use a custom parser
1884 for user commands and the configuration file.
1885 The input specification for this parser can be found in the file
1886 <code>parser-specs/*.spec</code>. Should you happen to use Vim as an editor, use
1887 :source parser-specs/highlighting.vim to get syntax highlighting for this file
1888 (highlighting files for other editors are welcome).</p></div>
1889 <div class="listingblock">
1890 <div class="title">Excerpt from commands.spec</div>
1891 <div class="content">
1892 <pre><code>state INITIAL:
1893 '[' -&gt; call cmd_criteria_init(); CRITERIA
1894 'move' -&gt; MOVE
1895 'exec' -&gt; EXEC
1896 'workspace' -&gt; WORKSPACE
1897 'exit' -&gt; call cmd_exit()
1898 'restart' -&gt; call cmd_restart()
1899 'reload' -&gt; call cmd_reload()</code></pre>
1900 </div></div>
1901 <div class="paragraph"><p>The input specification is written in an extremely simple format. The
1902 specification is then converted into C code by the Perl script
1903 generate-commands-parser.pl (the output file names begin with GENERATED and the
1904 files are stored in the <code>include</code> directory). The parser implementation
1905 <code>src/commands_parser.c</code> includes the generated C code at compile-time.</p></div>
1906 <div class="paragraph"><p>The above excerpt from commands.spec illustrates nearly all features of our
1907 specification format: You describe different states and what can happen within
1908 each state. State names are all-caps; the state in the above excerpt is called
1909 INITIAL. A list of tokens and their actions (separated by an ASCII arrow)
1910 follows. In the excerpt, all tokens are literals, that is, simple text strings
1911 which will be compared with the input. An action is either the name of a state
1912 in which the parser will transition into, or the keyword <em>call</em>, followed by
1913 the name of a function (and optionally a state).</p></div>
1914 <div class="sect2">
1915 <h3 id="_example_the_workspace_state">18.1. Example: The WORKSPACE state</h3>
1916 <div class="paragraph"><p>Let’s have a look at the WORKSPACE state, which is a good example of all
1917 features. This is its definition:</p></div>
1918 <div class="listingblock">
1919 <div class="title">WORKSPACE state (commands.spec)</div>
1920 <div class="content">
1921 <pre><code># workspace next|prev|next_on_output|prev_on_output
1922 # workspace back_and_forth
1923 # workspace &lt;name&gt;
1924 # workspace number &lt;number&gt;
1925 state WORKSPACE:
1926 direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
1927 -&gt; call cmd_workspace($direction)
1928 'back_and_forth'
1929 -&gt; call cmd_workspace_back_and_forth()
1930 'number'
1931 -&gt; WORKSPACE_NUMBER
1932 workspace = string
1933 -&gt; call cmd_workspace_name($workspace)</code></pre>
1934 </div></div>
1935 <div class="paragraph"><p>As you can see from the commands, there are multiple different valid variants
1936 of the workspace command:</p></div>
1937 <div class="dlist"><dl>
1938 <dt class="hdlist1">
1939 workspace &lt;direction&gt;
1940 </dt>
1941 <dd>
1942 <p>
1943 The word <em>workspace</em> can be followed by any of the tokens <em>next</em>,
1944 <em>prev</em>, <em>next_on_output</em> or <em>prev_on_output</em>. This command will
1945 switch to the next or previous workspace (optionally on the same
1946 output).<br />
1947 There is one function called <code>cmd_workspace</code>, which is defined
1948 in <code>src/commands.c</code>. It will handle this kind of command. To know which
1949 direction was specified, the direction token is stored on the stack
1950 with the name "direction", which is what the "direction = " means in
1951 the beginning.<br />
1952 </p>
1953 </dd>
1954 </dl></div>
1955 <div class="admonitionblock">
1956 <table><tr>
1957 <td class="icon">
1958 <div class="title">Note</div>
1959 </td>
1960 <td class="content">Note that you can specify multiple literals in the same line. This has
1961 exactly the same effect as if you specified <code>direction =
1962 'next_on_output' -&gt; call cmd_workspace($direction)</code> and so forth.<br /></td>
1963 </tr></table>
1964 </div>
1965 <div class="admonitionblock">
1966 <table><tr>
1967 <td class="icon">
1968 <div class="title">Note</div>
1969 </td>
1970 <td class="content">Also note that the order of literals is important here: If <em>next</em> were
1971 ordered before <em>next_on_output</em>, then <em>next_on_output</em> would never
1972 match.</td>
1973 </tr></table>
1974 </div>
1975 <div class="dlist"><dl>
1976 <dt class="hdlist1">
1977 workspace back_and_forth
1978 </dt>
1979 <dd>
1980 <p>
1981 This is a very simple case: When the literal <em>back_and_forth</em> is found
1982 in the input, the function <code>cmd_workspace_back_and_forth</code> will be
1983 called without parameters and the parser will return to the INITIAL
1984 state (since no other state was specified).
1985 </p>
1986 </dd>
1987 <dt class="hdlist1">
1988 workspace &lt;name&gt;
1989 </dt>
1990 <dd>
1991 <p>
1992 In this case, the workspace command is followed by an arbitrary string,
1993 possibly in quotes, for example "workspace 3" or "workspace bleh".<br />
1994 This is the first time that the token is actually not a literal (not in
1995 single quotes), but just called string. Other possible tokens are word
1996 (the same as string, but stops matching at a whitespace) and end
1997 (matches the end of the input).
1998 </p>
1999 </dd>
2000 <dt class="hdlist1">
2001 workspace number &lt;number&gt;
2002 </dt>
2003 <dd>
2004 <p>
2005 The workspace command has to be followed by the keyword <code>number</code>. It
2006 then transitions into the state <code>WORKSPACE_NUMBER</code>, where the actual
2007 parameter will be read.
2008 </p>
2009 </dd>
2010 </dl></div>
2011 </div>
2012 <div class="sect2">
2013 <h3 id="_introducing_a_new_command">18.2. Introducing a new command</h3>
2014 <div class="paragraph"><p>The following steps have to be taken in order to properly introduce a new
2015 command (or possibly extend an existing command):</p></div>
2016 <div class="olist arabic"><ol class="arabic">
2017 <li>
2018 <p>
2019 Define a function beginning with <code>cmd_</code> in the file <code>src/commands.c</code>. Copy
2020 the prototype of an existing function.
2021 </p>
2022 </li>
2023 <li>
2024 <p>
2025 After adding a comment on what the function does, copy the comment and
2026 function definition to <code>include/commands.h</code>. Make the comment in the header
2027 file use double asterisks to make doxygen pick it up.
2028 </p>
2029 </li>
2030 <li>
2031 <p>
2032 Write a test case (or extend an existing test case) for your feature, see
2033 <a href="testsuite.html">i3 testsuite</a>. For now, it is sufficient to simply call
2034 your command in all the various possible ways.
2035 </p>
2036 </li>
2037 <li>
2038 <p>
2039 Extend the parser specification in <code>parser-specs/commands.spec</code>. Run the
2040 testsuite and see if your new function gets called with the appropriate
2041 arguments for the appropriate input.
2042 </p>
2043 </li>
2044 <li>
2045 <p>
2046 Actually implement the feature.
2047 </p>
2048 </li>
2049 <li>
2050 <p>
2051 Document the feature in the <a href="userguide.html">User’s Guide</a>.
2052 </p>
2053 </li>
2054 </ol></div>
2055 </div>
2056 </div>
2057 </div>
2058 <div class="sect1">
2059 <h2 id="_moving_containers">19. Moving containers</h2>
2060 <div class="sectionbody">
2061 <div class="paragraph"><p>The movement code is pretty delicate. You need to consider all cases before
2062 making any changes or before being able to fully understand how it works.</p></div>
2063 <div class="sect2">
2064 <h3 id="_case_1_moving_inside_the_same_container">19.1. Case 1: Moving inside the same container</h3>
2065 <div class="paragraph"><p>The reference layout for this case is a single workspace in horizontal
2066 orientation with two containers on it. Focus is on the left container (1).</p></div>
2067 <div class="tableblock">
2068 <table rules="all"
2069 width="15%"
2070 frame="border"
2071 cellspacing="0" cellpadding="4">
2072 <col width="50%" />
2073 <col width="50%" />
2074 <tbody>
2075 <tr>
2076 <td align="center" valign="top"><p class="table">1</p></td>
2077 <td align="center" valign="top"><p class="table">2</p></td>
2078 </tr>
2079 </tbody>
2080 </table>
2081 </div>
2082 <div class="paragraph"><p>When moving the left window to the right (command <code>move right</code>), tree_move will
2083 look for a container with horizontal orientation and finds the parent of the
2084 left container, that is, the workspace. Afterwards, it runs the code branch
2085 commented with "the easy case": it calls TAILQ_NEXT to get the container right
2086 of the current one and swaps both containers.</p></div>
2087 </div>
2088 <div class="sect2">
2089 <h3 id="_case_2_move_a_container_into_a_split_container">19.2. Case 2: Move a container into a split container</h3>
2090 <div class="paragraph"><p>The reference layout for this case is a horizontal workspace with two
2091 containers. The right container is a v-split with two containers. Focus is on
2092 the left container (1).</p></div>
2093 <div class="tableblock">
2094 <table rules="all"
2095 width="15%"
2096 frame="border"
2097 cellspacing="0" cellpadding="4">
2098 <col width="50%" />
2099 <col width="50%" />
2100 <tbody>
2101 <tr>
2102 <td rowspan="2" align="center" valign="middle"><p class="table">1</p></td>
2103 <td align="center" valign="top"><p class="table">2</p></td>
2104 </tr>
2105 <tr>
2106 <td align="center" valign="top"><p class="table">3</p></td>
2107 </tr>
2108 </tbody>
2109 </table>
2110 </div>
2111 <div class="paragraph"><p>When moving to the right (command <code>move right</code>), i3 will work like in case 1
2112 ("the easy case"). However, as the right container is not a leaf container, but
2113 a v-split, the left container (1) will be inserted at the right position (below
2114 2, assuming that 2 is focused inside the v-split) by calling <code>insert_con_into</code>.</p></div>
2115 <div class="paragraph"><p><code>insert_con_into</code> detaches the container from its parent and inserts it
2116 before/after the given target container. Afterwards, the on_remove_child
2117 callback is called on the old parent container which will then be closed, if
2118 empty.</p></div>
2119 <div class="paragraph"><p>Afterwards, <code>con_focus</code> will be called to fix the focus stack and the tree will
2120 be flattened.</p></div>
2121 </div>
2122 <div class="sect2">
2123 <h3 id="_case_3_moving_to_non_existent_top_bottom">19.3. Case 3: Moving to non-existent top/bottom</h3>
2124 <div class="paragraph"><p>Like in case 1, the reference layout for this case is a single workspace in
2125 horizontal orientation with two containers on it. Focus is on the left
2126 container:</p></div>
2127 <div class="tableblock">
2128 <table rules="all"
2129 width="15%"
2130 frame="border"
2131 cellspacing="0" cellpadding="4">
2132 <col width="50%" />
2133 <col width="50%" />
2134 <tbody>
2135 <tr>
2136 <td align="center" valign="top"><p class="table">1</p></td>
2137 <td align="center" valign="top"><p class="table">2</p></td>
2138 </tr>
2139 </tbody>
2140 </table>
2141 </div>
2142 <div class="paragraph"><p>This time however, the command is <code>move up</code> or <code>move down</code>. tree_move will look
2143 for a container with vertical orientation. As it will not find any,
2144 <code>same_orientation</code> is NULL and therefore i3 will perform a forced orientation
2145 change on the workspace by creating a new h-split container, moving the
2146 workspace contents into it and then changing the workspace orientation to
2147 vertical. Now it will again search for parent containers with vertical
2148 orientation and it will find the workspace.</p></div>
2149 <div class="paragraph"><p>This time, the easy case code path will not be run as we are not moving inside
2150 the same container. Instead, <code>insert_con_into</code> will be called with the focused
2151 container and the container above/below the current one (on the level of
2152 <code>same_orientation</code>).</p></div>
2153 <div class="paragraph"><p>Now, <code>con_focus</code> will be called to fix the focus stack and the tree will be
2154 flattened.</p></div>
2155 </div>
2156 <div class="sect2">
2157 <h3 id="_case_4_moving_to_existent_top_bottom">19.4. Case 4: Moving to existent top/bottom</h3>
2158 <div class="paragraph"><p>The reference layout for this case is a vertical workspace with two containers.
2159 The bottom one is a h-split containing two containers (1 and 2). Focus is on
2160 the bottom left container (1).</p></div>
2161 <div class="tableblock">
2162 <table rules="all"
2163 width="15%"
2164 frame="border"
2165 cellspacing="0" cellpadding="4">
2166 <col width="50%" />
2167 <col width="50%" />
2168 <tbody>
2169 <tr>
2170 <td colspan="2" align="center" valign="top"><p class="table">3</p></td>
2171 </tr>
2172 <tr>
2173 <td align="center" valign="top"><p class="table">1</p></td>
2174 <td align="center" valign="top"><p class="table">2</p></td>
2175 </tr>
2176 </tbody>
2177 </table>
2178 </div>
2179 <div class="paragraph"><p>This case is very much like case 3, only this time the forced workspace
2180 orientation change does not need to be performed because the workspace already
2181 is in vertical orientation.</p></div>
2182 </div>
2183 <div class="sect2">
2184 <h3 id="_case_5_moving_in_one_child_h_split">19.5. Case 5: Moving in one-child h-split</h3>
2185 <div class="paragraph"><p>The reference layout for this case is a horizontal workspace with two
2186 containers having a v-split on the left side with a one-child h-split on the
2187 bottom. Focus is on the bottom left container (2(h)):</p></div>
2188 <div class="tableblock">
2189 <table rules="all"
2190 width="15%"
2191 frame="border"
2192 cellspacing="0" cellpadding="4">
2193 <col width="50%" />
2194 <col width="50%" />
2195 <tbody>
2196 <tr>
2197 <td align="center" valign="top"><p class="table">1</p></td>
2198 <td rowspan="2" align="center" valign="middle"><p class="table">3</p></td>
2199 </tr>
2200 <tr>
2201 <td align="center" valign="top"><p class="table">2(h)</p></td>
2202 </tr>
2203 </tbody>
2204 </table>
2205 </div>
2206 <div class="paragraph"><p>In this case, <code>same_orientation</code> will be set to the h-split container around
2207 the focused container. However, when trying the easy case, the next/previous
2208 container <code>swap</code> will be NULL. Therefore, i3 will search again for a
2209 <code>same_orientation</code> container, this time starting from the parent of the h-split
2210 container.</p></div>
2211 <div class="paragraph"><p>After determining a new <code>same_orientation</code> container (if it is NULL, the
2212 orientation will be force-changed), this case is equivalent to case 2 or case
2213 4.</p></div>
2214 </div>
2215 <div class="sect2">
2216 <h3 id="_case_6_floating_containers">19.6. Case 6: Floating containers</h3>
2217 <div class="paragraph"><p>The reference layout for this case is a horizontal workspace with two
2218 containers plus one floating h-split container. Focus is on the floating
2219 container.</p></div>
2220 <div class="paragraph"><p>TODO: nice illustration. table not possible?</p></div>
2221 <div class="paragraph"><p>When moving up/down, the container needs to leave the floating container and it
2222 needs to be placed on the workspace (at workspace level). This is accomplished
2223 by calling the function <code>attach_to_workspace</code>.</p></div>
2224 </div>
2225 </div>
2226 </div>
2227 <div class="sect1">
2228 <h2 id="_click_handling">20. Click handling</h2>
2229 <div class="sectionbody">
2230 <div class="paragraph"><p>Without much ado, here is the list of cases which need to be considered:</p></div>
2231 <div class="ulist"><ul>
2232 <li>
2233 <p>
2234 click to focus (tiling + floating) and raise (floating)
2235 </p>
2236 </li>
2237 <li>
2238 <p>
2239 click to focus/raise when in stacked/tabbed mode
2240 </p>
2241 </li>
2242 <li>
2243 <p>
2244 floating_modifier + left mouse button to drag a floating con
2245 </p>
2246 </li>
2247 <li>
2248 <p>
2249 floating_modifier + right mouse button to resize a floating con
2250 </p>
2251 </li>
2252 <li>
2253 <p>
2254 click on decoration in a floating con to either initiate a resize (if there
2255 is more than one child in the floating con) or to drag the
2256 floating con (if it’s the one at the top).
2257 </p>
2258 </li>
2259 <li>
2260 <p>
2261 click on border in a floating con to resize the floating con
2262 </p>
2263 </li>
2264 <li>
2265 <p>
2266 floating_modifier + right mouse button to resize a tiling con
2267 </p>
2268 </li>
2269 <li>
2270 <p>
2271 click on border/decoration to resize a tiling con
2272 </p>
2273 </li>
2274 </ul></div>
2275 </div>
2276 </div>
2277 <div class="sect1">
2278 <h2 id="_gotchas">21. Gotchas</h2>
2279 <div class="sectionbody">
2280 <div class="ulist"><ul>
2281 <li>
2282 <p>
2283 Forgetting to call <code>xcb_flush(conn);</code> after sending a request. This usually
2284 leads to code which looks like it works fine but which does not work under
2285 certain conditions.
2286 </p>
2287 </li>
2288 <li>
2289 <p>
2290 Forgetting to call <code>floating_fix_coordinates(con, old_rect, new_rect)</code> after
2291 moving workspaces across outputs. Coordinates for floating containers are
2292 not relative to workspace boundaries, so you must correct their coordinates
2293 or those containers will show up in the wrong workspace or not at all.
2294 </p>
2295 </li>
2296 </ul></div>
2297 </div>
2298 </div>
2299 <div class="sect1">
2300 <h2 id="_thought_experiments">22. Thought experiments</h2>
2301 <div class="sectionbody">
2302 <div class="paragraph"><p>In this section, we collect thought experiments, so that we don’t forget our
2303 thoughts about specific topics. They are not necessary to get into hacking i3,
2304 but if you are interested in one of the topics they cover, you should read them
2305 before asking us why things are the way they are or why we don’t implement
2306 things.</p></div>
2307 <div class="sect2">
2308 <h3 id="_using_cgroups_per_workspace">22.1. Using cgroups per workspace</h3>
2309 <div class="paragraph"><p>cgroups (control groups) are a linux-only feature which provides the ability to
2310 group multiple processes. For each group, you can individually set resource
2311 limits, like allowed memory usage. Furthermore, and more importantly for our
2312 purposes, they serve as a namespace, a label which you can attach to processes
2313 and their children.</p></div>
2314 <div class="paragraph"><p>One interesting use for cgroups is having one cgroup per workspace (or
2315 container, doesn’t really matter). That way, you could set different priorities
2316 and have a workspace for important stuff (say, writing a LaTeX document or
2317 programming) and a workspace for unimportant background stuff (say,
2318 JDownloader). Both tasks can obviously consume a lot of I/O resources, but in
2319 this example it doesn’t really matter if JDownloader unpacks the download a
2320 minute earlier or not. However, your compiler should work as fast as possible.
2321 Having one cgroup per workspace, you would assign more resources to the
2322 programming workspace.</p></div>
2323 <div class="paragraph"><p>Another interesting feature is that an inherent problem of the workspace
2324 concept could be solved by using cgroups: When starting an application on
2325 workspace 1, then switching to workspace 2, you will get the application’s
2326 window(s) on workspace 2 instead of the one you started it on. This is because
2327 the window manager does not have any mapping between the process it starts (or
2328 gets started in any way) and the window(s) which appear.</p></div>
2329 <div class="paragraph"><p>Imagine for example using dmenu: The user starts dmenu by pressing Mod+d, dmenu
2330 gets started with PID 3390. The user then decides to launch Firefox, which
2331 takes a long time. So they enter firefox into dmenu and press enter. Firefox
2332 gets started with PID 4001. When it finally finishes loading, it creates an X11
2333 window and uses MapWindow to make it visible. This is the first time i3
2334 actually gets in touch with Firefox. It decides to map the window, but it has
2335 no way of knowing that this window (even though it has the _NET_WM_PID property
2336 set to 4001) belongs to the dmenu the user started before.</p></div>
2337 <div class="paragraph"><p>How do cgroups help with this? Well, when pressing Mod+d to launch dmenu, i3
2338 would create a new cgroup, let’s call it i3-3390-1. It launches dmenu in that
2339 cgroup, which gets PID 3390. As before, the user enters firefox and Firefox
2340 gets launched with PID 4001. This time, though, the Firefox process with PID
2341 4001 is <strong>also</strong> member of the cgroup i3-3390-1 (because fork()ing in a cgroup
2342 retains the cgroup property). Therefore, when mapping the window, i3 can look
2343 up in which cgroup the process is and can establish a mapping between the
2344 workspace and the window.</p></div>
2345 <div class="paragraph"><p>There are multiple problems with this approach:</p></div>
2346 <div class="olist arabic"><ol class="arabic">
2347 <li>
2348 <p>
2349 Every application has to properly set <code>_NET_WM_PID</code>. This is acceptable and
2350 patches can be written for the few applications which don’t set the hint yet.
2351 </p>
2352 </li>
2353 <li>
2354 <p>
2355 It does only work on Linux, since cgroups are a Linux-only feature. Again,
2356 this is acceptable.
2357 </p>
2358 </li>
2359 <li>
2360 <p>
2361 The main problem is that some applications create X11 windows completely
2362 independent of UNIX processes. An example for this is Chromium (or
2363 gnome-terminal), which, when being started a second time, communicates with
2364 the first process and lets the first process open a new window. Therefore, if
2365 you have a Chromium window on workspace 2 and you are currently working on
2366 workspace 3, starting <code>chromium</code> does not lead to the desired result (the
2367 window will open on workspace 2).
2368 </p>
2369 </li>
2370 </ol></div>
2371 <div class="paragraph"><p>Therefore, my conclusion is that the only proper way of fixing the "window gets
2372 opened on the wrong workspace" problem is in the application itself. Most
2373 modern applications support freedesktop startup-notifications which can be
2374 used for this.</p></div>
2375 </div>
2376 </div>
2377 </div>
2378 </div>
2379 <div id="footnotes"><hr /></div>
2380 <div id="footer">
2381 <div id="footer-text">
2382 Last updated
2383 2019-01-27 16:45:19 CET
2384 </div>
2385 </div>
2386 </body>
2387 </html>
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use Pod::Simple::HTML;
6 use v5.10;
7
8 $Pod::Simple::HTML::Tagmap{'Verbatim'} = '<pre><tt>';
9 $Pod::Simple::HTML::Tagmap{'VerbatimFormatted'} = '<pre><tt>';
10 $Pod::Simple::HTML::Tagmap{'/Verbatim'} = '</tt></pre>';
11 $Pod::Simple::HTML::Tagmap{'/VerbatimFormatted'} = '</tt></pre>';
12
13 if (@ARGV < 2) {
14 say STDERR "Syntax: i3-pod2html <pod-input-path> <html-output-path>";
15 exit 1;
16 }
17
18 open(my $in, '<', $ARGV[0]) or die "Couldn’t open $ARGV[0] for reading: $!\n";
19 open(my $out, '>', $ARGV[1]) or die "Couldn’t open $ARGV[1] for writing: $!\n";
20
21 my $parser = Pod::Simple::HTML->new();
22
23 $parser->index(1);
24 $parser->html_header_before_title(
25 <<'EOF'
26 <!doctype html>
27 <html lang="en">
28 <head>
29 <link rel="icon" type="image/png" href="/favicon.png">
30 <meta charset="utf-8">
31 <meta name="generator" content="Pod::Simple::HTML">
32 <meta name="description" content="i3 Perl documentation">
33 <link rel="stylesheet" href="https://i3wm.org/css/style.css" type="text/css" />
34 <style type="text/css">
35 .pod pre {
36 background: #333;
37 border: 1px solid #555;
38 border-left: 5px solid #555;
39 padding: 0.5em;
40 padding-left: 0;
41 padding-right: 0.5em;
42 white-space: pre;
43 color: white;
44 }
45
46 .pod ul {
47 list-style-type: none;
48 }
49 .pod li {
50 margin-bottom: 0 !important;
51 }
52
53 tt {
54 font-family: 'Droid Sans Mono', sans-serif;
55 font-size: inherit;
56 }
57
58 .pod h1 a, .pod h2 a, .pod h3 a, .pod h4 a {
59 text-decoration: none;
60 color: white;
61 }
62 </style>
63 <title>
64 EOF
65 );
66 $parser->html_header_after_title(
67 <<'EOF'
68 </title>
69 </head>
70 <body>
71
72 <div id="main">
73 <a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>
74 <ul id="nav">
75 <li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>
76 <li><a href="/screenshots">Screens</a></li>
77 <li><a href="https://www.reddit.com/r/i3wm/">FAQ</a></li>
78 <li><a href="/contact">Contact</a></li>
79 <li><a href="https://bugs.i3wm.org/">Bugs</a></li>
80 </ul>
81 <br style="clear: both">
82 <div id="content" class="pod">
83 <h1>i3 Perl documentation</h1>
84
85 EOF
86 );
87
88 $parser->output_fh($out);
89 $parser->parse_file($in);
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 i3bar input protocol
1 ====================
2 Michael Stapelberg <michael@i3wm.org>
3 August 2012
4
5 This document explains the protocol in which i3bar expects its input. It
6 provides support for colors, urgency, shortening and easy manipulation.
7
8 == Rationale for choosing JSON
9
10 Before describing the protocol, let’s cover why JSON is a building block of
11 this protocol.
12
13 1. Other bar display programs such as dzen2 or xmobar are using in-band
14 signaling: they recognize certain sequences (like ^fg(#330000) in your input
15 text). We would like to avoid that and separate information from
16 meta-information. By information, we mean the actual output, like the IP
17 address of your ethernet adapter and by meta-information, we mean in which
18 color it should be displayed right now.
19 2. It is easy to write a simple script which manipulates part(s) of the input.
20 Each block of information (like a block for the disk space indicator, a block
21 for the current IP address, etc.) can be identified specifically and modified
22 in whichever way you like.
23 3. It remains easy to write a simple script which just suffixes (or prefixes) a
24 status line input, because tools like i3status will output their JSON in
25 such a way that each line array will be terminated by a newline. Therefore,
26 you are not required to use a streaming JSON parser, but you can use any
27 JSON parser and write your script in any programming language. In fact, you
28 can decide to not bother with the JSON parsing at all and just inject your
29 output at a specific position (beginning or end).
30 4. Relying on JSON does not introduce any new dependencies. In fact, the IPC
31 interface of i3 also uses JSON, therefore i3bar already depends on JSON.
32
33 The only point against using JSON is computational complexity. If that really
34 bothers you, just use the plain text input format (which i3bar will continue to
35 support).
36
37 == The protocol
38
39 The first message of the protocol is a header block, which contains (at least)
40 the version of the protocol to be used. In case there are significant changes
41 (not only additions), the version will be incremented. i3bar will still
42 understand the old protocol version, but in order to use the new one, you need
43 to provide the correct version. The header block is terminated by a newline and
44 consists of a single JSON hash:
45
46 *Minimal example*:
47 ------------------------------
48 { "version": 1 }
49 ------------------------------
50
51 *All features example*:
52 ------------------------------
53 { "version": 1, "stop_signal": 10, "cont_signal": 12, "click_events": true }
54 ------------------------------
55
56 (Note that before i3 v4.3 the precise format had to be +{"version":1}+,
57 byte-for-byte.)
58
59 What follows is an infinite array (so it should be parsed by a streaming JSON
60 parser, but as described above you can go for a simpler solution), whose
61 elements are one array per status line. A status line is one unit of
62 information which should be displayed at a time. i3bar will not display any
63 input until the status line is complete. In each status line, every block will
64 be represented by a JSON hash:
65
66 *Example*:
67 ------
68 [
69
70 [
71 {
72 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
73 "color": "#00ff00"
74 },
75 {
76 "full_text": "2012-01-05 20:00:01"
77 }
78 ],
79
80 [
81 {
82 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
83 "color": "#00ff00"
84 },
85 {
86 "full_text": "2012-01-05 20:00:02"
87 }
88 ],
89
90 ------
91
92 Please note that this example was pretty printed for human consumption.
93 i3status and others will output single statuslines in one line, separated by
94 \n.
95
96 You can find an example of a shell script which can be used as your
97 +status_command+ in the bar configuration at
98 https://github.com/i3/i3/blob/next/contrib/trivial-bar-script.sh
99
100 === Header in detail
101
102 version::
103 The version number (as an integer) of the i3bar protocol you will use.
104 stop_signal::
105 Specify to i3bar the signal (as an integer) to send to stop your
106 processing.
107 The default value (if none is specified) is SIGSTOP.
108 cont_signal::
109 Specify to i3bar the signal (as an integer) to send to continue your
110 processing.
111 The default value (if none is specified) is SIGCONT.
112 click_events::
113 If specified and true i3bar will write an infinite array (same as above)
114 to your stdin.
115
116 === Blocks in detail
117
118 full_text::
119 The +full_text+ will be displayed by i3bar on the status line. This is the
120 only required key. If +full_text+ is an empty string, the block will be
121 skipped.
122 short_text::
123 Where appropriate, the +short_text+ (string) entry should also be
124 provided. It will be used in case the status line needs to be shortened
125 because it uses more space than your screen provides. For example, when
126 displaying an IPv6 address, the prefix is usually (!) more relevant
127 than the suffix, because the latter stays constant when using autoconf,
128 while the prefix changes. When displaying the date, the time is more
129 important than the date (it is more likely that you know which day it
130 is than what time it is).
131 color::
132 To make the current state of the information easy to spot, colors can
133 be used. For example, the wireless block could be displayed in red
134 (using the +color+ (string) entry) if the card is not associated with
135 any network and in green or yellow (depending on the signal strength)
136 when it is associated.
137 Colors are specified in hex (like in HTML), starting with a leading
138 hash sign. For example, +#ff0000+ means red.
139 background::
140 Overrides the background color for this particular block.
141 border::
142 Overrides the border color for this particular block.
143 min_width::
144 The minimum width (in pixels) of the block. If the content of the
145 +full_text+ key take less space than the specified min_width, the block
146 will be padded to the left and/or the right side, according to the +align+
147 key. This is useful when you want to prevent the whole status line to shift
148 when value take more or less space between each iteration.
149 The value can also be a string. In this case, the width of the text given
150 by +min_width+ determines the minimum width of the block. This is useful
151 when you want to set a sensible minimum width regardless of which font you
152 are using, and at what particular size.
153 align::
154 Align text on the +center+, +right+ or +left+ (default) of the block, when
155 the minimum width of the latter, specified by the +min_width+ key, is not
156 reached.
157 name and instance::
158 Every block should have a unique +name+ (string) entry so that it can
159 be easily identified in scripts which process the output. i3bar
160 completely ignores the name and instance fields. Make sure to also
161 specify an +instance+ (string) entry where appropriate. For example,
162 the user can have multiple disk space blocks for multiple mount points.
163 urgent::
164 A boolean which specifies whether the current value is urgent. Examples
165 are battery charge values below 1 percent or no more available disk
166 space (for non-root users). The presentation of urgency is up to i3bar.
167 separator::
168 A boolean which specifies whether a separator line should be drawn
169 after this block. The default is true, meaning the separator line will
170 be drawn. Note that if you disable the separator line, there will still
171 be a gap after the block, unless you also use +separator_block_width+.
172 separator_block_width::
173 The amount of pixels to leave blank after the block. In the middle of
174 this gap, a separator line will be drawn unless +separator+ is
175 disabled. Normally, you want to set this to an odd value (the default
176 is 9 pixels), since the separator line is drawn in the middle.
177 markup::
178 A string that indicates how the text of the block should be parsed. Set to
179 +"pango"+ to use https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
180 Set to +"none"+ to not use any markup (default). Pango markup only works
181 if you use a pango font.
182
183 If you want to put in your own entries into a block, prefix the key with an
184 underscore (_). i3bar will ignore all keys it doesn’t understand, and prefixing
185 them with an underscore makes it clear in every script that they are not part
186 of the i3bar protocol.
187
188 *Example*:
189 ------------------------------------------
190 {
191 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
192 "_ethernet_vendor": "Intel"
193 }
194 ------------------------------------------
195
196 In the following example, the longest (widest) possible value of the block is
197 used to set the minimum width:
198
199 ------------------------------------------
200 {
201 "full_text": "CPU 4%",
202 "min_width": "CPU 100%",
203 "align": "left"
204 }
205 ------------------------------------------
206
207 An example of a block which uses all possible entries follows:
208
209 *Example*:
210 ------------------------------------------
211 {
212 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
213 "short_text": "10.0.0.1",
214 "color": "#00ff00",
215 "background": "#1c1c1c",
216 "border": "#ee0000",
217 "min_width": 300,
218 "align": "right",
219 "urgent": false,
220 "name": "ethernet",
221 "instance": "eth0",
222 "separator": true,
223 "separator_block_width": 9
224 }
225 ------------------------------------------
226
227 === Click events
228
229 If enabled i3bar will send you notifications if the user clicks on a block and
230 looks like this:
231
232 name::
233 Name of the block, if set
234 instance::
235 Instance of the block, if set
236 x, y::
237 X11 root window coordinates where the click occurred
238 button::
239 X11 button ID (for example 1 to 3 for left/middle/right mouse button)
240 relative_x, relative_y::
241 Coordinates where the click occurred, with respect to the top left corner
242 of the block
243 width, height::
244 Width and height (in px) of the block
245 modifiers::
246 An array of the modifiers active when the click occurred. The order in which
247 modifiers are listed is not guaranteed.
248
249 *Example*:
250 ------------------------------------------
251 {
252 "name": "ethernet",
253 "instance": "eth0",
254 "button": 1,
255 "modifiers": ["Shift", "Mod1"],
256 "x": 1320,
257 "y": 1400,
258 "relative_x": 12,
259 "relative_y": 8,
260 "width": 50,
261 "height": 22
262 }
263 ------------------------------------------
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>i3bar input protocol</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>i3bar input protocol</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">August 2012</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>This document explains the protocol in which i3bar expects its input. It
749 provides support for colors, urgency, shortening and easy manipulation.</p></div>
750 </div>
751 </div>
752 <div class="sect1">
753 <h2 id="_rationale_for_choosing_json">1. Rationale for choosing JSON</h2>
754 <div class="sectionbody">
755 <div class="paragraph"><p>Before describing the protocol, let’s cover why JSON is a building block of
756 this protocol.</p></div>
757 <div class="olist arabic"><ol class="arabic">
758 <li>
759 <p>
760 Other bar display programs such as dzen2 or xmobar are using in-band
761 signaling: they recognize certain sequences (like ^fg(#330000) in your input
762 text). We would like to avoid that and separate information from
763 meta-information. By information, we mean the actual output, like the IP
764 address of your ethernet adapter and by meta-information, we mean in which
765 color it should be displayed right now.
766 </p>
767 </li>
768 <li>
769 <p>
770 It is easy to write a simple script which manipulates part(s) of the input.
771 Each block of information (like a block for the disk space indicator, a block
772 for the current IP address, etc.) can be identified specifically and modified
773 in whichever way you like.
774 </p>
775 </li>
776 <li>
777 <p>
778 It remains easy to write a simple script which just suffixes (or prefixes) a
779 status line input, because tools like i3status will output their JSON in
780 such a way that each line array will be terminated by a newline. Therefore,
781 you are not required to use a streaming JSON parser, but you can use any
782 JSON parser and write your script in any programming language. In fact, you
783 can decide to not bother with the JSON parsing at all and just inject your
784 output at a specific position (beginning or end).
785 </p>
786 </li>
787 <li>
788 <p>
789 Relying on JSON does not introduce any new dependencies. In fact, the IPC
790 interface of i3 also uses JSON, therefore i3bar already depends on JSON.
791 </p>
792 </li>
793 </ol></div>
794 <div class="paragraph"><p>The only point against using JSON is computational complexity. If that really
795 bothers you, just use the plain text input format (which i3bar will continue to
796 support).</p></div>
797 </div>
798 </div>
799 <div class="sect1">
800 <h2 id="_the_protocol">2. The protocol</h2>
801 <div class="sectionbody">
802 <div class="paragraph"><p>The first message of the protocol is a header block, which contains (at least)
803 the version of the protocol to be used. In case there are significant changes
804 (not only additions), the version will be incremented. i3bar will still
805 understand the old protocol version, but in order to use the new one, you need
806 to provide the correct version. The header block is terminated by a newline and
807 consists of a single JSON hash:</p></div>
808 <div class="paragraph"><p><strong>Minimal example</strong>:</p></div>
809 <div class="listingblock">
810 <div class="content">
811 <pre><code>{ "version": 1 }</code></pre>
812 </div></div>
813 <div class="paragraph"><p><strong>All features example</strong>:</p></div>
814 <div class="listingblock">
815 <div class="content">
816 <pre><code>{ "version": 1, "stop_signal": 10, "cont_signal": 12, "click_events": true }</code></pre>
817 </div></div>
818 <div class="paragraph"><p>(Note that before i3 v4.3 the precise format had to be <code>{"version":1}</code>,
819 byte-for-byte.)</p></div>
820 <div class="paragraph"><p>What follows is an infinite array (so it should be parsed by a streaming JSON
821 parser, but as described above you can go for a simpler solution), whose
822 elements are one array per status line. A status line is one unit of
823 information which should be displayed at a time. i3bar will not display any
824 input until the status line is complete. In each status line, every block will
825 be represented by a JSON hash:</p></div>
826 <div class="paragraph"><p><strong>Example</strong>:</p></div>
827 <div class="listingblock">
828 <div class="content">
829 <pre><code>[
830
831 [
832 {
833 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
834 "color": "#00ff00"
835 },
836 {
837 "full_text": "2012-01-05 20:00:01"
838 }
839 ],
840
841 [
842 {
843 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
844 "color": "#00ff00"
845 },
846 {
847 "full_text": "2012-01-05 20:00:02"
848 }
849 ],
850 …</code></pre>
851 </div></div>
852 <div class="paragraph"><p>Please note that this example was pretty printed for human consumption.
853 i3status and others will output single statuslines in one line, separated by
854 \n.</p></div>
855 <div class="paragraph"><p>You can find an example of a shell script which can be used as your
856 <code>status_command</code> in the bar configuration at
857 <a href="https://github.com/i3/i3/blob/next/contrib/trivial-bar-script.sh">https://github.com/i3/i3/blob/next/contrib/trivial-bar-script.sh</a></p></div>
858 <div class="sect2">
859 <h3 id="_header_in_detail">2.1. Header in detail</h3>
860 <div class="dlist"><dl>
861 <dt class="hdlist1">
862 version
863 </dt>
864 <dd>
865 <p>
866 The version number (as an integer) of the i3bar protocol you will use.
867 </p>
868 </dd>
869 <dt class="hdlist1">
870 stop_signal
871 </dt>
872 <dd>
873 <p>
874 Specify to i3bar the signal (as an integer) to send to stop your
875 processing.
876 The default value (if none is specified) is SIGSTOP.
877 </p>
878 </dd>
879 <dt class="hdlist1">
880 cont_signal
881 </dt>
882 <dd>
883 <p>
884 Specify to i3bar the signal (as an integer) to send to continue your
885 processing.
886 The default value (if none is specified) is SIGCONT.
887 </p>
888 </dd>
889 <dt class="hdlist1">
890 click_events
891 </dt>
892 <dd>
893 <p>
894 If specified and true i3bar will write an infinite array (same as above)
895 to your stdin.
896 </p>
897 </dd>
898 </dl></div>
899 </div>
900 <div class="sect2">
901 <h3 id="_blocks_in_detail">2.2. Blocks in detail</h3>
902 <div class="dlist"><dl>
903 <dt class="hdlist1">
904 full_text
905 </dt>
906 <dd>
907 <p>
908 The <code>full_text</code> will be displayed by i3bar on the status line. This is the
909 only required key. If <code>full_text</code> is an empty string, the block will be
910 skipped.
911 </p>
912 </dd>
913 <dt class="hdlist1">
914 short_text
915 </dt>
916 <dd>
917 <p>
918 Where appropriate, the <code>short_text</code> (string) entry should also be
919 provided. It will be used in case the status line needs to be shortened
920 because it uses more space than your screen provides. For example, when
921 displaying an IPv6 address, the prefix is usually (!) more relevant
922 than the suffix, because the latter stays constant when using autoconf,
923 while the prefix changes. When displaying the date, the time is more
924 important than the date (it is more likely that you know which day it
925 is than what time it is).
926 </p>
927 </dd>
928 <dt class="hdlist1">
929 color
930 </dt>
931 <dd>
932 <p>
933 To make the current state of the information easy to spot, colors can
934 be used. For example, the wireless block could be displayed in red
935 (using the <code>color</code> (string) entry) if the card is not associated with
936 any network and in green or yellow (depending on the signal strength)
937 when it is associated.
938 Colors are specified in hex (like in HTML), starting with a leading
939 hash sign. For example, <code>#ff0000</code> means red.
940 </p>
941 </dd>
942 <dt class="hdlist1">
943 background
944 </dt>
945 <dd>
946 <p>
947 Overrides the background color for this particular block.
948 </p>
949 </dd>
950 <dt class="hdlist1">
951 border
952 </dt>
953 <dd>
954 <p>
955 Overrides the border color for this particular block.
956 </p>
957 </dd>
958 <dt class="hdlist1">
959 min_width
960 </dt>
961 <dd>
962 <p>
963 The minimum width (in pixels) of the block. If the content of the
964 <code>full_text</code> key take less space than the specified min_width, the block
965 will be padded to the left and/or the right side, according to the <code>align</code>
966 key. This is useful when you want to prevent the whole status line to shift
967 when value take more or less space between each iteration.
968 The value can also be a string. In this case, the width of the text given
969 by <code>min_width</code> determines the minimum width of the block. This is useful
970 when you want to set a sensible minimum width regardless of which font you
971 are using, and at what particular size.
972 </p>
973 </dd>
974 <dt class="hdlist1">
975 align
976 </dt>
977 <dd>
978 <p>
979 Align text on the <code>center</code>, <code>right</code> or <code>left</code> (default) of the block, when
980 the minimum width of the latter, specified by the <code>min_width</code> key, is not
981 reached.
982 </p>
983 </dd>
984 <dt class="hdlist1">
985 name and instance
986 </dt>
987 <dd>
988 <p>
989 Every block should have a unique <code>name</code> (string) entry so that it can
990 be easily identified in scripts which process the output. i3bar
991 completely ignores the name and instance fields. Make sure to also
992 specify an <code>instance</code> (string) entry where appropriate. For example,
993 the user can have multiple disk space blocks for multiple mount points.
994 </p>
995 </dd>
996 <dt class="hdlist1">
997 urgent
998 </dt>
999 <dd>
1000 <p>
1001 A boolean which specifies whether the current value is urgent. Examples
1002 are battery charge values below 1 percent or no more available disk
1003 space (for non-root users). The presentation of urgency is up to i3bar.
1004 </p>
1005 </dd>
1006 <dt class="hdlist1">
1007 separator
1008 </dt>
1009 <dd>
1010 <p>
1011 A boolean which specifies whether a separator line should be drawn
1012 after this block. The default is true, meaning the separator line will
1013 be drawn. Note that if you disable the separator line, there will still
1014 be a gap after the block, unless you also use <code>separator_block_width</code>.
1015 </p>
1016 </dd>
1017 <dt class="hdlist1">
1018 separator_block_width
1019 </dt>
1020 <dd>
1021 <p>
1022 The amount of pixels to leave blank after the block. In the middle of
1023 this gap, a separator line will be drawn unless <code>separator</code> is
1024 disabled. Normally, you want to set this to an odd value (the default
1025 is 9 pixels), since the separator line is drawn in the middle.
1026 </p>
1027 </dd>
1028 <dt class="hdlist1">
1029 markup
1030 </dt>
1031 <dd>
1032 <p>
1033 A string that indicates how the text of the block should be parsed. Set to
1034 <code>"pango"</code> to use <a href="https://developer.gnome.org/pango/stable/PangoMarkupFormat.html">Pango markup</a>.
1035 Set to <code>"none"</code> to not use any markup (default). Pango markup only works
1036 if you use a pango font.
1037 </p>
1038 </dd>
1039 </dl></div>
1040 <div class="paragraph"><p>If you want to put in your own entries into a block, prefix the key with an
1041 underscore (_). i3bar will ignore all keys it doesn’t understand, and prefixing
1042 them with an underscore makes it clear in every script that they are not part
1043 of the i3bar protocol.</p></div>
1044 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1045 <div class="listingblock">
1046 <div class="content">
1047 <pre><code>{
1048 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
1049 "_ethernet_vendor": "Intel"
1050 }</code></pre>
1051 </div></div>
1052 <div class="paragraph"><p>In the following example, the longest (widest) possible value of the block is
1053 used to set the minimum width:</p></div>
1054 <div class="listingblock">
1055 <div class="content">
1056 <pre><code>{
1057 "full_text": "CPU 4%",
1058 "min_width": "CPU 100%",
1059 "align": "left"
1060 }</code></pre>
1061 </div></div>
1062 <div class="paragraph"><p>An example of a block which uses all possible entries follows:</p></div>
1063 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1064 <div class="listingblock">
1065 <div class="content">
1066 <pre><code>{
1067 "full_text": "E: 10.0.0.1 (1000 Mbit/s)",
1068 "short_text": "10.0.0.1",
1069 "color": "#00ff00",
1070 "background": "#1c1c1c",
1071 "border": "#ee0000",
1072 "min_width": 300,
1073 "align": "right",
1074 "urgent": false,
1075 "name": "ethernet",
1076 "instance": "eth0",
1077 "separator": true,
1078 "separator_block_width": 9
1079 }</code></pre>
1080 </div></div>
1081 </div>
1082 <div class="sect2">
1083 <h3 id="_click_events">2.3. Click events</h3>
1084 <div class="paragraph"><p>If enabled i3bar will send you notifications if the user clicks on a block and
1085 looks like this:</p></div>
1086 <div class="dlist"><dl>
1087 <dt class="hdlist1">
1088 name
1089 </dt>
1090 <dd>
1091 <p>
1092 Name of the block, if set
1093 </p>
1094 </dd>
1095 <dt class="hdlist1">
1096 instance
1097 </dt>
1098 <dd>
1099 <p>
1100 Instance of the block, if set
1101 </p>
1102 </dd>
1103 <dt class="hdlist1">
1104 x, y
1105 </dt>
1106 <dd>
1107 <p>
1108 X11 root window coordinates where the click occurred
1109 </p>
1110 </dd>
1111 <dt class="hdlist1">
1112 button
1113 </dt>
1114 <dd>
1115 <p>
1116 X11 button ID (for example 1 to 3 for left/middle/right mouse button)
1117 </p>
1118 </dd>
1119 <dt class="hdlist1">
1120 relative_x, relative_y
1121 </dt>
1122 <dd>
1123 <p>
1124 Coordinates where the click occurred, with respect to the top left corner
1125 of the block
1126 </p>
1127 </dd>
1128 <dt class="hdlist1">
1129 width, height
1130 </dt>
1131 <dd>
1132 <p>
1133 Width and height (in px) of the block
1134 </p>
1135 </dd>
1136 <dt class="hdlist1">
1137 modifiers
1138 </dt>
1139 <dd>
1140 <p>
1141 An array of the modifiers active when the click occurred. The order in which
1142 modifiers are listed is not guaranteed.
1143 </p>
1144 </dd>
1145 </dl></div>
1146 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1147 <div class="listingblock">
1148 <div class="content">
1149 <pre><code>{
1150 "name": "ethernet",
1151 "instance": "eth0",
1152 "button": 1,
1153 "modifiers": ["Shift", "Mod1"],
1154 "x": 1320,
1155 "y": 1400,
1156 "relative_x": 12,
1157 "relative_y": 8,
1158 "width": 50,
1159 "height": 22
1160 }</code></pre>
1161 </div></div>
1162 </div>
1163 </div>
1164 </div>
1165 </div>
1166 <div id="footnotes"><hr /></div>
1167 <div id="footer">
1168 <div id="footer-text">
1169 Last updated
1170 2019-01-27 16:45:19 CET
1171 </div>
1172 </div>
1173 </body>
1174 </html>
0 IPC interface (interprocess communication)
1 ==========================================
2 Michael Stapelberg <michael@i3wm.org>
3 September 2017
4
5 This document describes how to interface with i3 from a separate process. This
6 is useful for example to remote-control i3 (to write test cases for example) or
7 to get various information like the current workspaces to implement an external
8 workspace bar.
9
10 The method of choice for IPC in our case is a unix socket because it has very
11 little overhead on both sides and is usually available without headaches in
12 most languages. In the default configuration file, the ipc-socket gets created
13 in +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username, +%p+ is
14 the PID of i3 and XXXXXX is a string of random characters from the portable
15 filename character set (see mkdtemp(3)). You can get the socketpath from i3 by
16 calling +i3 --get-socketpath+.
17
18 All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
19 X11 property, stored on the X11 root window.
20
21 [WARNING]
22 .Use an existing library!
23 There are existing libraries for many languages. You can have a look at
24 <<libraries>> or search the web if your language of choice is not mentioned.
25 Usually, it is not necessary to implement low-level communication with i3
26 directly.
27
28 == Establishing a connection
29
30 To establish a connection, simply open the IPC socket. The following code
31 snippet illustrates this in Perl:
32
33 -------------------------------------------------------------
34 use IO::Socket::UNIX;
35 chomp(my $path = qx(i3 --get-socketpath));
36 my $sock = IO::Socket::UNIX->new(Peer => $path);
37 -------------------------------------------------------------
38
39 == Sending messages to i3
40
41 To send a message to i3, you have to format in the binary message format which
42 i3 expects. This format specifies a magic string in the beginning to ensure
43 the integrity of messages (to prevent follow-up errors). Following the magic
44 string comes the length of the payload of the message as 32-bit integer, and
45 the type of the message as 32-bit integer (the integers are not converted, so
46 they are in native byte order).
47
48 The magic string currently is "i3-ipc" and will only be changed when a change
49 in the IPC API is done which breaks compatibility (we hope that we don’t need
50 to do that).
51
52 .Currently implemented message types
53 [options="header",cols="^10%,^20%,^20%,^50%"]
54 |======================================================
55 | Type (numeric) | Type (name) | Reply type | Purpose
56 | 0 | +RUN_COMMAND+ | <<_command_reply,COMMAND>> | Run the payload as an i3 command (like the commands you can bind to keys).
57 | 1 | +GET_WORKSPACES+ | <<_workspaces_reply,WORKSPACES>> | Get the list of current workspaces.
58 | 2 | +SUBSCRIBE+ | <<_subscribe_reply,SUBSCRIBE>> | Subscribe this IPC connection to the event types specified in the message payload. See <<events>>.
59 | 3 | +GET_OUTPUTS+ | <<_outputs_reply,OUTPUTS>> | Get the list of current outputs.
60 | 4 | +GET_TREE+ | <<_tree_reply,TREE>> | Get the i3 layout tree.
61 | 5 | +GET_MARKS+ | <<_marks_reply,MARKS>> | Gets the names of all currently set marks.
62 | 6 | +GET_BAR_CONFIG+ | <<_bar_config_reply,BAR_CONFIG>> | Gets the specified bar configuration or the names of all bar configurations if payload is empty.
63 | 7 | +GET_VERSION+ | <<_version_reply,VERSION>> | Gets the i3 version.
64 | 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes.
65 | 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config.
66 | 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload.
67 | 11 | +SYNC+ | <<_sync_reply,SYNC>> | Sends an i3 sync event with the specified random value to the specified window.
68 |======================================================
69
70 So, a typical message could look like this:
71 --------------------------------------------------
72 "i3-ipc" <message length> <message type> <payload>
73 --------------------------------------------------
74
75 Or, as a hexdump:
76 ------------------------------------------------------------------------------
77 00000000 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex|
78 00000010 69 74 |it|
79 ------------------------------------------------------------------------------
80
81 To generate and send such a message, you could use the following code in Perl:
82 ------------------------------------------------------------
83 sub format_ipc_command {
84 my ($msg) = @_;
85 my $len;
86 # Get the real byte count (vs. amount of characters)
87 { use bytes; $len = length($msg); }
88 return "i3-ipc" . pack("LL", $len, 0) . $msg;
89 }
90
91 $sock->write(format_ipc_command("exit"));
92 ------------------------------------------------------------------------------
93
94 == Receiving replies from i3
95
96 Replies from i3 usually consist of a simple string (the length of the string
97 is the message_length, so you can consider them length-prefixed) which in turn
98 contain the JSON serialization of a data structure. For example, the
99 GET_WORKSPACES message returns an array of workspaces (each workspace is a map
100 with certain attributes).
101
102 === Reply format
103
104 The reply format is identical to the normal message format. There also is
105 the magic string, then the message length, then the message type and the
106 payload.
107
108 The following reply types are implemented:
109
110 COMMAND (0)::
111 Confirmation/Error code for the RUN_COMMAND message.
112 WORKSPACES (1)::
113 Reply to the GET_WORKSPACES message.
114 SUBSCRIBE (2)::
115 Confirmation/Error code for the SUBSCRIBE message.
116 OUTPUTS (3)::
117 Reply to the GET_OUTPUTS message.
118 TREE (4)::
119 Reply to the GET_TREE message.
120 MARKS (5)::
121 Reply to the GET_MARKS message.
122 BAR_CONFIG (6)::
123 Reply to the GET_BAR_CONFIG message.
124 VERSION (7)::
125 Reply to the GET_VERSION message.
126 BINDING_MODES (8)::
127 Reply to the GET_BINDING_MODES message.
128 GET_CONFIG (9)::
129 Reply to the GET_CONFIG message.
130 TICK (10)::
131 Reply to the SEND_TICK message.
132
133 [[_command_reply]]
134 === COMMAND reply
135
136 The reply consists of a list of serialized maps for each command that was
137 parsed. Each has the property +success (bool)+ and may also include a
138 human-readable error message in the property +error (string)+.
139
140 *Example:*
141 -------------------
142 [{ "success": true }]
143 -------------------
144
145 [[_workspaces_reply]]
146 === WORKSPACES reply
147
148 The reply consists of a serialized list of workspaces. Each workspace has the
149 following properties:
150
151 num (integer)::
152 The logical number of the workspace. Corresponds to the command
153 to switch to this workspace. For named workspaces, this will be -1.
154 name (string)::
155 The name of this workspace (by default num+1), as changed by the
156 user. Encoded in UTF-8.
157 visible (boolean)::
158 Whether this workspace is currently visible on an output (multiple
159 workspaces can be visible at the same time).
160 focused (boolean)::
161 Whether this workspace currently has the focus (only one workspace
162 can have the focus at the same time).
163 urgent (boolean)::
164 Whether a window on this workspace has the "urgent" flag set.
165 rect (map)::
166 The rectangle of this workspace (equals the rect of the output it
167 is on), consists of x, y, width, height.
168 output (string)::
169 The video output this workspace is on (LVDS1, VGA1, …).
170
171 *Example:*
172 -------------------
173 [
174 {
175 "num": 0,
176 "name": "1",
177 "visible": true,
178 "focused": true,
179 "urgent": false,
180 "rect": {
181 "x": 0,
182 "y": 0,
183 "width": 1280,
184 "height": 800
185 },
186 "output": "LVDS1"
187 },
188 {
189 "num": 1,
190 "name": "2",
191 "visible": false,
192 "focused": false,
193 "urgent": false,
194 "rect": {
195 "x": 0,
196 "y": 0,
197 "width": 1280,
198 "height": 800
199 },
200 "output": "LVDS1"
201 }
202 ]
203 -------------------
204
205 [[_subscribe_reply]]
206 === SUBSCRIBE reply
207
208 The reply consists of a single serialized map. The only property is
209 +success (bool)+, indicating whether the subscription was successful (the
210 default) or whether a JSON parse error occurred.
211
212 *Example:*
213 -------------------
214 { "success": true }
215 -------------------
216
217 [[_outputs_reply]]
218 === OUTPUTS reply
219
220 The reply consists of a serialized list of outputs. Each output has the
221 following properties:
222
223 name (string)::
224 The name of this output (as seen in +xrandr(1)+). Encoded in UTF-8.
225 active (boolean)::
226 Whether this output is currently active (has a valid mode).
227 primary (boolean)::
228 Whether this output is currently the primary output.
229 current_workspace (string)::
230 The name of the current workspace that is visible on this output. +null+ if
231 the output is not active.
232 rect (map)::
233 The rectangle of this output (equals the rect of the output it
234 is on), consists of x, y, width, height.
235
236 *Example:*
237 -------------------
238 [
239 {
240 "name": "LVDS1",
241 "active": true,
242 "current_workspace": "4",
243 "rect": {
244 "x": 0,
245 "y": 0,
246 "width": 1280,
247 "height": 800
248 }
249 },
250 {
251 "name": "VGA1",
252 "active": true,
253 "current_workspace": "1",
254 "rect": {
255 "x": 1280,
256 "y": 0,
257 "width": 1280,
258 "height": 1024
259 }
260 }
261 ]
262 -------------------
263
264 [[_tree_reply]]
265 === TREE reply
266
267 The reply consists of a serialized tree. Each node in the tree (representing
268 one container) has at least the properties listed below. While the nodes might
269 have more properties, please do not use any properties which are not documented
270 here. They are not yet finalized and will probably change!
271
272 id (integer)::
273 The internal ID (actually a C pointer value) of this container. Do not
274 make any assumptions about it. You can use it to (re-)identify and
275 address containers when talking to i3.
276 name (string)::
277 The internal name of this container. For all containers which are part
278 of the tree structure down to the workspace contents, this is set to a
279 nice human-readable name of the container.
280 For containers that have an X11 window, the content is the title
281 (_NET_WM_NAME property) of that window.
282 For all other containers, the content is not defined (yet).
283 type (string)::
284 Type of this container. Can be one of "root", "output", "con",
285 "floating_con", "workspace" or "dockarea".
286 border (string)::
287 Can be either "normal", "none" or "pixel", depending on the
288 container’s border style.
289 current_border_width (integer)::
290 Number of pixels of the border width.
291 layout (string)::
292 Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
293 "output".
294 Other values might be possible in the future, should we add new
295 layouts.
296 orientation (string)::
297 Can be either "none" (for non-split containers), "horizontal" or
298 "vertical".
299 THIS FIELD IS OBSOLETE. It is still present, but your code should not
300 use it. Instead, rely on the layout field.
301 percent (float)::
302 The percentage which this container takes in its parent. A value of
303 +null+ means that the percent property does not make sense for this
304 container, for example for the root container.
305 rect (map)::
306 The absolute display coordinates for this container. Display
307 coordinates means that when you have two 1600x1200 monitors on a single
308 X11 Display (the standard way), the coordinates of the first window on
309 the second monitor are +{ "x": 1600, "y": 0, "width": 1600, "height":
310 1200 }+.
311 window_rect (map)::
312 The coordinates of the *actual client window* inside its container.
313 These coordinates are relative to the container and do not include the
314 window decoration (which is actually rendered on the parent container).
315 So, when using the +default+ layout, you will have a 2 pixel border on
316 each side, making the window_rect +{ "x": 2, "y": 0, "width": 632,
317 "height": 366 }+ (for example).
318 deco_rect (map)::
319 The coordinates of the *window decoration* inside its container. These
320 coordinates are relative to the container and do not include the actual
321 client window.
322 geometry (map)::
323 The original geometry the window specified when i3 mapped it. Used when
324 switching a window to floating mode, for example.
325 window (integer)::
326 The X11 window ID of the *actual client window* inside this container.
327 This field is set to null for split containers or otherwise empty
328 containers. This ID corresponds to what xwininfo(1) and other
329 X11-related tools display (usually in hex).
330 window_properties (map)::
331 X11 window properties title, instance, class, window_role and transient_for.
332 urgent (bool)::
333 Whether this container (window, split container, floating container or
334 workspace) has the urgency hint set, directly or indirectly. All parent
335 containers up until the workspace container will be marked urgent if they
336 have at least one urgent child.
337 focused (bool)::
338 Whether this container is currently focused.
339 focus (array of integer)::
340 List of child node IDs (see +nodes+, +floating_nodes+ and +id+) in focus
341 order. Traversing the tree by following the first entry in this array
342 will result in eventually reaching the one node with +focused+ set to
343 true.
344 nodes (array of node)::
345 The tiling (i.e. non-floating) child containers of this node.
346 floating_nodes (array of node)::
347 The floating child containers of this node. Only non-empty on nodes with
348 type +workspace+.
349
350 Please note that in the following example, I have left out some keys/values
351 which are not relevant for the type of the node. Otherwise, the example would
352 be by far too long (it already is quite long, despite showing only 1 window and
353 one dock window).
354
355 It is useful to have an overview of the structure before taking a look at the
356 JSON dump:
357
358 * root
359 ** LVDS1
360 *** topdock
361 *** content
362 **** workspace 1
363 ***** window 1
364 *** bottomdock
365 **** dock window 1
366 ** VGA1
367
368 *Example:*
369 -----------------------
370 {
371 "id": 6875648,
372 "name": "root",
373 "rect": {
374 "x": 0,
375 "y": 0,
376 "width": 1280,
377 "height": 800
378 },
379 "nodes": [
380
381 {
382 "id": 6878320,
383 "name": "LVDS1",
384 "layout": "output",
385 "rect": {
386 "x": 0,
387 "y": 0,
388 "width": 1280,
389 "height": 800
390 },
391 "nodes": [
392
393 {
394 "id": 6878784,
395 "name": "topdock",
396 "layout": "dockarea",
397 "orientation": "vertical",
398 "rect": {
399 "x": 0,
400 "y": 0,
401 "width": 1280,
402 "height": 0
403 }
404 },
405
406 {
407 "id": 6879344,
408 "name": "content",
409 "rect": {
410 "x": 0,
411 "y": 0,
412 "width": 1280,
413 "height": 782
414 },
415 "nodes": [
416
417 {
418 "id": 6880464,
419 "name": "1",
420 "orientation": "horizontal",
421 "rect": {
422 "x": 0,
423 "y": 0,
424 "width": 1280,
425 "height": 782
426 },
427 "window_properties": {
428 "class": "Evince",
429 "instance": "evince",
430 "title": "Properties",
431 "transient_for": 52428808
432 },
433 "floating_nodes": [],
434 "nodes": [
435
436 {
437 "id": 6929968,
438 "name": "#aa0000",
439 "border": "normal",
440 "percent": 1,
441 "rect": {
442 "x": 0,
443 "y": 18,
444 "width": 1280,
445 "height": 782
446 }
447 }
448
449 ]
450 }
451
452 ]
453 },
454
455 {
456 "id": 6880208,
457 "name": "bottomdock",
458 "layout": "dockarea",
459 "orientation": "vertical",
460 "rect": {
461 "x": 0,
462 "y": 782,
463 "width": 1280,
464 "height": 18
465 },
466 "nodes": [
467
468 {
469 "id": 6931312,
470 "name": "#00aa00",
471 "percent": 1,
472 "rect": {
473 "x": 0,
474 "y": 782,
475 "width": 1280,
476 "height": 18
477 }
478 }
479
480 ]
481 }
482 ]
483 }
484 ]
485 }
486 ------------------------
487
488 [[_marks_reply]]
489 === MARKS reply
490
491 The reply consists of a single array of strings for each container that has a
492 mark. A mark can only be set on one container, so the array is unique.
493 The order of that array is undefined.
494
495 If no window has a mark the response will be the empty array [].
496
497 [[_bar_config_reply]]
498 === BAR_CONFIG reply
499
500 This can be used by third-party workspace bars (especially i3bar, but others
501 are free to implement compatible alternatives) to get the +bar+ block
502 configuration from i3.
503
504 Depending on the input, the reply is either:
505
506 empty input::
507 An array of configured bar IDs
508 Bar ID::
509 A JSON map containing the configuration for the specified bar.
510
511 Each bar configuration has the following properties:
512
513 id (string)::
514 The ID for this bar. Included in case you request multiple
515 configurations and want to differentiate the different replies.
516 mode (string)::
517 Either +dock+ (the bar sets the dock window type) or +hide+ (the bar
518 does not show unless a specific key is pressed).
519 position (string)::
520 Either +bottom+ or +top+ at the moment.
521 status_command (string)::
522 Command which will be run to generate a statusline. Each line on stdout
523 of this command will be displayed in the bar. At the moment, no
524 formatting is supported.
525 font (string)::
526 The font to use for text on the bar.
527 workspace_buttons (boolean)::
528 Display workspace buttons or not? Defaults to true.
529 binding_mode_indicator (boolean)::
530 Display the mode indicator or not? Defaults to true.
531 verbose (boolean)::
532 Should the bar enable verbose output for debugging? Defaults to false.
533 colors (map)::
534 Contains key/value pairs of colors. Each value is a color code in hex,
535 formatted #rrggbb (like in HTML).
536
537 The following colors can be configured at the moment:
538
539 background::
540 Background color of the bar.
541 statusline::
542 Text color to be used for the statusline.
543 separator::
544 Text color to be used for the separator.
545 focused_background::
546 Background color of the bar on the currently focused monitor output.
547 focused_statusline::
548 Text color to be used for the statusline on the currently focused
549 monitor output.
550 focused_separator::
551 Text color to be used for the separator on the currently focused
552 monitor output.
553 focused_workspace_text/focused_workspace_bg/focused_workspace_border::
554 Text/background/border color for a workspace button when the workspace
555 has focus.
556 active_workspace_text/active_workspace_bg/active_workspace_border::
557 Text/background/border color for a workspace button when the workspace
558 is active (visible) on some output, but the focus is on another one.
559 You can only tell this apart from the focused workspace when you are
560 using multiple monitors.
561 inactive_workspace_text/inactive_workspace_bg/inactive_workspace_border::
562 Text/background/border color for a workspace button when the workspace
563 does not have focus and is not active (visible) on any output. This
564 will be the case for most workspaces.
565 urgent_workspace_text/urgent_workspace_bg/urgent_workspace_border::
566 Text/background/border color for workspaces which contain at least one
567 window with the urgency hint set.
568 binding_mode_text/binding_mode_bg/binding_mode_border::
569 Text/background/border color for the binding mode indicator.
570
571
572 *Example of configured bars:*
573 --------------
574 ["bar-bxuqzf"]
575 --------------
576
577 *Example of bar configuration:*
578 --------------
579 {
580 "id": "bar-bxuqzf",
581 "mode": "dock",
582 "position": "bottom",
583 "status_command": "i3status",
584 "font": "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1",
585 "workspace_buttons": true,
586 "binding_mode_indicator": true,
587 "verbose": false,
588 "colors": {
589 "background": "#c0c0c0",
590 "statusline": "#00ff00",
591 "focused_workspace_text": "#ffffff",
592 "focused_workspace_bg": "#000000"
593 }
594 }
595 --------------
596
597 [[_version_reply]]
598 === VERSION reply
599
600 The reply consists of a single JSON dictionary with the following keys:
601
602 major (integer)::
603 The major version of i3, such as +4+.
604 minor (integer)::
605 The minor version of i3, such as +2+. Changes in the IPC interface (new
606 features) will only occur with new minor (or major) releases. However,
607 bugfixes might be introduced in patch releases, too.
608 patch (integer)::
609 The patch version of i3, such as +1+ (when the complete version is
610 +4.2.1+). For versions such as +4.2+, patch will be set to +0+.
611 human_readable (string)::
612 A human-readable version of i3 containing the precise git version,
613 build date and branch name. When you need to display the i3 version to
614 your users, use the human-readable version whenever possible (since
615 this is what +i3 --version+ displays, too).
616 loaded_config_file_name (string)::
617 The current config path.
618
619 *Example:*
620 -------------------
621 {
622 "human_readable" : "4.2-169-gf80b877 (2012-08-05, branch \"next\")",
623 "loaded_config_file_name" : "/home/hwangcc23/.i3/config",
624 "minor" : 2,
625 "patch" : 0,
626 "major" : 4
627 }
628 -------------------
629
630 [[_binding_modes_reply]]
631 === BINDING_MODES reply
632
633 The reply consists of an array of all currently configured binding modes.
634
635 *Example:*
636 ---------------------
637 ["default", "resize"]
638 ---------------------
639
640 [[_config_reply]]
641 === CONFIG reply
642
643 The config reply is a map which currently only contains the "config" member,
644 which is a string containing the config file as loaded by i3 most recently.
645
646 *Example:*
647 -------------------
648 { "config": "font pango:monospace 8\nbindsym Mod4+q exit\n" }
649 -------------------
650
651 [[_tick_reply]]
652 === TICK reply
653
654 The reply is a map containing the "success" member. After the reply was
655 received, the tick event has been written to all IPC connections which subscribe
656 to tick events. UNIX sockets are usually buffered, but you can be certain that
657 once you receive the tick event you just triggered, you must have received all
658 events generated prior to the +SEND_TICK+ message (happened-before relation).
659
660 *Example:*
661 -------------------
662 { "success": true }
663 -------------------
664
665 [[_sync_reply]]
666 === SYNC reply
667
668 The reply is a map containing the "success" member. After the reply was
669 received, the https://i3wm.org/docs/testsuite.html#i3_sync[i3 sync message] was
670 responded to.
671
672 *Example:*
673 -------------------
674 { "success": true }
675 -------------------
676
677 == Events
678
679 [[events]]
680
681 To get informed when certain things happen in i3, clients can subscribe to
682 events. Events consist of a name (like "workspace") and an event reply type
683 (like I3_IPC_EVENT_WORKSPACE). The events sent by i3 are in the same format
684 as replies to specific commands. However, the highest bit of the message type
685 is set to 1 to indicate that this is an event reply instead of a normal reply.
686
687 Caveat: As soon as you subscribe to an event, it is not guaranteed any longer
688 that the requests to i3 are processed in order. This means, the following
689 situation can happen: You send a GET_WORKSPACES request but you receive a
690 "workspace" event before receiving the reply to GET_WORKSPACES. If your
691 program does not want to cope which such kinds of race conditions (an
692 event based library may not have a problem here), I suggest you create a
693 separate connection to receive events.
694
695 If an event message needs to be sent and the socket is not writeable (write
696 returns EAGAIN, happens when the socket doesn't have enough buffer space for
697 writing new data) then i3 uses a queue system to store outgoing messages for
698 each client. This is combined with a timer: if the message queue for a client is
699 not empty and no data where successfully written in the past 10 seconds, the
700 connection is killed. Practically, this means that your client should try to
701 always read events from the socket to avoid having its connection closed.
702
703 === Subscribing to events
704
705 By sending a message of type SUBSCRIBE with a JSON-encoded array as payload
706 you can register to an event.
707
708 *Example:*
709 ---------------------------------
710 type: SUBSCRIBE
711 payload: [ "workspace", "output" ]
712 ---------------------------------
713
714
715 === Available events
716
717 The numbers in parenthesis is the event type (keep in mind that you need to
718 strip the highest bit first).
719
720 workspace (0)::
721 Sent when the user switches to a different workspace, when a new
722 workspace is initialized or when a workspace is removed (because the
723 last client vanished).
724 output (1)::
725 Sent when RandR issues a change notification (of either screens,
726 outputs, CRTCs or output properties).
727 mode (2)::
728 Sent whenever i3 changes its binding mode.
729 window (3)::
730 Sent when a client's window is successfully reparented (that is when i3
731 has finished fitting it into a container), when a window received input
732 focus or when certain properties of the window have changed.
733 barconfig_update (4)::
734 Sent when the hidden_state or mode field in the barconfig of any bar
735 instance was updated and when the config is reloaded.
736 binding (5)::
737 Sent when a configured command binding is triggered with the keyboard or
738 mouse
739 shutdown (6)::
740 Sent when the ipc shuts down because of a restart or exit by user command
741 tick (7)::
742 Sent when the ipc client subscribes to the tick event (with +"first":
743 true+) or when any ipc client sends a SEND_TICK message (with +"first":
744 false+).
745
746 *Example:*
747 --------------------------------------------------------------------
748 # the appropriate 4 bytes read from the socket are stored in $input
749
750 # unpack a 32-bit unsigned integer
751 my $message_type = unpack("L", $input);
752
753 # check if the highest bit is 1
754 my $is_event = (($message_type >> 31) == 1);
755
756 # use the other bits
757 my $event_type = ($message_type & 0x7F);
758
759 if ($is_event) {
760 say "Received event of type $event_type";
761 }
762 --------------------------------------------------------------------
763
764 === workspace event
765
766 This event consists of a single serialized map containing a property
767 +change (string)+ which indicates the type of the change ("focus", "init",
768 "empty", "urgent", "reload", "rename", "restored", "move"). A
769 +current (object)+ property will be present with the affected workspace
770 whenever the type of event affects a workspace (otherwise, it will be +null).
771
772 When the change is "focus", an +old (object)+ property will be present with the
773 previous workspace. When the first switch occurs (when i3 focuses the
774 workspace visible at the beginning) there is no previous workspace, and the
775 +old+ property will be set to +null+. Also note that if the previous is empty
776 it will get destroyed when switching, but will still be present in the "old"
777 property.
778
779 *Example:*
780 ---------------------
781 {
782 "change": "focus",
783 "current": {
784 "id": 28489712,
785 "type": "workspace",
786 ...
787 }
788 "old": {
789 "id": 28489715,
790 "type": "workspace",
791 ...
792 }
793 }
794 ---------------------
795
796 === output event
797
798 This event consists of a single serialized map containing a property
799 +change (string)+ which indicates the type of the change (currently only
800 "unspecified").
801
802 *Example:*
803 ---------------------------
804 { "change": "unspecified" }
805 ---------------------------
806
807 === mode event
808
809 This event consists of a single serialized map containing a property
810 +change (string)+ which holds the name of current mode in use. The name
811 is the same as specified in config when creating a mode. The default
812 mode is simply named default. It contains a second property, +pango_markup+, which
813 defines whether pango markup shall be used for displaying this mode.
814
815 *Example:*
816 ---------------------------
817 {
818 "change": "default",
819 "pango_markup": true
820 }
821 ---------------------------
822
823 === window event
824
825 This event consists of a single serialized map containing a property
826 +change (string)+ which indicates the type of the change
827
828 * +new+ – the window has become managed by i3
829 * +close+ – the window has closed
830 * +focus+ – the window has received input focus
831 * +title+ – the window's title has changed
832 * +fullscreen_mode+ – the window has entered or exited fullscreen mode
833 * +move+ – the window has changed its position in the tree
834 * +floating+ – the window has transitioned to or from floating
835 * +urgent+ – the window has become urgent or lost its urgent status
836 * +mark+ – a mark has been added to or removed from the window
837
838 Additionally a +container (object)+ field will be present, which consists
839 of the window's parent container. Be aware that for the "new" event, the
840 container will hold the initial name of the newly reparented window (e.g.
841 if you run urxvt with a shell that changes the title, you will still at
842 this point get the window title as "urxvt").
843
844 *Example:*
845 ---------------------------
846 {
847 "change": "new",
848 "container": {
849 "id": 35569536,
850 "type": "con",
851 ...
852 }
853 }
854 ---------------------------
855
856 === barconfig_update event
857
858 This event consists of a single serialized map reporting on options from the
859 barconfig of the specified bar_id that were updated in i3. This event is the
860 same as a +GET_BAR_CONFIG+ reply for the bar with the given id.
861
862 === binding event
863
864 This event consists of a single serialized map reporting on the details of a
865 binding that ran a command because of user input. The +change (string)+ field
866 indicates what sort of binding event was triggered (right now it will always be
867 +"run"+ but may be expanded in the future).
868
869 The +binding (object)+ field contains details about the binding that was run:
870
871 command (string)::
872 The i3 command that is configured to run for this binding.
873 event_state_mask (array of strings)::
874 The group and modifier keys that were configured with this binding.
875 input_code (integer)::
876 If the binding was configured with +bindcode+, this will be the key code
877 that was given for the binding. If the binding is a mouse binding, it will be
878 the number of the mouse button that was pressed. Otherwise it will be 0.
879 symbol (string or null)::
880 If this is a keyboard binding that was configured with +bindsym+, this
881 field will contain the given symbol. Otherwise it will be +null+.
882 input_type (string)::
883 This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was
884 a keyboard or a mouse binding.
885
886 *Example:*
887 ---------------------------
888 {
889 "change": "run",
890 "binding": {
891 "command": "nop",
892 "event_state_mask": [
893 "shift",
894 "ctrl"
895 ],
896 "input_code": 0,
897 "symbol": "t",
898 "input_type": "keyboard"
899 }
900 }
901 ---------------------------
902
903 === shutdown event
904
905 This event is triggered when the connection to the ipc is about to shutdown
906 because of a user action such as a +restart+ or +exit+ command. The +change
907 (string)+ field indicates why the ipc is shutting down. It can be either
908 +"restart"+ or +"exit"+.
909
910 *Example:*
911 ---------------------------
912 {
913 "change": "restart"
914 }
915 ---------------------------
916
917 === tick event
918
919 This event is triggered by a subscription to tick events or by a +SEND_TICK+
920 message.
921
922 *Example (upon subscription):*
923 --------------------------------------------------------------------------------
924 {
925 "first": true,
926 "payload": ""
927 }
928 --------------------------------------------------------------------------------
929
930 *Example (upon +SEND_TICK+ with a payload of +arbitrary string+):*
931 --------------------------------------------------------------------------------
932 {
933 "first": false,
934 "payload": "arbitrary string"
935 }
936 --------------------------------------------------------------------------------
937
938 == See also (existing libraries)
939
940 [[libraries]]
941
942 For some languages, libraries are available (so you don’t have to implement
943 all this on your own). This list names some (if you wrote one, please let me
944 know):
945
946 C::
947 * i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
948 * https://github.com/acrisci/i3ipc-glib
949 C++::
950 * https://github.com/drmgc/i3ipcpp
951 Go::
952 * https://github.com/mdirkse/i3ipc-go
953 * https://github.com/i3/go-i3
954 JavaScript::
955 * https://github.com/acrisci/i3ipc-gjs
956 Lua::
957 * https://github.com/acrisci/i3ipc-lua
958 Perl::
959 * https://metacpan.org/module/AnyEvent::I3
960 Python::
961 * https://github.com/acrisci/i3ipc-python
962 * https://github.com/whitelynx/i3ipc (not maintained)
963 * https://github.com/ziberna/i3-py (not maintained)
964 Ruby::
965 * https://github.com/veelenga/i3ipc-ruby
966 * https://github.com/badboy/i3-ipc (not maintained)
967 Rust::
968 * https://github.com/tmerr/i3ipc-rs
969 OCaml::
970 * https://github.com/Armael/ocaml-i3ipc
971
972 == Appendix A: Detecting byte order in memory-safe languages
973
974 Some programming languages such as Go don’t offer a way to serialize data in the
975 native byte order of the machine they’re running on without resorting to tricks
976 involving the +unsafe+ package.
977
978 The following technique can be used (and will not be broken by changes to i3) to
979 detect the byte order i3 is using:
980
981 1. The byte order dependent fields of an IPC message are message type and
982 payload length.
983
984 * The message type +RUN_COMMAND+ (0) is the same in big and little endian, so
985 we can use it in either byte order to elicit a reply from i3.
986
987 * The payload length 65536 + 256 (+0x00 01 01 00+) is the same in big and
988 little endian, and also small enough to not worry about memory allocations
989 of that size. We must use payloads of length 65536 + 256 in every message
990 we send, so that i3 will be able to read the entire message regardless of
991 the byte order it uses.
992
993 2. Send a big endian encoded message of type +SUBSCRIBE+ (2) with payload `[]`
994 followed by 65536 + 256 - 2 +SPACE+ (ASCII 0x20) bytes.
995
996 * If i3 is running in big endian, this message is treated as a noop,
997 resulting in a +SUBSCRIBE+ reply with payload `{"success":true}`
998 footnote:[A small payload is important: that way, we circumvent dealing
999 with UNIX domain socket buffer sizes, whose size depends on the
1000 implementation/operating system. Exhausting such a buffer results in an i3
1001 deadlock unless you concurrently read and write, which — depending on the
1002 programming language — makes the technique much more complicated.].
1003
1004 * If i3 is running in little endian, this message is read in its entirety due
1005 to the byte order independent payload length, then
1006 https://github.com/i3/i3/blob/d726d09d496577d1c337a4b97486f2c9fbc914f1/src/ipc.c#L1188[silently
1007 discarded] due to the unknown message type.
1008
1009 3. Send a byte order independent message, i.e. type +RUN_COMMAND+ (0) with
1010 payload +nop byte order detection. padding:+, padded to 65536 + 256 bytes
1011 with +a+ (ASCII 0x61) bytes. i3 will reply to this message with a reply of
1012 type +COMMAND+ (0).
1013
1014 * The human-readable prefix is in there to not confuse readers of the i3 log.
1015
1016 * This messages serves as a synchronization primitive so that we know whether
1017 i3 discarded the +SUBSCRIBE+ message or didn’t answer it yet.
1018
1019 4. Receive a message header from i3, decoding the message type as big endian.
1020
1021 * If the message’s reply type is +COMMAND+ (0), i3 is running in little
1022 endian (because the +SUBSCRIBE+ message was discarded). Decode the message
1023 payload length as little endian, receive the message payload.
1024
1025 * If the message’s reply type is anything else, i3 is running in big endian
1026 (because our big endian encoded +SUBSCRIBE+ message was answered). Decode
1027 the message payload length in big endian, receive the message
1028 payload. Then, receive the pending +COMMAND+ message reply in big endian.
1029
1030 5. From here on out, send/receive all messages using the detected byte order.
1031
1032 Find an example implementation of this technique in
1033 https://github.com/i3/go-i3/blob/master/byteorder.go
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>IPC interface (interprocess communication)</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>IPC interface (interprocess communication)</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">September 2017</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>This document describes how to interface with i3 from a separate process. This
749 is useful for example to remote-control i3 (to write test cases for example) or
750 to get various information like the current workspaces to implement an external
751 workspace bar.</p></div>
752 <div class="paragraph"><p>The method of choice for IPC in our case is a unix socket because it has very
753 little overhead on both sides and is usually available without headaches in
754 most languages. In the default configuration file, the ipc-socket gets created
755 in <code>/tmp/i3-%u.XXXXXX/ipc-socket.%p</code> where <code>%u</code> is your UNIX username, <code>%p</code> is
756 the PID of i3 and XXXXXX is a string of random characters from the portable
757 filename character set (see mkdtemp(3)). You can get the socketpath from i3 by
758 calling <code>i3 --get-socketpath</code>.</p></div>
759 <div class="paragraph"><p>All i3 utilities, like <code>i3-msg</code> and <code>i3-input</code> will read the <code>I3_SOCKET_PATH</code>
760 X11 property, stored on the X11 root window.</p></div>
761 <div class="admonitionblock">
762 <table><tr>
763 <td class="icon">
764 <div class="title">Warning</div>
765 </td>
766 <td class="content">
767 <div class="title">Use an existing library!</div>There are existing libraries for many languages. You can have a look at
768 <a href="#libraries">[libraries]</a> or search the web if your language of choice is not mentioned.
769 Usually, it is not necessary to implement low-level communication with i3
770 directly.</td>
771 </tr></table>
772 </div>
773 </div>
774 </div>
775 <div class="sect1">
776 <h2 id="_establishing_a_connection">1. Establishing a connection</h2>
777 <div class="sectionbody">
778 <div class="paragraph"><p>To establish a connection, simply open the IPC socket. The following code
779 snippet illustrates this in Perl:</p></div>
780 <div class="listingblock">
781 <div class="content">
782 <pre><code>use IO::Socket::UNIX;
783 chomp(my $path = qx(i3 --get-socketpath));
784 my $sock = IO::Socket::UNIX-&gt;new(Peer =&gt; $path);</code></pre>
785 </div></div>
786 </div>
787 </div>
788 <div class="sect1">
789 <h2 id="_sending_messages_to_i3">2. Sending messages to i3</h2>
790 <div class="sectionbody">
791 <div class="paragraph"><p>To send a message to i3, you have to format in the binary message format which
792 i3 expects. This format specifies a magic string in the beginning to ensure
793 the integrity of messages (to prevent follow-up errors). Following the magic
794 string comes the length of the payload of the message as 32-bit integer, and
795 the type of the message as 32-bit integer (the integers are not converted, so
796 they are in native byte order).</p></div>
797 <div class="paragraph"><p>The magic string currently is "i3-ipc" and will only be changed when a change
798 in the IPC API is done which breaks compatibility (we hope that we don’t need
799 to do that).</p></div>
800 <div class="tableblock">
801 <table rules="all"
802 width="100%"
803 frame="border"
804 cellspacing="0" cellpadding="4">
805 <caption class="title">Table 1. Currently implemented message types</caption>
806 <col width="10%" />
807 <col width="20%" />
808 <col width="20%" />
809 <col width="50%" />
810 <thead>
811 <tr>
812 <th align="center" valign="top"> Type (numeric) </th>
813 <th align="center" valign="top"> Type (name) </th>
814 <th align="center" valign="top"> Reply type </th>
815 <th align="center" valign="top"> Purpose</th>
816 </tr>
817 </thead>
818 <tbody>
819 <tr>
820 <td align="center" valign="top"><p class="table">0</p></td>
821 <td align="center" valign="top"><p class="table"><code>RUN_COMMAND</code></p></td>
822 <td align="center" valign="top"><p class="table"><a href="#_command_reply">COMMAND</a></p></td>
823 <td align="center" valign="top"><p class="table">Run the payload as an i3 command (like the commands you can bind to keys).</p></td>
824 </tr>
825 <tr>
826 <td align="center" valign="top"><p class="table">1</p></td>
827 <td align="center" valign="top"><p class="table"><code>GET_WORKSPACES</code></p></td>
828 <td align="center" valign="top"><p class="table"><a href="#_workspaces_reply">WORKSPACES</a></p></td>
829 <td align="center" valign="top"><p class="table">Get the list of current workspaces.</p></td>
830 </tr>
831 <tr>
832 <td align="center" valign="top"><p class="table">2</p></td>
833 <td align="center" valign="top"><p class="table"><code>SUBSCRIBE</code></p></td>
834 <td align="center" valign="top"><p class="table"><a href="#_subscribe_reply">SUBSCRIBE</a></p></td>
835 <td align="center" valign="top"><p class="table">Subscribe this IPC connection to the event types specified in the message payload. See <a href="#events">[events]</a>.</p></td>
836 </tr>
837 <tr>
838 <td align="center" valign="top"><p class="table">3</p></td>
839 <td align="center" valign="top"><p class="table"><code>GET_OUTPUTS</code></p></td>
840 <td align="center" valign="top"><p class="table"><a href="#_outputs_reply">OUTPUTS</a></p></td>
841 <td align="center" valign="top"><p class="table">Get the list of current outputs.</p></td>
842 </tr>
843 <tr>
844 <td align="center" valign="top"><p class="table">4</p></td>
845 <td align="center" valign="top"><p class="table"><code>GET_TREE</code></p></td>
846 <td align="center" valign="top"><p class="table"><a href="#_tree_reply">TREE</a></p></td>
847 <td align="center" valign="top"><p class="table">Get the i3 layout tree.</p></td>
848 </tr>
849 <tr>
850 <td align="center" valign="top"><p class="table">5</p></td>
851 <td align="center" valign="top"><p class="table"><code>GET_MARKS</code></p></td>
852 <td align="center" valign="top"><p class="table"><a href="#_marks_reply">MARKS</a></p></td>
853 <td align="center" valign="top"><p class="table">Gets the names of all currently set marks.</p></td>
854 </tr>
855 <tr>
856 <td align="center" valign="top"><p class="table">6</p></td>
857 <td align="center" valign="top"><p class="table"><code>GET_BAR_CONFIG</code></p></td>
858 <td align="center" valign="top"><p class="table"><a href="#_bar_config_reply">BAR_CONFIG</a></p></td>
859 <td align="center" valign="top"><p class="table">Gets the specified bar configuration or the names of all bar configurations if payload is empty.</p></td>
860 </tr>
861 <tr>
862 <td align="center" valign="top"><p class="table">7</p></td>
863 <td align="center" valign="top"><p class="table"><code>GET_VERSION</code></p></td>
864 <td align="center" valign="top"><p class="table"><a href="#_version_reply">VERSION</a></p></td>
865 <td align="center" valign="top"><p class="table">Gets the i3 version.</p></td>
866 </tr>
867 <tr>
868 <td align="center" valign="top"><p class="table">8</p></td>
869 <td align="center" valign="top"><p class="table"><code>GET_BINDING_MODES</code></p></td>
870 <td align="center" valign="top"><p class="table"><a href="#_binding_modes_reply">BINDING_MODES</a></p></td>
871 <td align="center" valign="top"><p class="table">Gets the names of all currently configured binding modes.</p></td>
872 </tr>
873 <tr>
874 <td align="center" valign="top"><p class="table">9</p></td>
875 <td align="center" valign="top"><p class="table"><code>GET_CONFIG</code></p></td>
876 <td align="center" valign="top"><p class="table"><a href="#_config_reply">CONFIG</a></p></td>
877 <td align="center" valign="top"><p class="table">Returns the last loaded i3 config.</p></td>
878 </tr>
879 <tr>
880 <td align="center" valign="top"><p class="table">10</p></td>
881 <td align="center" valign="top"><p class="table"><code>SEND_TICK</code></p></td>
882 <td align="center" valign="top"><p class="table"><a href="#_tick_reply">TICK</a></p></td>
883 <td align="center" valign="top"><p class="table">Sends a tick event with the specified payload.</p></td>
884 </tr>
885 <tr>
886 <td align="center" valign="top"><p class="table">11</p></td>
887 <td align="center" valign="top"><p class="table"><code>SYNC</code></p></td>
888 <td align="center" valign="top"><p class="table"><a href="#_sync_reply">SYNC</a></p></td>
889 <td align="center" valign="top"><p class="table">Sends an i3 sync event with the specified random value to the specified window.</p></td>
890 </tr>
891 </tbody>
892 </table>
893 </div>
894 <div class="paragraph"><p>So, a typical message could look like this:</p></div>
895 <div class="listingblock">
896 <div class="content">
897 <pre><code>"i3-ipc" &lt;message length&gt; &lt;message type&gt; &lt;payload&gt;</code></pre>
898 </div></div>
899 <div class="paragraph"><p>Or, as a hexdump:</p></div>
900 <div class="listingblock">
901 <div class="content">
902 <pre><code>00000000 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex|
903 00000010 69 74 |it|</code></pre>
904 </div></div>
905 <div class="paragraph"><p>To generate and send such a message, you could use the following code in Perl:</p></div>
906 <div class="listingblock">
907 <div class="content">
908 <pre><code>sub format_ipc_command {
909 my ($msg) = @_;
910 my $len;
911 # Get the real byte count (vs. amount of characters)
912 { use bytes; $len = length($msg); }
913 return "i3-ipc" . pack("LL", $len, 0) . $msg;
914 }
915
916 $sock-&gt;write(format_ipc_command("exit"));</code></pre>
917 </div></div>
918 </div>
919 </div>
920 <div class="sect1">
921 <h2 id="_receiving_replies_from_i3">3. Receiving replies from i3</h2>
922 <div class="sectionbody">
923 <div class="paragraph"><p>Replies from i3 usually consist of a simple string (the length of the string
924 is the message_length, so you can consider them length-prefixed) which in turn
925 contain the JSON serialization of a data structure. For example, the
926 GET_WORKSPACES message returns an array of workspaces (each workspace is a map
927 with certain attributes).</p></div>
928 <div class="sect2">
929 <h3 id="_reply_format">3.1. Reply format</h3>
930 <div class="paragraph"><p>The reply format is identical to the normal message format. There also is
931 the magic string, then the message length, then the message type and the
932 payload.</p></div>
933 <div class="paragraph"><p>The following reply types are implemented:</p></div>
934 <div class="dlist"><dl>
935 <dt class="hdlist1">
936 COMMAND (0)
937 </dt>
938 <dd>
939 <p>
940 Confirmation/Error code for the RUN_COMMAND message.
941 </p>
942 </dd>
943 <dt class="hdlist1">
944 WORKSPACES (1)
945 </dt>
946 <dd>
947 <p>
948 Reply to the GET_WORKSPACES message.
949 </p>
950 </dd>
951 <dt class="hdlist1">
952 SUBSCRIBE (2)
953 </dt>
954 <dd>
955 <p>
956 Confirmation/Error code for the SUBSCRIBE message.
957 </p>
958 </dd>
959 <dt class="hdlist1">
960 OUTPUTS (3)
961 </dt>
962 <dd>
963 <p>
964 Reply to the GET_OUTPUTS message.
965 </p>
966 </dd>
967 <dt class="hdlist1">
968 TREE (4)
969 </dt>
970 <dd>
971 <p>
972 Reply to the GET_TREE message.
973 </p>
974 </dd>
975 <dt class="hdlist1">
976 MARKS (5)
977 </dt>
978 <dd>
979 <p>
980 Reply to the GET_MARKS message.
981 </p>
982 </dd>
983 <dt class="hdlist1">
984 BAR_CONFIG (6)
985 </dt>
986 <dd>
987 <p>
988 Reply to the GET_BAR_CONFIG message.
989 </p>
990 </dd>
991 <dt class="hdlist1">
992 VERSION (7)
993 </dt>
994 <dd>
995 <p>
996 Reply to the GET_VERSION message.
997 </p>
998 </dd>
999 <dt class="hdlist1">
1000 BINDING_MODES (8)
1001 </dt>
1002 <dd>
1003 <p>
1004 Reply to the GET_BINDING_MODES message.
1005 </p>
1006 </dd>
1007 <dt class="hdlist1">
1008 GET_CONFIG (9)
1009 </dt>
1010 <dd>
1011 <p>
1012 Reply to the GET_CONFIG message.
1013 </p>
1014 </dd>
1015 <dt class="hdlist1">
1016 TICK (10)
1017 </dt>
1018 <dd>
1019 <p>
1020 Reply to the SEND_TICK message.
1021 </p>
1022 </dd>
1023 </dl></div>
1024 </div>
1025 <div class="sect2">
1026 <h3 id="_command_reply">3.2. COMMAND reply</h3>
1027 <div class="paragraph"><p>The reply consists of a list of serialized maps for each command that was
1028 parsed. Each has the property <code>success (bool)</code> and may also include a
1029 human-readable error message in the property <code>error (string)</code>.</p></div>
1030 <div class="paragraph"><p><strong>Example:</strong></p></div>
1031 <div class="listingblock">
1032 <div class="content">
1033 <pre><code>[{ "success": true }]</code></pre>
1034 </div></div>
1035 </div>
1036 <div class="sect2">
1037 <h3 id="_workspaces_reply">3.3. WORKSPACES reply</h3>
1038 <div class="paragraph"><p>The reply consists of a serialized list of workspaces. Each workspace has the
1039 following properties:</p></div>
1040 <div class="dlist"><dl>
1041 <dt class="hdlist1">
1042 num (integer)
1043 </dt>
1044 <dd>
1045 <p>
1046 The logical number of the workspace. Corresponds to the command
1047 to switch to this workspace. For named workspaces, this will be -1.
1048 </p>
1049 </dd>
1050 <dt class="hdlist1">
1051 name (string)
1052 </dt>
1053 <dd>
1054 <p>
1055 The name of this workspace (by default num+1), as changed by the
1056 user. Encoded in UTF-8.
1057 </p>
1058 </dd>
1059 <dt class="hdlist1">
1060 visible (boolean)
1061 </dt>
1062 <dd>
1063 <p>
1064 Whether this workspace is currently visible on an output (multiple
1065 workspaces can be visible at the same time).
1066 </p>
1067 </dd>
1068 <dt class="hdlist1">
1069 focused (boolean)
1070 </dt>
1071 <dd>
1072 <p>
1073 Whether this workspace currently has the focus (only one workspace
1074 can have the focus at the same time).
1075 </p>
1076 </dd>
1077 <dt class="hdlist1">
1078 urgent (boolean)
1079 </dt>
1080 <dd>
1081 <p>
1082 Whether a window on this workspace has the "urgent" flag set.
1083 </p>
1084 </dd>
1085 <dt class="hdlist1">
1086 rect (map)
1087 </dt>
1088 <dd>
1089 <p>
1090 The rectangle of this workspace (equals the rect of the output it
1091 is on), consists of x, y, width, height.
1092 </p>
1093 </dd>
1094 <dt class="hdlist1">
1095 output (string)
1096 </dt>
1097 <dd>
1098 <p>
1099 The video output this workspace is on (LVDS1, VGA1, …).
1100 </p>
1101 </dd>
1102 </dl></div>
1103 <div class="paragraph"><p><strong>Example:</strong></p></div>
1104 <div class="listingblock">
1105 <div class="content">
1106 <pre><code>[
1107 {
1108 "num": 0,
1109 "name": "1",
1110 "visible": true,
1111 "focused": true,
1112 "urgent": false,
1113 "rect": {
1114 "x": 0,
1115 "y": 0,
1116 "width": 1280,
1117 "height": 800
1118 },
1119 "output": "LVDS1"
1120 },
1121 {
1122 "num": 1,
1123 "name": "2",
1124 "visible": false,
1125 "focused": false,
1126 "urgent": false,
1127 "rect": {
1128 "x": 0,
1129 "y": 0,
1130 "width": 1280,
1131 "height": 800
1132 },
1133 "output": "LVDS1"
1134 }
1135 ]</code></pre>
1136 </div></div>
1137 </div>
1138 <div class="sect2">
1139 <h3 id="_subscribe_reply">3.4. SUBSCRIBE reply</h3>
1140 <div class="paragraph"><p>The reply consists of a single serialized map. The only property is
1141 <code>success (bool)</code>, indicating whether the subscription was successful (the
1142 default) or whether a JSON parse error occurred.</p></div>
1143 <div class="paragraph"><p><strong>Example:</strong></p></div>
1144 <div class="listingblock">
1145 <div class="content">
1146 <pre><code>{ "success": true }</code></pre>
1147 </div></div>
1148 </div>
1149 <div class="sect2">
1150 <h3 id="_outputs_reply">3.5. OUTPUTS reply</h3>
1151 <div class="paragraph"><p>The reply consists of a serialized list of outputs. Each output has the
1152 following properties:</p></div>
1153 <div class="dlist"><dl>
1154 <dt class="hdlist1">
1155 name (string)
1156 </dt>
1157 <dd>
1158 <p>
1159 The name of this output (as seen in <code>xrandr(1)</code>). Encoded in UTF-8.
1160 </p>
1161 </dd>
1162 <dt class="hdlist1">
1163 active (boolean)
1164 </dt>
1165 <dd>
1166 <p>
1167 Whether this output is currently active (has a valid mode).
1168 </p>
1169 </dd>
1170 <dt class="hdlist1">
1171 primary (boolean)
1172 </dt>
1173 <dd>
1174 <p>
1175 Whether this output is currently the primary output.
1176 </p>
1177 </dd>
1178 <dt class="hdlist1">
1179 current_workspace (string)
1180 </dt>
1181 <dd>
1182 <p>
1183 The name of the current workspace that is visible on this output. <code>null</code> if
1184 the output is not active.
1185 </p>
1186 </dd>
1187 <dt class="hdlist1">
1188 rect (map)
1189 </dt>
1190 <dd>
1191 <p>
1192 The rectangle of this output (equals the rect of the output it
1193 is on), consists of x, y, width, height.
1194 </p>
1195 </dd>
1196 </dl></div>
1197 <div class="paragraph"><p><strong>Example:</strong></p></div>
1198 <div class="listingblock">
1199 <div class="content">
1200 <pre><code>[
1201 {
1202 "name": "LVDS1",
1203 "active": true,
1204 "current_workspace": "4",
1205 "rect": {
1206 "x": 0,
1207 "y": 0,
1208 "width": 1280,
1209 "height": 800
1210 }
1211 },
1212 {
1213 "name": "VGA1",
1214 "active": true,
1215 "current_workspace": "1",
1216 "rect": {
1217 "x": 1280,
1218 "y": 0,
1219 "width": 1280,
1220 "height": 1024
1221 }
1222 }
1223 ]</code></pre>
1224 </div></div>
1225 </div>
1226 <div class="sect2">
1227 <h3 id="_tree_reply">3.6. TREE reply</h3>
1228 <div class="paragraph"><p>The reply consists of a serialized tree. Each node in the tree (representing
1229 one container) has at least the properties listed below. While the nodes might
1230 have more properties, please do not use any properties which are not documented
1231 here. They are not yet finalized and will probably change!</p></div>
1232 <div class="dlist"><dl>
1233 <dt class="hdlist1">
1234 id (integer)
1235 </dt>
1236 <dd>
1237 <p>
1238 The internal ID (actually a C pointer value) of this container. Do not
1239 make any assumptions about it. You can use it to (re-)identify and
1240 address containers when talking to i3.
1241 </p>
1242 </dd>
1243 <dt class="hdlist1">
1244 name (string)
1245 </dt>
1246 <dd>
1247 <p>
1248 The internal name of this container. For all containers which are part
1249 of the tree structure down to the workspace contents, this is set to a
1250 nice human-readable name of the container.
1251 For containers that have an X11 window, the content is the title
1252 (_NET_WM_NAME property) of that window.
1253 For all other containers, the content is not defined (yet).
1254 </p>
1255 </dd>
1256 <dt class="hdlist1">
1257 type (string)
1258 </dt>
1259 <dd>
1260 <p>
1261 Type of this container. Can be one of "root", "output", "con",
1262 "floating_con", "workspace" or "dockarea".
1263 </p>
1264 </dd>
1265 <dt class="hdlist1">
1266 border (string)
1267 </dt>
1268 <dd>
1269 <p>
1270 Can be either "normal", "none" or "pixel", depending on the
1271 container’s border style.
1272 </p>
1273 </dd>
1274 <dt class="hdlist1">
1275 current_border_width (integer)
1276 </dt>
1277 <dd>
1278 <p>
1279 Number of pixels of the border width.
1280 </p>
1281 </dd>
1282 <dt class="hdlist1">
1283 layout (string)
1284 </dt>
1285 <dd>
1286 <p>
1287 Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
1288 "output".
1289 Other values might be possible in the future, should we add new
1290 layouts.
1291 </p>
1292 </dd>
1293 <dt class="hdlist1">
1294 orientation (string)
1295 </dt>
1296 <dd>
1297 <p>
1298 Can be either "none" (for non-split containers), "horizontal" or
1299 "vertical".
1300 THIS FIELD IS OBSOLETE. It is still present, but your code should not
1301 use it. Instead, rely on the layout field.
1302 </p>
1303 </dd>
1304 <dt class="hdlist1">
1305 percent (float)
1306 </dt>
1307 <dd>
1308 <p>
1309 The percentage which this container takes in its parent. A value of
1310 <code>null</code> means that the percent property does not make sense for this
1311 container, for example for the root container.
1312 </p>
1313 </dd>
1314 <dt class="hdlist1">
1315 rect (map)
1316 </dt>
1317 <dd>
1318 <p>
1319 The absolute display coordinates for this container. Display
1320 coordinates means that when you have two 1600x1200 monitors on a single
1321 X11 Display (the standard way), the coordinates of the first window on
1322 the second monitor are <code>{ "x": 1600, "y": 0, "width": 1600, "height":
1323 1200 }</code>.
1324 </p>
1325 </dd>
1326 <dt class="hdlist1">
1327 window_rect (map)
1328 </dt>
1329 <dd>
1330 <p>
1331 The coordinates of the <strong>actual client window</strong> inside its container.
1332 These coordinates are relative to the container and do not include the
1333 window decoration (which is actually rendered on the parent container).
1334 So, when using the <code>default</code> layout, you will have a 2 pixel border on
1335 each side, making the window_rect <code>{ "x": 2, "y": 0, "width": 632,
1336 "height": 366 }</code> (for example).
1337 </p>
1338 </dd>
1339 <dt class="hdlist1">
1340 deco_rect (map)
1341 </dt>
1342 <dd>
1343 <p>
1344 The coordinates of the <strong>window decoration</strong> inside its container. These
1345 coordinates are relative to the container and do not include the actual
1346 client window.
1347 </p>
1348 </dd>
1349 <dt class="hdlist1">
1350 geometry (map)
1351 </dt>
1352 <dd>
1353 <p>
1354 The original geometry the window specified when i3 mapped it. Used when
1355 switching a window to floating mode, for example.
1356 </p>
1357 </dd>
1358 <dt class="hdlist1">
1359 window (integer)
1360 </dt>
1361 <dd>
1362 <p>
1363 The X11 window ID of the <strong>actual client window</strong> inside this container.
1364 This field is set to null for split containers or otherwise empty
1365 containers. This ID corresponds to what xwininfo(1) and other
1366 X11-related tools display (usually in hex).
1367 </p>
1368 </dd>
1369 <dt class="hdlist1">
1370 window_properties (map)
1371 </dt>
1372 <dd>
1373 <p>
1374 X11 window properties title, instance, class, window_role and transient_for.
1375 </p>
1376 </dd>
1377 <dt class="hdlist1">
1378 urgent (bool)
1379 </dt>
1380 <dd>
1381 <p>
1382 Whether this container (window, split container, floating container or
1383 workspace) has the urgency hint set, directly or indirectly. All parent
1384 containers up until the workspace container will be marked urgent if they
1385 have at least one urgent child.
1386 </p>
1387 </dd>
1388 <dt class="hdlist1">
1389 focused (bool)
1390 </dt>
1391 <dd>
1392 <p>
1393 Whether this container is currently focused.
1394 </p>
1395 </dd>
1396 <dt class="hdlist1">
1397 focus (array of integer)
1398 </dt>
1399 <dd>
1400 <p>
1401 List of child node IDs (see <code>nodes</code>, <code>floating_nodes</code> and <code>id</code>) in focus
1402 order. Traversing the tree by following the first entry in this array
1403 will result in eventually reaching the one node with <code>focused</code> set to
1404 true.
1405 </p>
1406 </dd>
1407 <dt class="hdlist1">
1408 nodes (array of node)
1409 </dt>
1410 <dd>
1411 <p>
1412 The tiling (i.e. non-floating) child containers of this node.
1413 </p>
1414 </dd>
1415 <dt class="hdlist1">
1416 floating_nodes (array of node)
1417 </dt>
1418 <dd>
1419 <p>
1420 The floating child containers of this node. Only non-empty on nodes with
1421 type <code>workspace</code>.
1422 </p>
1423 </dd>
1424 </dl></div>
1425 <div class="paragraph"><p>Please note that in the following example, I have left out some keys/values
1426 which are not relevant for the type of the node. Otherwise, the example would
1427 be by far too long (it already is quite long, despite showing only 1 window and
1428 one dock window).</p></div>
1429 <div class="paragraph"><p>It is useful to have an overview of the structure before taking a look at the
1430 JSON dump:</p></div>
1431 <div class="ulist"><ul>
1432 <li>
1433 <p>
1434 root
1435 </p>
1436 <div class="ulist"><ul>
1437 <li>
1438 <p>
1439 LVDS1
1440 </p>
1441 <div class="ulist"><ul>
1442 <li>
1443 <p>
1444 topdock
1445 </p>
1446 </li>
1447 <li>
1448 <p>
1449 content
1450 </p>
1451 <div class="ulist"><ul>
1452 <li>
1453 <p>
1454 workspace 1
1455 </p>
1456 <div class="ulist"><ul>
1457 <li>
1458 <p>
1459 window 1
1460 </p>
1461 </li>
1462 </ul></div>
1463 </li>
1464 </ul></div>
1465 </li>
1466 <li>
1467 <p>
1468 bottomdock
1469 </p>
1470 <div class="ulist"><ul>
1471 <li>
1472 <p>
1473 dock window 1
1474 </p>
1475 </li>
1476 </ul></div>
1477 </li>
1478 </ul></div>
1479 </li>
1480 <li>
1481 <p>
1482 VGA1
1483 </p>
1484 </li>
1485 </ul></div>
1486 </li>
1487 </ul></div>
1488 <div class="paragraph"><p><strong>Example:</strong></p></div>
1489 <div class="listingblock">
1490 <div class="content">
1491 <pre><code>{
1492 "id": 6875648,
1493 "name": "root",
1494 "rect": {
1495 "x": 0,
1496 "y": 0,
1497 "width": 1280,
1498 "height": 800
1499 },
1500 "nodes": [
1501
1502 {
1503 "id": 6878320,
1504 "name": "LVDS1",
1505 "layout": "output",
1506 "rect": {
1507 "x": 0,
1508 "y": 0,
1509 "width": 1280,
1510 "height": 800
1511 },
1512 "nodes": [
1513
1514 {
1515 "id": 6878784,
1516 "name": "topdock",
1517 "layout": "dockarea",
1518 "orientation": "vertical",
1519 "rect": {
1520 "x": 0,
1521 "y": 0,
1522 "width": 1280,
1523 "height": 0
1524 }
1525 },
1526
1527 {
1528 "id": 6879344,
1529 "name": "content",
1530 "rect": {
1531 "x": 0,
1532 "y": 0,
1533 "width": 1280,
1534 "height": 782
1535 },
1536 "nodes": [
1537
1538 {
1539 "id": 6880464,
1540 "name": "1",
1541 "orientation": "horizontal",
1542 "rect": {
1543 "x": 0,
1544 "y": 0,
1545 "width": 1280,
1546 "height": 782
1547 },
1548 "window_properties": {
1549 "class": "Evince",
1550 "instance": "evince",
1551 "title": "Properties",
1552 "transient_for": 52428808
1553 },
1554 "floating_nodes": [],
1555 "nodes": [
1556
1557 {
1558 "id": 6929968,
1559 "name": "#aa0000",
1560 "border": "normal",
1561 "percent": 1,
1562 "rect": {
1563 "x": 0,
1564 "y": 18,
1565 "width": 1280,
1566 "height": 782
1567 }
1568 }
1569
1570 ]
1571 }
1572
1573 ]
1574 },
1575
1576 {
1577 "id": 6880208,
1578 "name": "bottomdock",
1579 "layout": "dockarea",
1580 "orientation": "vertical",
1581 "rect": {
1582 "x": 0,
1583 "y": 782,
1584 "width": 1280,
1585 "height": 18
1586 },
1587 "nodes": [
1588
1589 {
1590 "id": 6931312,
1591 "name": "#00aa00",
1592 "percent": 1,
1593 "rect": {
1594 "x": 0,
1595 "y": 782,
1596 "width": 1280,
1597 "height": 18
1598 }
1599 }
1600
1601 ]
1602 }
1603 ]
1604 }
1605 ]
1606 }</code></pre>
1607 </div></div>
1608 </div>
1609 <div class="sect2">
1610 <h3 id="_marks_reply">3.7. MARKS reply</h3>
1611 <div class="paragraph"><p>The reply consists of a single array of strings for each container that has a
1612 mark. A mark can only be set on one container, so the array is unique.
1613 The order of that array is undefined.</p></div>
1614 <div class="paragraph"><p>If no window has a mark the response will be the empty array [].</p></div>
1615 </div>
1616 <div class="sect2">
1617 <h3 id="_bar_config_reply">3.8. BAR_CONFIG reply</h3>
1618 <div class="paragraph"><p>This can be used by third-party workspace bars (especially i3bar, but others
1619 are free to implement compatible alternatives) to get the <code>bar</code> block
1620 configuration from i3.</p></div>
1621 <div class="paragraph"><p>Depending on the input, the reply is either:</p></div>
1622 <div class="dlist"><dl>
1623 <dt class="hdlist1">
1624 empty input
1625 </dt>
1626 <dd>
1627 <p>
1628 An array of configured bar IDs
1629 </p>
1630 </dd>
1631 <dt class="hdlist1">
1632 Bar ID
1633 </dt>
1634 <dd>
1635 <p>
1636 A JSON map containing the configuration for the specified bar.
1637 </p>
1638 </dd>
1639 </dl></div>
1640 <div class="paragraph"><p>Each bar configuration has the following properties:</p></div>
1641 <div class="dlist"><dl>
1642 <dt class="hdlist1">
1643 id (string)
1644 </dt>
1645 <dd>
1646 <p>
1647 The ID for this bar. Included in case you request multiple
1648 configurations and want to differentiate the different replies.
1649 </p>
1650 </dd>
1651 <dt class="hdlist1">
1652 mode (string)
1653 </dt>
1654 <dd>
1655 <p>
1656 Either <code>dock</code> (the bar sets the dock window type) or <code>hide</code> (the bar
1657 does not show unless a specific key is pressed).
1658 </p>
1659 </dd>
1660 <dt class="hdlist1">
1661 position (string)
1662 </dt>
1663 <dd>
1664 <p>
1665 Either <code>bottom</code> or <code>top</code> at the moment.
1666 </p>
1667 </dd>
1668 <dt class="hdlist1">
1669 status_command (string)
1670 </dt>
1671 <dd>
1672 <p>
1673 Command which will be run to generate a statusline. Each line on stdout
1674 of this command will be displayed in the bar. At the moment, no
1675 formatting is supported.
1676 </p>
1677 </dd>
1678 <dt class="hdlist1">
1679 font (string)
1680 </dt>
1681 <dd>
1682 <p>
1683 The font to use for text on the bar.
1684 </p>
1685 </dd>
1686 <dt class="hdlist1">
1687 workspace_buttons (boolean)
1688 </dt>
1689 <dd>
1690 <p>
1691 Display workspace buttons or not? Defaults to true.
1692 </p>
1693 </dd>
1694 <dt class="hdlist1">
1695 binding_mode_indicator (boolean)
1696 </dt>
1697 <dd>
1698 <p>
1699 Display the mode indicator or not? Defaults to true.
1700 </p>
1701 </dd>
1702 <dt class="hdlist1">
1703 verbose (boolean)
1704 </dt>
1705 <dd>
1706 <p>
1707 Should the bar enable verbose output for debugging? Defaults to false.
1708 </p>
1709 </dd>
1710 <dt class="hdlist1">
1711 colors (map)
1712 </dt>
1713 <dd>
1714 <p>
1715 Contains key/value pairs of colors. Each value is a color code in hex,
1716 formatted #rrggbb (like in HTML).
1717 </p>
1718 </dd>
1719 </dl></div>
1720 <div class="paragraph"><p>The following colors can be configured at the moment:</p></div>
1721 <div class="dlist"><dl>
1722 <dt class="hdlist1">
1723 background
1724 </dt>
1725 <dd>
1726 <p>
1727 Background color of the bar.
1728 </p>
1729 </dd>
1730 <dt class="hdlist1">
1731 statusline
1732 </dt>
1733 <dd>
1734 <p>
1735 Text color to be used for the statusline.
1736 </p>
1737 </dd>
1738 <dt class="hdlist1">
1739 separator
1740 </dt>
1741 <dd>
1742 <p>
1743 Text color to be used for the separator.
1744 </p>
1745 </dd>
1746 <dt class="hdlist1">
1747 focused_background
1748 </dt>
1749 <dd>
1750 <p>
1751 Background color of the bar on the currently focused monitor output.
1752 </p>
1753 </dd>
1754 <dt class="hdlist1">
1755 focused_statusline
1756 </dt>
1757 <dd>
1758 <p>
1759 Text color to be used for the statusline on the currently focused
1760 monitor output.
1761 </p>
1762 </dd>
1763 <dt class="hdlist1">
1764 focused_separator
1765 </dt>
1766 <dd>
1767 <p>
1768 Text color to be used for the separator on the currently focused
1769 monitor output.
1770 </p>
1771 </dd>
1772 <dt class="hdlist1">
1773 focused_workspace_text/focused_workspace_bg/focused_workspace_border
1774 </dt>
1775 <dd>
1776 <p>
1777 Text/background/border color for a workspace button when the workspace
1778 has focus.
1779 </p>
1780 </dd>
1781 <dt class="hdlist1">
1782 active_workspace_text/active_workspace_bg/active_workspace_border
1783 </dt>
1784 <dd>
1785 <p>
1786 Text/background/border color for a workspace button when the workspace
1787 is active (visible) on some output, but the focus is on another one.
1788 You can only tell this apart from the focused workspace when you are
1789 using multiple monitors.
1790 </p>
1791 </dd>
1792 <dt class="hdlist1">
1793 inactive_workspace_text/inactive_workspace_bg/inactive_workspace_border
1794 </dt>
1795 <dd>
1796 <p>
1797 Text/background/border color for a workspace button when the workspace
1798 does not have focus and is not active (visible) on any output. This
1799 will be the case for most workspaces.
1800 </p>
1801 </dd>
1802 <dt class="hdlist1">
1803 urgent_workspace_text/urgent_workspace_bg/urgent_workspace_border
1804 </dt>
1805 <dd>
1806 <p>
1807 Text/background/border color for workspaces which contain at least one
1808 window with the urgency hint set.
1809 </p>
1810 </dd>
1811 <dt class="hdlist1">
1812 binding_mode_text/binding_mode_bg/binding_mode_border
1813 </dt>
1814 <dd>
1815 <p>
1816 Text/background/border color for the binding mode indicator.
1817 </p>
1818 </dd>
1819 </dl></div>
1820 <div class="paragraph"><p><strong>Example of configured bars:</strong></p></div>
1821 <div class="listingblock">
1822 <div class="content">
1823 <pre><code>["bar-bxuqzf"]</code></pre>
1824 </div></div>
1825 <div class="paragraph"><p><strong>Example of bar configuration:</strong></p></div>
1826 <div class="listingblock">
1827 <div class="content">
1828 <pre><code>{
1829 "id": "bar-bxuqzf",
1830 "mode": "dock",
1831 "position": "bottom",
1832 "status_command": "i3status",
1833 "font": "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1",
1834 "workspace_buttons": true,
1835 "binding_mode_indicator": true,
1836 "verbose": false,
1837 "colors": {
1838 "background": "#c0c0c0",
1839 "statusline": "#00ff00",
1840 "focused_workspace_text": "#ffffff",
1841 "focused_workspace_bg": "#000000"
1842 }
1843 }</code></pre>
1844 </div></div>
1845 </div>
1846 <div class="sect2">
1847 <h3 id="_version_reply">3.9. VERSION reply</h3>
1848 <div class="paragraph"><p>The reply consists of a single JSON dictionary with the following keys:</p></div>
1849 <div class="dlist"><dl>
1850 <dt class="hdlist1">
1851 major (integer)
1852 </dt>
1853 <dd>
1854 <p>
1855 The major version of i3, such as <code>4</code>.
1856 </p>
1857 </dd>
1858 <dt class="hdlist1">
1859 minor (integer)
1860 </dt>
1861 <dd>
1862 <p>
1863 The minor version of i3, such as <code>2</code>. Changes in the IPC interface (new
1864 features) will only occur with new minor (or major) releases. However,
1865 bugfixes might be introduced in patch releases, too.
1866 </p>
1867 </dd>
1868 <dt class="hdlist1">
1869 patch (integer)
1870 </dt>
1871 <dd>
1872 <p>
1873 The patch version of i3, such as <code>1</code> (when the complete version is
1874 <code>4.2.1</code>). For versions such as <code>4.2</code>, patch will be set to <code>0</code>.
1875 </p>
1876 </dd>
1877 <dt class="hdlist1">
1878 human_readable (string)
1879 </dt>
1880 <dd>
1881 <p>
1882 A human-readable version of i3 containing the precise git version,
1883 build date and branch name. When you need to display the i3 version to
1884 your users, use the human-readable version whenever possible (since
1885 this is what <code>i3 --version</code> displays, too).
1886 </p>
1887 </dd>
1888 <dt class="hdlist1">
1889 loaded_config_file_name (string)
1890 </dt>
1891 <dd>
1892 <p>
1893 The current config path.
1894 </p>
1895 </dd>
1896 </dl></div>
1897 <div class="paragraph"><p><strong>Example:</strong></p></div>
1898 <div class="listingblock">
1899 <div class="content">
1900 <pre><code>{
1901 "human_readable" : "4.2-169-gf80b877 (2012-08-05, branch \"next\")",
1902 "loaded_config_file_name" : "/home/hwangcc23/.i3/config",
1903 "minor" : 2,
1904 "patch" : 0,
1905 "major" : 4
1906 }</code></pre>
1907 </div></div>
1908 </div>
1909 <div class="sect2">
1910 <h3 id="_binding_modes_reply">3.10. BINDING_MODES reply</h3>
1911 <div class="paragraph"><p>The reply consists of an array of all currently configured binding modes.</p></div>
1912 <div class="paragraph"><p><strong>Example:</strong></p></div>
1913 <div class="listingblock">
1914 <div class="content">
1915 <pre><code>["default", "resize"]</code></pre>
1916 </div></div>
1917 </div>
1918 <div class="sect2">
1919 <h3 id="_config_reply">3.11. CONFIG reply</h3>
1920 <div class="paragraph"><p>The config reply is a map which currently only contains the "config" member,
1921 which is a string containing the config file as loaded by i3 most recently.</p></div>
1922 <div class="paragraph"><p><strong>Example:</strong></p></div>
1923 <div class="listingblock">
1924 <div class="content">
1925 <pre><code>{ "config": "font pango:monospace 8\nbindsym Mod4+q exit\n" }</code></pre>
1926 </div></div>
1927 </div>
1928 <div class="sect2">
1929 <h3 id="_tick_reply">3.12. TICK reply</h3>
1930 <div class="paragraph"><p>The reply is a map containing the "success" member. After the reply was
1931 received, the tick event has been written to all IPC connections which subscribe
1932 to tick events. UNIX sockets are usually buffered, but you can be certain that
1933 once you receive the tick event you just triggered, you must have received all
1934 events generated prior to the <code>SEND_TICK</code> message (happened-before relation).</p></div>
1935 <div class="paragraph"><p><strong>Example:</strong></p></div>
1936 <div class="listingblock">
1937 <div class="content">
1938 <pre><code>{ "success": true }</code></pre>
1939 </div></div>
1940 </div>
1941 <div class="sect2">
1942 <h3 id="_sync_reply">3.13. SYNC reply</h3>
1943 <div class="paragraph"><p>The reply is a map containing the "success" member. After the reply was
1944 received, the <a href="https://i3wm.org/docs/testsuite.html#i3_sync">i3 sync message</a> was
1945 responded to.</p></div>
1946 <div class="paragraph"><p><strong>Example:</strong></p></div>
1947 <div class="listingblock">
1948 <div class="content">
1949 <pre><code>{ "success": true }</code></pre>
1950 </div></div>
1951 </div>
1952 </div>
1953 </div>
1954 <div class="sect1">
1955 <h2 id="_events">4. Events</h2>
1956 <div class="sectionbody">
1957 <div class="paragraph" id="events"><p>To get informed when certain things happen in i3, clients can subscribe to
1958 events. Events consist of a name (like "workspace") and an event reply type
1959 (like I3_IPC_EVENT_WORKSPACE). The events sent by i3 are in the same format
1960 as replies to specific commands. However, the highest bit of the message type
1961 is set to 1 to indicate that this is an event reply instead of a normal reply.</p></div>
1962 <div class="paragraph"><p>Caveat: As soon as you subscribe to an event, it is not guaranteed any longer
1963 that the requests to i3 are processed in order. This means, the following
1964 situation can happen: You send a GET_WORKSPACES request but you receive a
1965 "workspace" event before receiving the reply to GET_WORKSPACES. If your
1966 program does not want to cope which such kinds of race conditions (an
1967 event based library may not have a problem here), I suggest you create a
1968 separate connection to receive events.</p></div>
1969 <div class="paragraph"><p>If an event message needs to be sent and the socket is not writeable (write
1970 returns EAGAIN, happens when the socket doesn&#8217;t have enough buffer space for
1971 writing new data) then i3 uses a queue system to store outgoing messages for
1972 each client. This is combined with a timer: if the message queue for a client is
1973 not empty and no data where successfully written in the past 10 seconds, the
1974 connection is killed. Practically, this means that your client should try to
1975 always read events from the socket to avoid having its connection closed.</p></div>
1976 <div class="sect2">
1977 <h3 id="_subscribing_to_events">4.1. Subscribing to events</h3>
1978 <div class="paragraph"><p>By sending a message of type SUBSCRIBE with a JSON-encoded array as payload
1979 you can register to an event.</p></div>
1980 <div class="paragraph"><p><strong>Example:</strong></p></div>
1981 <div class="listingblock">
1982 <div class="content">
1983 <pre><code>type: SUBSCRIBE
1984 payload: [ "workspace", "output" ]</code></pre>
1985 </div></div>
1986 </div>
1987 <div class="sect2">
1988 <h3 id="_available_events">4.2. Available events</h3>
1989 <div class="paragraph"><p>The numbers in parenthesis is the event type (keep in mind that you need to
1990 strip the highest bit first).</p></div>
1991 <div class="dlist"><dl>
1992 <dt class="hdlist1">
1993 workspace (0)
1994 </dt>
1995 <dd>
1996 <p>
1997 Sent when the user switches to a different workspace, when a new
1998 workspace is initialized or when a workspace is removed (because the
1999 last client vanished).
2000 </p>
2001 </dd>
2002 <dt class="hdlist1">
2003 output (1)
2004 </dt>
2005 <dd>
2006 <p>
2007 Sent when RandR issues a change notification (of either screens,
2008 outputs, CRTCs or output properties).
2009 </p>
2010 </dd>
2011 <dt class="hdlist1">
2012 mode (2)
2013 </dt>
2014 <dd>
2015 <p>
2016 Sent whenever i3 changes its binding mode.
2017 </p>
2018 </dd>
2019 <dt class="hdlist1">
2020 window (3)
2021 </dt>
2022 <dd>
2023 <p>
2024 Sent when a client&#8217;s window is successfully reparented (that is when i3
2025 has finished fitting it into a container), when a window received input
2026 focus or when certain properties of the window have changed.
2027 </p>
2028 </dd>
2029 <dt class="hdlist1">
2030 barconfig_update (4)
2031 </dt>
2032 <dd>
2033 <p>
2034 Sent when the hidden_state or mode field in the barconfig of any bar
2035 instance was updated and when the config is reloaded.
2036 </p>
2037 </dd>
2038 <dt class="hdlist1">
2039 binding (5)
2040 </dt>
2041 <dd>
2042 <p>
2043 Sent when a configured command binding is triggered with the keyboard or
2044 mouse
2045 </p>
2046 </dd>
2047 <dt class="hdlist1">
2048 shutdown (6)
2049 </dt>
2050 <dd>
2051 <p>
2052 Sent when the ipc shuts down because of a restart or exit by user command
2053 </p>
2054 </dd>
2055 <dt class="hdlist1">
2056 tick (7)
2057 </dt>
2058 <dd>
2059 <p>
2060 Sent when the ipc client subscribes to the tick event (with <code>"first":
2061 true</code>) or when any ipc client sends a SEND_TICK message (with <code>"first":
2062 false</code>).
2063 </p>
2064 </dd>
2065 </dl></div>
2066 <div class="paragraph"><p><strong>Example:</strong></p></div>
2067 <div class="listingblock">
2068 <div class="content">
2069 <pre><code># the appropriate 4 bytes read from the socket are stored in $input
2070
2071 # unpack a 32-bit unsigned integer
2072 my $message_type = unpack("L", $input);
2073
2074 # check if the highest bit is 1
2075 my $is_event = (($message_type &gt;&gt; 31) == 1);
2076
2077 # use the other bits
2078 my $event_type = ($message_type &amp; 0x7F);
2079
2080 if ($is_event) {
2081 say "Received event of type $event_type";
2082 }</code></pre>
2083 </div></div>
2084 </div>
2085 <div class="sect2">
2086 <h3 id="_workspace_event">4.3. workspace event</h3>
2087 <div class="paragraph"><p>This event consists of a single serialized map containing a property
2088 <code>change (string)</code> which indicates the type of the change ("focus", "init",
2089 "empty", "urgent", "reload", "rename", "restored", "move"). A
2090 <code>current (object)</code> property will be present with the affected workspace
2091 whenever the type of event affects a workspace (otherwise, it will be +null).</p></div>
2092 <div class="paragraph"><p>When the change is "focus", an <code>old (object)</code> property will be present with the
2093 previous workspace. When the first switch occurs (when i3 focuses the
2094 workspace visible at the beginning) there is no previous workspace, and the
2095 <code>old</code> property will be set to <code>null</code>. Also note that if the previous is empty
2096 it will get destroyed when switching, but will still be present in the "old"
2097 property.</p></div>
2098 <div class="paragraph"><p><strong>Example:</strong></p></div>
2099 <div class="listingblock">
2100 <div class="content">
2101 <pre><code>{
2102 "change": "focus",
2103 "current": {
2104 "id": 28489712,
2105 "type": "workspace",
2106 ...
2107 }
2108 "old": {
2109 "id": 28489715,
2110 "type": "workspace",
2111 ...
2112 }
2113 }</code></pre>
2114 </div></div>
2115 </div>
2116 <div class="sect2">
2117 <h3 id="_output_event">4.4. output event</h3>
2118 <div class="paragraph"><p>This event consists of a single serialized map containing a property
2119 <code>change (string)</code> which indicates the type of the change (currently only
2120 "unspecified").</p></div>
2121 <div class="paragraph"><p><strong>Example:</strong></p></div>
2122 <div class="listingblock">
2123 <div class="content">
2124 <pre><code>{ "change": "unspecified" }</code></pre>
2125 </div></div>
2126 </div>
2127 <div class="sect2">
2128 <h3 id="_mode_event">4.5. mode event</h3>
2129 <div class="paragraph"><p>This event consists of a single serialized map containing a property
2130 <code>change (string)</code> which holds the name of current mode in use. The name
2131 is the same as specified in config when creating a mode. The default
2132 mode is simply named default. It contains a second property, <code>pango_markup</code>, which
2133 defines whether pango markup shall be used for displaying this mode.</p></div>
2134 <div class="paragraph"><p><strong>Example:</strong></p></div>
2135 <div class="listingblock">
2136 <div class="content">
2137 <pre><code>{
2138 "change": "default",
2139 "pango_markup": true
2140 }</code></pre>
2141 </div></div>
2142 </div>
2143 <div class="sect2">
2144 <h3 id="_window_event">4.6. window event</h3>
2145 <div class="paragraph"><p>This event consists of a single serialized map containing a property
2146 <code>change (string)</code> which indicates the type of the change</p></div>
2147 <div class="ulist"><ul>
2148 <li>
2149 <p>
2150 <code>new</code> – the window has become managed by i3
2151 </p>
2152 </li>
2153 <li>
2154 <p>
2155 <code>close</code> – the window has closed
2156 </p>
2157 </li>
2158 <li>
2159 <p>
2160 <code>focus</code> – the window has received input focus
2161 </p>
2162 </li>
2163 <li>
2164 <p>
2165 <code>title</code> – the window&#8217;s title has changed
2166 </p>
2167 </li>
2168 <li>
2169 <p>
2170 <code>fullscreen_mode</code> – the window has entered or exited fullscreen mode
2171 </p>
2172 </li>
2173 <li>
2174 <p>
2175 <code>move</code> – the window has changed its position in the tree
2176 </p>
2177 </li>
2178 <li>
2179 <p>
2180 <code>floating</code> – the window has transitioned to or from floating
2181 </p>
2182 </li>
2183 <li>
2184 <p>
2185 <code>urgent</code> – the window has become urgent or lost its urgent status
2186 </p>
2187 </li>
2188 <li>
2189 <p>
2190 <code>mark</code> – a mark has been added to or removed from the window
2191 </p>
2192 </li>
2193 </ul></div>
2194 <div class="paragraph"><p>Additionally a <code>container (object)</code> field will be present, which consists
2195 of the window&#8217;s parent container. Be aware that for the "new" event, the
2196 container will hold the initial name of the newly reparented window (e.g.
2197 if you run urxvt with a shell that changes the title, you will still at
2198 this point get the window title as "urxvt").</p></div>
2199 <div class="paragraph"><p><strong>Example:</strong></p></div>
2200 <div class="listingblock">
2201 <div class="content">
2202 <pre><code>{
2203 "change": "new",
2204 "container": {
2205 "id": 35569536,
2206 "type": "con",
2207 ...
2208 }
2209 }</code></pre>
2210 </div></div>
2211 </div>
2212 <div class="sect2">
2213 <h3 id="_barconfig_update_event">4.7. barconfig_update event</h3>
2214 <div class="paragraph"><p>This event consists of a single serialized map reporting on options from the
2215 barconfig of the specified bar_id that were updated in i3. This event is the
2216 same as a <code>GET_BAR_CONFIG</code> reply for the bar with the given id.</p></div>
2217 </div>
2218 <div class="sect2">
2219 <h3 id="_binding_event">4.8. binding event</h3>
2220 <div class="paragraph"><p>This event consists of a single serialized map reporting on the details of a
2221 binding that ran a command because of user input. The <code>change (string)</code> field
2222 indicates what sort of binding event was triggered (right now it will always be
2223 <code>"run"</code> but may be expanded in the future).</p></div>
2224 <div class="paragraph"><p>The <code>binding (object)</code> field contains details about the binding that was run:</p></div>
2225 <div class="dlist"><dl>
2226 <dt class="hdlist1">
2227 command (string)
2228 </dt>
2229 <dd>
2230 <p>
2231 The i3 command that is configured to run for this binding.
2232 </p>
2233 </dd>
2234 <dt class="hdlist1">
2235 event_state_mask (array of strings)
2236 </dt>
2237 <dd>
2238 <p>
2239 The group and modifier keys that were configured with this binding.
2240 </p>
2241 </dd>
2242 <dt class="hdlist1">
2243 input_code (integer)
2244 </dt>
2245 <dd>
2246 <p>
2247 If the binding was configured with <code>bindcode</code>, this will be the key code
2248 that was given for the binding. If the binding is a mouse binding, it will be
2249 the number of the mouse button that was pressed. Otherwise it will be 0.
2250 </p>
2251 </dd>
2252 <dt class="hdlist1">
2253 symbol (string or null)
2254 </dt>
2255 <dd>
2256 <p>
2257 If this is a keyboard binding that was configured with <code>bindsym</code>, this
2258 field will contain the given symbol. Otherwise it will be <code>null</code>.
2259 </p>
2260 </dd>
2261 <dt class="hdlist1">
2262 input_type (string)
2263 </dt>
2264 <dd>
2265 <p>
2266 This will be <code>"keyboard"</code> or <code>"mouse"</code> depending on whether or not this was
2267 a keyboard or a mouse binding.
2268 </p>
2269 </dd>
2270 </dl></div>
2271 <div class="paragraph"><p><strong>Example:</strong></p></div>
2272 <div class="listingblock">
2273 <div class="content">
2274 <pre><code>{
2275 "change": "run",
2276 "binding": {
2277 "command": "nop",
2278 "event_state_mask": [
2279 "shift",
2280 "ctrl"
2281 ],
2282 "input_code": 0,
2283 "symbol": "t",
2284 "input_type": "keyboard"
2285 }
2286 }</code></pre>
2287 </div></div>
2288 </div>
2289 <div class="sect2">
2290 <h3 id="_shutdown_event">4.9. shutdown event</h3>
2291 <div class="paragraph"><p>This event is triggered when the connection to the ipc is about to shutdown
2292 because of a user action such as a <code>restart</code> or <code>exit</code> command. The <code>change
2293 (string)</code> field indicates why the ipc is shutting down. It can be either
2294 <code>"restart"</code> or <code>"exit"</code>.</p></div>
2295 <div class="paragraph"><p><strong>Example:</strong></p></div>
2296 <div class="listingblock">
2297 <div class="content">
2298 <pre><code>{
2299 "change": "restart"
2300 }</code></pre>
2301 </div></div>
2302 </div>
2303 <div class="sect2">
2304 <h3 id="_tick_event">4.10. tick event</h3>
2305 <div class="paragraph"><p>This event is triggered by a subscription to tick events or by a <code>SEND_TICK</code>
2306 message.</p></div>
2307 <div class="paragraph"><p><strong>Example (upon subscription):</strong></p></div>
2308 <div class="listingblock">
2309 <div class="content">
2310 <pre><code>{
2311 "first": true,
2312 "payload": ""
2313 }</code></pre>
2314 </div></div>
2315 <div class="paragraph"><p><strong>Example (upon <code>SEND_TICK</code> with a payload of <code>arbitrary string</code>):</strong></p></div>
2316 <div class="listingblock">
2317 <div class="content">
2318 <pre><code>{
2319 "first": false,
2320 "payload": "arbitrary string"
2321 }</code></pre>
2322 </div></div>
2323 </div>
2324 </div>
2325 </div>
2326 <div class="sect1">
2327 <h2 id="_see_also_existing_libraries">5. See also (existing libraries)</h2>
2328 <div class="sectionbody">
2329 <div class="paragraph" id="libraries"><p>For some languages, libraries are available (so you don’t have to implement
2330 all this on your own). This list names some (if you wrote one, please let me
2331 know):</p></div>
2332 <div class="dlist"><dl>
2333 <dt class="hdlist1">
2334 C
2335 </dt>
2336 <dd>
2337 <div class="ulist"><ul>
2338 <li>
2339 <p>
2340 i3 includes a headerfile <code>i3/ipc.h</code> which provides you all constants.
2341 </p>
2342 </li>
2343 <li>
2344 <p>
2345 <a href="https://github.com/acrisci/i3ipc-glib">https://github.com/acrisci/i3ipc-glib</a>
2346 </p>
2347 </li>
2348 </ul></div>
2349 </dd>
2350 <dt class="hdlist1">
2351 C++
2352 </dt>
2353 <dd>
2354 <div class="ulist"><ul>
2355 <li>
2356 <p>
2357 <a href="https://github.com/drmgc/i3ipcpp">https://github.com/drmgc/i3ipcpp</a>
2358 </p>
2359 </li>
2360 </ul></div>
2361 </dd>
2362 <dt class="hdlist1">
2363 Go
2364 </dt>
2365 <dd>
2366 <div class="ulist"><ul>
2367 <li>
2368 <p>
2369 <a href="https://github.com/mdirkse/i3ipc-go">https://github.com/mdirkse/i3ipc-go</a>
2370 </p>
2371 </li>
2372 <li>
2373 <p>
2374 <a href="https://github.com/i3/go-i3">https://github.com/i3/go-i3</a>
2375 </p>
2376 </li>
2377 </ul></div>
2378 </dd>
2379 <dt class="hdlist1">
2380 JavaScript
2381 </dt>
2382 <dd>
2383 <div class="ulist"><ul>
2384 <li>
2385 <p>
2386 <a href="https://github.com/acrisci/i3ipc-gjs">https://github.com/acrisci/i3ipc-gjs</a>
2387 </p>
2388 </li>
2389 </ul></div>
2390 </dd>
2391 <dt class="hdlist1">
2392 Lua
2393 </dt>
2394 <dd>
2395 <div class="ulist"><ul>
2396 <li>
2397 <p>
2398 <a href="https://github.com/acrisci/i3ipc-lua">https://github.com/acrisci/i3ipc-lua</a>
2399 </p>
2400 </li>
2401 </ul></div>
2402 </dd>
2403 <dt class="hdlist1">
2404 Perl
2405 </dt>
2406 <dd>
2407 <div class="ulist"><ul>
2408 <li>
2409 <p>
2410 <a href="https://metacpan.org/module/AnyEvent::I3">https://metacpan.org/module/AnyEvent::I3</a>
2411 </p>
2412 </li>
2413 </ul></div>
2414 </dd>
2415 <dt class="hdlist1">
2416 Python
2417 </dt>
2418 <dd>
2419 <div class="ulist"><ul>
2420 <li>
2421 <p>
2422 <a href="https://github.com/acrisci/i3ipc-python">https://github.com/acrisci/i3ipc-python</a>
2423 </p>
2424 </li>
2425 <li>
2426 <p>
2427 <a href="https://github.com/whitelynx/i3ipc">https://github.com/whitelynx/i3ipc</a> (not maintained)
2428 </p>
2429 </li>
2430 <li>
2431 <p>
2432 <a href="https://github.com/ziberna/i3-py">https://github.com/ziberna/i3-py</a> (not maintained)
2433 </p>
2434 </li>
2435 </ul></div>
2436 </dd>
2437 <dt class="hdlist1">
2438 Ruby
2439 </dt>
2440 <dd>
2441 <div class="ulist"><ul>
2442 <li>
2443 <p>
2444 <a href="https://github.com/veelenga/i3ipc-ruby">https://github.com/veelenga/i3ipc-ruby</a>
2445 </p>
2446 </li>
2447 <li>
2448 <p>
2449 <a href="https://github.com/badboy/i3-ipc">https://github.com/badboy/i3-ipc</a> (not maintained)
2450 </p>
2451 </li>
2452 </ul></div>
2453 </dd>
2454 <dt class="hdlist1">
2455 Rust
2456 </dt>
2457 <dd>
2458 <div class="ulist"><ul>
2459 <li>
2460 <p>
2461 <a href="https://github.com/tmerr/i3ipc-rs">https://github.com/tmerr/i3ipc-rs</a>
2462 </p>
2463 </li>
2464 </ul></div>
2465 </dd>
2466 <dt class="hdlist1">
2467 OCaml
2468 </dt>
2469 <dd>
2470 <div class="ulist"><ul>
2471 <li>
2472 <p>
2473 <a href="https://github.com/Armael/ocaml-i3ipc">https://github.com/Armael/ocaml-i3ipc</a>
2474 </p>
2475 </li>
2476 </ul></div>
2477 </dd>
2478 </dl></div>
2479 </div>
2480 </div>
2481 <div class="sect1">
2482 <h2 id="_appendix_a_detecting_byte_order_in_memory_safe_languages">6. Appendix A: Detecting byte order in memory-safe languages</h2>
2483 <div class="sectionbody">
2484 <div class="paragraph"><p>Some programming languages such as Go don’t offer a way to serialize data in the
2485 native byte order of the machine they’re running on without resorting to tricks
2486 involving the <code>unsafe</code> package.</p></div>
2487 <div class="paragraph"><p>The following technique can be used (and will not be broken by changes to i3) to
2488 detect the byte order i3 is using:</p></div>
2489 <div class="olist arabic"><ol class="arabic">
2490 <li>
2491 <p>
2492 The byte order dependent fields of an IPC message are message type and
2493 payload length.
2494 </p>
2495 <div class="ulist"><ul>
2496 <li>
2497 <p>
2498 The message type <code>RUN_COMMAND</code> (0) is the same in big and little endian, so
2499 we can use it in either byte order to elicit a reply from i3.
2500 </p>
2501 </li>
2502 <li>
2503 <p>
2504 The payload length 65536 + 256 (<code>0x00 01 01 00</code>) is the same in big and
2505 little endian, and also small enough to not worry about memory allocations
2506 of that size. We must use payloads of length 65536 + 256 in every message
2507 we send, so that i3 will be able to read the entire message regardless of
2508 the byte order it uses.
2509 </p>
2510 </li>
2511 </ul></div>
2512 </li>
2513 <li>
2514 <p>
2515 Send a big endian encoded message of type <code>SUBSCRIBE</code> (2) with payload <code>[]</code>
2516 followed by 65536 + 256 - 2 <code>SPACE</code> (ASCII 0x20) bytes.
2517 </p>
2518 <div class="ulist"><ul>
2519 <li>
2520 <p>
2521 If i3 is running in big endian, this message is treated as a noop,
2522 resulting in a <code>SUBSCRIBE</code> reply with payload <code>{"success":true}</code>
2523 <span class="footnote"><br />[A small payload is important: that way, we circumvent dealing
2524 with UNIX domain socket buffer sizes, whose size depends on the
2525 implementation/operating system. Exhausting such a buffer results in an i3
2526 deadlock unless you concurrently read and write, which — depending on the
2527 programming language — makes the technique much more complicated.]<br /></span>.
2528 </p>
2529 </li>
2530 <li>
2531 <p>
2532 If i3 is running in little endian, this message is read in its entirety due
2533 to the byte order independent payload length, then
2534 <a href="https://github.com/i3/i3/blob/d726d09d496577d1c337a4b97486f2c9fbc914f1/src/ipc.c#L1188">silently
2535 discarded</a> due to the unknown message type.
2536 </p>
2537 </li>
2538 </ul></div>
2539 </li>
2540 <li>
2541 <p>
2542 Send a byte order independent message, i.e. type <code>RUN_COMMAND</code> (0) with
2543 payload <code>nop byte order detection. padding:</code>, padded to 65536 + 256 bytes
2544 with <code>a</code> (ASCII 0x61) bytes. i3 will reply to this message with a reply of
2545 type <code>COMMAND</code> (0).
2546 </p>
2547 <div class="ulist"><ul>
2548 <li>
2549 <p>
2550 The human-readable prefix is in there to not confuse readers of the i3 log.
2551 </p>
2552 </li>
2553 <li>
2554 <p>
2555 This messages serves as a synchronization primitive so that we know whether
2556 i3 discarded the <code>SUBSCRIBE</code> message or didn’t answer it yet.
2557 </p>
2558 </li>
2559 </ul></div>
2560 </li>
2561 <li>
2562 <p>
2563 Receive a message header from i3, decoding the message type as big endian.
2564 </p>
2565 <div class="ulist"><ul>
2566 <li>
2567 <p>
2568 If the message’s reply type is <code>COMMAND</code> (0), i3 is running in little
2569 endian (because the <code>SUBSCRIBE</code> message was discarded). Decode the message
2570 payload length as little endian, receive the message payload.
2571 </p>
2572 </li>
2573 <li>
2574 <p>
2575 If the message’s reply type is anything else, i3 is running in big endian
2576 (because our big endian encoded <code>SUBSCRIBE</code> message was answered). Decode
2577 the message payload length in big endian, receive the message
2578 payload. Then, receive the pending <code>COMMAND</code> message reply in big endian.
2579 </p>
2580 </li>
2581 </ul></div>
2582 </li>
2583 <li>
2584 <p>
2585 From here on out, send/receive all messages using the detected byte order.
2586 </p>
2587 </li>
2588 </ol></div>
2589 <div class="paragraph"><p>Find an example implementation of this technique in
2590 <a href="https://github.com/i3/go-i3/blob/master/byteorder.go">https://github.com/i3/go-i3/blob/master/byteorder.go</a></p></div>
2591 </div>
2592 </div>
2593 </div>
2594 <div id="footnotes"><hr /></div>
2595 <div id="footer">
2596 <div id="footer-text">
2597 Last updated
2598 2019-01-27 16:45:19 CET
2599 </div>
2600 </div>
2601 </body>
2602 </html>
Binary diff not shown
Binary diff not shown
0 Layout saving in i3
1 ===================
2 Michael Stapelberg <michael@i3wm.org>
3 April 2014
4
5 Layout saving/restoring is a feature that was introduced in i3 v4.8.
6
7 Layout saving/restoring allows you to load a JSON layout file so that you can
8 have a base layout to start working with after powering on your computer.
9 Dynamic use-cases also come to mind: if you frequently (but not always!) need a
10 grid layout of terminals with ping/traceroute commands to diagnose network
11 issues, you can easily automate opening these windows in just the right layout.
12
13 == Saving the layout
14
15 You can save the layout of either a single workspace or an entire output (e.g.
16 LVDS1). Of course, you can repeat this step multiple times if you want to
17 save/restore multiple workspaces/outputs.
18
19 +i3-save-tree(1)+ is a tool to save the layout. It will print a JSON
20 representation of i3’s internal layout data structures to stdout. Typically,
21 you may want to take a quick look at the output, then save it to a file and
22 tweak it a little bit:
23
24 ---------------------------------------------------
25 i3-save-tree --workspace 1 > ~/.i3/workspace-1.json
26 ---------------------------------------------------
27
28 Please note that the output of +i3-save-tree(1)+ is *NOT useful* until you
29 manually modify it — you need to tell i3 how to match/distinguish windows (for
30 example based on their WM_CLASS, title, etc.). By default, all the different
31 window properties are included in the output, but commented out. This is partly
32 to avoid relying on heuristics and partly to make you aware how i3 works so
33 that you can easily solve layout restoring problems.
34
35 How to modify the file manually is described in <<EditingLayoutFiles>>.
36
37 == Restoring the layout
38
39 After restoring the example layout from <<EditingLayoutFiles>>, i3 will open
40 placeholder windows for all the windows that were specified in the layout file.
41 You can recognize the placeholder windows by the watch symbol
42 footnote:[Depending on the font you are using, a placeholder symbol may show up
43 instead of the watch symbol.] in the center of the window, and by the swallow
44 criteria specification at the top of the window:
45
46 image:layout-saving-1.png["Restored layout",width=400,link="layout-saving-1.png"]
47
48 When an application opens a window that matches the specified swallow criteria,
49 it will be placed in the corresponding placeholder window. We say it gets
50 *swallowed* by the placeholder container, hence the term.
51
52 Note: Swallowing windows into unsatisfied placeholder windows takes precedence
53 over
54 link:https://i3wm.org/docs/userguide.html#_automatically_putting_clients_on_specific_workspaces[assignment
55 rules]. For example, if you assign all Emacs windows to workspace 1 in your i3
56 configuration file, but there is a placeholder window on workspace 2 which
57 matches Emacs as well, your newly started Emacs window will end up in the
58 placeholder window on workspace 2.
59
60 The placeholder windows are just regular windows, so feel free to move them
61 around or close them, for example.
62
63 === append_layout command
64
65 The +append_layout+ command is used to load a layout file into i3. It accepts a
66 path (relative to i3’s current working directory or absolute) to a JSON file.
67
68 *Syntax*:
69 --------------------------------------------------------------------------------
70 append_layout <path>
71 --------------------------------------------------------------------------------
72
73 *Examples*:
74 --------------------------------------------------------------------------------
75 # From a terminal or script:
76 i3-msg "workspace 1; append_layout /home/michael/.i3/workspace-1.json"
77
78 # In your i3 configuration file, you can autostart i3-msg like this:
79 # (Note that those lines will quickly become long, so typically you would store
80 # them in a script with proper indentation.)
81 exec --no-startup-id "i3-msg 'workspace 1; append_layout /home/michael/.i3/workspace-1.json'"
82 --------------------------------------------------------------------------------
83
84 == Editing layout files
85
86 [[EditingLayoutFiles]]
87
88 === Anatomy of a layout file
89
90 Here is an example layout file that we’ll discuss:
91
92 --------------------------------------------------------------------------------
93 {
94 // splitv split container with 2 children
95 "layout": "splitv",
96 "percent": 0.4,
97 "type": "con",
98 "nodes": [
99 {
100 "border": "none",
101 "name": "irssi",
102 "percent": 0.5,
103 "type": "con",
104 "swallows": [
105 {
106 "class": "^URxvt$",
107 "instance": "^irssi$"
108 }
109 ]
110 },
111 {
112 // stacked split container with 2 children
113 "layout": "stacked",
114 "percent": 0.5,
115 "type": "con",
116 "nodes": [
117 {
118 "name": "notmuch",
119 "percent": 0.5,
120 "type": "con",
121 "swallows": [
122 {
123 "class": "^Emacs$",
124 "instance": "^notmuch$"
125 }
126 ]
127 },
128 {
129 "name": "midna: ~",
130 "percent": 0.5,
131 "type": "con"
132 }
133 ]
134 }
135 ]
136 }
137
138 {
139 // stacked split container with 1 children
140 "layout": "stacked",
141 "percent": 0.6,
142 "type": "con",
143 "nodes": [
144 {
145 "name": "chrome",
146 "type": "con",
147 "swallows": [
148 {
149 "class": "^Google-chrome$"
150 }
151 ]
152 }
153 ]
154 }
155 --------------------------------------------------------------------------------
156
157 In this layout, the screen is divided into two columns. In the left column,
158 which covers 40% of the screen, there is a terminal emulator running irssi on
159 the top, and a stacked split container with an Emacs window and a terminal
160 emulator on the bottom. In the right column, there is a stacked container with
161 a Chrome window:
162
163 image:layout-saving-1.png["Restored layout",width=400,link="layout-saving-1.png"]
164
165 The structure of this JSON file looks a lot like the +TREE+ reply, see
166 https://build.i3wm.org/docs/ipc.html#_tree_reply for documentation on that. Some
167 properties are excluded because they are not relevant when restoring a layout.
168
169 Most importantly, look at the "swallows" section of each window. This is where
170 you need to be more or less specific. As an example, remember the section about
171 the Emacs window:
172
173 --------------------------------------------------------------------------------
174 "swallows": [
175 {
176 "class": "^Emacs$",
177 "instance": "^notmuch$"
178 }
179 ]
180 --------------------------------------------------------------------------------
181
182 Here you can see that i3 will require both the class and the instance to match.
183 Therefore, if you just start Emacs via dmenu, it will not get swallowed by that
184 container. Only if you start Emacs with the proper instance name (+emacs24
185 --name notmuch+), it will get swallowed.
186
187 You can match on "class", "instance", "window_role" and "title". All values are
188 case-sensitive regular expressions (PCRE). Use +xprop(1)+ and click into a
189 window to see its properties:
190
191 --------------------------------------------------------------------------------
192 $ xprop
193 WM_WINDOW_ROLE(STRING) = "gimp-toolbox-color-dialog"
194 WM_CLASS(STRING) = "gimp-2.8", "Gimp-2.8"
195 _NET_WM_NAME(UTF8_STRING) = "Change Foreground Color"
196 --------------------------------------------------------------------------------
197
198 The first part of +WM_CLASS+ is the "instance" (gimp-2.8 in this case), the
199 second part is the "class" (Gimp-2.8 in this case). "title" matches against
200 +_NET_WM_NAME+ and "window_role" matches against +WM_WINDOW_ROLE+.
201
202 In general, you should try to be as specific as possible in your swallow
203 criteria. Try to use criteria that match one window and only one window, to
204 have a reliable startup procedure.
205
206 If you specify multiple swallow criteria, the placeholder will be replaced by
207 the window which matches any of the criteria. As an example:
208
209 --------------------------------------------------------------------------------
210 // Matches either Emacs or Gvim, whichever one is started first.
211 "swallows": [
212 {"class": "^Emacs$"},
213 {"class": "^Gvim$"}
214 ]
215 --------------------------------------------------------------------------------
216
217 === JSON standard non-compliance
218
219 A layout file as generated by +i3-save-tree(1)+ is not strictly valid JSON:
220
221 1. Layout files contain multiple “JSON texts” at the top level. The JSON
222 standard doesn't prohibit this, but in practice most JSON parsers only
223 allow precisely one “text” per document/file, and will mark multiple texts
224 as invalid JSON.
225
226 2. Layout files contain comments which are not allowed by the JSON standard,
227 but are understood by many parsers.
228
229 Both of these deviations from the norm are to make manual editing by humans
230 easier. In case you are writing a more elaborate tool for manipulating these
231 layouts, you can either use a JSON parser that supports these deviations (for
232 example libyajl), transform the layout file to a JSON-conforming file, or
233 link:https://github.com/i3/i3/blob/next/.github/CONTRIBUTING.md[submit a patch]
234 to make +i3-save-tree(1)+ optionally output standard-conforming JSON.
235
236 == Troubleshooting
237
238 === Restoring a vertically split workspace
239
240 When using +i3-save-tree+ with the +--workspace+ switch, only the *contents* of
241 the workspace will be dumped. This means that properties of the workspace
242 itself will be lost.
243
244 This is relevant for, e.g., a vertically split container as the base container of
245 a workspace. Since the split mode is a property of the workspace, it will not be
246 stored. In this case, you will have to manually wrap your layout in such a
247 container:
248
249 --------------------------------------------------------------------------------
250 // vim:ts=4:sw=4:et
251 {
252 // this is a manually added container to restore the vertical split
253 "layout": "splitv",
254 "percent": 0.5,
255 "type": "con",
256 "nodes": [
257
258 // the dumped workspace layout goes here
259
260 ]
261 }
262 --------------------------------------------------------------------------------
263
264 === Placeholders using window title matches don't swallow the window
265
266 If you use the +title+ attribute to match a window and find that it doesn't
267 work or only works sometimes, the reason might be that the application sets the
268 title only after making the window visible. This will be especially true for
269 programs running inside terminal emulators, e.g., +urxvt -e irssi+ when
270 matching on +title: "irssi"+.
271
272 One way to deal with this is to not rely on the title, but instead use, e.g.,
273 the +instance+ attribute and running the program to set this window instance to
274 that value:
275
276 --------------------------------------------------------------------------------
277 # Run irssi via
278 # urxvt -name "irssi-container" -e irssi
279
280 "swallows": [
281 {
282 "class": "URxvt",
283 "instance": "irssi-container"
284 }
285 ]
286 --------------------------------------------------------------------------------
Binary diff not shown
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>Layout saving in i3</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>Layout saving in i3</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">April 2014</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>Layout saving/restoring is a feature that was introduced in i3 v4.8.</p></div>
749 <div class="paragraph"><p>Layout saving/restoring allows you to load a JSON layout file so that you can
750 have a base layout to start working with after powering on your computer.
751 Dynamic use-cases also come to mind: if you frequently (but not always!) need a
752 grid layout of terminals with ping/traceroute commands to diagnose network
753 issues, you can easily automate opening these windows in just the right layout.</p></div>
754 </div>
755 </div>
756 <div class="sect1">
757 <h2 id="_saving_the_layout">1. Saving the layout</h2>
758 <div class="sectionbody">
759 <div class="paragraph"><p>You can save the layout of either a single workspace or an entire output (e.g.
760 LVDS1). Of course, you can repeat this step multiple times if you want to
761 save/restore multiple workspaces/outputs.</p></div>
762 <div class="paragraph"><p><code>i3-save-tree(1)</code> is a tool to save the layout. It will print a JSON
763 representation of i3’s internal layout data structures to stdout. Typically,
764 you may want to take a quick look at the output, then save it to a file and
765 tweak it a little bit:</p></div>
766 <div class="listingblock">
767 <div class="content">
768 <pre><code>i3-save-tree --workspace 1 &gt; ~/.i3/workspace-1.json</code></pre>
769 </div></div>
770 <div class="paragraph"><p>Please note that the output of <code>i3-save-tree(1)</code> is <strong>NOT useful</strong> until you
771 manually modify it — you need to tell i3 how to match/distinguish windows (for
772 example based on their WM_CLASS, title, etc.). By default, all the different
773 window properties are included in the output, but commented out. This is partly
774 to avoid relying on heuristics and partly to make you aware how i3 works so
775 that you can easily solve layout restoring problems.</p></div>
776 <div class="paragraph"><p>How to modify the file manually is described in <a href="#EditingLayoutFiles">[EditingLayoutFiles]</a>.</p></div>
777 </div>
778 </div>
779 <div class="sect1">
780 <h2 id="_restoring_the_layout">2. Restoring the layout</h2>
781 <div class="sectionbody">
782 <div class="paragraph"><p>After restoring the example layout from <a href="#EditingLayoutFiles">[EditingLayoutFiles]</a>, i3 will open
783 placeholder windows for all the windows that were specified in the layout file.
784 You can recognize the placeholder windows by the watch symbol
785 <span class="footnote"><br />[Depending on the font you are using, a placeholder symbol may show up
786 instead of the watch symbol.]<br /></span> in the center of the window, and by the swallow
787 criteria specification at the top of the window:</p></div>
788 <div class="paragraph"><p><span class="image">
789 <a class="image" href="layout-saving-1.png">
790 <img src="layout-saving-1.png" alt="Restored layout" width="400" />
791 </a>
792 </span></p></div>
793 <div class="paragraph"><p>When an application opens a window that matches the specified swallow criteria,
794 it will be placed in the corresponding placeholder window. We say it gets
795 <strong>swallowed</strong> by the placeholder container, hence the term.</p></div>
796 <div class="paragraph"><p>Note: Swallowing windows into unsatisfied placeholder windows takes precedence
797 over
798 <a href="https://i3wm.org/docs/userguide.html#_automatically_putting_clients_on_specific_workspaces">assignment
799 rules</a>. For example, if you assign all Emacs windows to workspace 1 in your i3
800 configuration file, but there is a placeholder window on workspace 2 which
801 matches Emacs as well, your newly started Emacs window will end up in the
802 placeholder window on workspace 2.</p></div>
803 <div class="paragraph"><p>The placeholder windows are just regular windows, so feel free to move them
804 around or close them, for example.</p></div>
805 <div class="sect2">
806 <h3 id="_append_layout_command">2.1. append_layout command</h3>
807 <div class="paragraph"><p>The <code>append_layout</code> command is used to load a layout file into i3. It accepts a
808 path (relative to i3’s current working directory or absolute) to a JSON file.</p></div>
809 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
810 <div class="listingblock">
811 <div class="content">
812 <pre><code>append_layout &lt;path&gt;</code></pre>
813 </div></div>
814 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
815 <div class="listingblock">
816 <div class="content">
817 <pre><code># From a terminal or script:
818 i3-msg "workspace 1; append_layout /home/michael/.i3/workspace-1.json"
819
820 # In your i3 configuration file, you can autostart i3-msg like this:
821 # (Note that those lines will quickly become long, so typically you would store
822 # them in a script with proper indentation.)
823 exec --no-startup-id "i3-msg 'workspace 1; append_layout /home/michael/.i3/workspace-1.json'"</code></pre>
824 </div></div>
825 </div>
826 </div>
827 </div>
828 <div class="sect1">
829 <h2 id="_editing_layout_files">3. Editing layout files</h2>
830 <div class="sectionbody">
831 <div class="sect2">
832 <h3 id="EditingLayoutFiles">3.1. Anatomy of a layout file</h3>
833 <div class="paragraph"><p>Here is an example layout file that we’ll discuss:</p></div>
834 <div class="listingblock">
835 <div class="content">
836 <pre><code>{
837 // splitv split container with 2 children
838 "layout": "splitv",
839 "percent": 0.4,
840 "type": "con",
841 "nodes": [
842 {
843 "border": "none",
844 "name": "irssi",
845 "percent": 0.5,
846 "type": "con",
847 "swallows": [
848 {
849 "class": "^URxvt$",
850 "instance": "^irssi$"
851 }
852 ]
853 },
854 {
855 // stacked split container with 2 children
856 "layout": "stacked",
857 "percent": 0.5,
858 "type": "con",
859 "nodes": [
860 {
861 "name": "notmuch",
862 "percent": 0.5,
863 "type": "con",
864 "swallows": [
865 {
866 "class": "^Emacs$",
867 "instance": "^notmuch$"
868 }
869 ]
870 },
871 {
872 "name": "midna: ~",
873 "percent": 0.5,
874 "type": "con"
875 }
876 ]
877 }
878 ]
879 }
880
881 {
882 // stacked split container with 1 children
883 "layout": "stacked",
884 "percent": 0.6,
885 "type": "con",
886 "nodes": [
887 {
888 "name": "chrome",
889 "type": "con",
890 "swallows": [
891 {
892 "class": "^Google-chrome$"
893 }
894 ]
895 }
896 ]
897 }</code></pre>
898 </div></div>
899 <div class="paragraph"><p>In this layout, the screen is divided into two columns. In the left column,
900 which covers 40% of the screen, there is a terminal emulator running irssi on
901 the top, and a stacked split container with an Emacs window and a terminal
902 emulator on the bottom. In the right column, there is a stacked container with
903 a Chrome window:</p></div>
904 <div class="paragraph"><p><span class="image">
905 <a class="image" href="layout-saving-1.png">
906 <img src="layout-saving-1.png" alt="Restored layout" width="400" />
907 </a>
908 </span></p></div>
909 <div class="paragraph"><p>The structure of this JSON file looks a lot like the <code>TREE</code> reply, see
910 <a href="https://build.i3wm.org/docs/ipc.html#_tree_reply">https://build.i3wm.org/docs/ipc.html#_tree_reply</a> for documentation on that. Some
911 properties are excluded because they are not relevant when restoring a layout.</p></div>
912 <div class="paragraph"><p>Most importantly, look at the "swallows" section of each window. This is where
913 you need to be more or less specific. As an example, remember the section about
914 the Emacs window:</p></div>
915 <div class="listingblock">
916 <div class="content">
917 <pre><code>"swallows": [
918 {
919 "class": "^Emacs$",
920 "instance": "^notmuch$"
921 }
922 ]</code></pre>
923 </div></div>
924 <div class="paragraph"><p>Here you can see that i3 will require both the class and the instance to match.
925 Therefore, if you just start Emacs via dmenu, it will not get swallowed by that
926 container. Only if you start Emacs with the proper instance name (<code>emacs24
927 --name notmuch</code>), it will get swallowed.</p></div>
928 <div class="paragraph"><p>You can match on "class", "instance", "window_role" and "title". All values are
929 case-sensitive regular expressions (PCRE). Use <code>xprop(1)</code> and click into a
930 window to see its properties:</p></div>
931 <div class="listingblock">
932 <div class="content">
933 <pre><code>$ xprop
934 WM_WINDOW_ROLE(STRING) = "gimp-toolbox-color-dialog"
935 WM_CLASS(STRING) = "gimp-2.8", "Gimp-2.8"
936 _NET_WM_NAME(UTF8_STRING) = "Change Foreground Color"</code></pre>
937 </div></div>
938 <div class="paragraph"><p>The first part of <code>WM_CLASS</code> is the "instance" (gimp-2.8 in this case), the
939 second part is the "class" (Gimp-2.8 in this case). "title" matches against
940 <code>_NET_WM_NAME</code> and "window_role" matches against <code>WM_WINDOW_ROLE</code>.</p></div>
941 <div class="paragraph"><p>In general, you should try to be as specific as possible in your swallow
942 criteria. Try to use criteria that match one window and only one window, to
943 have a reliable startup procedure.</p></div>
944 <div class="paragraph"><p>If you specify multiple swallow criteria, the placeholder will be replaced by
945 the window which matches any of the criteria. As an example:</p></div>
946 <div class="listingblock">
947 <div class="content">
948 <pre><code>// Matches either Emacs or Gvim, whichever one is started first.
949 "swallows": [
950 {"class": "^Emacs$"},
951 {"class": "^Gvim$"}
952 ]</code></pre>
953 </div></div>
954 </div>
955 <div class="sect2">
956 <h3 id="_json_standard_non_compliance">3.2. JSON standard non-compliance</h3>
957 <div class="paragraph"><p>A layout file as generated by <code>i3-save-tree(1)</code> is not strictly valid JSON:</p></div>
958 <div class="olist arabic"><ol class="arabic">
959 <li>
960 <p>
961 Layout files contain multiple “JSON texts” at the top level. The JSON
962 standard doesn&#8217;t prohibit this, but in practice most JSON parsers only
963 allow precisely one “text” per document/file, and will mark multiple texts
964 as invalid JSON.
965 </p>
966 </li>
967 <li>
968 <p>
969 Layout files contain comments which are not allowed by the JSON standard,
970 but are understood by many parsers.
971 </p>
972 </li>
973 </ol></div>
974 <div class="paragraph"><p>Both of these deviations from the norm are to make manual editing by humans
975 easier. In case you are writing a more elaborate tool for manipulating these
976 layouts, you can either use a JSON parser that supports these deviations (for
977 example libyajl), transform the layout file to a JSON-conforming file, or
978 <a href="https://github.com/i3/i3/blob/next/.github/CONTRIBUTING.md">submit a patch</a>
979 to make <code>i3-save-tree(1)</code> optionally output standard-conforming JSON.</p></div>
980 </div>
981 </div>
982 </div>
983 <div class="sect1">
984 <h2 id="_troubleshooting">4. Troubleshooting</h2>
985 <div class="sectionbody">
986 <div class="sect2">
987 <h3 id="_restoring_a_vertically_split_workspace">4.1. Restoring a vertically split workspace</h3>
988 <div class="paragraph"><p>When using <code>i3-save-tree</code> with the <code>--workspace</code> switch, only the <strong>contents</strong> of
989 the workspace will be dumped. This means that properties of the workspace
990 itself will be lost.</p></div>
991 <div class="paragraph"><p>This is relevant for, e.g., a vertically split container as the base container of
992 a workspace. Since the split mode is a property of the workspace, it will not be
993 stored. In this case, you will have to manually wrap your layout in such a
994 container:</p></div>
995 <div class="listingblock">
996 <div class="content">
997 <pre><code>// vim:ts=4:sw=4:et
998 {
999 // this is a manually added container to restore the vertical split
1000 "layout": "splitv",
1001 "percent": 0.5,
1002 "type": "con",
1003 "nodes": [
1004
1005 // the dumped workspace layout goes here
1006
1007 ]
1008 }</code></pre>
1009 </div></div>
1010 </div>
1011 <div class="sect2">
1012 <h3 id="_placeholders_using_window_title_matches_don_8217_t_swallow_the_window">4.2. Placeholders using window title matches don&#8217;t swallow the window</h3>
1013 <div class="paragraph"><p>If you use the <code>title</code> attribute to match a window and find that it doesn&#8217;t
1014 work or only works sometimes, the reason might be that the application sets the
1015 title only after making the window visible. This will be especially true for
1016 programs running inside terminal emulators, e.g., <code>urxvt -e irssi</code> when
1017 matching on <code>title: "irssi"</code>.</p></div>
1018 <div class="paragraph"><p>One way to deal with this is to not rely on the title, but instead use, e.g.,
1019 the <code>instance</code> attribute and running the program to set this window instance to
1020 that value:</p></div>
1021 <div class="listingblock">
1022 <div class="content">
1023 <pre><code># Run irssi via
1024 # urxvt -name "irssi-container" -e irssi
1025
1026 "swallows": [
1027 {
1028 "class": "URxvt",
1029 "instance": "irssi-container"
1030 }
1031 ]</code></pre>
1032 </div></div>
1033 </div>
1034 </div>
1035 </div>
1036 </div>
1037 <div id="footnotes"><hr /></div>
1038 <div id="footer">
1039 <div id="footer-text">
1040 Last updated
1041 2019-01-27 16:45:19 CET
1042 </div>
1043 </div>
1044 </body>
1045 </html>
0 <!doctype html>
1 <html lang="en">
2 <head>
3 <link rel="icon" type="image/png" href="/favicon.png">
4 <meta charset="utf-8">
5 <meta name="generator" content="Pod::Simple::HTML">
6 <meta name="description" content="i3 Perl documentation">
7 <link rel="stylesheet" href="https://i3wm.org/css/style.css" type="text/css" />
8 <style type="text/css">
9 .pod pre {
10 background: #333;
11 border: 1px solid #555;
12 border-left: 5px solid #555;
13 padding: 0.5em;
14 padding-left: 0;
15 padding-right: 0.5em;
16 white-space: pre;
17 color: white;
18 }
19
20 .pod ul {
21 list-style-type: none;
22 }
23 .pod li {
24 margin-bottom: 0 !important;
25 }
26
27 tt {
28 font-family: 'Droid Sans Mono', sans-serif;
29 font-size: inherit;
30 }
31
32 .pod h1 a, .pod h2 a, .pod h3 a, .pod h4 a {
33 text-decoration: none;
34 color: white;
35 }
36 </style>
37 <title>
38 i3test::Test</title>
39 </head>
40 <body>
41
42 <div id="main">
43 <a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>
44 <ul id="nav">
45 <li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>
46 <li><a href="/screenshots">Screens</a></li>
47 <li><a href="https://www.reddit.com/r/i3wm/">FAQ</a></li>
48 <li><a href="/contact">Contact</a></li>
49 <li><a href="https://bugs.i3wm.org/">Bugs</a></li>
50 </ul>
51 <br style="clear: both">
52 <div id="content" class="pod">
53 <h1>i3 Perl documentation</h1>
54
55 <a name='___top' class='dummyTopAnchor' ></a>
56
57 <div class='indexgroup'>
58 <ul class='indexList indexList1'>
59 <li class='indexItem indexItem1'><a href='#NAME'>NAME</a>
60 <li class='indexItem indexItem1'><a href='#SYNOPSIS'>SYNOPSIS</a>
61 <li class='indexItem indexItem1'><a href='#DESCRIPTION'>DESCRIPTION</a>
62 <li class='indexItem indexItem1'><a href='#EXPORT'>EXPORT</a>
63 <ul class='indexList indexList2'>
64 <li class='indexItem indexItem2'><a href='#is_num_children(%24workspace%2C_%24expected%2C_%24test_name)'>is_num_children($workspace, $expected, $test_name)</a>
65 <li class='indexItem indexItem2'><a href='#is_num_fullscreen(%24workspace%2C_%24expected%2C_%24test_name)'>is_num_fullscreen($workspace, $expected, $test_name)</a>
66 <li class='indexItem indexItem2'><a href='#cmp_float(%24a%2C_%24b)'>cmp_float($a, $b)</a>
67 <li class='indexItem indexItem2'><a href='#does_i3_live'>does_i3_live</a>
68 </ul>
69 <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
70 </ul>
71 </div>
72
73 <h1><a class='u' href='#___top' title='click to go to top of document'
74 name="NAME"
75 >NAME</a></h1>
76
77 <p>i3test::Test - Additional test instructions for use in i3 testcases</p>
78
79 <h1><a class='u' href='#___top' title='click to go to top of document'
80 name="SYNOPSIS"
81 >SYNOPSIS</a></h1>
82 <pre><tt> use i3test;
83
84 my $ws = fresh_workspace;
85 is_num_children($ws, 0, &#39;no containers on this workspace yet&#39;);
86 cmd &#39;open&#39;;
87 is_num_children($ws, 1, &#39;one container after &#34;open&#34;&#39;);
88
89 done_testing;</tt></pre>
90 <h1><a class='u' href='#___top' title='click to go to top of document'
91 name="DESCRIPTION"
92 >DESCRIPTION</a></h1>
93
94 <p>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.</p>
95
96 <h1><a class='u' href='#___top' title='click to go to top of document'
97 name="EXPORT"
98 >EXPORT</a></h1>
99
100 <h2><a class='u' href='#___top' title='click to go to top of document'
101 name="is_num_children($workspace,_$expected,_$test_name)"
102 >is_num_children($workspace, $expected, $test_name)</a></h2>
103
104 <p>Gets the number of children on the given workspace and verifies that they match the expected amount of children.</p>
105 <pre><tt> is_num_children(&#39;1&#39;, 0, &#39;no containers on workspace 1 at startup&#39;);</tt></pre>
106 <h2><a class='u' href='#___top' title='click to go to top of document'
107 name="is_num_fullscreen($workspace,_$expected,_$test_name)"
108 >is_num_fullscreen($workspace, $expected, $test_name)</a></h2>
109
110 <p>Gets the number of fullscreen containers on the given workspace and verifies that they match the expected amount.</p>
111 <pre><tt> is_num_fullscreen(&#39;1&#39;, 0, &#39;no fullscreen containers on workspace 1&#39;);</tt></pre>
112 <h2><a class='u' href='#___top' title='click to go to top of document'
113 name="cmp_float($a,_$b)"
114 >cmp_float($a, $b)</a></h2>
115
116 <p>Compares floating point numbers <code>$a</code> and <code>$b</code> and returns true if they differ less then 1e-6.</p>
117 <pre><tt> $tmp = fresh_workspace;
118
119 open_window for (1..4);
120
121 cmd &#39;resize grow width 10 px or 25 ppt&#39;;
122
123 ($nodes, $focus) = get_ws_content($tmp);
124 ok(cmp_float($nodes-&#62;[0]-&#62;{percent}, 0.166666666666667), &#39;first window got 16%&#39;);
125 ok(cmp_float($nodes-&#62;[1]-&#62;{percent}, 0.166666666666667), &#39;second window got 16%&#39;);
126 ok(cmp_float($nodes-&#62;[2]-&#62;{percent}, 0.166666666666667), &#39;third window got 16%&#39;);
127 ok(cmp_float($nodes-&#62;[3]-&#62;{percent}, 0.50), &#39;fourth window got 50%&#39;);</tt></pre>
128 <h2><a class='u' href='#___top' title='click to go to top of document'
129 name="does_i3_live"
130 >does_i3_live</a></h2>
131
132 <p>Returns true if the layout tree can still be received from i3.</p>
133 <pre><tt> # i3 used to crash on invalid commands in revision X
134 cmd &#39;invalid command&#39;;
135 does_i3_live;</tt></pre>
136 <h1><a class='u' href='#___top' title='click to go to top of document'
137 name="AUTHOR"
138 >AUTHOR</a></h1>
139
140 <p>Michael Stapelberg &#60;michael@i3wm.org&#62;</p>
141
142 <!-- end doc -->
143
144 </body></html>
0 <!doctype html>
1 <html lang="en">
2 <head>
3 <link rel="icon" type="image/png" href="/favicon.png">
4 <meta charset="utf-8">
5 <meta name="generator" content="Pod::Simple::HTML">
6 <meta name="description" content="i3 Perl documentation">
7 <link rel="stylesheet" href="https://i3wm.org/css/style.css" type="text/css" />
8 <style type="text/css">
9 .pod pre {
10 background: #333;
11 border: 1px solid #555;
12 border-left: 5px solid #555;
13 padding: 0.5em;
14 padding-left: 0;
15 padding-right: 0.5em;
16 white-space: pre;
17 color: white;
18 }
19
20 .pod ul {
21 list-style-type: none;
22 }
23 .pod li {
24 margin-bottom: 0 !important;
25 }
26
27 tt {
28 font-family: 'Droid Sans Mono', sans-serif;
29 font-size: inherit;
30 }
31
32 .pod h1 a, .pod h2 a, .pod h3 a, .pod h4 a {
33 text-decoration: none;
34 color: white;
35 }
36 </style>
37 <title>
38 i3test</title>
39 </head>
40 <body>
41
42 <div id="main">
43 <a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>
44 <ul id="nav">
45 <li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>
46 <li><a href="/screenshots">Screens</a></li>
47 <li><a href="https://www.reddit.com/r/i3wm/">FAQ</a></li>
48 <li><a href="/contact">Contact</a></li>
49 <li><a href="https://bugs.i3wm.org/">Bugs</a></li>
50 </ul>
51 <br style="clear: both">
52 <div id="content" class="pod">
53 <h1>i3 Perl documentation</h1>
54
55 <a name='___top' class='dummyTopAnchor' ></a>
56
57 <div class='indexgroup'>
58 <ul class='indexList indexList1'>
59 <li class='indexItem indexItem1'><a href='#NAME'>NAME</a>
60 <li class='indexItem indexItem1'><a href='#SYNOPSIS'>SYNOPSIS</a>
61 <li class='indexItem indexItem1'><a href='#DESCRIPTION'>DESCRIPTION</a>
62 <li class='indexItem indexItem1'><a href='#EXPORT'>EXPORT</a>
63 <ul class='indexList indexList2'>
64 <li class='indexItem indexItem2'><a href='#wait_for_event(%24timeout%2C_%24callback)'>wait_for_event($timeout, $callback)</a>
65 <li class='indexItem indexItem2'><a href='#wait_for_map(%24window)'>wait_for_map($window)</a>
66 <li class='indexItem indexItem2'><a href='#wait_for_unmap(%24window)'>wait_for_unmap($window)</a>
67 <li class='indexItem indexItem2'><a href='#open_window(%5B_%24args_%5D)'>open_window([ $args ])</a>
68 <li class='indexItem indexItem2'><a href='#open_floating_window(%5B_%24args_%5D)'>open_floating_window([ $args ])</a>
69 <li class='indexItem indexItem2'><a href='#get_workspace_names()'>get_workspace_names()</a>
70 <li class='indexItem indexItem2'><a href='#get_output_for_workspace()'>get_output_for_workspace()</a>
71 <li class='indexItem indexItem2'><a href='#get_unused_workspace'>get_unused_workspace</a>
72 <li class='indexItem indexItem2'><a href='#fresh_workspace(%5B_%24args_%5D)'>fresh_workspace([ $args ])</a>
73 <li class='indexItem indexItem2'><a href='#get_ws(%24workspace)'>get_ws($workspace)</a>
74 <li class='indexItem indexItem2'><a href='#get_ws_content(%24workspace)'>get_ws_content($workspace)</a>
75 <li class='indexItem indexItem2'><a href='#get_focused(%24workspace)'>get_focused($workspace)</a>
76 <li class='indexItem indexItem2'><a href='#get_dock_clients(%5B_%24dockarea_%5D)'>get_dock_clients([ $dockarea ])</a>
77 <li class='indexItem indexItem2'><a href='#cmd(%24command)'>cmd($command)</a>
78 <li class='indexItem indexItem2'><a href='#workspace_exists(%24workspace)'>workspace_exists($workspace)</a>
79 <li class='indexItem indexItem2'><a href='#focused_ws'>focused_ws</a>
80 <li class='indexItem indexItem2'><a href='#sync_with_i3(%5B_%24args_%5D)'>sync_with_i3([ $args ])</a>
81 <li class='indexItem indexItem2'><a href='#exit_gracefully(%24pid%2C_%5B_%24socketpath_%5D)'>exit_gracefully($pid, [ $socketpath ])</a>
82 <li class='indexItem indexItem2'><a href='#exit_forcefully(%24pid%2C_%5B_%24signal_%5D)'>exit_forcefully($pid, [ $signal ])</a>
83 <li class='indexItem indexItem2'><a href='#get_socket_path(%5B_%24cache_%5D)'>get_socket_path([ $cache ])</a>
84 <li class='indexItem indexItem2'><a href='#launch_with_config(%24config%2C_%5B_%24args_%5D)'>launch_with_config($config, [ $args ])</a>
85 <li class='indexItem indexItem2'><a href='#get_i3_log'>get_i3_log</a>
86 <li class='indexItem indexItem2'><a href='#kill_all_windows'>kill_all_windows</a>
87 <li class='indexItem indexItem2'><a href='#events_for(%24subscribecb%2C_%5B_%24rettype_%5D%2C_%5B_%24eventcbs_%5D)'>events_for($subscribecb, [ $rettype ], [ $eventcbs ])</a>
88 <li class='indexItem indexItem2'><a href='#listen_for_binding(%24cb)'>listen_for_binding($cb)</a>
89 <li class='indexItem indexItem2'><a href='#is_net_wm_state_focused'>is_net_wm_state_focused</a>
90 </ul>
91 <li class='indexItem indexItem1'><a href='#AUTHOR'>AUTHOR</a>
92 </ul>
93 </div>
94
95 <h1><a class='u' href='#___top' title='click to go to top of document'
96 name="NAME"
97 >NAME</a></h1>
98
99 <p>i3test - Testcase setup module</p>
100
101 <h1><a class='u' href='#___top' title='click to go to top of document'
102 name="SYNOPSIS"
103 >SYNOPSIS</a></h1>
104 <pre><tt> use i3test;
105
106 my $ws = fresh_workspace;
107 is_num_children($ws, 0, &#39;no containers on this workspace yet&#39;);
108 cmd &#39;open&#39;;
109 is_num_children($ws, 1, &#39;one container after &#34;open&#34;&#39;);
110
111 done_testing;</tt></pre>
112 <h1><a class='u' href='#___top' title='click to go to top of document'
113 name="DESCRIPTION"
114 >DESCRIPTION</a></h1>
115
116 <p>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.</p>
117
118 <p>i3test automatically &#34;use&#34;s <code>Test::More</code>, <code>Data::Dumper</code>, <code>AnyEvent::I3</code>, <code>Time::HiRes</code>&#8217;s <code>sleep</code> and <code>i3test::Test</code> so that all of them are available to you in your testcase.</p>
119
120 <p>See also <code>i3test::Test</code> (<a href="https://build.i3wm.org/docs/lib-i3test-test.html" class="podlinkurl"
121 >https://build.i3wm.org/docs/lib-i3test-test.html</a>) which provides additional test instructions (like <code>ok</code> or <code>is</code>).</p>
122
123 <h1><a class='u' href='#___top' title='click to go to top of document'
124 name="EXPORT"
125 >EXPORT</a></h1>
126
127 <h2><a class='u' href='#___top' title='click to go to top of document'
128 name="wait_for_event($timeout,_$callback)"
129 >wait_for_event($timeout, $callback)</a></h2>
130
131 <p>Waits for the next event and calls the given callback for every event to determine if this is the event we are waiting for.</p>
132
133 <p>Can be used to wait until a window is mapped, until a ClientMessage is received, etc.</p>
134 <pre><tt> wait_for_event 0.25, sub { $_[0]-&#62;{response_type} == MAP_NOTIFY };</tt></pre>
135 <h2><a class='u' href='#___top' title='click to go to top of document'
136 name="wait_for_map($window)"
137 >wait_for_map($window)</a></h2>
138
139 <p>Thin wrapper around wait_for_event which waits for MAP_NOTIFY. Make sure to include &#39;structure_notify&#39; in the window&#8217;s event_mask attribute.</p>
140
141 <p>This function is called by <code>open_window</code>, so in most cases, you don&#8217;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:</p>
142 <pre><tt> my $window = open_window(dont_map =&#62; 1);
143 # Do something special with the window first
144 # &#8230;
145
146 # Now map it and wait until it&#8217;s been mapped
147 $window-&#62;map;
148 wait_for_map($window);</tt></pre>
149 <h2><a class='u' href='#___top' title='click to go to top of document'
150 name="wait_for_unmap($window)"
151 >wait_for_unmap($window)</a></h2>
152
153 <p>Wrapper around <code>wait_for_event</code> which waits for UNMAP_NOTIFY. Also calls <code>sync_with_i3</code> to make sure i3 also picked up and processed the UnmapNotify event.</p>
154 <pre><tt> my $ws = fresh_workspace;
155 my $window = open_window;
156 is_num_children($ws, 1, &#39;one window on workspace&#39;);
157 $window-&#62;unmap;
158 wait_for_unmap;
159 is_num_children($ws, 0, &#39;no more windows on this workspace&#39;);</tt></pre>
160 <h2><a class='u' href='#___top' title='click to go to top of document'
161 name="open_window([_$args_])"
162 >open_window([ $args ])</a></h2>
163
164 <p>Opens a new window (see <code>X11::XCB::Window</code>), maps it, waits until it got mapped and synchronizes with i3.</p>
165
166 <p>The following arguments can be passed:</p>
167
168 <dl>
169 <dt><a name="class"
170 >class</a></dt>
171
172 <dd>
173 <p>The X11 window class (e.g. WINDOW_CLASS_INPUT_OUTPUT), not to be confused with the WM_CLASS!</p>
174
175 <dt><a name="rect"
176 >rect</a></dt>
177
178 <dd>
179 <p>An arrayref with 4 members specifying the initial geometry (position and size) of the window, e.g. <code>[ 0, 100, 70, 50 ]</code> for a window appearing at x=0, y=100 with width=70 and height=50.</p>
180
181 <p>Note that this is entirely irrelevant for tiling windows.</p>
182
183 <dt><a name="background_color"
184 >background_color</a></dt>
185
186 <dd>
187 <p>The background pixel color of the window, formatted as &#34;#rrggbb&#34;, like HTML color codes (e.g. #c0c0c0). This is useful to tell windows apart when actually watching the testcases.</p>
188
189 <dt><a name="event_mask"
190 >event_mask</a></dt>
191
192 <dd>
193 <p>An arrayref containing strings which describe the X11 event mask we use for that window. The default is <code>[ &#39;structure_notify&#39; ]</code>.</p>
194
195 <dt><a name="name"
196 >name</a></dt>
197
198 <dd>
199 <p>The window&#8217;s <code>_NET_WM_NAME</code> (UTF-8 window title). By default, this is &#34;Window n&#34; with n being replaced by a counter to keep windows apart.</p>
200
201 <dt><a name="dont_map"
202 >dont_map</a></dt>
203
204 <dd>
205 <p>Set to a true value to avoid mapping the window (making it visible).</p>
206
207 <dt><a name="before_map"
208 >before_map</a></dt>
209
210 <dd>
211 <p>A coderef which is called before the window is mapped (unless <code>dont_map</code> is true). The freshly created <code>$window</code> is passed as <code>$_</code> and as the first argument.</p>
212 </dd>
213 </dl>
214
215 <p>The default values are equivalent to this call:</p>
216 <pre><tt> open_window(
217 class =&#62; WINDOW_CLASS_INPUT_OUTPUT
218 rect =&#62; [ 0, 0, 30, 30 ]
219 background_color =&#62; &#39;#c0c0c0&#39;
220 event_mask =&#62; [ &#39;structure_notify&#39; ]
221 name =&#62; &#39;Window &#60;n&#62;&#39;
222 );</tt></pre>
223 <p>Usually, though, calls are simpler:</p>
224 <pre><tt> my $top_window = open_window;</tt></pre>
225 <p>To identify the resulting window object in i3 commands, use the id property:</p>
226 <pre><tt> my $top_window = open_window;
227 cmd &#39;[id=&#34;&#39; . $top_window-&#62;id . &#39;&#34;] kill&#39;;</tt></pre>
228 <h2><a class='u' href='#___top' title='click to go to top of document'
229 name="open_floating_window([_$args_])"
230 >open_floating_window([ $args ])</a></h2>
231
232 <p>Thin wrapper around open_window which sets window_type to <code>_NET_WM_WINDOW_TYPE_UTILITY</code> to make the window floating.</p>
233
234 <p>The arguments are the same as those of <code>open_window</code>.</p>
235
236 <h2><a class='u' href='#___top' title='click to go to top of document'
237 name="get_workspace_names()"
238 >get_workspace_names()</a></h2>
239
240 <p>Returns an arrayref containing the name of every workspace (regardless of its output) which currently exists.</p>
241 <pre><tt> my $workspace_names = get_workspace_names;
242 is(scalar @$workspace_names, 3, &#39;three workspaces exist currently&#39;);</tt></pre>
243 <h2><a class='u' href='#___top' title='click to go to top of document'
244 name="get_output_for_workspace()"
245 >get_output_for_workspace()</a></h2>
246
247 <p>Returns the name of the output on which this workspace resides</p>
248 <pre><tt> cmd &#39;focus output fake-1&#39;;
249 cmd &#39;workspace 1&#39;;
250 is(get_output_for_workspace(&#39;1&#39;, &#39;fake-0&#39;, &#39;Workspace 1 in output fake-0&#39;);</tt></pre>
251 <h2><a class='u' href='#___top' title='click to go to top of document'
252 name="get_unused_workspace"
253 >get_unused_workspace</a></h2>
254
255 <p>Returns a workspace name which has not yet been used. See also <code>fresh_workspace</code> which directly switches to an unused workspace.</p>
256 <pre><tt> my $ws = get_unused_workspace;
257 cmd &#34;workspace $ws&#34;;</tt></pre>
258 <h2><a class='u' href='#___top' title='click to go to top of document'
259 name="fresh_workspace([_$args_])"
260 >fresh_workspace([ $args ])</a></h2>
261
262 <p>Switches to an unused workspace and returns the name of that workspace.</p>
263
264 <p>Optionally switches to the specified output first.</p>
265 <pre><tt> my $ws = fresh_workspace;
266
267 # Get a fresh workspace on the second output.
268 my $ws = fresh_workspace(output =&#62; 1);</tt></pre>
269 <h2><a class='u' href='#___top' title='click to go to top of document'
270 name="get_ws($workspace)"
271 >get_ws($workspace)</a></h2>
272
273 <p>Returns the container (from the i3 layout tree) which represents <code>$workspace</code>.</p>
274 <pre><tt> my $ws = fresh_workspace;
275 my $ws_con = get_ws($ws);
276 ok(!$ws_con-&#62;{urgent}, &#39;fresh workspace not marked urgent&#39;);</tt></pre>
277 <p>Here is an example which counts the number of urgent containers recursively, starting from the workspace container:</p>
278 <pre><tt> sub count_urgent {
279 my ($con) = @_;
280
281 my @children = (@{$con-&#62;{nodes}}, @{$con-&#62;{floating_nodes}});
282 my $urgent = grep { $_-&#62;{urgent} } @children;
283 $urgent += count_urgent($_) for @children;
284 return $urgent;
285 }
286 my $urgent = count_urgent(get_ws($ws));
287 is($urgent, 3, &#34;three urgent windows on workspace $ws&#34;);</tt></pre>
288 <h2><a class='u' href='#___top' title='click to go to top of document'
289 name="get_ws_content($workspace)"
290 >get_ws_content($workspace)</a></h2>
291
292 <p>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.</p>
293 <pre><tt> my $nodes = get_ws_content($ws);
294 is(scalar @$nodes, 4, &#39;there are four containers at workspace-level&#39;);</tt></pre>
295 <p>Or, in array context:</p>
296 <pre><tt> my $window = open_window;
297 my ($nodes, $focus) = get_ws_content($ws);
298 is($focus-&#62;[0], $window-&#62;id, &#39;newly opened window focused&#39;);</tt></pre>
299 <p>Note that this function does not do recursion for you! It only returns the containers <b>on workspace level</b>. If you want to work with all containers (even nested ones) on a workspace, you have to use recursion:</p>
300 <pre><tt> # NB: This function does not count floating windows
301 sub count_urgent {
302 my ($nodes) = @_;
303
304 my $urgent = 0;
305 for my $con (@$nodes) {
306 $urgent++ if $con-&#62;{urgent};
307 $urgent += count_urgent($con-&#62;{nodes});
308 }
309
310 return $urgent;
311 }
312 my $nodes = get_ws_content($ws);
313 my $urgent = count_urgent($nodes);
314 is($urgent, 3, &#34;three urgent windows on workspace $ws&#34;);</tt></pre>
315 <p>If you also want to deal with floating windows, you have to use <code>get_ws</code> instead and access <code>-&#62;{nodes}</code> and <code>-&#62;{floating_nodes}</code> on your own.</p>
316
317 <h2><a class='u' href='#___top' title='click to go to top of document'
318 name="get_focused($workspace)"
319 >get_focused($workspace)</a></h2>
320
321 <p>Returns the container ID of the currently focused container on <code>$workspace</code>.</p>
322
323 <p>Note that the container ID is <b>not</b> the X11 window ID, so comparing the result of <code>get_focused</code> with a window&#39;s <code>-&#62;{id}</code> property does <b>not</b> work.</p>
324 <pre><tt> my $ws = fresh_workspace;
325 my $first_window = open_window;
326 my $first_id = get_focused();
327
328 my $second_window = open_window;
329 my $second_id = get_focused();
330
331 cmd &#39;focus left&#39;;
332
333 is(get_focused($ws), $first_id, &#39;second window focused&#39;);</tt></pre>
334 <h2><a class='u' href='#___top' title='click to go to top of document'
335 name="get_dock_clients([_$dockarea_])"
336 >get_dock_clients([ $dockarea ])</a></h2>
337
338 <p>Returns an array of all dock containers in <code>$dockarea</code> (one of &#34;top&#34; or &#34;bottom&#34;). If <code>$dockarea</code> is not specified, returns an array of all dock containers in any dockarea.</p>
339 <pre><tt> my @docked = get_dock_clients;
340 is(scalar @docked, 0, &#39;no dock clients yet&#39;);</tt></pre>
341 <h2><a class='u' href='#___top' title='click to go to top of document'
342 name="cmd($command)"
343 >cmd($command)</a></h2>
344
345 <p>Sends the specified command to i3 and returns the output.</p>
346 <pre><tt> my $ws = unused_workspace;
347 cmd &#34;workspace $ws&#34;;
348 cmd &#39;focus right&#39;;</tt></pre>
349 <h2><a class='u' href='#___top' title='click to go to top of document'
350 name="workspace_exists($workspace)"
351 >workspace_exists($workspace)</a></h2>
352
353 <p>Returns true if <code>$workspace</code> is the name of an existing workspace.</p>
354 <pre><tt> my $old_ws = focused_ws;
355 # switch away from where we currently are
356 fresh_workspace;
357
358 ok(workspace_exists($old_ws), &#39;old workspace still exists&#39;);</tt></pre>
359 <h2><a class='u' href='#___top' title='click to go to top of document'
360 name="focused_ws"
361 >focused_ws</a></h2>
362
363 <p>Returns the name of the currently focused workspace.</p>
364 <pre><tt> my $ws = focused_ws;
365 is($ws, &#39;1&#39;, &#39;i3 starts on workspace 1&#39;);</tt></pre>
366 <h2><a class='u' href='#___top' title='click to go to top of document'
367 name="sync_with_i3([_$args_])"
368 >sync_with_i3([ $args ])</a></h2>
369
370 <p>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.</p>
371
372 <p>This can be used to ensure the results of a cmd &#39;focus left&#39; are pushed to X11 and that <code>$x-&#62;input_focus</code> returns the correct value afterwards.</p>
373
374 <p>See also <a href="https://build.i3wm.org/docs/testsuite.html" class="podlinkurl"
375 >https://build.i3wm.org/docs/testsuite.html</a> for a longer explanation.</p>
376 <pre><tt> my $window = open_window;
377 $window-&#62;add_hint(&#39;urgency&#39;);
378 # Ensure i3 picked up the change
379 sync_with_i3;</tt></pre>
380 <p>The only time when you need to use the <code>no_cache</code> argument is when you just killed your own X11 connection:</p>
381 <pre><tt> cmd &#39;kill client&#39;;
382 # We need to re-establish the X11 connection which we just killed :).
383 $x = i3test::X11-&#62;new;
384 sync_with_i3(no_cache =&#62; 1);</tt></pre>
385 <h2><a class='u' href='#___top' title='click to go to top of document'
386 name="exit_gracefully($pid,_[_$socketpath_])"
387 >exit_gracefully($pid, [ $socketpath ])</a></h2>
388
389 <p>Tries to exit i3 gracefully (with the &#39;exit&#39; cmd) or kills the PID if that fails.</p>
390
391 <p>If <code>$socketpath</code> is not specified, <code>get_socket_path()</code> will be called.</p>
392
393 <p>You only need to use this function if you have launched i3 on your own with <code>launch_with_config</code>. Otherwise, it will be automatically called when the testcase ends.</p>
394 <pre><tt> use i3test i3_autostart =&#62; 0;
395 my $pid = launch_with_config($config);
396 # &#8230;
397 exit_gracefully($pid);</tt></pre>
398 <h2><a class='u' href='#___top' title='click to go to top of document'
399 name="exit_forcefully($pid,_[_$signal_])"
400 >exit_forcefully($pid, [ $signal ])</a></h2>
401
402 <p>Tries to exit i3 forcefully by sending a signal (defaults to SIGTERM).</p>
403
404 <p>You only need to use this function if you want to test signal handling (in which case you must have launched i3 on your own with <code>launch_with_config</code>).</p>
405 <pre><tt> use i3test i3_autostart =&#62; 0;
406 my $pid = launch_with_config($config);
407 # &#8230;
408 exit_forcefully($pid);</tt></pre>
409 <h2><a class='u' href='#___top' title='click to go to top of document'
410 name="get_socket_path([_$cache_])"
411 >get_socket_path([ $cache ])</a></h2>
412
413 <p>Gets the socket path from the <code>I3_SOCKET_PATH</code> 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 <code>$cache</code>.</p>
414 <pre><tt> my $i3 = i3(get_socket_path());
415 $i3-&#62;command(&#39;nop test example&#39;)-&#62;recv;</tt></pre>
416 <p>To avoid caching:</p>
417 <pre><tt> my $i3 = i3(get_socket_path(0));</tt></pre>
418 <h2><a class='u' href='#___top' title='click to go to top of document'
419 name="launch_with_config($config,_[_$args_])"
420 >launch_with_config($config, [ $args ])</a></h2>
421
422 <p>Launches a new i3 process with <code>$config</code> as configuration file. Useful for tests which test specific config file directives.</p>
423 <pre><tt> use i3test i3_autostart =&#62; 0;
424
425 my $config = &#60;&#60;EOT;
426 # i3 config file (v4)
427 for_window [class=&#34;borderless&#34;] border none
428 for_window [title=&#34;special borderless title&#34;] border none
429 EOT
430
431 my $pid = launch_with_config($config);
432
433 # &#8230;
434
435 exit_gracefully($pid);</tt></pre>
436 <h2><a class='u' href='#___top' title='click to go to top of document'
437 name="get_i3_log"
438 >get_i3_log</a></h2>
439
440 <p>Returns the content of the log file for the current test.</p>
441
442 <h2><a class='u' href='#___top' title='click to go to top of document'
443 name="kill_all_windows"
444 >kill_all_windows</a></h2>
445
446 <p>Kills all windows to clean up between tests.</p>
447
448 <h2><a class='u' href='#___top' title='click to go to top of document'
449 name="events_for($subscribecb,_[_$rettype_],_[_$eventcbs_])"
450 >events_for($subscribecb, [ $rettype ], [ $eventcbs ])</a></h2>
451
452 <p>Helper function which returns an array containing all events of type $rettype which were generated by i3 while $subscribecb was running.</p>
453
454 <p>Set $eventcbs to subscribe to multiple event types and/or perform your own event aggregation.</p>
455
456 <h2><a class='u' href='#___top' title='click to go to top of document'
457 name="listen_for_binding($cb)"
458 >listen_for_binding($cb)</a></h2>
459
460 <p>Helper function to evaluate whether sending KeyPress/KeyRelease events via XTEST triggers an i3 key binding or not. Expects key bindings to be configured in the form &#8220;bindsym &#60;binding&#62; nop &#60;binding&#62;&#8221;, e.g. &#8220;bindsym Mod4+Return nop Mod4+Return&#8221;.</p>
461 <pre><tt> is(listen_for_binding(
462 sub {
463 xtest_key_press(133); # Super_L
464 xtest_key_press(36); # Return
465 xtest_key_release(36); # Return
466 xtest_key_release(133); # Super_L
467 xtest_sync_with_i3;
468 },
469 ),
470 &#39;Mod4+Return&#39;,
471 &#39;triggered the &#34;Mod4+Return&#34; keybinding&#39;);</tt></pre>
472 <h2><a class='u' href='#___top' title='click to go to top of document'
473 name="is_net_wm_state_focused"
474 >is_net_wm_state_focused</a></h2>
475
476 <p>Returns true if the given window has the _NET_WM_STATE_FOCUSED atom.</p>
477 <pre><tt> ok(is_net_wm_state_focused($window), &#39;_NET_WM_STATE_FOCUSED set&#39;);</tt></pre>
478 <h1><a class='u' href='#___top' title='click to go to top of document'
479 name="AUTHOR"
480 >AUTHOR</a></h1>
481
482 <p>Michael Stapelberg &#60;michael@i3wm.org&#62;</p>
483
484 <!-- end doc -->
485
486 </body></html>
Binary diff not shown
Binary diff not shown
0 The multi-monitor situation
1 ===========================
2 Michael Stapelberg <michael@i3wm.org>
3 April 2013
4
5 Please upgrade your nVidia driver to version 302.17 or newer and i3 will just
6 work. This document is kept around for historic reasons only.
7
8 == The quick fix
9
10 If you are using the nVidia binary graphics driver (also known as 'blob')
11 before version 302.17, you need to use the +--force-xinerama+ flag (in your
12 .xsession) when starting i3, like so:
13
14 .Example:
15 ----------------------------------------------
16 exec i3 --force-xinerama -V >>~/.i3/i3log 2>&1
17 ----------------------------------------------
18
19 …or use +force_xinerama yes+ in your configuration file.
20
21 == The explanation
22
23 Starting with version 3.ε, i3 uses the RandR (Rotate and Resize) API instead
24 of Xinerama. The reason for this, is that RandR provides more information
25 about your outputs and connected screens than Xinerama does. To be specific,
26 the code which handled on-the-fly screen reconfiguration (meaning without
27 restarting the X server) was a very messy heuristic and most of the time did
28 not work correctly -- that is just not possible with the little information
29 Xinerama offers (just a list of screen resolutions, no identifiers for the
30 screens or any additional information). Xinerama simply was not designed
31 for dynamic configuration.
32
33 So RandR came along, as a more powerful alternative (RandR 1.2 to be specific).
34 It offers all of Xinerama’s possibilities and lots more. Using the RandR API
35 made our code much more robust and clean. Also, you can now reliably assign
36 workspaces to output names instead of some rather unreliable screen identifier
37 (position inside the list of screens, which could change, and so on…).
38
39 As RandR has been around for about three years as of this writing, it seemed
40 like a very good idea to us, and it still is a very good one. What we did not
41 expect, however, was the nVidia binary driver. It still does not support RandR
42 (as of March 2010), even though nVidia has announced that it will support RandR
43 eventually. What does this mean for you, if you are stuck with the binary
44 driver for some reason (say the free drivers don’t work with your card)? First
45 of all, you are stuck with TwinView and cannot use +xrandr+. While this ruins
46 the user experience, the more grave problem is that the nVidia driver not only
47 does not support dynamic configuration using RandR, it also does not expose
48 correct multi-monitor information via the RandR API. So, in some setups, i3
49 will not find any screens; in others, it will find one large screen which
50 actually contains both of your physical screens (but it will not know that
51 these are two screens).
52
53 For this very reason, we decided to implement the following workaround: As
54 long as the nVidia driver does not support RandR, an option called
55 +--force-xinerama+ is available in i3 (alternatively, you can use the
56 +force_xinerama+ configuration file directive). This option gets the list of
57 screens *once* when starting, and never updates it. As the nVidia driver cannot
58 do dynamic configuration anyways, this is not a big deal.
59
60 Also note that your output names are not descriptive (like +HDMI1+) when using
61 Xinerama, instead they are counted up, starting at 0: +xinerama-0+, +xinerama-1+, …
62
63 == See also
64
65 For more information on how to use multi-monitor setups, see the i3 User’s
66 Guide.
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>The multi-monitor situation</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>The multi-monitor situation</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">April 2013</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>Please upgrade your nVidia driver to version 302.17 or newer and i3 will just
749 work. This document is kept around for historic reasons only.</p></div>
750 </div>
751 </div>
752 <div class="sect1">
753 <h2 id="_the_quick_fix">1. The quick fix</h2>
754 <div class="sectionbody">
755 <div class="paragraph"><p>If you are using the nVidia binary graphics driver (also known as <em>blob</em>)
756 before version 302.17, you need to use the <code>--force-xinerama</code> flag (in your
757 .xsession) when starting i3, like so:</p></div>
758 <div class="listingblock">
759 <div class="title">Example:</div>
760 <div class="content">
761 <pre><code>exec i3 --force-xinerama -V &gt;&gt;~/.i3/i3log 2&gt;&amp;1</code></pre>
762 </div></div>
763 <div class="paragraph"><p>…or use <code>force_xinerama yes</code> in your configuration file.</p></div>
764 </div>
765 </div>
766 <div class="sect1">
767 <h2 id="_the_explanation">2. The explanation</h2>
768 <div class="sectionbody">
769 <div class="paragraph"><p>Starting with version 3.ε, i3 uses the RandR (Rotate and Resize) API instead
770 of Xinerama. The reason for this, is that RandR provides more information
771 about your outputs and connected screens than Xinerama does. To be specific,
772 the code which handled on-the-fly screen reconfiguration (meaning without
773 restarting the X server) was a very messy heuristic and most of the time did
774 not work correctly&#8201;&#8212;&#8201;that is just not possible with the little information
775 Xinerama offers (just a list of screen resolutions, no identifiers for the
776 screens or any additional information). Xinerama simply was not designed
777 for dynamic configuration.</p></div>
778 <div class="paragraph"><p>So RandR came along, as a more powerful alternative (RandR 1.2 to be specific).
779 It offers all of Xinerama’s possibilities and lots more. Using the RandR API
780 made our code much more robust and clean. Also, you can now reliably assign
781 workspaces to output names instead of some rather unreliable screen identifier
782 (position inside the list of screens, which could change, and so on…).</p></div>
783 <div class="paragraph"><p>As RandR has been around for about three years as of this writing, it seemed
784 like a very good idea to us, and it still is a very good one. What we did not
785 expect, however, was the nVidia binary driver. It still does not support RandR
786 (as of March 2010), even though nVidia has announced that it will support RandR
787 eventually. What does this mean for you, if you are stuck with the binary
788 driver for some reason (say the free drivers don’t work with your card)? First
789 of all, you are stuck with TwinView and cannot use <code>xrandr</code>. While this ruins
790 the user experience, the more grave problem is that the nVidia driver not only
791 does not support dynamic configuration using RandR, it also does not expose
792 correct multi-monitor information via the RandR API. So, in some setups, i3
793 will not find any screens; in others, it will find one large screen which
794 actually contains both of your physical screens (but it will not know that
795 these are two screens).</p></div>
796 <div class="paragraph"><p>For this very reason, we decided to implement the following workaround: As
797 long as the nVidia driver does not support RandR, an option called
798 <code>--force-xinerama</code> is available in i3 (alternatively, you can use the
799 <code>force_xinerama</code> configuration file directive). This option gets the list of
800 screens <strong>once</strong> when starting, and never updates it. As the nVidia driver cannot
801 do dynamic configuration anyways, this is not a big deal.</p></div>
802 <div class="paragraph"><p>Also note that your output names are not descriptive (like <code>HDMI1</code>) when using
803 Xinerama, instead they are counted up, starting at 0: <code>xinerama-0</code>, <code>xinerama-1</code>, …</p></div>
804 </div>
805 </div>
806 <div class="sect1">
807 <h2 id="_see_also">3. See also</h2>
808 <div class="sectionbody">
809 <div class="paragraph"><p>For more information on how to use multi-monitor setups, see the i3 User’s
810 Guide.</p></div>
811 </div>
812 </div>
813 </div>
814 <div id="footnotes"><hr /></div>
815 <div id="footer">
816 <div id="footer-text">
817 Last updated
818 2019-01-27 16:45:19 CET
819 </div>
820 </div>
821 </body>
822 </html>
0 <!DOCTYPE html>
1 <html>
2 <head>
3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
4 <title>i3 Reference Card</title>
5 <link rel="stylesheet" href="refcard_style.css" />
6 <style type="text/css">
7 * { margin: 0; padding: 0; vertical-align: middle; }
8 html { font-family: LinuxLibertine, Linux Libertine O, Linux Libertine, serif; font-size: 15px; /* column-count: 3; column-gap: 10px; -moz-column-count: 3; -moz-column-gap: 10px; -webkit-column-count: 3; -webkit-column-gap: 10px; */ }
9 body > div { display: inline-block; width: 33%; margin: 0.25%; vertical-align: top; }
10 body > div:first-child { margin-left: 0; }
11 body > div:last-child { margin-right: 0; }
12 #logo { float: left; width: 40px; margin: 7px; }
13 header { font-size: 1.1em; text-align: center; }
14 h1 { font-size: 1.1em; }
15 header a { font-size: 0.7em; }
16 header p { margin: 5px 0; font-size: 0.8em; text-align: left; }
17 kbd { font-family: LinuxBiolinumKeyboard, Linux Biolinum Keyboard O, Linux Biolinum Keyboard, DejaVu Sans Mono, monospace; font-size: 0.9em; }
18 code { font-family: DejaVu Sans Mono, monospace; font-size: 0.8em; }
19 section { break-inside: avoid-column; -moz-break-inside: -moz-avoid-column; -webkit-break-inside: avoid-column; }
20 h2 { margin: 7px 0 2px; padding: 2px 4px; font-size: 1.1em; font-family: LinuxBiolinum, Linux Biolinum O, Linux Biolinum, sans; background-color: #b3b3b3; }
21 table { width: 100%; }
22 .i3mod { width: 15px; }
23 td:last-child { text-align: right; }
24 .ref { font-size: 0.8em; }
25 #copyright { margin: 20px 0 15px; font-size: 0.7em; text-align: center; }
26 #licence { max-width: 70%; margin: 3px auto; font-size: 0.6em; text-align: right; }
27 </style>
28 <style type="text/css" media="print">
29 html { font-size: 13px; }
30 </style>
31 </head>
32 <body><div>
33 <header>
34 <img id="logo" src="logo-30.png" alt="" />
35 <h1>i3 Reference Card</h1>
36 <a href="https://i3wm.org/docs/userguide.html">https://i3wm.org/docs/userguide.html</a>
37 <p>
38 Throughout this guide, the i3 logo will be used to refer to the configured modifier.
39 This is the <kbd></kbd> key (<code>Mod1</code>) by default,
40 with super/<kbd></kbd> (<code>Mod4</code>) being a popular alternative.
41 </p>
42 </header>
43
44
45 <section>
46 <h2>Basics</h2>
47 <table>
48 <tr>
49 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
50 <td>open new terminal
51 <tr>
52 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>j</kbd>
53 <td>focus left
54
55 <tr>
56 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>k</kbd>
57 <td>focus down
58
59 <tr>
60 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>l</kbd>
61 <td>focus up
62
63 <tr>
64 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>;</kbd>
65 <td>focus right
66 <tr>
67 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
68 <td>toggle focus mode
69 </table>
70 </section>
71
72 <section>
73 <h2>Moving windows</h2>
74 <table>
75 <tr>
76 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>j</kbd>
77 <td>move window left
78 <tr>
79 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>k</kbd>
80 <td>move window down
81 <tr>
82 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>l</kbd>
83 <td>move window up
84 <tr>
85 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>;</kbd>
86 <td>move window right
87 </table>
88 </section>
89
90 </div><div>
91
92 <section>
93 <h2>Modifying windows</h2>
94 <table>
95 <tr>
96 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>f</kbd>
97 <td>toggle fullscreen
98 <tr>
99 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>v</kbd>
100 <td>split a window vertically
101 <tr>
102 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>h</kbd>
103 <td>split a window horizontally
104 <tr>
105 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>r</kbd>
106 <td>resize mode
107 </table>
108 <p class="ref">Look at the “Resizing containers / windows” section of the user guide.</p>
109 </section>
110
111 <section>
112 <h2>Changing the container layout</h2>
113 <table>
114 <tr>
115 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>e</kbd>
116 <td>default
117
118 <tr>
119 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>s</kbd>
120 <td>stacking
121
122 <tr>
123 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>w</kbd>
124 <td>tabbed
125 </table>
126 </section>
127
128 <section>
129 <h2>Floating</h2>
130 <table>
131 <tr>
132 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd></kbd>
133 <td>toggle floating
134 <tr>
135 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd>
136 <td>drag floating
137 </table>
138 </section>
139
140
141 <section>
142 <h2>Using workspaces</h2>
143 <table>
144 <tr>
145 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>0</kbd>-<kbd>9</kbd>
146 <td>switch to another workspace
147 <tr>
148 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>0</kbd>-<kbd>9</kbd>
149 <td>move a window to another workspace
150 </table>
151 </section>
152
153 </div><div>
154
155 <section>
156 <h2>Opening applications / Closing windows</h2>
157 <table>
158 <tr>
159 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd>d</kbd>
160 <td>open application launcher (dmenu)
161 <tr>
162 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>q</kbd>
163 <td>kill a window
164 </table>
165 </section>
166
167 <section>
168 <h2>Restart / Exit</h2>
169 <table>
170 <tr>
171 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>c</kbd>
172 <td>reload the configuration file
173 <tr>
174 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>r</kbd>
175 <td>restart i3 inplace
176
177 <tr>
178 <td><img class="i3mod" src="logo-30.png" alt="" /> + <kbd></kbd> + <kbd>e</kbd>
179 <td>exit i3
180 </section>
181 </table>
182
183
184 <!-- footer -->
185 <p id="copyright">
186 Copyright © 2012, Michael Stapelberg
187 <br />
188 All rights reserved
189 <br />
190 Designed by Zeus Panchenko, updated by Moritz Bandemer
191 </p>
192 <p id="licence">
193 Permission is granted to copy, distribute and/or modify this document provided
194 the copyright notice and this permission notice are preserved on all copies.
195 </p>
196 </div></body>
197 </html>
0 /* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 12, 2012 */
1
2
3
4 @font-face {
5 /* This declaration targets Internet Explorer */
6 font-family: 'LinuxLibertine';
7 src: url('linlibertine_r-webfont.eot');
8 }
9
10 @font-face {
11 /* This declaration targets everything else */
12 font-family: 'LinuxLibertine';
13 src: url(//:) format('no404'), url(data:font/woff;charset=utf-8;base64,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) format('woff'), url(data:font/truetype;charset=utf-8;base64,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AGUAcgAgAFQAZQByAG0AcwAgAG8AZgAgAGYAbwBsAGwAbwB3AGkAbgBnACAARgByAGUAZQAgAFMAbwBmAHQAdwBhAHIAZQAgAEwAaQBjAGUAbgBzAGUAcwA6AAoARwBQAEwAIAAoAEcAZQBuAGUAcgBhAGwAIABQAHUAYgBsAGkAYwAgAEwAaQBjAGUAbgBzAGUAKQAgAHcAaQB0AGgAIABmAG8AbgB0AC0AZQB4AGMAZQBwAHQAaQBvAG4AIABhAG4AZAAgAE8ARgBMACAAKABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUAKQAuAAoAQwByAGUAYQB0AGUAZAAgAHcAaQB0AGgAIABGAG8AbgB0AEYAbwByAGcAZQAgACgAaAB0AHQAcAA6AC8ALwBmAG8AbgB0AGYAbwByAGcAZQAuAHMAZgAuAG4AZQB0ACkACgBTAGUAcAB0ACAAMgAwADAAMwAsACAAMgAwADAANAAsACAAMgAwADAANQAsACAAMgAwADAANgAsACAAMgAwADAANwAsACAAMgAwADAAOAAsACAAMgAwADAAOQAsACAAMgAwADEAMAAsACAAMgAwADEAMQBMAGkAbgB1AHgAIABMAGkAYgBlAHIAdABpAG4AZQBSAGUAZwB1AGwAYQByAHcAZQBiAGYAbwBuAHQATABpAG4AdQB4ACAATABpAGIAZQByAHQAaQBuAGUAIABSAGUAZwB1AGwAYQByAFYAZQByAHMAaQBvAG4AIAA1AC4AMQAuADMAIABMAGkAbgBMAGkAYgBlAHIAdABpAG4AZQBUAGgAaQBzACAAZgBvAG4AdAAgAHcAYQBzACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIAB0AGgAZQAgAEYAbwBuAHQAIABTAHEAdQBpAHIAcgBlAGwAIABHAGUAbgBlAHIAYQB0AG8AcgAuAAAAAgAAAAAAAP8PAFEAAAAAAAAAAAAAAAAAAAAAAAAAAAB6AAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQECAQMBBAEFAQYBBwEIAQkBCgELAQwBDQEOAQ8BEAERALIAswESARMBFAEVARYBFwd1bmkwMEEwB3VuaTAwQUQHdW5pMjAwMAd1bmkyMDAxB3VuaTIwMDIHdW5pMjAwMwd1bmkyMDA0B3VuaTIwMDUHdW5pMjAwNgd1bmkyMDA3B3VuaTIwMDgHdW5pMjAwOQd1bmkyMDBBB3VuaTIwMTAHdW5pMjAxMQpmaWd1cmVkYXNoB3VuaTIwMkYHdW5pMjA1Rgd1bmlFMDAwB3VuaUUxMzgHdW5pRTE4OAd1bmlFMTg5AAAAuAH/hbABjQBLsAhQWLEBAY5ZsUYGK1ghsBBZS7AUUlghsIBZHbAGK1xYALAEIEWwAytEsAUgRbIEZwIrsAMrRAGwBiBFsAMrRLAHIEWyBmQCK7EDRnYrRLAIIEW6AAZ//wACK7EDRnYrRFmwFCsAAA==) format('truetype'), url('linlibertine_r-webfont.svg#LinuxLibertineRegular') format('svg');
14 font-weight: normal;
15 font-style: normal;
16 }
17
18 @font-face {
19 /* This declaration targets Internet Explorer */
20 font-family: 'LinuxBiolinumKeyboard';
21 src: url('linbiolinum_k-webfont.eot');
22 }
23
24 @font-face {
25 /* This declaration targets everything else */
26 font-family: 'LinuxBiolinumKeyboard';
27 src: url(//:) format('no404'), url(data:font/woff;charset=utf-8;base64,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) format('woff'), url(data:font/truetype;charset=utf-8;base64,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AAEAAAC7BQEAEwAAAAAAAgABAAIAFgAAAQACEgAAAAAAAAAIAGYAAwABBAkAAAIuAAAAAwABBAkAAQAuAi4AAwABBAkAAgAOAlwAAwABBAkAAwAOAmoAAwABBAkABAA+AngAAwABBAkABQAcArYAAwABBAkABgAYAtIAAwABBAkAyABuAuoATABpAG4AdQB4ACAATABpAGIAZQByAHQAaQBuAGUAIABiAHkAIABQAGgAaQBsAGkAcABwACAASAAuACAAUABvAGwAbAAsAAoATwBwAGUAbgAgAEYAbwBuAHQAIAB1AG4AZABlAHIAIABUAGUAcgBtAHMAIABvAGYAIABmAG8AbABsAG8AdwBpAG4AZwAgAEYAcgBlAGUAIABTAG8AZgB0AHcAYQByAGUAIABMAGkAYwBlAG4AcwBlAHMAOgAKAEcAUABMACAAKABHAGUAbgBlAHIAYQBsACAAUAB1AGIAbABpAGMAIABMAGkAYwBlAG4AcwBlACkAIAB3AGkAdABoACAAZgBvAG4AdAAtAGUAeABjAGUAcAB0AGkAbwBuACAAYQBuAGQAIABPAEYATAAgACgATwBwAGUAbgAgAEYAbwBuAHQAIABMAGkAYwBlAG4AcwBlACkALgAKAEMAcgBlAGEAdABlAGQAIAB3AGkAdABoACAARgBvAG4AdABGAG8AcgBnAGUAIAAoAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABmAG8AcgBnAGUALgBzAGYALgBuAGUAdAApAAoAUwBlAHAAdAAgADIAMAAwADMALAAgADIAMAAwADQALAAgADIAMAAwADUALAAgADIAMAAwADYALAAgADIAMAAwADcALAAgADIAMAAwADgALAAgADIAMAAwADkALAAgADIAMAAxADAALAAgADIAMAAxADEATABpAG4AdQB4ACAAQgBpAG8AbABpAG4AdQBtACAASwBlAHkAYgBvAGEAcgBkAFIAZQBnAHUAbABhAHIAdwBlAGIAZgBvAG4AdABMAGkAbgB1AHgAIABCAGkAbwBsAGkAbgB1AG0AIABLAGUAeQBiAG8AYQByAGQAIABSAGUAZwB1AGwAYQByAFYAZQByAHMAaQBvAG4AIAAwAC4ANgAuADEAIABMAGkAbgBCAGkAbwBsAGkAbgB1AG0ASwBUAGgAaQBzACAAZgBvAG4AdAAgAHcAYQBzACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIAB0AGgAZQAgAEYAbwBuAHQAIABTAHEAdQBpAHIAcgBlAGwAIABHAGUAbgBlAHIAYQB0AG8AcgAuAAAAAgAAAAAAAP8PAFEAAAAAAAAAAAAAAAAAAAAAAAAAAAC7AAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQECAQMBBAEFAQYBBwEIAQkBCgELAQwBDQEOAQ8BEAERALIAswESARMBFAEVARYBFwEYARkBGgEbARwBHQEeAR8BIAEhASIBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEBMgEzATQBNQE2ATcBOAE5AToBOwE8AT0BPgE/AUABQQFCAUMBRAFFAUYBRwFIAUkBSgFLAUwBTQFOAU8BUAFRAVIBUwFUAVUBVgFXAVgHdW5pMDBBMAd1bmkwMEFEB3VuaTIwMDAHdW5pMjAwMQd1bmkyMDAyB3VuaTIwMDMHdW5pMjAwNAd1bmkyMDA1B3VuaTIwMDYHdW5pMjAwNwd1bmkyMDA4B3VuaTIwMDkHdW5pMjAwQQd1bmkyMDEwB3VuaTIwMTEKZmlndXJlZGFzaAd1bmkyMDJGB3VuaTIwNUYHdW5pRTAwMAd1bmlFMTMxB3VuaUUxMzIHdW5pRTEzMwd1bmlFMTM0B3VuaUUxMzUHdW5pRTEzOAd1bmlFMTM5B3VuaUUxM0EHdW5pRTEzQwd1bmlFMTNEB3VuaUUxNjgHdW5pRTE2RQd1bmlFMTcwB3VuaUUxNzEHdW5pRTE3Mgd1bmlFMTczB3VuaUUxNzQHdW5pRTE3NQd1bmlFMTc2B3VuaUUxNzcHdW5pRTE3OAd1bmlFMTc5B3VuaUUxN0EHdW5pRTE3Qgd1bmlFMTdDB3VuaUUxN0QHdW5pRTE3RQd1bmlFMTdGB3VuaUUxODAHdW5pRTE4MQd1bmlFMTgyB3VuaUUxODMHdW5pRTE4NAd1bmlFMTg1B3VuaUUxODYHdW5pRTE4Nwd1bmlFMTg4B3VuaUUxODkHdW5pRTE4QQd1bmlFMThCB3VuaUUxOEMHdW5pRTE4RQd1bmlFMTkwB3VuaUUxOTEHdW5pRTE5Mgd1bmlFMTkzB3VuaUUxOTQHdW5pRTE5NQd1bmlFMTk2B3VuaUUxOTgHdW5pRTE5OQd1bmlFMTlBB3VuaUUxOUIHdW5pRTFBMAd1bmlFMUExB3VuaUUxQTIHdW5pRTFBMwd1bmlFMUE0B3VuaUUxQTUHdW5pRTFBNgd1bmlFMUE3B3VuaUUxQTgHdW5pRTFBOQd1bmlFMUFBB3VuaUUxQUIHdW5pRTFBQwd1bmlFMUFEB3VuaUUxQUUAuAH/hbABjQBLsAhQWLEBAY5ZsUYGK1ghsBBZS7AUUlghsIBZHbAGK1xYALABIEWwAytEsAIgRbIBFAIrsAMrRAGwAyBFsAMrRLAEIEWyAxACK7EDRnYrRLAFIEW6AAN//wACK7EDRnYrRFmwFCsAAA==) format('truetype'), url('linbiolinum_k-webfont.svg#LinuxBiolinumKeyboardRegular') format('svg');
28 font-weight: normal;
29 font-style: normal;
30 }
31
32 @font-face {
33 /* This declaration targets Internet Explorer */
34 font-family: 'LinuxBiolinum';
35 src: url('linbiolinum_r-webfont.eot');
36 }
37
38 @font-face {
39 /* This declaration targets everything else */
40 font-family: 'LinuxBiolinum';
41 src: url(//:) format('no404'), url(data:font/woff;charset=utf-8;base64,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) format('woff'), url(data:font/truetype;charset=utf-8;base64,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) format('truetype'), url('linbiolinum_r-webfont.svg#LinuxBiolinumRegular') format('svg');
42 font-weight: normal;
43 font-style: normal;
44 }
Binary diff not shown
Binary diff not shown
0 i3 testsuite
1 ============
2 Michael Stapelberg <michael@i3wm.org>
3 September 2012
4
5 This document explains how the i3 testsuite works, how to use it and extend it.
6 It is targeted at developers who not necessarily have been doing testing before
7 or have not been testing in Perl before. In general, the testsuite is not of
8 interest for end users.
9
10
11 == Introduction
12
13 The i3 testsuite is a collection of files which contain testcases for various
14 i3 features. Some of them test if a certain workflow works correctly (moving
15 windows, focus behaviour, …). Others are regression tests and contain code
16 which previously made i3 crash or lead to unexpected behaviour. They then check
17 if i3 still runs (meaning it did not crash) and if it handled everything
18 correctly.
19
20 The goal of having these tests is to automatically find problems and to
21 automatically get a feel for whether a change in the source code breaks any
22 existing feature. After every modification of the i3 sourcecode, the developer
23 should run the full testsuite. If one of the tests fails, the corresponding
24 problem should be fixed (or, in some cases, the testcase has to be modified).
25 For every bugreport, a testcase should be written to test the correct
26 behaviour. Initially, it will fail, but after fixing the bug, it will pass.
27 This ensures (or increases the chance) that bugs which have been fixed once
28 will never be found again.
29
30 Also, when implementing a new feature, a testcase might be a good way to be
31 able to easily test if the feature is working correctly. Many developers will
32 test manually if everything works. Having a testcase not only helps you with
33 that, but it will also be useful for every future change.
34
35 == Relevant documentation
36
37 Apart from this document, you should also have a look at:
38
39 1. The "Modern Perl" book, which can be found at
40 http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
41 2. The latest Perl documentation of the "i3test" (general testcase setup) and
42 "i3test::Test" (additional test instructions) modules:
43 https://build.i3wm.org/docs/lib-i3test.html respectively
44 https://build.i3wm.org/docs/lib-i3test-test.html
45 3. The latest documentation on i3’s IPC interface:
46 https://build.i3wm.org/docs/ipc.html
47
48 == Implementation
49
50 For several reasons, the i3 testsuite has been implemented in Perl:
51
52 1. Perl has a long tradition of testing. Every popular/bigger Perl module which
53 you can find on CPAN will not only come with documentation, but also with
54 tests. Therefore, the available infrastructure for tests is comprehensive.
55 See for example the excellent http://search.cpan.org/perldoc?Test::More
56 and the referenced http://search.cpan.org/perldoc?Test::Tutorial.
57
58 2. Perl is widely available and has a well-working package infrastructure.
59 3. The author is familiar with Perl :).
60 4. It is a good idea to use a different language for the tests than the
61 implementation itself.
62
63 Please do not start programming language flamewars at this point.
64
65 === Installing the dependencies
66
67 As usual with Perl programs, the testsuite ships with a +Makefile.PL+.
68 This file specifies which Perl modules the testsuite depends on and can be used
69 to install all of them.
70
71 Perl modules are distributed via CPAN, and there is the official, standard CPAN
72 client, simply called +cpan+. It comes with every Perl installation and can be
73 used to install the testsuite. Many users prefer to use the more modern
74 +cpanminus+ instead, though (because it asks no questions and just works):
75
76 The tests additionally require +Xephyr(1)+ to run a nested X server. Install
77 +xserver-xephyr+ on Debian or +xorg-server-xephyr+ on Arch Linux.
78
79 .Installing testsuite dependencies using cpanminus (preferred)
80 --------------------------------------------------------------------------------
81 $ cd ~/i3/testcases
82 $ sudo apt-get install cpanminus
83 $ sudo cpanm .
84 $ cd ~/i3/AnyEvent-I3
85 $ sudo cpanm Module::Install
86 $ sudo cpanm .
87 --------------------------------------------------------------------------------
88
89 If you don’t want to use cpanminus for some reason, the same works with cpan:
90
91 .Installing testsuite dependencies using cpan
92 --------------------------------------------------------------------------------
93 $ cd ~/i3/testcases
94 $ sudo cpan .
95 $ cd ~/i3/AnyEvent-I3
96 $ sudo cpan Module::Install
97 $ sudo cpan .
98 --------------------------------------------------------------------------------
99
100 In case you don’t have root permissions, you can also install into your home
101 directory, see https://michael.stapelberg.de/cpan/
102
103 === Mechanisms
104
105 ==== Script: complete-run
106
107 The testcases are run by a script called +complete-run.pl+. It runs all
108 testcases by default, but you can be more specific and let it only run one or
109 more testcases. Also, it takes care of starting up a separate instance of i3
110 with an appropriate configuration file and creates a folder for each run
111 containing the appropriate i3 logfile for each testcase. The latest folder can
112 always be found under the symlink +latest/+. Unless told differently, it will
113 run the tests on a separate X server instance (using Xephyr).
114
115 Xephyr will open a window where you can inspect the running test. By default,
116 tests are run under Xvfb.
117
118 .Example invocation of +complete-run.pl+
119 ---------------------------------------
120 $ cd ~/i3
121
122 $ autoreconf -fi
123
124 $ mkdir -p build && cd build
125
126 $ ../configure
127
128 $ make -j8
129 # output omitted because it is very long
130
131 $ cd testcases
132
133 $ ./complete-run.pl
134 # output omitted because it is very long
135 All tests successful.
136 Files=78, Tests=734, 27 wallclock secs ( 0.38 usr 0.48 sys + 17.65 cusr 3.21 csys = 21.72 CPU)
137 Result: PASS
138
139 $ ./complete-run.pl t/04-floating.t
140 [:3] i3 startup: took 0.07s, status = 1
141 [:3] Running t/04-floating.t with logfile testsuite-2011-09-24-16-06-04-4.0.2-226-g1eb011a/i3-log-for-04-floating.t
142 [:3] t/04-floating.t finished
143 [:3] killing i3
144 output for t/04-floating.t:
145 ok 1 - use X11::XCB::Window;
146 ok 2 - The object isa X11::XCB::Window
147 ok 3 - Window is mapped
148 ok 4 - i3 raised the width to 75
149 ok 5 - i3 raised the height to 50
150 ok 6 - i3 did not map it to (0x0)
151 ok 7 - The object isa X11::XCB::Window
152 ok 8 - i3 let the width at 80
153 ok 9 - i3 let the height at 90
154 ok 10 - i3 mapped it to x=1
155 ok 11 - i3 mapped it to y=18
156 ok 12 - The object isa X11::XCB::Window
157 ok 13 - i3 let the width at 80
158 ok 14 - i3 let the height at 90
159 1..14
160
161 All tests successful.
162 Files=1, Tests=14, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.19 cusr 0.03 csys = 0.23 CPU)
163 Result: PASS
164
165 $ less latest/i3-log-for-04-floating.t
166 ----------------------------------------
167
168 If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:
169
170 ---------------------------------------------------
171 $ ./complete-run.pl --parallel=1 --keep-xserver-output
172 ---------------------------------------------------
173
174 This will show the output of Xephyr, which is the X server implementation we
175 use for testing.
176
177 ===== make command: +make check+
178 Make check runs the i3 testsuite.
179 You can still use ./testcases/complete-run.pl to get the interactive progress output.
180
181 .Example invocation of +make check+
182 ---------------------------------------
183 $ cd ~/i3
184
185 $ autoreconf -fi
186
187 $ mkdir -p build && cd build
188
189 $ ../configure
190
191 $ make -j8
192 # output omitted because it is very long
193
194 $ make check
195 # output omitted because it is very long
196 PASS: testcases/complete-run.pl
197 ============================================================================
198 Testsuite summary for i3 4.13
199 ============================================================================
200 # TOTAL: 1
201 # PASS: 1
202 # SKIP: 0
203 # XFAIL: 0
204 # FAIL: 0
205 # XPASS: 0
206 # ERROR: 0
207 ============================================================================
208
209 $ less test-suite.log
210 ----------------------------------------
211
212 ==== Coverage testing
213
214 Coverage testing is possible with +lcov+, the front-end for GCC's coverage
215 testing tool +gcov+. The testcases can generate a nice html report that tells
216 you which functions and lines were covered during a run of the tests. You can
217 use this tool to judge how effective your tests are.
218
219 To use test coverage tools, first compile with coverage enabled.
220
221 ---------------------------------------------------
222 COVERAGE=1 make
223 ---------------------------------------------------
224
225 Then run the tests with the +--coverage-testing+ flag.
226
227 ---------------------------------------------------
228 ./complete-run.pl --coverage-testing
229 ---------------------------------------------------
230
231 Then open +latest/i3-coverage/index.html+ in your web browser.
232
233 ==== IPC interface
234
235 The testsuite makes extensive use of the IPC (Inter-Process Communication)
236 interface which i3 provides. It is used for the startup process of i3, for
237 terminating it cleanly and (most importantly) for modifying and getting the
238 current state (layout tree).
239
240 See [https://i3wm.org/docs/ipc.html] for documentation on the IPC interface.
241
242 ==== X11::XCB
243
244 In order to open new windows, change attributes, get events, etc., the
245 testsuite uses X11::XCB, a new (and quite specific to i3 at the moment) Perl
246 module which uses the XCB protocol description to generate Perl bindings to
247 X11. They work in a very similar way to libxcb (which i3 uses) and provide
248 relatively high-level interfaces (objects such as +X11::XCB::Window+) as well as
249 access to the low-level interface, which is very useful when testing a window
250 manager.
251
252 === Filesystem structure
253
254 In the git root of i3, the testcases live in the folder +testcases+. This
255 folder contains the +complete-run.pl+ and a base configuration file which will
256 be used for the tests. The different testcases (their file extension is .t, not
257 .pl) themselves can be found in the conventionally named subfolder +t+:
258
259 .Filesystem structure
260 --------------------------------------------
261 ├── testcases
262 │   ├── complete-run.pl
263 │   ├── i3-test.config
264 │   ├── lib
265 │   │   ├── i3test.pm
266 │   │   ├── SocketActivation.pm
267 │   │   └── StartXDummy.pm
268 │   ├── t
269 │   │   ├── 00-load.t
270 │   │   ├── 01-tile.t
271 │   │   ├── 02-fullscreen.t
272 │   │   ├── ...
273 │   │   ├── omitted for brevity
274 │   │   ├── ...
275 │   │   └── 74-regress-focus-toggle.t
276 --------------------------------------------
277
278 == Anatomy of a testcase
279
280 Learning by example is definitely a good strategy when you are wondering how to
281 write a testcase. Let's take +t/11-goto.t+ as an easy example and go through it
282 step by step:
283
284 .t/11-goto.t: Boilerplate
285 ----------------------
286 #!perl
287 # vim:ts=4:sw=4:expandtab
288
289 use i3test;
290 use File::Temp;
291
292 my $x = X11::XCB::Connection->new;
293 -----------------------
294
295 This is what we call boilerplate. It exists at the top of every test file (to
296 some extent). The first line is the shebang, which specifies that this file is
297 a Perl script. The second line contains VIM specific settings on how to
298 edit/format this file (use spaces instead of tabs, indent using 4 spaces).
299 Afterwards, the +i3test+ module is used. This module contains i3 testsuite
300 specific functions which you are strongly encouraged to use. They make writing
301 testcases a lot easier and will make it easier for other people to read your
302 tests.
303
304 The next line uses the +File::Temp+ module. This is specific to this testcase,
305 because it needs to generate a temporary name during the test. Many testcases
306 use only the +i3test+ module.
307
308 The last line opens a connection to X11. You might or might not need this in
309 your testcase, depending on whether you are going to open windows (etc.) or
310 only use i3 commands.
311
312 .t/11-goto.t: Setup
313 ----------------------
314 my $tmp = fresh_workspace;
315
316 cmd 'split h';
317 ----------------------
318
319 The first line calls i3test's +fresh_workspace+ function which looks for a
320 currently unused workspace, switches to it, and returns its name. The variable
321 +$tmp+ will end up having a value such as +"/tmp/87kBVcHbA9"+. Note that this
322 is not (necessarily) a valid path, it's just a random workspace name.
323
324 So, now that we are on a new workspace, we ensure that the workspace uses
325 horizontal orientation by issuing the +split h+ command (see the i3 User's
326 Guide for a list of commands). This is not strictly necessary, but good style.
327 In general, the +cmd+ function executes the specified i3 command by using the
328 IPC interface and returns once i3 acknowledged the command.
329
330 .t/11-goto.t: Setup
331 ----------------------
332 #####################################################################
333 # Create two windows and make sure focus switching works
334 #####################################################################
335
336 my $top = open_window($x);
337 my $mid = open_window($x);
338 my $bottom = open_window($x);
339 ----------------------
340
341 In every major section of a testcase, you should put a comment like the one
342 above. This makes it immediately clear how the file is structured.
343
344 The +open_window+ function opens a standard window, which will then be put into
345 tiling mode by i3. If you want a floating window, use the
346 +open_floating_window+ function. These functions accept the same parameters as
347 +X11::XCB::Window->new+, see the i3test documentation at TODO.
348
349 .t/11-goto.t: Helper function
350 ----------------------
351 #
352 # Returns the input focus after sending the given command to i3 via IPC
353 # and syncing with i3
354 #
355 sub focus_after {
356 my $msg = shift;
357
358 cmd $msg;
359 sync_with_i3 $x;
360 return $x->input_focus;
361 }
362 ----------------------
363
364 This section defines a helper function which will be used over and over in this
365 testcase. If you have code which gets executed more than once or twice
366 (depending on the length of your test, use your best judgement), please put it
367 in a function. Tests should be short, concise and clear.
368
369 The +focus_after+ function executes a command and returns the X11 focus after
370 the command was executed. The +sync_with_i3+ command makes sure that i3 could
371 push its state to X11. See <<i3_sync>> to learn how this works exactly.
372
373 .t/11-goto.t: Test assumptions
374 ----------------------
375 $focus = $x->input_focus;
376 is($focus, $bottom->id, "Latest window focused");
377
378 $focus = focus_after('focus left');
379 is($focus, $mid->id, "Middle window focused");
380 ----------------------
381
382 Now, we run the first two real tests. They use +Test::More+'s +is+ function,
383 which compares two values and prints the differences if they are not the same.
384 After the arguments, we supply a short comment to indicate what we are testing
385 here. This makes it vastly more easy for the developer to spot which testcase
386 is the problem in case one fails.
387
388 The first test checks that the most recently opened window is focused.
389 Afterwards, the command +focus left+ is issued and it is verified that the
390 middle window now has focus.
391
392 Note that this is not a comprehensive test of the +focus+ command -- we would
393 have to test wrapping, focus when using a more complex layout, focusing the
394 parent/child containers, etc. But that is not the point of this testcase.
395 Instead, we just want to know if +$x->input_focus+ corresponds with what we are
396 expecting. If not, something is completely wrong with the test environment and
397 this trivial test will fail.
398
399 .t/11-goto.t: Test that the feature does not work (yet)
400 ----------------------
401 #####################################################################
402 # Now goto a mark which does not exist
403 #####################################################################
404
405 my $random_mark = mktemp('mark.XXXXXX');
406
407 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
408 is($focus, $mid->id, "focus unchanged");
409 ----------------------
410
411 Syntax hint: The qq keyword is the interpolating quote operator. It lets you
412 chose a quote character (in this case the +|+ character, a pipe). This makes
413 having double quotes in our string easy.
414
415 In this new major section, a random mark (mark is an identifier for a window,
416 see "VIM-like marks" in the i3 User’s Guide) will be generated. Afterwards, we
417 test that trying to focus that mark will not do anything. This is important: Do
418 not only test that using a feature has the expected outcome, but also test that
419 using it without properly initializing it does no harm. This command could for
420 example have changed focus anyways (a bug) or crash i3 (obviously a bug).
421
422 .t/11-goto.t: Test that the feature does work
423 ----------------------
424 cmd "mark $random_mark";
425
426 $focus = focus_after('focus left');
427 is($focus, $top->id, "Top window focused");
428
429 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
430 is($focus, $mid->id, "goto worked");
431 ----------------------
432
433 Remember: Focus was on the middle window (we verified that earlier in "Test
434 assumptions"). We now mark the middle window with our randomly generated mark.
435 Afterwards, we switch focus away from the middle window to be able to tell if
436 focusing it via its mark will work. If the test works, the goto command seems
437 to be working.
438
439 .t/11-goto.t: Test corner case
440 ----------------------
441 # check that we can specify multiple criteria
442
443 $focus = focus_after('focus left');
444 is($focus, $top->id, "Top window focused");
445
446 $focus = focus_after(qq|[con_mark="$random_mark" con_mark="$random_mark"] focus|);
447 is($focus, $mid->id, "goto worked");
448 ----------------------
449
450 Now we test the same feature, but specifying the mark twice in the command.
451 This should have no effect, but let’s be sure: test it and see if things go
452 wrong.
453
454 .t/11-goto.t: Test second code path
455 ----------------------
456 #####################################################################
457 # Check whether the focus command will switch to a different
458 # workspace if necessary
459 #####################################################################
460
461 my $tmp2 = fresh_workspace;
462
463 is(focused_ws(), $tmp2, 'tmp2 now focused');
464
465 cmd qq|[con_mark="$random_mark"] focus|;
466
467 is(focused_ws(), $tmp, 'tmp now focused');
468 ----------------------
469
470 This part of the test checks that focusing windows by mark works across
471 workspaces. It uses i3test's +focused_ws+ function to get the current
472 workspace.
473
474 .t/11-goto.t: Test second code path
475 ----------------------
476 done_testing;
477 ----------------------
478
479 The end of every testcase has to contain the +done_testing+ line. This tells
480 +complete-run.pl+ that the test was finished successfully. If it does not
481 occur, the test might have crashed during execution -- some of the reasons why
482 that could happen are bugs in the used modules, bugs in the testcase itself or
483 an i3 crash resulting in the testcase being unable to communicate with i3 via
484 IPC anymore.
485
486 [[i3_sync]]
487 == Appendix A: The i3 sync protocol
488
489 Consider the following situation: You open two windows in your testcase, then
490 you use +focus left+ and want to verify that the X11 focus has been updated
491 properly. Sounds simple, right? Let’s assume you use this straight-forward
492 implementation:
493
494 .Racey focus testcase
495 -----------
496 my $left = open_window($x);
497 my $right = open_window($x);
498 cmd 'focus left';
499 is($x->input_focus, $left->id, 'left window focused');
500 ----------
501
502 However, the test fails. Sometimes. Apparently, there is a race condition in
503 your test. If you think about it, this is because you are using two different
504 pieces of software: You tell i3 to update focus, i3 confirms that, and then you
505 ask X11 to give you the current focus. There is a certain time i3 needs to
506 update the X11 state. If the testcase gets CPU time before X11 processed i3's
507 requests, the test will fail.
508
509 image::i3-sync.png["Diagram of the race condition", title="Diagram of the race condition"]
510
511 One way to "solve" this would be to add +sleep 0.5;+ after the +cmd+ call.
512 After 0.5 seconds it should be safe to assume that focus has been updated,
513 right?
514
515 In practice, this usually works. However, it has several problems:
516
517 1. This is obviously not a clean solution, but a workaround. Ugly.
518 2. On very slow machines, this might not work. Unlikely, but in different
519 situations (a delay to wait for i3 to startup) the necessary time is much
520 harder to guess, even for fast machines.
521 3. This *wastes a lot of time*. Usually, your computer is much faster than 0.5s
522 to update the status. However, sometimes, it might take 0.4s, so we can’t
523 make it +sleep 0.1+.
524
525 To illustrate how grave the problem with wasting time actually is: Before
526 removing all sleeps from the testsuite, a typical run using 4 separate X
527 servers took around 50 seconds on my machine. After removing all the sleeps,
528 we achieved times of about 25 seconds. This is very significant and influences
529 the way you think about tests -- the faster they are, the more likely you are
530 to check whether everything still works quite often (which you should).
531
532 What I am trying to say is: Delays adds up quickly and make the test suite
533 less robust.
534
535 The real solution for this problem is a mechanism which I call "the i3 sync
536 protocol". The idea is to send a request (which does not modify state) via X11
537 to i3 which will then be answered. Due to the request's position in the event
538 queue (*after* all previous events), you can be sure that by the time you
539 receive the reply, all other events have been dealt with by i3 (and, more
540 importantly, X11).
541
542 image::i3-sync-working.png["Diagram of the i3 sync solution", title="Diagram of the i3 sync solution"]
543
544 === Implementation details
545
546 The client which wants to sync with i3 initiates the protocol by sending a
547 ClientMessage to the X11 root window:
548
549 .Send ClientMessage
550 -------------------
551 # Generate a ClientMessage, see xcb_client_message_t
552 my $msg = pack "CCSLLLLLLL",
553 CLIENT_MESSAGE, # response_type
554 32, # format
555 0, # sequence
556 $root, # destination window
557 $x->atom(name => 'I3_SYNC')->id,
558
559 $_sync_window->id, # data[0]: our own window id
560 $myrnd, # data[1]: a random value to identify the request
561 0,
562 0,
563 0;
564
565 # Send it to the root window -- since i3 uses the SubstructureRedirect
566 # event mask, it will get the ClientMessage.
567 $x->send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
568 -------------------
569
570 i3 will then reply with the same ClientMessage, sent to the window specified in
571 +data[0]+. In the reply, +data[0]+ and +data[1]+ are exactly the same as in the
572 request. You should use a random value in +data[1]+ and check that you received
573 the same one when getting the reply.
574
575 == Appendix B: Socket activation
576
577 Socket activation is a mechanism which was made popular by systemd, an init
578 replacement. It basically describes creating a listening socket before starting
579 a program. systemd will invoke the program only when an actual connection to
580 the socket is made, hence the term socket activation.
581
582 The interesting part of this (in the i3 context) is that you can very precisely
583 detect when the program is ready (finished its initialization).
584
585 === Preparing the listening socket
586
587 +complete-run.pl+ will create a listening UNIX socket which it will then pass
588 to i3. This socket will be used by i3 as an additional IPC socket, just like
589 the one it will create on its own. Passing the socket happens implicitly
590 because children will inherit the parent’s sockets when fork()ing and sockets
591 will continue to exist after an exec() call (unless CLOEXEC is set of course).
592
593 The only explicit things +complete-run.pl+ has to do is setting the +LISTEN_FDS+
594 environment variable to the number of sockets which exist (1 in our case) and
595 setting the +LISTEN_PID+ environment variable to the current process ID. Both
596 variables are necessary so that the program (i3) knows how many sockets it
597 should use and if the environment variable is actually intended for it. i3 will
598 then start looking for sockets at file descriptor 3 (since 0, 1 and 2 are used
599 for stdin, stdout and stderr, respectively).
600
601 The actual Perl code which sets up the socket, fork()s, makes sure the socket
602 has file descriptor 3 and sets up the environment variables follows (shortened
603 a bit):
604
605
606 .Setup socket and environment
607 -----------------------------
608 my $socket = IO::Socket::UNIX->new(
609 Listen => 1,
610 Local => $args{unix_socket_path},
611 );
612
613 my $pid = fork;
614 if ($pid == 0) {
615 $ENV{LISTEN_PID} = $$;
616 $ENV{LISTEN_FDS} = 1;
617
618 # Only pass file descriptors 0 (stdin), 1 (stdout),
619 # 2 (stderr) and 3 (socket) to the child.
620 $^F = 3;
621
622 # If the socket does not use file descriptor 3 by chance
623 # already, we close fd 3 and dup2() the socket to 3.
624 if (fileno($socket) != 3) {
625 POSIX::close(3);
626 POSIX::dup2(fileno($socket), 3);
627 }
628
629 exec "/usr/bin/i3";
630 }
631 -----------------------------
632
633 === Waiting for a reply
634
635 In the parent process, we want to know when i3 is ready to answer our IPC
636 requests and handle our windows. Therefore, after forking, we immediately close
637 the listening socket (i3 will handle this side of the socket) and connect to it
638 (remember, we are talking about a named UNIX socket) as a client. This connect
639 call will immediately succeed because the kernel buffers it. Then, we send a
640 request (of type GET_TREE, but that is not really relevant). Writing data to
641 the socket will also succeed immediately because, again, the kernel buffers it
642 (only up to a certain amount of data of course).
643
644 Afterwards, we just blockingly wait until we get an answer. In the child
645 process, i3 will setup the listening socket in its event loop. Immediately
646 after actually starting the event loop, it will notice a new client connecting
647 (the parent process) and handle its request. Since all initialization has been
648 completed successfully by the time the event loop is entered, we can now assume
649 that i3 is ready.
650
651 === Timing and conclusion
652
653 A beautiful feature of this mechanism is that it does not depend on timing. It
654 does not matter when the child process gets CPU time or when the parent process
655 gets CPU time. On heavily loaded machines (or machines with multiple CPUs,
656 cores or unreliable schedulers), this makes waiting for i3 much more robust.
657
658 Before using socket activation, we typically used a +sleep(1)+ and hoped that
659 i3 was initialized by that time. Of course, this breaks on some (slow)
660 computers and wastes a lot of time on faster computers. By using socket
661 activation, we decreased the total amount of time necessary to run all tests
662 (72 files at the time of writing) from > 100 seconds to 16 seconds. This makes
663 it significantly more attractive to run the test suite more often (or at all)
664 during development.
665
666 An alternative approach to using socket activation is polling for the existence
667 of the IPC socket and connecting to it. While this might be slightly easier to
668 implement, it wastes CPU time and is considerably uglier than this solution
669 :). After all, +lib/SocketActivation.pm+ contains only 54 SLOC.
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>i3 testsuite</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>i3 testsuite</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">September 2012</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>This document explains how the i3 testsuite works, how to use it and extend it.
749 It is targeted at developers who not necessarily have been doing testing before
750 or have not been testing in Perl before. In general, the testsuite is not of
751 interest for end users.</p></div>
752 </div>
753 </div>
754 <div class="sect1">
755 <h2 id="_introduction">1. Introduction</h2>
756 <div class="sectionbody">
757 <div class="paragraph"><p>The i3 testsuite is a collection of files which contain testcases for various
758 i3 features. Some of them test if a certain workflow works correctly (moving
759 windows, focus behaviour, …). Others are regression tests and contain code
760 which previously made i3 crash or lead to unexpected behaviour. They then check
761 if i3 still runs (meaning it did not crash) and if it handled everything
762 correctly.</p></div>
763 <div class="paragraph"><p>The goal of having these tests is to automatically find problems and to
764 automatically get a feel for whether a change in the source code breaks any
765 existing feature. After every modification of the i3 sourcecode, the developer
766 should run the full testsuite. If one of the tests fails, the corresponding
767 problem should be fixed (or, in some cases, the testcase has to be modified).
768 For every bugreport, a testcase should be written to test the correct
769 behaviour. Initially, it will fail, but after fixing the bug, it will pass.
770 This ensures (or increases the chance) that bugs which have been fixed once
771 will never be found again.</p></div>
772 <div class="paragraph"><p>Also, when implementing a new feature, a testcase might be a good way to be
773 able to easily test if the feature is working correctly. Many developers will
774 test manually if everything works. Having a testcase not only helps you with
775 that, but it will also be useful for every future change.</p></div>
776 </div>
777 </div>
778 <div class="sect1">
779 <h2 id="_relevant_documentation">2. Relevant documentation</h2>
780 <div class="sectionbody">
781 <div class="paragraph"><p>Apart from this document, you should also have a look at:</p></div>
782 <div class="olist arabic"><ol class="arabic">
783 <li>
784 <p>
785 The "Modern Perl" book, which can be found at
786 <a href="http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf">http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf</a>
787 </p>
788 </li>
789 <li>
790 <p>
791 The latest Perl documentation of the "i3test" (general testcase setup) and
792 "i3test::Test" (additional test instructions) modules:
793 <a href="https://build.i3wm.org/docs/lib-i3test.html">https://build.i3wm.org/docs/lib-i3test.html</a> respectively
794 <a href="https://build.i3wm.org/docs/lib-i3test-test.html">https://build.i3wm.org/docs/lib-i3test-test.html</a>
795 </p>
796 </li>
797 <li>
798 <p>
799 The latest documentation on i3’s IPC interface:
800 <a href="https://build.i3wm.org/docs/ipc.html">https://build.i3wm.org/docs/ipc.html</a>
801 </p>
802 </li>
803 </ol></div>
804 </div>
805 </div>
806 <div class="sect1">
807 <h2 id="_implementation">3. Implementation</h2>
808 <div class="sectionbody">
809 <div class="paragraph"><p>For several reasons, the i3 testsuite has been implemented in Perl:</p></div>
810 <div class="olist arabic"><ol class="arabic">
811 <li>
812 <p>
813 Perl has a long tradition of testing. Every popular/bigger Perl module which
814 you can find on CPAN will not only come with documentation, but also with
815 tests. Therefore, the available infrastructure for tests is comprehensive.
816 See for example the excellent <a href="http://search.cpan.org/perldoc?Test::More">http://search.cpan.org/perldoc?Test::More</a>
817 and the referenced <a href="http://search.cpan.org/perldoc?Test::Tutorial">http://search.cpan.org/perldoc?Test::Tutorial</a>.
818 </p>
819 </li>
820 <li>
821 <p>
822 Perl is widely available and has a well-working package infrastructure.
823 </p>
824 </li>
825 <li>
826 <p>
827 The author is familiar with Perl :).
828 </p>
829 </li>
830 <li>
831 <p>
832 It is a good idea to use a different language for the tests than the
833 implementation itself.
834 </p>
835 </li>
836 </ol></div>
837 <div class="paragraph"><p>Please do not start programming language flamewars at this point.</p></div>
838 <div class="sect2">
839 <h3 id="_installing_the_dependencies">3.1. Installing the dependencies</h3>
840 <div class="paragraph"><p>As usual with Perl programs, the testsuite ships with a <code>Makefile.PL</code>.
841 This file specifies which Perl modules the testsuite depends on and can be used
842 to install all of them.</p></div>
843 <div class="paragraph"><p>Perl modules are distributed via CPAN, and there is the official, standard CPAN
844 client, simply called <code>cpan</code>. It comes with every Perl installation and can be
845 used to install the testsuite. Many users prefer to use the more modern
846 <code>cpanminus</code> instead, though (because it asks no questions and just works):</p></div>
847 <div class="paragraph"><p>The tests additionally require <code>Xephyr(1)</code> to run a nested X server. Install
848 <code>xserver-xephyr</code> on Debian or <code>xorg-server-xephyr</code> on Arch Linux.</p></div>
849 <div class="listingblock">
850 <div class="title">Installing testsuite dependencies using cpanminus (preferred)</div>
851 <div class="content">
852 <pre><code>$ cd ~/i3/testcases
853 $ sudo apt-get install cpanminus
854 $ sudo cpanm .
855 $ cd ~/i3/AnyEvent-I3
856 $ sudo cpanm Module::Install
857 $ sudo cpanm .</code></pre>
858 </div></div>
859 <div class="paragraph"><p>If you don’t want to use cpanminus for some reason, the same works with cpan:</p></div>
860 <div class="listingblock">
861 <div class="title">Installing testsuite dependencies using cpan</div>
862 <div class="content">
863 <pre><code>$ cd ~/i3/testcases
864 $ sudo cpan .
865 $ cd ~/i3/AnyEvent-I3
866 $ sudo cpan Module::Install
867 $ sudo cpan .</code></pre>
868 </div></div>
869 <div class="paragraph"><p>In case you don’t have root permissions, you can also install into your home
870 directory, see <a href="https://michael.stapelberg.de/cpan/">https://michael.stapelberg.de/cpan/</a></p></div>
871 </div>
872 <div class="sect2">
873 <h3 id="_mechanisms">3.2. Mechanisms</h3>
874 <div class="sect3">
875 <h4 id="_script_complete_run">3.2.1. Script: complete-run</h4>
876 <div class="paragraph"><p>The testcases are run by a script called <code>complete-run.pl</code>. It runs all
877 testcases by default, but you can be more specific and let it only run one or
878 more testcases. Also, it takes care of starting up a separate instance of i3
879 with an appropriate configuration file and creates a folder for each run
880 containing the appropriate i3 logfile for each testcase. The latest folder can
881 always be found under the symlink <code>latest/</code>. Unless told differently, it will
882 run the tests on a separate X server instance (using Xephyr).</p></div>
883 <div class="paragraph"><p>Xephyr will open a window where you can inspect the running test. By default,
884 tests are run under Xvfb.</p></div>
885 <div class="listingblock">
886 <div class="title">Example invocation of <code>complete-run.pl</code></div>
887 <div class="content">
888 <pre><code>$ cd ~/i3
889
890 $ autoreconf -fi
891
892 $ mkdir -p build &amp;&amp; cd build
893
894 $ ../configure
895
896 $ make -j8
897 # output omitted because it is very long
898
899 $ cd testcases
900
901 $ ./complete-run.pl
902 # output omitted because it is very long
903 All tests successful.
904 Files=78, Tests=734, 27 wallclock secs ( 0.38 usr 0.48 sys + 17.65 cusr 3.21 csys = 21.72 CPU)
905 Result: PASS
906
907 $ ./complete-run.pl t/04-floating.t
908 [:3] i3 startup: took 0.07s, status = 1
909 [:3] Running t/04-floating.t with logfile testsuite-2011-09-24-16-06-04-4.0.2-226-g1eb011a/i3-log-for-04-floating.t
910 [:3] t/04-floating.t finished
911 [:3] killing i3
912 output for t/04-floating.t:
913 ok 1 - use X11::XCB::Window;
914 ok 2 - The object isa X11::XCB::Window
915 ok 3 - Window is mapped
916 ok 4 - i3 raised the width to 75
917 ok 5 - i3 raised the height to 50
918 ok 6 - i3 did not map it to (0x0)
919 ok 7 - The object isa X11::XCB::Window
920 ok 8 - i3 let the width at 80
921 ok 9 - i3 let the height at 90
922 ok 10 - i3 mapped it to x=1
923 ok 11 - i3 mapped it to y=18
924 ok 12 - The object isa X11::XCB::Window
925 ok 13 - i3 let the width at 80
926 ok 14 - i3 let the height at 90
927 1..14
928
929 All tests successful.
930 Files=1, Tests=14, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.19 cusr 0.03 csys = 0.23 CPU)
931 Result: PASS
932
933 $ less latest/i3-log-for-04-floating.t</code></pre>
934 </div></div>
935 <div class="paragraph"><p>If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:</p></div>
936 <div class="listingblock">
937 <div class="content">
938 <pre><code>$ ./complete-run.pl --parallel=1 --keep-xserver-output</code></pre>
939 </div></div>
940 <div class="paragraph"><p>This will show the output of Xephyr, which is the X server implementation we
941 use for testing.</p></div>
942 <div class="sect4">
943 <h5 id="_make_command_code_make_check_code">make command: <code>make check</code></h5>
944 <div class="paragraph"><p>Make check runs the i3 testsuite.
945 You can still use ./testcases/complete-run.pl to get the interactive progress output.</p></div>
946 <div class="listingblock">
947 <div class="title">Example invocation of <code>make check</code></div>
948 <div class="content">
949 <pre><code>$ cd ~/i3
950
951 $ autoreconf -fi
952
953 $ mkdir -p build &amp;&amp; cd build
954
955 $ ../configure
956
957 $ make -j8
958 # output omitted because it is very long
959
960 $ make check
961 # output omitted because it is very long
962 PASS: testcases/complete-run.pl
963 ============================================================================
964 Testsuite summary for i3 4.13
965 ============================================================================
966 # TOTAL: 1
967 # PASS: 1
968 # SKIP: 0
969 # XFAIL: 0
970 # FAIL: 0
971 # XPASS: 0
972 # ERROR: 0
973 ============================================================================
974
975 $ less test-suite.log</code></pre>
976 </div></div>
977 </div>
978 </div>
979 <div class="sect3">
980 <h4 id="_coverage_testing">3.2.2. Coverage testing</h4>
981 <div class="paragraph"><p>Coverage testing is possible with <code>lcov</code>, the front-end for GCC&#8217;s coverage
982 testing tool <code>gcov</code>. The testcases can generate a nice html report that tells
983 you which functions and lines were covered during a run of the tests. You can
984 use this tool to judge how effective your tests are.</p></div>
985 <div class="paragraph"><p>To use test coverage tools, first compile with coverage enabled.</p></div>
986 <div class="listingblock">
987 <div class="content">
988 <pre><code>COVERAGE=1 make</code></pre>
989 </div></div>
990 <div class="paragraph"><p>Then run the tests with the <code>--coverage-testing</code> flag.</p></div>
991 <div class="listingblock">
992 <div class="content">
993 <pre><code>./complete-run.pl --coverage-testing</code></pre>
994 </div></div>
995 <div class="paragraph"><p>Then open <code>latest/i3-coverage/index.html</code> in your web browser.</p></div>
996 </div>
997 <div class="sect3">
998 <h4 id="_ipc_interface">3.2.3. IPC interface</h4>
999 <div class="paragraph"><p>The testsuite makes extensive use of the IPC (Inter-Process Communication)
1000 interface which i3 provides. It is used for the startup process of i3, for
1001 terminating it cleanly and (most importantly) for modifying and getting the
1002 current state (layout tree).</p></div>
1003 <div class="paragraph"><p>See [<a href="https://i3wm.org/docs/ipc.html">https://i3wm.org/docs/ipc.html</a>] for documentation on the IPC interface.</p></div>
1004 </div>
1005 <div class="sect3">
1006 <h4 id="_x11_xcb">3.2.4. X11::XCB</h4>
1007 <div class="paragraph"><p>In order to open new windows, change attributes, get events, etc., the
1008 testsuite uses X11::XCB, a new (and quite specific to i3 at the moment) Perl
1009 module which uses the XCB protocol description to generate Perl bindings to
1010 X11. They work in a very similar way to libxcb (which i3 uses) and provide
1011 relatively high-level interfaces (objects such as <code>X11::XCB::Window</code>) as well as
1012 access to the low-level interface, which is very useful when testing a window
1013 manager.</p></div>
1014 </div>
1015 </div>
1016 <div class="sect2">
1017 <h3 id="_filesystem_structure">3.3. Filesystem structure</h3>
1018 <div class="paragraph"><p>In the git root of i3, the testcases live in the folder <code>testcases</code>. This
1019 folder contains the <code>complete-run.pl</code> and a base configuration file which will
1020 be used for the tests. The different testcases (their file extension is .t, not
1021 .pl) themselves can be found in the conventionally named subfolder <code>t</code>:</p></div>
1022 <div class="listingblock">
1023 <div class="title">Filesystem structure</div>
1024 <div class="content">
1025 <pre><code>├── testcases
1026 │   ├── complete-run.pl
1027 │   ├── i3-test.config
1028 │   ├── lib
1029 │   │   ├── i3test.pm
1030 │   │   ├── SocketActivation.pm
1031 │   │   └── StartXDummy.pm
1032 │   ├── t
1033 │   │   ├── 00-load.t
1034 │   │   ├── 01-tile.t
1035 │   │   ├── 02-fullscreen.t
1036 │   │   ├── ...
1037 │   │   ├── omitted for brevity
1038 │   │   ├── ...
1039 │   │   └── 74-regress-focus-toggle.t</code></pre>
1040 </div></div>
1041 </div>
1042 </div>
1043 </div>
1044 <div class="sect1">
1045 <h2 id="_anatomy_of_a_testcase">4. Anatomy of a testcase</h2>
1046 <div class="sectionbody">
1047 <div class="paragraph"><p>Learning by example is definitely a good strategy when you are wondering how to
1048 write a testcase. Let&#8217;s take <code>t/11-goto.t</code> as an easy example and go through it
1049 step by step:</p></div>
1050 <div class="listingblock">
1051 <div class="title">t/11-goto.t: Boilerplate</div>
1052 <div class="content">
1053 <pre><code>#!perl
1054 # vim:ts=4:sw=4:expandtab
1055
1056 use i3test;
1057 use File::Temp;
1058
1059 my $x = X11::XCB::Connection-&gt;new;</code></pre>
1060 </div></div>
1061 <div class="paragraph"><p>This is what we call boilerplate. It exists at the top of every test file (to
1062 some extent). The first line is the shebang, which specifies that this file is
1063 a Perl script. The second line contains VIM specific settings on how to
1064 edit/format this file (use spaces instead of tabs, indent using 4 spaces).
1065 Afterwards, the <code>i3test</code> module is used. This module contains i3 testsuite
1066 specific functions which you are strongly encouraged to use. They make writing
1067 testcases a lot easier and will make it easier for other people to read your
1068 tests.</p></div>
1069 <div class="paragraph"><p>The next line uses the <code>File::Temp</code> module. This is specific to this testcase,
1070 because it needs to generate a temporary name during the test. Many testcases
1071 use only the <code>i3test</code> module.</p></div>
1072 <div class="paragraph"><p>The last line opens a connection to X11. You might or might not need this in
1073 your testcase, depending on whether you are going to open windows (etc.) or
1074 only use i3 commands.</p></div>
1075 <div class="listingblock">
1076 <div class="title">t/11-goto.t: Setup</div>
1077 <div class="content">
1078 <pre><code>my $tmp = fresh_workspace;
1079
1080 cmd 'split h';</code></pre>
1081 </div></div>
1082 <div class="paragraph"><p>The first line calls i3test&#8217;s <code>fresh_workspace</code> function which looks for a
1083 currently unused workspace, switches to it, and returns its name. The variable
1084 <code>$tmp</code> will end up having a value such as <code>"/tmp/87kBVcHbA9"</code>. Note that this
1085 is not (necessarily) a valid path, it&#8217;s just a random workspace name.</p></div>
1086 <div class="paragraph"><p>So, now that we are on a new workspace, we ensure that the workspace uses
1087 horizontal orientation by issuing the <code>split h</code> command (see the i3 User&#8217;s
1088 Guide for a list of commands). This is not strictly necessary, but good style.
1089 In general, the <code>cmd</code> function executes the specified i3 command by using the
1090 IPC interface and returns once i3 acknowledged the command.</p></div>
1091 <div class="listingblock">
1092 <div class="title">t/11-goto.t: Setup</div>
1093 <div class="content">
1094 <pre><code>#####################################################################
1095 # Create two windows and make sure focus switching works
1096 #####################################################################
1097
1098 my $top = open_window($x);
1099 my $mid = open_window($x);
1100 my $bottom = open_window($x);</code></pre>
1101 </div></div>
1102 <div class="paragraph"><p>In every major section of a testcase, you should put a comment like the one
1103 above. This makes it immediately clear how the file is structured.</p></div>
1104 <div class="paragraph"><p>The <code>open_window</code> function opens a standard window, which will then be put into
1105 tiling mode by i3. If you want a floating window, use the
1106 <code>open_floating_window</code> function. These functions accept the same parameters as
1107 <code>X11::XCB::Window&#8594;new</code>, see the i3test documentation at TODO.</p></div>
1108 <div class="listingblock">
1109 <div class="title">t/11-goto.t: Helper function</div>
1110 <div class="content">
1111 <pre><code>#
1112 # Returns the input focus after sending the given command to i3 via IPC
1113 # and syncing with i3
1114 #
1115 sub focus_after {
1116 my $msg = shift;
1117
1118 cmd $msg;
1119 sync_with_i3 $x;
1120 return $x-&gt;input_focus;
1121 }</code></pre>
1122 </div></div>
1123 <div class="paragraph"><p>This section defines a helper function which will be used over and over in this
1124 testcase. If you have code which gets executed more than once or twice
1125 (depending on the length of your test, use your best judgement), please put it
1126 in a function. Tests should be short, concise and clear.</p></div>
1127 <div class="paragraph"><p>The <code>focus_after</code> function executes a command and returns the X11 focus after
1128 the command was executed. The <code>sync_with_i3</code> command makes sure that i3 could
1129 push its state to X11. See <a href="#i3_sync">[i3_sync]</a> to learn how this works exactly.</p></div>
1130 <div class="listingblock">
1131 <div class="title">t/11-goto.t: Test assumptions</div>
1132 <div class="content">
1133 <pre><code>$focus = $x-&gt;input_focus;
1134 is($focus, $bottom-&gt;id, "Latest window focused");
1135
1136 $focus = focus_after('focus left');
1137 is($focus, $mid-&gt;id, "Middle window focused");</code></pre>
1138 </div></div>
1139 <div class="paragraph"><p>Now, we run the first two real tests. They use <code>Test::More</code>'s <code>is</code> function,
1140 which compares two values and prints the differences if they are not the same.
1141 After the arguments, we supply a short comment to indicate what we are testing
1142 here. This makes it vastly more easy for the developer to spot which testcase
1143 is the problem in case one fails.</p></div>
1144 <div class="paragraph"><p>The first test checks that the most recently opened window is focused.
1145 Afterwards, the command <code>focus left</code> is issued and it is verified that the
1146 middle window now has focus.</p></div>
1147 <div class="paragraph"><p>Note that this is not a comprehensive test of the <code>focus</code> command&#8201;&#8212;&#8201;we would
1148 have to test wrapping, focus when using a more complex layout, focusing the
1149 parent/child containers, etc. But that is not the point of this testcase.
1150 Instead, we just want to know if <code>$x&#8594;input_focus</code> corresponds with what we are
1151 expecting. If not, something is completely wrong with the test environment and
1152 this trivial test will fail.</p></div>
1153 <div class="listingblock">
1154 <div class="title">t/11-goto.t: Test that the feature does not work (yet)</div>
1155 <div class="content">
1156 <pre><code>#####################################################################
1157 # Now goto a mark which does not exist
1158 #####################################################################
1159
1160 my $random_mark = mktemp('mark.XXXXXX');
1161
1162 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
1163 is($focus, $mid-&gt;id, "focus unchanged");</code></pre>
1164 </div></div>
1165 <div class="paragraph"><p>Syntax hint: The qq keyword is the interpolating quote operator. It lets you
1166 chose a quote character (in this case the <code>|</code> character, a pipe). This makes
1167 having double quotes in our string easy.</p></div>
1168 <div class="paragraph"><p>In this new major section, a random mark (mark is an identifier for a window,
1169 see "VIM-like marks" in the i3 User’s Guide) will be generated. Afterwards, we
1170 test that trying to focus that mark will not do anything. This is important: Do
1171 not only test that using a feature has the expected outcome, but also test that
1172 using it without properly initializing it does no harm. This command could for
1173 example have changed focus anyways (a bug) or crash i3 (obviously a bug).</p></div>
1174 <div class="listingblock">
1175 <div class="title">t/11-goto.t: Test that the feature does work</div>
1176 <div class="content">
1177 <pre><code>cmd "mark $random_mark";
1178
1179 $focus = focus_after('focus left');
1180 is($focus, $top-&gt;id, "Top window focused");
1181
1182 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
1183 is($focus, $mid-&gt;id, "goto worked");</code></pre>
1184 </div></div>
1185 <div class="paragraph"><p>Remember: Focus was on the middle window (we verified that earlier in "Test
1186 assumptions"). We now mark the middle window with our randomly generated mark.
1187 Afterwards, we switch focus away from the middle window to be able to tell if
1188 focusing it via its mark will work. If the test works, the goto command seems
1189 to be working.</p></div>
1190 <div class="listingblock">
1191 <div class="title">t/11-goto.t: Test corner case</div>
1192 <div class="content">
1193 <pre><code># check that we can specify multiple criteria
1194
1195 $focus = focus_after('focus left');
1196 is($focus, $top-&gt;id, "Top window focused");
1197
1198 $focus = focus_after(qq|[con_mark="$random_mark" con_mark="$random_mark"] focus|);
1199 is($focus, $mid-&gt;id, "goto worked");</code></pre>
1200 </div></div>
1201 <div class="paragraph"><p>Now we test the same feature, but specifying the mark twice in the command.
1202 This should have no effect, but let’s be sure: test it and see if things go
1203 wrong.</p></div>
1204 <div class="listingblock">
1205 <div class="title">t/11-goto.t: Test second code path</div>
1206 <div class="content">
1207 <pre><code>#####################################################################
1208 # Check whether the focus command will switch to a different
1209 # workspace if necessary
1210 #####################################################################
1211
1212 my $tmp2 = fresh_workspace;
1213
1214 is(focused_ws(), $tmp2, 'tmp2 now focused');
1215
1216 cmd qq|[con_mark="$random_mark"] focus|;
1217
1218 is(focused_ws(), $tmp, 'tmp now focused');</code></pre>
1219 </div></div>
1220 <div class="paragraph"><p>This part of the test checks that focusing windows by mark works across
1221 workspaces. It uses i3test&#8217;s <code>focused_ws</code> function to get the current
1222 workspace.</p></div>
1223 <div class="listingblock">
1224 <div class="title">t/11-goto.t: Test second code path</div>
1225 <div class="content">
1226 <pre><code>done_testing;</code></pre>
1227 </div></div>
1228 <div class="paragraph"><p>The end of every testcase has to contain the <code>done_testing</code> line. This tells
1229 <code>complete-run.pl</code> that the test was finished successfully. If it does not
1230 occur, the test might have crashed during execution&#8201;&#8212;&#8201;some of the reasons why
1231 that could happen are bugs in the used modules, bugs in the testcase itself or
1232 an i3 crash resulting in the testcase being unable to communicate with i3 via
1233 IPC anymore.</p></div>
1234 </div>
1235 </div>
1236 <div class="sect1">
1237 <h2 id="i3_sync">5. Appendix A: The i3 sync protocol</h2>
1238 <div class="sectionbody">
1239 <div class="paragraph"><p>Consider the following situation: You open two windows in your testcase, then
1240 you use <code>focus left</code> and want to verify that the X11 focus has been updated
1241 properly. Sounds simple, right? Let’s assume you use this straight-forward
1242 implementation:</p></div>
1243 <div class="listingblock">
1244 <div class="title">Racey focus testcase</div>
1245 <div class="content">
1246 <pre><code>my $left = open_window($x);
1247 my $right = open_window($x);
1248 cmd 'focus left';
1249 is($x-&gt;input_focus, $left-&gt;id, 'left window focused');</code></pre>
1250 </div></div>
1251 <div class="paragraph"><p>However, the test fails. Sometimes. Apparently, there is a race condition in
1252 your test. If you think about it, this is because you are using two different
1253 pieces of software: You tell i3 to update focus, i3 confirms that, and then you
1254 ask X11 to give you the current focus. There is a certain time i3 needs to
1255 update the X11 state. If the testcase gets CPU time before X11 processed i3&#8217;s
1256 requests, the test will fail.</p></div>
1257 <div class="imageblock">
1258 <div class="content">
1259 <img src="i3-sync.png" alt="Diagram of the race condition" />
1260 </div>
1261 <div class="title">Figure 1. Diagram of the race condition</div>
1262 </div>
1263 <div class="paragraph"><p>One way to "solve" this would be to add <code>sleep 0.5;</code> after the <code>cmd</code> call.
1264 After 0.5 seconds it should be safe to assume that focus has been updated,
1265 right?</p></div>
1266 <div class="paragraph"><p>In practice, this usually works. However, it has several problems:</p></div>
1267 <div class="olist arabic"><ol class="arabic">
1268 <li>
1269 <p>
1270 This is obviously not a clean solution, but a workaround. Ugly.
1271 </p>
1272 </li>
1273 <li>
1274 <p>
1275 On very slow machines, this might not work. Unlikely, but in different
1276 situations (a delay to wait for i3 to startup) the necessary time is much
1277 harder to guess, even for fast machines.
1278 </p>
1279 </li>
1280 <li>
1281 <p>
1282 This <strong>wastes a lot of time</strong>. Usually, your computer is much faster than 0.5s
1283 to update the status. However, sometimes, it might take 0.4s, so we can’t
1284 make it <code>sleep 0.1</code>.
1285 </p>
1286 </li>
1287 </ol></div>
1288 <div class="paragraph"><p>To illustrate how grave the problem with wasting time actually is: Before
1289 removing all sleeps from the testsuite, a typical run using 4 separate X
1290 servers took around 50 seconds on my machine. After removing all the sleeps,
1291 we achieved times of about 25 seconds. This is very significant and influences
1292 the way you think about tests&#8201;&#8212;&#8201;the faster they are, the more likely you are
1293 to check whether everything still works quite often (which you should).</p></div>
1294 <div class="paragraph"><p>What I am trying to say is: Delays adds up quickly and make the test suite
1295 less robust.</p></div>
1296 <div class="paragraph"><p>The real solution for this problem is a mechanism which I call "the i3 sync
1297 protocol". The idea is to send a request (which does not modify state) via X11
1298 to i3 which will then be answered. Due to the request&#8217;s position in the event
1299 queue (<strong>after</strong> all previous events), you can be sure that by the time you
1300 receive the reply, all other events have been dealt with by i3 (and, more
1301 importantly, X11).</p></div>
1302 <div class="imageblock">
1303 <div class="content">
1304 <img src="i3-sync-working.png" alt="Diagram of the i3 sync solution" />
1305 </div>
1306 <div class="title">Figure 2. Diagram of the i3 sync solution</div>
1307 </div>
1308 <div class="sect2">
1309 <h3 id="_implementation_details">5.1. Implementation details</h3>
1310 <div class="paragraph"><p>The client which wants to sync with i3 initiates the protocol by sending a
1311 ClientMessage to the X11 root window:</p></div>
1312 <div class="listingblock">
1313 <div class="title">Send ClientMessage</div>
1314 <div class="content">
1315 <pre><code># Generate a ClientMessage, see xcb_client_message_t
1316 my $msg = pack "CCSLLLLLLL",
1317 CLIENT_MESSAGE, # response_type
1318 32, # format
1319 0, # sequence
1320 $root, # destination window
1321 $x-&gt;atom(name =&gt; 'I3_SYNC')-&gt;id,
1322
1323 $_sync_window-&gt;id, # data[0]: our own window id
1324 $myrnd, # data[1]: a random value to identify the request
1325 0,
1326 0,
1327 0;
1328
1329 # Send it to the root window -- since i3 uses the SubstructureRedirect
1330 # event mask, it will get the ClientMessage.
1331 $x-&gt;send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);</code></pre>
1332 </div></div>
1333 <div class="paragraph"><p>i3 will then reply with the same ClientMessage, sent to the window specified in
1334 <code>data[0]</code>. In the reply, <code>data[0]</code> and <code>data[1]</code> are exactly the same as in the
1335 request. You should use a random value in <code>data[1]</code> and check that you received
1336 the same one when getting the reply.</p></div>
1337 </div>
1338 </div>
1339 </div>
1340 <div class="sect1">
1341 <h2 id="_appendix_b_socket_activation">6. Appendix B: Socket activation</h2>
1342 <div class="sectionbody">
1343 <div class="paragraph"><p>Socket activation is a mechanism which was made popular by systemd, an init
1344 replacement. It basically describes creating a listening socket before starting
1345 a program. systemd will invoke the program only when an actual connection to
1346 the socket is made, hence the term socket activation.</p></div>
1347 <div class="paragraph"><p>The interesting part of this (in the i3 context) is that you can very precisely
1348 detect when the program is ready (finished its initialization).</p></div>
1349 <div class="sect2">
1350 <h3 id="_preparing_the_listening_socket">6.1. Preparing the listening socket</h3>
1351 <div class="paragraph"><p><code>complete-run.pl</code> will create a listening UNIX socket which it will then pass
1352 to i3. This socket will be used by i3 as an additional IPC socket, just like
1353 the one it will create on its own. Passing the socket happens implicitly
1354 because children will inherit the parent’s sockets when fork()ing and sockets
1355 will continue to exist after an exec() call (unless CLOEXEC is set of course).</p></div>
1356 <div class="paragraph"><p>The only explicit things <code>complete-run.pl</code> has to do is setting the <code>LISTEN_FDS</code>
1357 environment variable to the number of sockets which exist (1 in our case) and
1358 setting the <code>LISTEN_PID</code> environment variable to the current process ID. Both
1359 variables are necessary so that the program (i3) knows how many sockets it
1360 should use and if the environment variable is actually intended for it. i3 will
1361 then start looking for sockets at file descriptor 3 (since 0, 1 and 2 are used
1362 for stdin, stdout and stderr, respectively).</p></div>
1363 <div class="paragraph"><p>The actual Perl code which sets up the socket, fork()s, makes sure the socket
1364 has file descriptor 3 and sets up the environment variables follows (shortened
1365 a bit):</p></div>
1366 <div class="listingblock">
1367 <div class="title">Setup socket and environment</div>
1368 <div class="content">
1369 <pre><code>my $socket = IO::Socket::UNIX-&gt;new(
1370 Listen =&gt; 1,
1371 Local =&gt; $args{unix_socket_path},
1372 );
1373
1374 my $pid = fork;
1375 if ($pid == 0) {
1376 $ENV{LISTEN_PID} = $$;
1377 $ENV{LISTEN_FDS} = 1;
1378
1379 # Only pass file descriptors 0 (stdin), 1 (stdout),
1380 # 2 (stderr) and 3 (socket) to the child.
1381 $^F = 3;
1382
1383 # If the socket does not use file descriptor 3 by chance
1384 # already, we close fd 3 and dup2() the socket to 3.
1385 if (fileno($socket) != 3) {
1386 POSIX::close(3);
1387 POSIX::dup2(fileno($socket), 3);
1388 }
1389
1390 exec "/usr/bin/i3";
1391 }</code></pre>
1392 </div></div>
1393 </div>
1394 <div class="sect2">
1395 <h3 id="_waiting_for_a_reply">6.2. Waiting for a reply</h3>
1396 <div class="paragraph"><p>In the parent process, we want to know when i3 is ready to answer our IPC
1397 requests and handle our windows. Therefore, after forking, we immediately close
1398 the listening socket (i3 will handle this side of the socket) and connect to it
1399 (remember, we are talking about a named UNIX socket) as a client. This connect
1400 call will immediately succeed because the kernel buffers it. Then, we send a
1401 request (of type GET_TREE, but that is not really relevant). Writing data to
1402 the socket will also succeed immediately because, again, the kernel buffers it
1403 (only up to a certain amount of data of course).</p></div>
1404 <div class="paragraph"><p>Afterwards, we just blockingly wait until we get an answer. In the child
1405 process, i3 will setup the listening socket in its event loop. Immediately
1406 after actually starting the event loop, it will notice a new client connecting
1407 (the parent process) and handle its request. Since all initialization has been
1408 completed successfully by the time the event loop is entered, we can now assume
1409 that i3 is ready.</p></div>
1410 </div>
1411 <div class="sect2">
1412 <h3 id="_timing_and_conclusion">6.3. Timing and conclusion</h3>
1413 <div class="paragraph"><p>A beautiful feature of this mechanism is that it does not depend on timing. It
1414 does not matter when the child process gets CPU time or when the parent process
1415 gets CPU time. On heavily loaded machines (or machines with multiple CPUs,
1416 cores or unreliable schedulers), this makes waiting for i3 much more robust.</p></div>
1417 <div class="paragraph"><p>Before using socket activation, we typically used a <code>sleep(1)</code> and hoped that
1418 i3 was initialized by that time. Of course, this breaks on some (slow)
1419 computers and wastes a lot of time on faster computers. By using socket
1420 activation, we decreased the total amount of time necessary to run all tests
1421 (72 files at the time of writing) from &gt; 100 seconds to 16 seconds. This makes
1422 it significantly more attractive to run the test suite more often (or at all)
1423 during development.</p></div>
1424 <div class="paragraph"><p>An alternative approach to using socket activation is polling for the existence
1425 of the IPC socket and connecting to it. While this might be slightly easier to
1426 implement, it wastes CPU time and is considerably uglier than this solution
1427 :). After all, <code>lib/SocketActivation.pm</code> contains only 54 SLOC.</p></div>
1428 </div>
1429 </div>
1430 </div>
1431 </div>
1432 <div id="footnotes"><hr /></div>
1433 <div id="footer">
1434 <div id="footer-text">
1435 Last updated
1436 2019-01-27 16:45:19 CET
1437 </div>
1438 </div>
1439 </body>
1440 </html>
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 i3 User’s Guide
1 ===============
2 Michael Stapelberg <michael@i3wm.org>
3
4 This document contains all the information you need to configure and use the i3
5 window manager. If it does not, please check https://www.reddit.com/r/i3wm/
6 first, then contact us on IRC (preferred) or post your question(s) on the
7 mailing list.
8
9 == Default keybindings
10
11 For the "too long; didn’t read" people, here is an overview of the default
12 keybindings (click to see the full-size image):
13
14 *Keys to use with $mod (Alt):*
15
16 image:keyboard-layer1.png["Keys to use with $mod (Alt)",width=600,link="keyboard-layer1.png"]
17
18 *Keys to use with Shift+$mod:*
19
20 image:keyboard-layer2.png["Keys to use with Shift+$mod",width=600,link="keyboard-layer2.png"]
21
22 The red keys are the modifiers you need to press (by default), the blue keys
23 are your homerow.
24
25 Note that when starting i3 without a config file, i3-config-wizard will offer
26 you to create a config file in which the key positions (!) match what you see
27 in the image above, regardless of the keyboard layout you are using. If you
28 prefer to use a config file where the key letters match what you are seeing
29 above, just decline i3-config-wizard’s offer and base your config on
30 +/etc/i3/config+.
31
32 == Using i3
33
34 Throughout this guide, the keyword +$mod+ will be used to refer to the
35 configured modifier. This is the Alt key (+Mod1+) by default, with the Windows
36 key (+Mod4+) being a popular alternative that largely prevents conflicts with
37 application-defined shortcuts.
38
39 === Opening terminals and moving around
40
41 One very basic operation is opening a new terminal. By default, the keybinding
42 for this is +$mod+Enter+, that is Alt+Enter (+Mod1+Enter+) in the default
43 configuration. By pressing +$mod+Enter+, a new terminal will be opened. It
44 will fill the whole space available on your screen.
45
46 image:single_terminal.png[Single terminal]
47
48 If you now open another terminal, i3 will place it next to the current one,
49 splitting the screen size in half. Depending on your monitor, i3 will put the
50 created window beside the existing window (on wide displays) or below the
51 existing window (rotated displays).
52
53 image:two_terminals.png[Two terminals]
54
55 To move the focus between the two terminals, you can use the direction keys
56 which you may know from the editor +vi+. However, in i3, your homerow is used
57 for these keys (in +vi+, the keys are shifted to the left by one for
58 compatibility with most keyboard layouts). Therefore, +$mod+j+ is left, +$mod+k+
59 is down, +$mod+l+ is up and `$mod+;` is right. So, to switch between the
60 terminals, use +$mod+k+ or +$mod+l+. Of course, you can also use the arrow keys.
61
62 At the moment, your workspace is split (it contains two terminals) in a
63 specific direction (horizontal by default). Every window can be split
64 horizontally or vertically again, just like the workspace. The terminology is
65 "window" for a container that actually contains an X11 window (like a terminal
66 or browser) and "split container" for containers that consist of one or more
67 windows.
68
69 TODO: picture of the tree
70
71 To split a window vertically, press +$mod+v+ before you create the new window.
72 To split it horizontally, press +$mod+h+.
73
74 === Changing the container layout
75
76 A split container can have one of the following layouts:
77
78 splith/splitv::
79 Windows are sized so that every window gets an equal amount of space in the
80 container. splith distributes the windows horizontally (windows are right next
81 to each other), splitv distributes them vertically (windows are on top of each
82 other).
83 stacking::
84 Only the focused window in the container is displayed. You get a list of
85 windows at the top of the container.
86 tabbed::
87 The same principle as +stacking+, but the list of windows at the top is only
88 a single line which is vertically split.
89
90 To switch modes, press +$mod+e+ for splith/splitv (it toggles), +$mod+s+ for
91 stacking and +$mod+w+ for tabbed.
92
93 image:modes.png[Container modes]
94
95 === Toggling fullscreen mode for a window
96
97 To display a window in fullscreen mode or to go out of fullscreen mode again,
98 press +$mod+f+.
99
100 There is also a global fullscreen mode in i3 in which the client will span all
101 available outputs (the command is +fullscreen toggle global+).
102
103 === Opening other applications
104
105 Aside from opening applications from a terminal, you can also use the handy
106 +dmenu+ which is opened by pressing +$mod+d+ by default. Just type the name
107 (or a part of it) of the application which you want to open. The corresponding
108 application has to be in your +$PATH+ for this to work.
109
110 Additionally, if you have applications you open very frequently, you can
111 create a keybinding for starting the application directly. See the section
112 <<configuring>> for details.
113
114 === Closing windows
115
116 If an application does not provide a mechanism for closing (most applications
117 provide a menu, the escape key or a shortcut like +Control+w+ to close), you
118 can press +$mod+Shift+q+ to kill a window. For applications which support
119 the WM_DELETE protocol, this will correctly close the application (saving
120 any modifications or doing other cleanup). If the application doesn’t support
121 the WM_DELETE protocol your X server will kill the window and the behaviour
122 depends on the application.
123
124 === Using workspaces
125
126 Workspaces are an easy way to group a set of windows. By default, you are on
127 the first workspace, as the bar on the bottom left indicates. To switch to
128 another workspace, press +$mod+num+ where +num+ is the number of the workspace
129 you want to use. If the workspace does not exist yet, it will be created.
130
131 A common paradigm is to put the web browser on one workspace, communication
132 applications (+mutt+, +irssi+, ...) on another one, and the ones with which you
133 work, on the third one. Of course, there is no need to follow this approach.
134
135 If you have multiple screens, a workspace will be created on each screen at
136 startup. If you open a new workspace, it will be bound to the screen you
137 created it on. When you switch to a workspace on another screen, i3 will set
138 focus to that screen.
139
140 === Moving windows to workspaces
141
142 To move a window to another workspace, simply press +$mod+Shift+num+ where
143 +num+ is (like when switching workspaces) the number of the target workspace.
144 Similarly to switching workspaces, the target workspace will be created if
145 it does not yet exist.
146
147 === Resizing
148
149 The easiest way to resize a container is by using the mouse: Grab the border
150 and move it to the wanted size.
151
152 You can also use <<binding_modes>> to define a mode for resizing via the
153 keyboard. To see an example for this, look at the
154 https://github.com/i3/i3/blob/next/etc/config.keycodes[default config] provided
155 by i3.
156
157 === Restarting i3 inplace
158
159 To restart i3 in place (and thus get into a clean state if there is a bug, or
160 to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
161
162 === Exiting i3
163
164 To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
165 By default, a dialog will ask you to confirm if you really want to quit.
166
167 === Floating
168
169 Floating mode is the opposite of tiling mode. The position and size of
170 a window are not managed automatically by i3, but manually by
171 you. Using this mode violates the tiling paradigm but can be useful
172 for some corner cases like "Save as" dialog windows, or toolbar
173 windows (GIMP or similar). Those windows usually set the appropriate
174 hint and are opened in floating mode by default.
175
176 You can toggle floating mode for a window by pressing +$mod+Shift+Space+. By
177 dragging the window’s titlebar with your mouse you can move the window
178 around. By grabbing the borders and moving them you can resize the window. You
179 can also do that by using the <<floating_modifier>>. Another way to resize
180 floating windows using the mouse is to right-click on the titlebar and drag.
181
182 For resizing floating windows with your keyboard, see the resizing binding mode
183 provided by the i3 https://github.com/i3/i3/blob/next/etc/config.keycodes[default config].
184
185 Floating windows are always on top of tiling windows.
186
187 == Tree
188
189 i3 stores all information about the X11 outputs, workspaces and layout of the
190 windows on them in a tree. The root node is the X11 root window, followed by
191 the X11 outputs, then dock areas and a content container, then workspaces and
192 finally the windows themselves. In previous versions of i3 we had multiple lists
193 (of outputs, workspaces) and a table for each workspace. That approach turned
194 out to be complicated to use (snapping), understand and implement.
195
196 === The tree consists of Containers
197
198 The building blocks of our tree are so-called +Containers+. A +Container+ can
199 host a window (meaning an X11 window, one that you can actually see and use,
200 like a browser). Alternatively, it could contain one or more +Containers+. A
201 simple example is the workspace: When you start i3 with a single monitor, a
202 single workspace and you open two terminal windows, you will end up with a tree
203 like this:
204
205 image::tree-layout2.png["layout2",float="right"]
206 image::tree-shot4.png["shot4",title="Two terminals on standard workspace"]
207
208 [[OrientationSplit]]
209 === Orientation and Split Containers
210
211 It is only natural to use so-called +Split Containers+ in order to build a
212 layout when using a tree as data structure. In i3, every +Container+ has an
213 orientation (horizontal, vertical or unspecified) and the orientation depends
214 on the layout the container is in (vertical for splitv and stacking, horizontal
215 for splith and tabbed). So, in our example with the workspace, the default
216 layout of the workspace +Container+ is splith (most monitors are widescreen
217 nowadays). If you change the layout to splitv (+$mod+v+ in the default config)
218 and *then* open two terminals, i3 will configure your windows like this:
219
220 image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"]
221
222 An interesting new feature of i3 since version 4 is the ability to split anything:
223 Let’s assume you have two terminals on a workspace (with splith layout, that is
224 horizontal orientation), focus is on the right terminal. Now you want to open
225 another terminal window below the current one. If you would just open a new
226 terminal window, it would show up to the right due to the splith layout.
227 Instead, press +$mod+v+ to split the container with the splitv layout (to
228 open a +Horizontal Split Container+, use +$mod+h+). Now you can open a new
229 terminal and it will open below the current one:
230
231 image::tree-layout1.png["Layout",float="right"]
232 image::tree-shot1.png["shot",title="Vertical Split Container"]
233
234 unfloat::[]
235
236 You probably guessed it already: There is no limit on how deep your hierarchy
237 of splits can be.
238
239 === Focus parent
240
241 Let’s stay with our example from above. We have a terminal on the left and two
242 vertically split terminals on the right, focus is on the bottom right one. When
243 you open a new terminal, it will open below the current one.
244
245 So, how can you open a new terminal window to the *right* of the current one?
246 The solution is to use +focus parent+, which will focus the +Parent Container+ of
247 the current +Container+. In default configuration, use +$mod+a+ to navigate one
248 +Container+ up the tree (you can repeat this multiple times until you get to the
249 +Workspace Container+). In this case, you would focus the +Vertical Split Container+
250 which is *inside* the horizontally oriented workspace. Thus, now new windows will be
251 opened to the right of the +Vertical Split Container+:
252
253 image::tree-shot3.png["shot3",title="Focus parent, then open new terminal"]
254
255 === Implicit containers
256
257 In some cases, i3 needs to implicitly create a container to fulfill your
258 command.
259
260 One example is the following scenario: You start i3 with a single monitor and a
261 single workspace on which you open three terminal windows. All these terminal
262 windows are directly attached to one node inside i3’s layout tree, the
263 workspace node. By default, the workspace node’s orientation is +horizontal+.
264
265 Now you move one of these terminals down (+$mod+Shift+k+ by default). The
266 workspace node’s orientation will be changed to +vertical+. The terminal window
267 you moved down is directly attached to the workspace and appears on the bottom
268 of the screen. A new (horizontal) container was created to accommodate the
269 other two terminal windows. You will notice this when switching to tabbed mode
270 (for example). You would end up having one tab with a representation of the split
271 container (e.g., "H[urxvt firefox]") and the other one being the terminal window
272 you moved down.
273
274 [[configuring]]
275 == Configuring i3
276
277 This is where the real fun begins ;-). Most things are very dependent on your
278 ideal working environment so we can’t make reasonable defaults for them.
279
280 While not using a programming language for the configuration, i3 stays
281 quite flexible in regards to the things you usually want your window manager
282 to do.
283
284 For example, you can configure bindings to jump to specific windows,
285 you can set specific applications to start on specific workspaces, you can
286 automatically start applications, you can change the colors of i3, and you
287 can bind your keys to do useful things.
288
289 To change the configuration of i3, copy +/etc/i3/config+ to +\~/.i3/config+
290 (or +~/.config/i3/config+ if you like the XDG directory scheme) and edit it
291 with a text editor.
292
293 On first start (and on all following starts, unless you have a configuration
294 file), i3 will offer you to create a configuration file. You can tell the
295 wizard to use either Alt (+Mod1+) or Windows (+Mod4+) as modifier in the config
296 file. Also, the created config file will use the key symbols of your current
297 keyboard layout. To start the wizard, use the command +i3-config-wizard+.
298 Please note that you must not have +~/.i3/config+, otherwise the wizard will
299 exit.
300
301 Since i3 4.0, a new configuration format is used. i3 will try to automatically
302 detect the format version of a config file based on a few different keywords,
303 but if you want to make sure that your config is read with the new format,
304 include the following line in your config file:
305
306 ---------------------
307 # i3 config file (v4)
308 ---------------------
309
310 === Comments
311
312 It is possible and recommended to use comments in your configuration file to
313 properly document your setup for later reference. Comments are started with
314 a # and can only be used at the beginning of a line:
315
316 *Examples*:
317 -------------------
318 # This is a comment
319 -------------------
320
321 [[fonts]]
322 === Fonts
323
324 i3 has support for both X core fonts and FreeType fonts (through Pango) to
325 render window titles.
326
327 To generate an X core font description, you can use +xfontsel(1)+. To see
328 special characters (Unicode), you need to use a font which supports the
329 ISO-10646 encoding.
330
331 A FreeType font description is composed by a font family, a style, a weight,
332 a variant, a stretch and a size.
333 FreeType fonts support right-to-left rendering and contain often more
334 Unicode glyphs than X core fonts.
335
336 If i3 cannot open the configured font, it will output an error in the logfile
337 and fall back to a working font.
338
339 *Syntax*:
340 ------------------------------
341 font <X core font description>
342 font pango:<family list> [<style options>] <size>
343 ------------------------------
344
345 *Examples*:
346 --------------------------------------------------------------
347 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
348 font pango:DejaVu Sans Mono 10
349 font pango:DejaVu Sans Mono, Terminus Bold Semi-Condensed 11
350 font pango:Terminus 11px
351 --------------------------------------------------------------
352
353 [[keybindings]]
354 === Keyboard bindings
355
356 A keyboard binding makes i3 execute a command (see below) upon pressing a
357 specific key. i3 allows you to bind either on keycodes or on keysyms (you can
358 also mix your bindings, though i3 will not protect you from overlapping ones).
359
360 * A keysym (key symbol) is a description for a specific symbol, like "a"
361 or "b", but also more strange ones like "underscore" instead of "_". These
362 are the ones you use in Xmodmap to remap your keys. To get the current
363 mapping of your keys, use +xmodmap -pke+. To interactively enter a key and
364 see what keysym it is configured to, use +xev+.
365
366 * Keycodes do not need to have a symbol assigned (handy for custom vendor
367 hotkeys on some notebooks) and they will not change their meaning as you
368 switch to a different keyboard layout (when using +xmodmap+).
369
370 My recommendation is: If you often switch keyboard layouts but you want to keep
371 your bindings in the same physical location on the keyboard, use keycodes.
372 If you don’t switch layouts, and want a clean and simple config file, use
373 keysyms.
374
375 Some tools (such as +import+ or +xdotool+) might be unable to run upon a
376 KeyPress event, because the keyboard/pointer is still grabbed. For these
377 situations, the +--release+ flag can be used, which will execute the command
378 after the keys have been released.
379
380 *Syntax*:
381 ----------------------------------
382 bindsym [--release] [<Group>+][<Modifiers>+]<keysym> command
383 bindcode [--release] [<Group>+][<Modifiers>+]<keycode> command
384 ----------------------------------
385
386 *Examples*:
387 --------------------------------
388 # Fullscreen
389 bindsym $mod+f fullscreen toggle
390
391 # Restart
392 bindsym $mod+Shift+r restart
393
394 # Notebook-specific hotkeys
395 bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh
396
397 # Simulate ctrl+v upon pressing $mod+x
398 bindsym --release $mod+x exec --no-startup-id xdotool key --clearmodifiers ctrl+v
399
400 # Take a screenshot upon pressing $mod+x (select an area)
401 bindsym --release $mod+x exec --no-startup-id import /tmp/latest-screenshot.png
402 --------------------------------
403
404 Available Modifiers:
405
406 Mod1-Mod5, Shift, Control::
407 Standard modifiers, see +xmodmap(1)+
408
409 Group1, Group2, Group3, Group4::
410 When using multiple keyboard layouts (e.g. with `setxkbmap -layout us,ru`), you
411 can specify in which XKB group (also called “layout”) a keybinding should be
412 active. By default, keybindings are translated in Group1 and are active in all
413 groups. If you want to override keybindings in one of your layouts, specify the
414 corresponding group. For backwards compatibility, the group “Mode_switch” is an
415 alias for Group2.
416
417 [[mousebindings]]
418 === Mouse bindings
419
420 A mouse binding makes i3 execute a command upon pressing a specific mouse
421 button in the scope of the clicked container (see <<command_criteria>>). You
422 can configure mouse bindings in a similar way to key bindings.
423
424 *Syntax*:
425 ----------------------------------------------------------------------------------------------------
426 bindsym [--release] [--border] [--whole-window] [--exclude-titlebar] [<Modifiers>+]button<n> command
427 ----------------------------------------------------------------------------------------------------
428
429 By default, the binding will only run when you click on the titlebar of the
430 window. If the +--release+ flag is given, it will run when the mouse button
431 is released.
432
433 If the +--whole-window+ flag is given, the binding will also run when any part
434 of the window is clicked, with the exception of the border. To have a bind run
435 when the border is clicked, specify the +--border+ flag.
436
437 If the +--exclude-titlebar+ flag is given, the titlebar will not be considered
438 for the keybinding.
439
440 *Examples*:
441 --------------------------------
442 # The middle button over a titlebar kills the window
443 bindsym --release button2 kill
444
445 # The middle button and a modifer over any part of the window kills the window
446 bindsym --whole-window $mod+button2 kill
447
448 # The right button toggles floating
449 bindsym button3 floating toggle
450 bindsym $mod+button3 floating toggle
451
452 # The side buttons move the window around
453 bindsym button9 move left
454 bindsym button8 move right
455 --------------------------------
456
457 [[binding_modes]]
458 === Binding modes
459
460 You can have multiple sets of bindings by using different binding modes. When
461 you switch to another binding mode, all bindings from the current mode are
462 released and only the bindings defined in the new mode are valid for as long as
463 you stay in that binding mode. The only predefined binding mode is +default+,
464 which is the mode i3 starts out with and to which all bindings not defined in a
465 specific binding mode belong.
466
467 Working with binding modes consists of two parts: defining a binding mode and
468 switching to it. For these purposes, there are one config directive and one
469 command, both of which are called +mode+. The directive is used to define the
470 bindings belonging to a certain binding mode, while the command will switch to
471 the specified mode.
472
473 It is recommended to use binding modes in combination with <<variables>> in
474 order to make maintenance easier. Below is an example of how to use a binding
475 mode.
476
477 Note that it is advisable to define bindings for switching back to the default
478 mode.
479
480 Note that it is possible to use <<pango_markup>> for binding modes, but you
481 need to enable it explicitly by passing the +--pango_markup+ flag to the mode
482 definition.
483
484 *Syntax*:
485 ----------------------------
486 # config directive
487 mode [--pango_markup] <name>
488
489 # command
490 mode <name>
491 ----------------------------
492
493 *Example*:
494 ------------------------------------------------------------------------
495 # Press $mod+o followed by either f, t, Escape or Return to launch firefox,
496 # thunderbird or return to the default mode, respectively.
497 set $mode_launcher Launch: [f]irefox [t]hunderbird
498 bindsym $mod+o mode "$mode_launcher"
499
500 mode "$mode_launcher" {
501 bindsym f exec firefox
502 bindsym t exec thunderbird
503
504 bindsym Escape mode "default"
505 bindsym Return mode "default"
506 }
507 ------------------------------------------------------------------------
508
509 [[floating_modifier]]
510 === The floating modifier
511
512 To move floating windows with your mouse, you can either grab their titlebar
513 or configure the so-called floating modifier which you can then press and
514 click anywhere in the window itself to move it. The most common setup is to
515 use the same key you use for managing windows (Mod1 for example). Then
516 you can press Mod1, click into a window using your left mouse button, and drag
517 it to the position you want.
518
519 When holding the floating modifier, you can resize a floating window by
520 pressing the right mouse button on it and moving around while holding it. If
521 you hold the shift button as well, the resize will be proportional (the aspect
522 ratio will be preserved).
523
524 *Syntax*:
525 --------------------------------
526 floating_modifier <Modifier>
527 --------------------------------
528
529 *Example*:
530 --------------------------------
531 floating_modifier Mod1
532 --------------------------------
533
534 === Constraining floating window size
535
536 The maximum and minimum dimensions of floating windows can be specified. If
537 either dimension of +floating_maximum_size+ is specified as -1, that dimension
538 will be unconstrained with respect to its maximum value. If either dimension of
539 +floating_maximum_size+ is undefined, or specified as 0, i3 will use a default
540 value to constrain the maximum size. +floating_minimum_size+ is treated in a
541 manner analogous to +floating_maximum_size+.
542
543 *Syntax*:
544 ----------------------------------------
545 floating_minimum_size <width> x <height>
546 floating_maximum_size <width> x <height>
547 ----------------------------------------
548
549 *Example*:
550 --------------------------------------
551 floating_minimum_size 75 x 50
552 floating_maximum_size -1 x -1
553 --------------------------------------
554
555 === Orientation for new workspaces
556
557 New workspaces get a reasonable default orientation: Wide-screen monitors
558 (anything wider than high) get horizontal orientation, rotated monitors
559 (anything higher than wide) get vertical orientation.
560
561 With the +default_orientation+ configuration directive, you can override that
562 behavior.
563
564 *Syntax*:
565 --------------------------------------------
566 default_orientation horizontal|vertical|auto
567 --------------------------------------------
568
569 *Example*:
570 ----------------------------
571 default_orientation vertical
572 ----------------------------
573
574 === Layout mode for new containers
575
576 This option determines in which mode new containers on workspace level will
577 start.
578
579 *Syntax*:
580 ---------------------------------------------
581 workspace_layout default|stacking|tabbed
582 ---------------------------------------------
583
584 *Example*:
585 ---------------------
586 workspace_layout tabbed
587 ---------------------
588
589 === Window title alignment
590
591 This option determines the window title's text alignment.
592 Default is +left+
593
594 *Syntax*:
595 ---------------------------------------------
596 title_align left|center|right
597 ---------------------------------------------
598
599 === Default border style for new windows
600
601 This option determines which border style new windows will have. The default is
602 +normal+. Note that default_floating_border applies only to windows which are starting out as
603 floating windows, e.g., dialog windows, but not windows that are floated later on.
604
605 *Syntax*:
606 ---------------------------------------------
607 default_border normal|none|pixel
608 default_border normal|pixel <px>
609 default_floating_border normal|none|pixel
610 default_floating_border normal|pixel <px>
611 ---------------------------------------------
612
613 Please note that +new_window+ and +new_float+ have been deprecated in favor of the above options
614 and will be removed in a future release. We strongly recommend using the new options instead.
615
616 *Example*:
617 ---------------------
618 default_border pixel
619 ---------------------
620
621 The "normal" and "pixel" border styles support an optional border width in
622 pixels:
623
624 *Example*:
625 ---------------------
626 # The same as default_border none
627 default_border pixel 0
628
629 # A 3 px border
630 default_border pixel 3
631 ---------------------
632
633
634 [[_hiding_vertical_borders]]
635 === Hiding borders adjacent to the screen edges
636
637 You can hide container borders adjacent to the screen edges using
638 +hide_edge_borders+. This is useful if you are using scrollbars, or do not want
639 to waste even two pixels in displayspace. The "smart" setting hides borders on
640 workspaces with only one window visible, but keeps them on workspaces with
641 multiple windows visible. Default is none.
642
643 *Syntax*:
644 -----------------------------------------------
645 hide_edge_borders none|vertical|horizontal|both|smart
646 -----------------------------------------------
647
648 *Example*:
649 ----------------------
650 hide_edge_borders vertical
651 ----------------------
652
653 [[for_window]]
654 === Arbitrary commands for specific windows (for_window)
655
656 With the +for_window+ command, you can let i3 execute any command when it
657 encounters a specific window. This can be used to set windows to floating or to
658 change their border style, for example.
659
660 *Syntax*:
661 -------------------------------
662 for_window <criteria> <command>
663 -------------------------------
664
665 *Examples*:
666 ------------------------------------------------
667 # enable floating mode for all XTerm windows
668 for_window [class="XTerm"] floating enable
669
670 # Make all urxvts use a 1-pixel border:
671 for_window [class="urxvt"] border pixel 1
672
673 # A less useful, but rather funny example:
674 # makes the window floating as soon as I change
675 # directory to ~/work
676 for_window [title="x200: ~/work"] floating enable
677 ------------------------------------------------
678
679 The valid criteria are the same as those for commands, see <<command_criteria>>.
680
681 [[no_focus]]
682 === Don't focus window upon opening
683
684 When a new window appears, it will be focused. The +no_focus+ directive allows preventing
685 this from happening and must be used in combination with <<command_criteria>>.
686
687 Note that this does not apply to all cases, e.g., when feeding data into a running application
688 causing it to request being focused. To configure the behavior in such cases, refer to
689 <<focus_on_window_activation>>.
690
691 +no_focus+ will also be ignored for the first window on a workspace as there shouldn't be
692 a reason to not focus the window in this case. This allows for better usability in
693 combination with +workspace_layout+.
694
695 *Syntax*:
696 -------------------
697 no_focus <criteria>
698 -------------------
699
700 *Example*:
701 -------------------------------
702 no_focus [window_role="pop-up"]
703 -------------------------------
704
705 [[variables]]
706 === Variables
707
708 As you learned in the section about keyboard bindings, you will have
709 to configure lots of bindings containing modifier keys. If you want to save
710 yourself some typing and be able to change the modifier you use later,
711 variables can be handy.
712
713 *Syntax*:
714 -------------------
715 set $<name> <value>
716 -------------------
717
718 *Example*:
719 ------------------------
720 set $m Mod1
721 bindsym $m+Shift+r restart
722 ------------------------
723
724 Variables are directly replaced in the file when parsing. Variables expansion
725 is not recursive so it is not possible to define a variable with a value
726 containing another variable. There is no fancy handling and there are
727 absolutely no plans to change this. If you need a more dynamic configuration
728 you should create a little script which generates a configuration file and run
729 it before starting i3 (for example in your +~/.xsession+ file).
730
731 Also see <<xresources>> to learn how to create variables based on resources
732 loaded from the X resource database.
733
734 [[xresources]]
735 === X resources
736
737 <<variables>> can also be created using a value configured in the X resource
738 database. This is useful, for example, to avoid configuring color values within
739 the i3 configuration. Instead, the values can be configured, once, in the X
740 resource database to achieve an easily maintainable, consistent color theme
741 across many X applications.
742
743 Defining a resource will load this resource from the resource database and
744 assign its value to the specified variable. This is done verbatim and the value
745 must therefore be in the format that i3 uses. A fallback must be specified in
746 case the resource cannot be loaded from the database.
747
748 *Syntax*:
749 ----------------------------------------------------
750 set_from_resource $<name> <resource_name> <fallback>
751 ----------------------------------------------------
752
753 *Example*:
754 ----------------------------------------------------------------------------
755 # The ~/.Xresources should contain a line such as
756 # *color0: #121212
757 # and must be loaded properly, e.g., by using
758 # xrdb ~/.Xresources
759 # This value is picked up on by other applications (e.g., the URxvt terminal
760 # emulator) and can be used in i3 like this:
761 set_from_resource $black i3wm.color0 #000000
762 ----------------------------------------------------------------------------
763
764 [[assign_workspace]]
765 === Automatically putting clients on specific workspaces
766
767 To automatically make a specific window show up on a specific workspace, you
768 can use an *assignment*. You can match windows by using any criteria,
769 see <<command_criteria>>. The difference between +assign+ and
770 +for_window <criteria> move to workspace+ is that the former will only be
771 executed when the application maps the window (mapping means actually displaying
772 it on the screen) but the latter will be executed whenever a window changes its
773 properties to something that matches the specified criteria.
774
775 Thus, it is recommended that you match on window classes (and instances, when
776 appropriate) instead of window titles whenever possible because some
777 applications first create their window, and then worry about setting the correct
778 title. Firefox with Vimperator comes to mind. The window starts up being named
779 Firefox, and only when Vimperator is loaded does the title change. As i3 will
780 get the title as soon as the application maps the window, you’d need to have to
781 match on 'Firefox' in this case.
782 Another known issue is with Spotify, which doesn't set the class hints when
783 mapping the window, meaning you'll have to use a +for_window+ rule to assign
784 Spotify to a specific workspace.
785 Finally, using +assign [tiling]+ and +assign [floating]+ is not supported.
786
787 You can also assign a window to show up on a specific output. You can use RandR
788 names such as +VGA1+ or names relative to the output with the currently focused
789 workspace such as +left+ and +down+.
790
791 Assignments are processed by i3 in the order in which they appear in the config
792 file. The first one which matches the window wins and later assignments are not
793 considered.
794
795 *Syntax*:
796 ------------------------------------------------------------
797 assign <criteria> [→] [workspace] [number] <workspace>
798 assign <criteria> [→] output left|right|up|down|primary|<output>
799 ------------------------------------------------------------
800
801 *Examples*:
802 ----------------------
803 # Assign URxvt terminals to workspace 2
804 assign [class="URxvt"] 2
805
806 # Same thing, but more precise (exact match instead of substring)
807 assign [class="^URxvt$"] 2
808
809 # Same thing, but with a beautiful arrow :)
810 assign [class="^URxvt$"] → 2
811
812 # Assignment to a named workspace
813 assign [class="^URxvt$"] → work
814
815 # Assign to the workspace with number 2, regardless of name
816 assign [class="^URxvt$"] → number 2
817
818 # You can also specify a number + name. If the workspace with number 2 exists, assign will skip the text part.
819 assign [class="^URxvt$"] → number "2: work"
820
821 # Start urxvt -name irssi
822 assign [class="^URxvt$" instance="^irssi$"] → 3
823
824 # Assign urxvt to the output right of the current one
825 assign [class="^URxvt$"] → output right
826
827 # Assign urxvt to the primary output
828 assign [class="^URxvt$"] → output primary
829 ----------------------
830
831 Note that you might not have a primary output configured yet. To do so, run:
832 -------------------------
833 xrandr --output <output> --primary
834 -------------------------
835
836 Also, the arrow is not required, it just looks good :-). If you decide to
837 use it, it has to be a UTF-8 encoded arrow, not `->` or something like that.
838
839 To get the class and instance, you can use +xprop+. After clicking on the
840 window, you will see the following output:
841
842 *xprop*:
843 -----------------------------------
844 WM_CLASS(STRING) = "irssi", "URxvt"
845 -----------------------------------
846
847 The first part of the WM_CLASS is the instance ("irssi" in this example), the
848 second part is the class ("URxvt" in this example).
849
850 Should you have any problems with assignments, make sure to check the i3
851 logfile first (see https://i3wm.org/docs/debugging.html). It includes more
852 details about the matching process and the window’s actual class, instance and
853 title when starting up.
854
855 Note that if you want to start an application just once on a specific
856 workspace, but you don’t want to assign all instances of it permanently, you
857 can make use of i3’s startup-notification support (see <<exec>>) in your config
858 file in the following way:
859
860 *Start iceweasel on workspace 3 (once)*:
861 -------------------------------------------------------------------------------
862 # Start iceweasel on workspace 3, then switch back to workspace 1
863 # (Being a command-line utility, i3-msg does not support startup notifications,
864 # hence the exec --no-startup-id.)
865 # (Starting iceweasel with i3’s exec command is important in order to make i3
866 # create a startup notification context, without which the iceweasel window(s)
867 # cannot be matched onto the workspace on which the command was started.)
868 exec --no-startup-id i3-msg 'workspace 3; exec iceweasel; workspace 1'
869 -------------------------------------------------------------------------------
870
871 === Automatically starting applications on i3 startup
872
873 By using the +exec+ keyword outside a keybinding, you can configure
874 which commands will be performed by i3 on initial startup. +exec+
875 commands will not run when restarting i3, if you need a command to run
876 also when restarting i3 you should use the +exec_always+
877 keyword. These commands will be run in order.
878
879 See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
880 and +,+ (comma): they chain commands together in i3, so you need to use quoted
881 strings (as shown in <<exec_quoting>>) if they appear in your command.
882
883 *Syntax*:
884 ---------------------------------------
885 exec [--no-startup-id] <command>
886 exec_always [--no-startup-id] <command>
887 ---------------------------------------
888
889 *Examples*:
890 --------------------------------
891 exec chromium
892 exec_always ~/my_script.sh
893
894 # Execute the terminal emulator urxvt, which is not yet startup-notification aware.
895 exec --no-startup-id urxvt
896 --------------------------------
897
898 The flag --no-startup-id is explained in <<exec>>.
899
900 [[workspace_screen]]
901 === Automatically putting workspaces on specific screens
902
903 If you assign clients to workspaces, it might be handy to put the
904 workspaces on specific screens. Also, the assignment of workspaces to screens
905 will determine which workspace i3 uses for a new screen when adding screens
906 or when starting (e.g., by default it will use 1 for the first screen, 2 for
907 the second screen and so on).
908
909 *Syntax*:
910 -------------------------------------
911 workspace <workspace> output <output1> [output2]…
912 -------------------------------------
913
914 The 'output' is the name of the RandR output you attach your screen to. On a
915 laptop, you might have VGA1 and LVDS1 as output names. You can see the
916 available outputs by running +xrandr --current+.
917
918 If your X server supports RandR 1.5 or newer, i3 will use RandR monitor objects
919 instead of output objects. Run +xrandr --listmonitors+ to see a list. Usually,
920 a monitor object contains exactly one output, and has the same name as the
921 output; but should that not be the case, you may specify the name of either the
922 monitor or the output in i3's configuration. For example, the Dell UP2414Q uses
923 two scalers internally, so its output names might be “DP1” and “DP2”, but the
924 monitor name is “Dell UP2414Q”.
925
926 (Note that even if you specify the name of an output which doesn't span the
927 entire monitor, i3 will still use the entire area of the containing monitor
928 rather than that of just the output's.)
929
930 You can specify multiple outputs. The first available will be used.
931
932 If you use named workspaces, they must be quoted:
933
934 *Examples*:
935 ---------------------------
936 workspace 1 output LVDS1
937 workspace 2 output primary
938 workspace 5 output VGA1 LVDS1
939 workspace "2: vim" output VGA1
940 ---------------------------
941
942 === Changing colors
943
944 You can change all colors which i3 uses to draw the window decorations.
945
946 *Syntax*:
947 --------------------------------------------------------------------
948 <colorclass> <border> <background> <text> <indicator> <child_border>
949 --------------------------------------------------------------------
950
951 Where colorclass can be one of:
952
953 client.focused::
954 A client which currently has the focus.
955 client.focused_inactive::
956 A client which is the focused one of its container, but it does not have
957 the focus at the moment.
958 client.unfocused::
959 A client which is not the focused one of its container.
960 client.urgent::
961 A client which has its urgency hint activated.
962 client.placeholder::
963 Background and text color are used to draw placeholder window contents
964 (when restoring layouts). Border and indicator are ignored.
965 client.background::
966 Background color which will be used to paint the background of the
967 client window on top of which the client will be rendered. Only clients
968 which do not cover the whole area of this window expose the color. Note
969 that this colorclass only takes a single color.
970
971 Colors are in HTML hex format (#rrggbb), see the following example:
972
973 *Examples (default colors)*:
974 ----------------------------------------------------------------------
975 # class border backgr. text indicator child_border
976 client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
977 client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
978 client.unfocused #333333 #222222 #888888 #292d2e #222222
979 client.urgent #2f343a #900000 #ffffff #900000 #900000
980 client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
981
982 client.background #ffffff
983 ----------------------------------------------------------------------
984
985 Note that for the window decorations, the color around the child window is the
986 "child_border", and "border" color is only the two thin lines around the
987 titlebar.
988
989 The indicator color is used for indicating where a new window will be opened.
990 For horizontal split containers, the right border will be painted in indicator
991 color, for vertical split containers, the bottom border. This only applies to
992 single windows within a split container, which are otherwise indistinguishable
993 from single windows outside of a split container.
994
995 === Interprocess communication
996
997 i3 uses Unix sockets to provide an IPC interface. This allows third-party
998 programs to get information from i3, such as the current workspaces
999 (to display a workspace bar), and to control i3.
1000
1001 The IPC socket is enabled by default and will be created in
1002 +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username, +%p+ is
1003 the PID of i3 and XXXXXX is a string of random characters from the portable
1004 filename character set (see mkdtemp(3)).
1005
1006 You can override the default path through the environment-variable +I3SOCK+ or
1007 by specifying the +ipc-socket+ directive. This is discouraged, though, since i3
1008 does the right thing by default. If you decide to change it, it is strongly
1009 recommended to set this to a location in your home directory so that no other
1010 user can create that directory.
1011
1012 *Examples*:
1013 ----------------------------
1014 ipc-socket ~/.i3/i3-ipc.sock
1015 ----------------------------
1016
1017 You can then use the +i3-msg+ application to perform any command listed in
1018 <<list_of_commands>>.
1019
1020 === Focus follows mouse
1021
1022 By default, window focus follows your mouse movements as the mouse crosses
1023 window borders. However, if you have a setup where your mouse usually is in your
1024 way (like a touchpad on your laptop which you do not want to disable
1025 completely), you might want to disable 'focus follows mouse' and control focus
1026 only by using your keyboard. The mouse will still be useful inside the
1027 currently active window (for example to click on links in your browser window).
1028
1029 *Syntax*:
1030 --------------------------
1031 focus_follows_mouse yes|no
1032 --------------------------
1033
1034 *Example*:
1035 ----------------------
1036 focus_follows_mouse no
1037 ----------------------
1038
1039 === Mouse warping
1040
1041 By default, when switching focus to a window on a different output (e.g.
1042 focusing a window on workspace 3 on output VGA-1, coming from workspace 2 on
1043 LVDS-1), the mouse cursor is warped to the center of that window.
1044
1045 With the +mouse_warping+ option, you can control when the mouse cursor should
1046 be warped. +none+ disables warping entirely, whereas +output+ is the default
1047 behavior described above.
1048
1049 *Syntax*:
1050 -------------------------
1051 mouse_warping output|none
1052 -------------------------
1053
1054 *Example*:
1055 ------------------
1056 mouse_warping none
1057 ------------------
1058
1059 === Popups during fullscreen mode
1060
1061 When you are in fullscreen mode, some applications still open popup windows
1062 (take Xpdf for example). This is because these applications may not be aware
1063 that they are in fullscreen mode (they do not check the corresponding hint).
1064 There are three things which are possible to do in this situation:
1065
1066 1. Display the popup if it belongs to the fullscreen application only. This is
1067 the default and should be reasonable behavior for most users.
1068 2. Just ignore the popup (don’t map it). This won’t interrupt you while you are
1069 in fullscreen. However, some apps might react badly to this (deadlock until
1070 you go out of fullscreen).
1071 3. Leave fullscreen mode.
1072
1073 *Syntax*:
1074 -----------------------------------------------------
1075 popup_during_fullscreen smart|ignore|leave_fullscreen
1076 -----------------------------------------------------
1077
1078 *Example*:
1079 ------------------------------
1080 popup_during_fullscreen smart
1081 ------------------------------
1082
1083 === Focus wrapping
1084
1085 By default, when in a container with several windows or child containers, the
1086 opposite window will be focused when trying to move the focus over the edge of
1087 a container (and there are no other containers in that direction) -- the focus
1088 wraps.
1089
1090 If desired, you can disable this behavior by setting the +focus_wrapping+
1091 configuration directive to the value +no+.
1092
1093 When enabled, focus wrapping does not occur by default if there is another
1094 window or container in the specified direction, and focus will instead be set
1095 on that window or container. This is the default behavior so you can navigate
1096 to all your windows without having to use +focus parent+.
1097
1098 If you want the focus to *always* wrap and you are aware of using +focus
1099 parent+ to switch to different containers, you can instead set +focus_wrapping+
1100 to the value +force+.
1101
1102 *Syntax*:
1103 ---------------------------
1104 focus_wrapping yes|no|force
1105
1106 # Legacy syntax, equivalent to "focus_wrapping force"
1107 force_focus_wrapping yes
1108 ---------------------------
1109
1110 *Examples*:
1111 -----------------
1112 # Disable focus wrapping
1113 focus_wrapping no
1114
1115 # Force focus wrapping
1116 focus_wrapping force
1117 -----------------
1118
1119 === Forcing Xinerama
1120
1121 As explained in-depth in <https://i3wm.org/docs/multi-monitor.html>, some X11
1122 video drivers (especially the nVidia binary driver) only provide support for
1123 Xinerama instead of RandR. In such a situation, i3 must be told to use the
1124 inferior Xinerama API explicitly and therefore don’t provide support for
1125 reconfiguring your screens on the fly (they are read only once on startup and
1126 that’s it).
1127
1128 For people who cannot modify their +~/.xsession+ to add the
1129 +--force-xinerama+ commandline parameter, a configuration option is provided:
1130
1131 *Syntax*:
1132 ---------------------
1133 force_xinerama yes|no
1134 ---------------------
1135
1136 *Example*:
1137 ------------------
1138 force_xinerama yes
1139 ------------------
1140
1141 Also note that your output names are not descriptive (like +HDMI1+) when using
1142 Xinerama, instead they are counted up, starting at 0: +xinerama-0+, +xinerama-1+, …
1143
1144 [[workspace_auto_back_and_forth]]
1145 === Automatic back-and-forth when switching to the current workspace
1146
1147 This configuration directive enables automatic +workspace back_and_forth+ (see
1148 <<back_and_forth>>) when switching to the workspace that is currently focused.
1149
1150 For instance: Assume you are on workspace "1: www" and switch to "2: IM" using
1151 mod+2 because somebody sent you a message. You don’t need to remember where you
1152 came from now, you can just press $mod+2 again to switch back to "1: www".
1153
1154 *Syntax*:
1155 ------------------------------------
1156 workspace_auto_back_and_forth yes|no
1157 ------------------------------------
1158
1159 *Example*:
1160 ---------------------------------
1161 workspace_auto_back_and_forth yes
1162 ---------------------------------
1163
1164 === Delaying urgency hint reset on workspace change
1165
1166 If an application on another workspace sets an urgency hint, switching to this
1167 workspace may lead to immediate focus of the application, which also means the
1168 window decoration color would be immediately reset to +client.focused+. This
1169 may make it unnecessarily hard to tell which window originally raised the
1170 event.
1171
1172 In order to prevent this, you can tell i3 to delay resetting the urgency state
1173 by a certain time using the +force_display_urgency_hint+ directive. Setting the
1174 value to 0 disables this feature.
1175
1176 The default is 500ms.
1177
1178 *Syntax*:
1179 ---------------------------------------
1180 force_display_urgency_hint <timeout> ms
1181 ---------------------------------------
1182
1183 *Example*:
1184 ---------------------------------
1185 force_display_urgency_hint 500 ms
1186 ---------------------------------
1187
1188 [[focus_on_window_activation]]
1189 === Focus on window activation
1190
1191 If a window is activated, e.g., via +google-chrome www.google.com+, it may request
1192 to take focus. Since this may not preferable, different reactions can be configured.
1193
1194 Note that this may not affect windows that are being opened. To prevent new windows
1195 from being focused, see <<no_focus>>.
1196
1197 *Syntax*:
1198 --------------------------------------------------
1199 focus_on_window_activation smart|urgent|focus|none
1200 --------------------------------------------------
1201
1202 The different modes will act as follows:
1203
1204 smart::
1205 This is the default behavior. If the window requesting focus is on an active
1206 workspace, it will receive the focus. Otherwise, the urgency hint will be set.
1207 urgent::
1208 The window will always be marked urgent, but the focus will not be stolen.
1209 focus::
1210 The window will always be focused and not be marked urgent.
1211 none::
1212 The window will neither be focused, nor be marked urgent.
1213
1214 [[show_marks]]
1215 === Drawing marks on window decoration
1216
1217 If activated, marks (see <<vim_like_marks>>) on windows are drawn in their window
1218 decoration. However, any mark starting with an underscore in its name (+_+) will
1219 not be drawn even if this option is activated.
1220
1221 The default for this option is +yes+.
1222
1223 *Syntax*:
1224 -----------------
1225 show_marks yes|no
1226 -----------------
1227
1228 *Example*:
1229 --------------
1230 show_marks yes
1231 --------------
1232
1233 [[line_continuation]]
1234 === Line continuation
1235
1236 Config files support line continuation, meaning when you end a line in a
1237 backslash character (`\`), the line-break will be ignored by the parser. This
1238 feature can be used to create more readable configuration files.
1239 Commented lines are not continued.
1240
1241 *Examples*:
1242 -------------------
1243 bindsym Mod1+f \
1244 fullscreen toggle
1245
1246 # this line is not continued \
1247 bindsym Mod1+F fullscreen toggle
1248 -------------------
1249
1250 == Configuring i3bar
1251
1252 The bar at the bottom of your monitor is drawn by a separate process called
1253 i3bar. Having this part of "the i3 user interface" in a separate process has
1254 several advantages:
1255
1256 1. It is a modular approach. If you don’t need a workspace bar at all, or if
1257 you prefer a different one (dzen2, xmobar, maybe even gnome-panel?), you can
1258 just remove the i3bar configuration and start your favorite bar instead.
1259 2. It follows the UNIX philosophy of "Make each program do one thing well".
1260 While i3 manages your windows well, i3bar is good at displaying a bar on
1261 each monitor (unless you configure it otherwise).
1262 3. It leads to two separate, clean codebases. If you want to understand i3, you
1263 don’t need to bother with the details of i3bar and vice versa.
1264
1265 That said, i3bar is configured in the same configuration file as i3. This is
1266 because it is tightly coupled with i3 (in contrary to i3lock or i3status which
1267 are useful for people using other window managers). Therefore, it makes no
1268 sense to use a different configuration place when we already have a good
1269 configuration infrastructure in place.
1270
1271 Configuring your workspace bar starts with opening a +bar+ block. You can have
1272 multiple bar blocks to use different settings for different outputs (monitors):
1273
1274 *Example*:
1275 ---------------------------
1276 bar {
1277 status_command i3status
1278 }
1279 ---------------------------
1280
1281 === i3bar command
1282
1283 By default i3 will just pass +i3bar+ and let your shell handle the execution,
1284 searching your +$PATH+ for a correct version.
1285 If you have a different +i3bar+ somewhere or the binary is not in your +$PATH+ you can
1286 tell i3 what to execute.
1287
1288 The specified command will be passed to +sh -c+, so you can use globbing and
1289 have to have correct quoting etc.
1290
1291 *Syntax*:
1292 -----------------------
1293 i3bar_command <command>
1294 -----------------------
1295
1296 *Example*:
1297 -------------------------------------------------
1298 bar {
1299 i3bar_command /home/user/bin/i3bar
1300 }
1301 -------------------------------------------------
1302
1303 [[status_command]]
1304 === Statusline command
1305
1306 i3bar can run a program and display every line of its +stdout+ output on the
1307 right hand side of the bar. This is useful to display system information like
1308 your current IP address, battery status or date/time.
1309
1310 The specified command will be passed to +sh -c+, so you can use globbing and
1311 have to have correct quoting etc. Note that for signal handling, depending on
1312 your shell (users of dash(1) are known to be affected), you have to use the
1313 shell’s exec command so that signals are passed to your program, not to the
1314 shell.
1315
1316 *Syntax*:
1317 ------------------------
1318 status_command <command>
1319 ------------------------
1320
1321 *Example*:
1322 -------------------------------------------------
1323 bar {
1324 status_command i3status --config ~/.i3status.conf
1325
1326 # For dash(1) users who want signal handling to work:
1327 status_command exec ~/.bin/my_status_command
1328 }
1329 -------------------------------------------------
1330
1331 === Display mode
1332
1333 You can either have i3bar be visible permanently at one edge of the screen
1334 (+dock+ mode) or make it show up when you press your modifier key (+hide+ mode).
1335 It is also possible to force i3bar to always stay hidden (+invisible+
1336 mode). The modifier key can be configured using the +modifier+ option.
1337
1338 The mode option can be changed during runtime through the +bar mode+ command.
1339 On reload the mode will be reverted to its configured value.
1340
1341 The hide mode maximizes screen space that can be used for actual windows. Also,
1342 i3bar sends the +SIGSTOP+ and +SIGCONT+ signals to the statusline process to
1343 save battery power.
1344
1345 Invisible mode allows to permanently maximize screen space, as the bar is never
1346 shown. Thus, you can configure i3bar to not disturb you by popping up because
1347 of an urgency hint or because the modifier key is pressed.
1348
1349 In order to control whether i3bar is hidden or shown in hide mode, there exists
1350 the hidden_state option, which has no effect in dock mode or invisible mode. It
1351 indicates the current hidden_state of the bar: (1) The bar acts like in normal
1352 hide mode, it is hidden and is only unhidden in case of urgency hints or by
1353 pressing the modifier key (+hide+ state), or (2) it is drawn on top of the
1354 currently visible workspace (+show+ state).
1355
1356 Like the mode, the hidden_state can also be controlled through i3, this can be
1357 done by using the +bar hidden_state+ command.
1358
1359 The default mode is dock mode; in hide mode, the default modifier is Mod4 (usually
1360 the windows key). The default value for the hidden_state is hide.
1361
1362 *Syntax*:
1363 -------------------------
1364 mode dock|hide|invisible
1365 hidden_state hide|show
1366 modifier <Modifier>|none
1367 ------------------------
1368
1369 *Example*:
1370 ----------------
1371 bar {
1372 mode hide
1373 hidden_state hide
1374 modifier Mod1
1375 }
1376 ----------------
1377
1378 Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). You can
1379 also use "none" if you don't want any modifier to trigger this behavior.
1380
1381 === Mouse button commands
1382
1383 Specifies a command to run when a button was pressed on i3bar to override the
1384 default behavior. This is useful, e.g., for disabling the scroll wheel action
1385 or running scripts that implement custom behavior for these buttons.
1386
1387 A button is always named +button<n>+, where 1 to 5 are default buttons as follows and higher
1388 numbers can be special buttons on devices offering more buttons:
1389
1390 button1::
1391 Left mouse button.
1392 button2::
1393 Middle mouse button.
1394 button3::
1395 Right mouse button.
1396 button4::
1397 Scroll wheel up.
1398 button5::
1399 Scroll wheel down.
1400
1401 Please note that the old +wheel_up_cmd+ and +wheel_down_cmd+ commands are deprecated
1402 and will be removed in a future release. We strongly recommend using the more general
1403 +bindsym+ with +button4+ and +button5+ instead.
1404
1405 *Syntax*:
1406 ----------------------------
1407 bindsym [--release] button<n> <command>
1408 ----------------------------
1409
1410 *Example*:
1411 ---------------------------------------------------------
1412 bar {
1413 # disable clicking on workspace buttons
1414 bindsym button1 nop
1415 # Take a screenshot by right clicking on the bar
1416 bindsym --release button3 exec --no-startup-id import /tmp/latest-screenshot.png
1417 # execute custom script when scrolling downwards
1418 bindsym button5 exec ~/.i3/scripts/custom_wheel_down
1419 }
1420 ---------------------------------------------------------
1421
1422 === Bar ID
1423
1424 Specifies the bar ID for the configured bar instance. If this option is missing,
1425 the ID is set to 'bar-x', where x corresponds to the position of the embedding
1426 bar block in the config file ('bar-0', 'bar-1', ...).
1427
1428 *Syntax*:
1429 ---------------------
1430 id <bar_id>
1431 ---------------------
1432
1433 *Example*:
1434 ---------------------
1435 bar {
1436 id bar-1
1437 }
1438 ---------------------
1439
1440 [[i3bar_position]]
1441 === Position
1442
1443 This option determines in which edge of the screen i3bar should show up.
1444
1445 The default is bottom.
1446
1447 *Syntax*:
1448 -------------------
1449 position top|bottom
1450 -------------------
1451
1452 *Example*:
1453 ---------------------
1454 bar {
1455 position top
1456 }
1457 ---------------------
1458
1459 === Output(s)
1460
1461 You can restrict i3bar to one or more outputs (monitors). The default is to
1462 handle all outputs. Restricting the outputs is useful for using different
1463 options for different outputs by using multiple 'bar' blocks.
1464
1465 To make a particular i3bar instance handle multiple outputs, specify the output
1466 directive multiple times.
1467
1468 *Syntax*:
1469 ---------------
1470 output primary|<output>
1471 ---------------
1472
1473 *Example*:
1474 -------------------------------
1475 # big monitor: everything
1476 bar {
1477 # The display is connected either via HDMI or via DisplayPort
1478 output HDMI2
1479 output DP2
1480 status_command i3status
1481 }
1482
1483 # laptop monitor: bright colors and i3status with less modules.
1484 bar {
1485 output LVDS1
1486 status_command i3status --config ~/.i3status-small.conf
1487 colors {
1488 background #000000
1489 statusline #ffffff
1490 }
1491 }
1492
1493 # show bar on the primary monitor and on HDMI2
1494 bar {
1495 output primary
1496 output HDMI2
1497 status_command i3status
1498 }
1499
1500 -------------------------------
1501 Note that you might not have a primary output configured yet. To do so, run:
1502 -------------------------
1503 xrandr --output <output> --primary
1504 -------------------------
1505
1506 === Tray output
1507
1508 i3bar by default provides a system tray area where programs such as
1509 NetworkManager, VLC, Pidgin, etc. can place little icons.
1510
1511 You can configure on which output (monitor) the icons should be displayed or
1512 you can turn off the functionality entirely.
1513
1514 You can use multiple +tray_output+ directives in your config to specify a list
1515 of outputs on which you want the tray to appear. The first available output in
1516 that list as defined by the order of the directives will be used for the tray
1517 output.
1518
1519 *Syntax*:
1520 ---------------------------------
1521 tray_output none|primary|<output>
1522 ---------------------------------
1523
1524 *Example*:
1525 -------------------------
1526 # disable system tray
1527 bar {
1528 tray_output none
1529 }
1530
1531 # show tray icons on the primary monitor
1532 bar {
1533 tray_output primary
1534 }
1535
1536 # show tray icons on the big monitor
1537 bar {
1538 tray_output HDMI2
1539 }
1540 -------------------------
1541
1542 Note that you might not have a primary output configured yet. To do so, run:
1543 -------------------------
1544 xrandr --output <output> --primary
1545 -------------------------
1546
1547 Note that when you use multiple bar configuration blocks, either specify
1548 `tray_output primary` in all of them or explicitly specify `tray_output none`
1549 in bars which should not display the tray, otherwise the different instances
1550 might race each other in trying to display tray icons.
1551
1552 === Tray padding
1553
1554 The tray is shown on the right-hand side of the bar. By default, a padding of 2
1555 pixels is used for the upper, lower and right-hand side of the tray area and
1556 between the individual icons.
1557
1558 *Syntax*:
1559 -------------------------
1560 tray_padding <px> [px]
1561 -------------------------
1562
1563 *Example*:
1564 -------------------------
1565 # Obey Fitts's law
1566 tray_padding 0
1567 -------------------------
1568
1569 === Font
1570
1571 Specifies the font to be used in the bar. See <<fonts>>.
1572
1573 *Syntax*:
1574 ---------------------
1575 font <font>
1576 ---------------------
1577
1578 *Example*:
1579 --------------------------------------------------------------
1580 bar {
1581 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
1582 font pango:DejaVu Sans Mono 10
1583 }
1584 --------------------------------------------------------------
1585
1586 === Custom separator symbol
1587
1588 Specifies a custom symbol to be used for the separator as opposed to the vertical,
1589 one pixel thick separator.
1590
1591 *Syntax*:
1592 -------------------------
1593 separator_symbol <symbol>
1594 -------------------------
1595
1596 *Example*:
1597 ------------------------
1598 bar {
1599 separator_symbol ":|:"
1600 }
1601 ------------------------
1602
1603 === Workspace buttons
1604
1605 Specifies whether workspace buttons should be shown or not. This is useful if
1606 you want to display a statusline-only bar containing additional information.
1607
1608 The default is to show workspace buttons.
1609
1610 *Syntax*:
1611 ------------------------
1612 workspace_buttons yes|no
1613 ------------------------
1614
1615 *Example*:
1616 ------------------------
1617 bar {
1618 workspace_buttons no
1619 }
1620 ------------------------
1621
1622 === Strip workspace numbers/name
1623
1624 Specifies whether workspace numbers should be displayed within the workspace
1625 buttons. This is useful if you want to have a named workspace that stays in
1626 order on the bar according to its number without displaying the number prefix.
1627
1628 When +strip_workspace_numbers+ is set to +yes+, any workspace that has a name of
1629 the form "[n]:[NAME]" will display only the name. You could use this, for
1630 instance, to display Roman numerals rather than digits by naming your
1631 workspaces to "1:I", "2:II", "3:III", "4:IV", ...
1632
1633 When +strip_workspace_name+ is set to +yes+, any workspace that has a name of
1634 the form "[n]:[NAME]" will display only the number.
1635
1636 The default is to display the full name within the workspace button.
1637
1638 *Syntax*:
1639 ------------------------------
1640 strip_workspace_numbers yes|no
1641 strip_workspace_name yes|no
1642 ------------------------------
1643
1644 *Example*:
1645 ----------------------------
1646 bar {
1647 strip_workspace_numbers yes
1648 }
1649 ----------------------------
1650
1651 === Binding Mode indicator
1652
1653 Specifies whether the current binding mode indicator should be shown or not.
1654 This is useful if you want to hide the workspace buttons but still be able
1655 to see the current binding mode indicator. See <<binding_modes>> to learn what
1656 modes are and how to use them.
1657
1658 The default is to show the mode indicator.
1659
1660 *Syntax*:
1661 -----------------------------
1662 binding_mode_indicator yes|no
1663 -----------------------------
1664
1665 *Example*:
1666 -----------------------------
1667 bar {
1668 binding_mode_indicator no
1669 }
1670 -----------------------------
1671
1672 === Colors
1673
1674 As with i3, colors are in HTML hex format (#rrggbb). The following colors can
1675 be configured at the moment:
1676
1677 background::
1678 Background color of the bar.
1679 statusline::
1680 Text color to be used for the statusline.
1681 separator::
1682 Text color to be used for the separator.
1683 focused_background::
1684 Background color of the bar on the currently focused monitor output. If
1685 not used, the color will be taken from +background+.
1686 focused_statusline::
1687 Text color to be used for the statusline on the currently focused
1688 monitor output. If not used, the color will be taken from +statusline+.
1689 focused_separator::
1690 Text color to be used for the separator on the currently focused
1691 monitor output. If not used, the color will be taken from +separator+.
1692 focused_workspace::
1693 Border, background and text color for a workspace button when the workspace
1694 has focus.
1695 active_workspace::
1696 Border, background and text color for a workspace button when the workspace
1697 is active (visible) on some output, but the focus is on another one.
1698 You can only tell this apart from the focused workspace when you are
1699 using multiple monitors.
1700 inactive_workspace::
1701 Border, background and text color for a workspace button when the workspace
1702 does not have focus and is not active (visible) on any output. This
1703 will be the case for most workspaces.
1704 urgent_workspace::
1705 Border, background and text color for a workspace button when the workspace
1706 contains a window with the urgency hint set.
1707 binding_mode::
1708 Border, background and text color for the binding mode indicator. If not used,
1709 the colors will be taken from +urgent_workspace+.
1710
1711 *Syntax*:
1712 ----------------------------------------
1713 colors {
1714 background <color>
1715 statusline <color>
1716 separator <color>
1717
1718 <colorclass> <border> <background> <text>
1719 }
1720 ----------------------------------------
1721
1722 *Example (default colors)*:
1723 --------------------------------------
1724 bar {
1725 colors {
1726 background #000000
1727 statusline #ffffff
1728 separator #666666
1729
1730 focused_workspace #4c7899 #285577 #ffffff
1731 active_workspace #333333 #5f676a #ffffff
1732 inactive_workspace #333333 #222222 #888888
1733 urgent_workspace #2f343a #900000 #ffffff
1734 binding_mode #2f343a #900000 #ffffff
1735 }
1736 }
1737 --------------------------------------
1738
1739 [[list_of_commands]]
1740 == List of commands
1741
1742 Commands are what you bind to specific keypresses. You can also issue commands
1743 at runtime without pressing a key by using the IPC interface. An easy way to
1744 do this is to use the +i3-msg+ utility:
1745
1746 *Example*:
1747 --------------------------
1748 # execute this on your shell to make the current container borderless
1749 i3-msg border none
1750 --------------------------
1751
1752 [[command_chaining]]
1753
1754 Commands can be chained by using +;+ (a semicolon). So, to move a window to a
1755 specific workspace and immediately switch to that workspace, you can configure
1756 the following keybinding:
1757
1758 *Example*:
1759 --------------------------------------------------------
1760 bindsym $mod+x move container to workspace 3; workspace 3
1761 --------------------------------------------------------
1762
1763 [[command_criteria]]
1764
1765 Furthermore, you can change the scope of a command - that is, which containers
1766 should be affected by that command, by using various criteria. The criteria
1767 are specified before any command in a pair of square brackets and are separated
1768 by space.
1769
1770 When using multiple commands, separate them by using a +,+ (a comma) instead of
1771 a semicolon. Criteria apply only until the next semicolon, so if you use a
1772 semicolon to separate commands, only the first one will be executed for the
1773 matched window(s).
1774
1775 *Example*:
1776 ------------------------------------
1777 # if you want to kill all windows which have the class Firefox, use:
1778 bindsym $mod+x [class="Firefox"] kill
1779
1780 # same thing, but case-insensitive
1781 bindsym $mod+x [class="(?i)firefox"] kill
1782
1783 # kill only the About dialog from Firefox
1784 bindsym $mod+x [class="Firefox" window_role="About"] kill
1785
1786 # enable floating mode and move container to workspace 4
1787 for_window [class="^evil-app$"] floating enable, move container to workspace 4
1788
1789 # move all floating windows to the scratchpad
1790 bindsym $mod+x [floating] move scratchpad
1791 ------------------------------------
1792
1793 The criteria which are currently implemented are:
1794
1795 class::
1796 Compares the window class (the second part of WM_CLASS). Use the
1797 special value +\_\_focused__+ to match all windows having the same window
1798 class as the currently focused window.
1799 instance::
1800 Compares the window instance (the first part of WM_CLASS). Use the
1801 special value +\_\_focused__+ to match all windows having the same window
1802 instance as the currently focused window.
1803 window_role::
1804 Compares the window role (WM_WINDOW_ROLE). Use the special value
1805 +\_\_focused__+ to match all windows having the same window role as the
1806 currently focused window.
1807 window_type::
1808 Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
1809 +normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
1810 +popup_menu+, +tooltip+ and +notification+.
1811 id::
1812 Compares the X11 window ID, which you can get via +xwininfo+ for example.
1813 title::
1814 Compares the X11 window title (\_NET_WM_NAME or WM_NAME as fallback).
1815 Use the special value +\_\_focused__+ to match all windows having the
1816 same window title as the currently focused window.
1817 urgent::
1818 Compares the urgent state of the window. Can be "latest" or "oldest".
1819 Matches the latest or oldest urgent window, respectively.
1820 (The following aliases are also available: newest, last, recent, first)
1821 workspace::
1822 Compares the workspace name of the workspace the window belongs to. Use
1823 the special value +\_\_focused__+ to match all windows in the currently
1824 focused workspace.
1825 con_mark::
1826 Compares the marks set for this container, see <<vim_like_marks>>. A
1827 match is made if any of the container's marks matches the specified
1828 mark.
1829 con_id::
1830 Compares the i3-internal container ID, which you can get via the IPC
1831 interface. Handy for scripting. Use the special value +\_\_focused__+
1832 to match only the currently focused window.
1833 floating::
1834 Only matches floating windows. This criterion requires no value.
1835 tiling::
1836 Only matches tiling windows. This criterion requires no value.
1837
1838 The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are
1839 actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
1840 information on how to use them.
1841
1842 [[exec]]
1843 === Executing applications (exec)
1844
1845 What good is a window manager if you can’t actually start any applications?
1846 The exec command starts an application by passing the command you specify to a
1847 shell. This implies that you can use globbing (wildcards) and programs will be
1848 searched in your +$PATH+.
1849
1850 See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
1851 and +,+ (comma): they chain commands together in i3, so you need to use quoted
1852 strings (as shown in <<exec_quoting>>) if they appear in your command.
1853
1854 *Syntax*:
1855 --------------------------------
1856 exec [--no-startup-id] <command>
1857 --------------------------------
1858
1859 *Example*:
1860 ------------------------------
1861 # Start the GIMP
1862 bindsym $mod+g exec gimp
1863
1864 # Start the terminal emulator urxvt which is not yet startup-notification-aware
1865 bindsym $mod+Return exec --no-startup-id urxvt
1866 ------------------------------
1867
1868 The +--no-startup-id+ parameter disables startup-notification support for this
1869 particular exec command. With startup-notification, i3 can make sure that a
1870 window appears on the workspace on which you used the exec command. Also, it
1871 will change the X11 cursor to +watch+ (a clock) while the application is
1872 launching. So, if an application is not startup-notification aware (most GTK
1873 and Qt using applications seem to be, though), you will end up with a watch
1874 cursor for 60 seconds.
1875
1876 [[exec_quoting]]
1877 If the command to be executed contains a +;+ (semicolon) and/or a +,+ (comma),
1878 the entire command must be quoted. For example, to have a keybinding for the
1879 shell command +notify-send Hello, i3+, you would add an entry to your
1880 configuration file like this:
1881
1882 *Example*:
1883 ------------------------------
1884 # Execute a command with a comma in it
1885 bindsym $mod+p exec "notify-send Hello, i3"
1886 ------------------------------
1887
1888 If however a command with a comma and/or semicolon itself requires quotes, you
1889 must escape the internal quotation marks with double backslashes, like this:
1890
1891 *Example*:
1892 ------------------------------
1893 # Execute a command with a comma, semicolon and internal quotes
1894 bindsym $mod+p exec "notify-send \\"Hello, i3; from $USER\\""
1895 ------------------------------
1896
1897 === Splitting containers
1898
1899 The split command makes the current window a split container. Split containers
1900 can contain multiple windows. Depending on the layout of the split container,
1901 new windows get placed to the right of the current one (splith) or new windows
1902 get placed below the current one (splitv).
1903
1904 If you apply this command to a split container with the same orientation,
1905 nothing will happen. If you use a different orientation, the split container’s
1906 orientation will be changed (if it does not have more than one window).
1907 The +toggle+ option will toggle the orientation of the split container if it
1908 contains a single window. Otherwise it makes the current window a split
1909 container with opposite orientation compared to the parent container.
1910 Use +layout toggle split+ to change the layout of any split container from
1911 splitv to splith or vice-versa. You can also define a custom sequence of layouts
1912 to cycle through with +layout toggle+, see <<manipulating_layout>>.
1913
1914 *Syntax*:
1915 --------------------------------
1916 split vertical|horizontal|toggle
1917 --------------------------------
1918
1919 *Example*:
1920 -------------------------------
1921 bindsym $mod+v split vertical
1922 bindsym $mod+h split horizontal
1923 bindsym $mod+t split toggle
1924 -------------------------------
1925
1926 === Manipulating layout
1927
1928 Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+
1929 or +layout splith+ to change the current container layout to splith/splitv,
1930 stacking, tabbed layout, splitv or splith, respectively.
1931
1932 Specify up to four layouts after +layout toggle+ to cycle through them. Every
1933 time the command is executed, the layout specified after the currently active
1934 one will be applied. If the currently active layout is not in the list, the
1935 first layout in the list will be activated.
1936
1937 To make the current window (!) fullscreen, use +fullscreen enable+ (or
1938 +fullscreen enable global+ for the global mode), to leave either fullscreen
1939 mode use +fullscreen disable+, and to toggle between these two states use
1940 +fullscreen toggle+ (or +fullscreen toggle global+).
1941
1942 Likewise, to make the current window floating (or tiling again) use +floating
1943 enable+ respectively +floating disable+ (or +floating toggle+):
1944
1945 *Syntax*:
1946 --------------------------------------------
1947 layout default|tabbed|stacking|splitv|splith
1948 layout toggle [split|all]
1949 layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]…
1950 --------------------------------------------
1951
1952 *Examples*:
1953 --------------
1954 bindsym $mod+s layout stacking
1955 bindsym $mod+l layout toggle split
1956 bindsym $mod+w layout tabbed
1957
1958 # Toggle between stacking/tabbed/split:
1959 bindsym $mod+x layout toggle
1960
1961 # Toggle between stacking/tabbed/splith/splitv:
1962 bindsym $mod+x layout toggle all
1963
1964 # Toggle between stacking/tabbed/splith:
1965 bindsym $mod+x layout toggle stacking tabbed splith
1966
1967 # Toggle between splitv/tabbed
1968 bindsym $mod+x layout toggle splitv tabbed
1969
1970 # Toggle between last split layout/tabbed/stacking
1971 bindsym $mod+x layout toggle split tabbed stacking
1972
1973 # Toggle fullscreen
1974 bindsym $mod+f fullscreen toggle
1975
1976 # Toggle floating/tiling
1977 bindsym $mod+t floating toggle
1978 --------------
1979
1980 [[_focusing_moving_containers]]
1981 === Focusing containers
1982
1983 To change focus, you can use the +focus+ command. The following options are
1984 available:
1985
1986 <criteria>::
1987 Sets focus to the container that matches the specified criteria.
1988 See <<command_criteria>>.
1989 left|right|up|down::
1990 Sets focus to the nearest container in the given direction.
1991 parent::
1992 Sets focus to the parent container of the current container.
1993 child::
1994 The opposite of +focus parent+, sets the focus to the last focused
1995 child container.
1996 floating::
1997 Sets focus to the last focused floating container.
1998 tiling::
1999 Sets focus to the last focused tiling container.
2000 mode_toggle::
2001 Toggles between floating/tiling containers.
2002 output::
2003 Followed by a direction or an output name, this will focus the
2004 corresponding output.
2005
2006 *Syntax*:
2007 ----------------------------------------------
2008 <criteria> focus
2009 focus left|right|down|up
2010 focus parent|child|floating|tiling|mode_toggle
2011 focus output left|right|up|down|primary|<output>
2012 ----------------------------------------------
2013
2014 *Examples*:
2015 -------------------------------------------------
2016 # Focus firefox
2017 bindsym $mod+F1 [class="Firefox"] focus
2018
2019 # Focus container on the left, bottom, top, right
2020 bindsym $mod+j focus left
2021 bindsym $mod+k focus down
2022 bindsym $mod+l focus up
2023 bindsym $mod+semicolon focus right
2024
2025 # Focus parent container
2026 bindsym $mod+u focus parent
2027
2028 # Focus last floating/tiling container
2029 bindsym $mod+g focus mode_toggle
2030
2031 # Focus the output right to the current one
2032 bindsym $mod+x focus output right
2033
2034 # Focus the big output
2035 bindsym $mod+x focus output HDMI-2
2036
2037 # Focus the primary output
2038 bindsym $mod+x focus output primary
2039 -------------------------------------------------
2040
2041 Note that you might not have a primary output configured yet. To do so, run:
2042 -------------------------
2043 xrandr --output <output> --primary
2044 -------------------------
2045
2046 === Moving containers
2047
2048 Use the +move+ command to move a container.
2049
2050 *Syntax*:
2051 -----------------------------------------------------
2052 # Moves the container into the given direction.
2053 # The optional pixel argument specifies how far the
2054 # container should be moved if it is floating and
2055 # defaults to 10 pixels.
2056 move <left|right|down|up> [<px> px]
2057
2058 # Moves the container to the specified pos_x and pos_y
2059 # coordinates on the screen.
2060 move position <pos_x> [px] <pos_y> [px]
2061
2062 # Moves the container to the center of the screen.
2063 # If 'absolute' is used, it is moved to the center of
2064 # all outputs.
2065 move [absolute] position center
2066
2067 # Moves the container to the current position of the
2068 # mouse cursor. Only affects floating containers.
2069 move position mouse
2070 -----------------------------------------------------
2071
2072 *Examples*:
2073 -------------------------------------------------------
2074 # Move container to the left, bottom, top, right
2075 bindsym $mod+j move left
2076 bindsym $mod+k move down
2077 bindsym $mod+l move up
2078 bindsym $mod+semicolon move right
2079
2080 # Move container, but make floating containers
2081 # move more than the default
2082 bindsym $mod+j move left 20 px
2083
2084 # Move floating container to the center of all outputs
2085 bindsym $mod+c move absolute position center
2086
2087 # Move container to the current position of the cursor
2088 bindsym $mod+m move position mouse
2089 -------------------------------------------------------
2090
2091 === Swapping containers
2092
2093 Two containers can be swapped (i.e., move to each other's position) by using
2094 the +swap+ command. They will assume the position and geometry of the container
2095 they are swapped with.
2096
2097 The first container to participate in the swapping can be selected through the
2098 normal command criteria process with the focused window being the usual
2099 fallback if no criteria are specified. The second container can be selected
2100 using one of the following methods:
2101
2102 +id+:: The X11 window ID of a client window.
2103 +con_id+:: The i3 container ID of a container.
2104 +mark+:: A container with the specified mark, see <<vim_like_marks>>.
2105
2106 Note that swapping does not work with all containers. Most notably, swapping
2107 floating containers or containers that have a parent-child relationship to one
2108 another does not work.
2109
2110 *Syntax*:
2111 ----------------------------------------
2112 swap container with id|con_id|mark <arg>
2113 ----------------------------------------
2114
2115 *Examples*:
2116 -----------------------------------------------------------------
2117 # Swaps the focused container with the container marked »swapee«.
2118 swap container with mark swapee
2119
2120 # Swaps container marked »A« and »B«
2121 [con_mark="^A$"] swap container with mark B
2122 -----------------------------------------------------------------
2123
2124 === Sticky floating windows
2125
2126 If you want a window to stick to the glass, i.e., have it stay on screen even
2127 if you switch to another workspace, you can use the +sticky+ command. For
2128 example, this can be useful for notepads, a media player or a video chat
2129 window.
2130
2131 Note that while any window can be made sticky through this command, it will
2132 only take effect if the window is floating.
2133
2134 *Syntax*:
2135 ----------------------------
2136 sticky enable|disable|toggle
2137 ----------------------------
2138
2139 *Examples*:
2140 ------------------------------------------------------
2141 # make a terminal sticky that was started as a notepad
2142 for_window [instance=notepad] sticky enable
2143 ------------------------------------------------------
2144
2145 === Changing (named) workspaces/moving to workspaces
2146
2147 To change to a specific workspace, use the +workspace+ command, followed by the
2148 number or name of the workspace. Pass the optional flag
2149 +--no-auto-back-and-forth+ to disable <<workspace_auto_back_and_forth>> for this
2150 specific call only.
2151
2152 To move containers to specific workspaces, use +move container to workspace+.
2153
2154 You can also switch to the next and previous workspace with the commands
2155 +workspace next+ and +workspace prev+, which is handy, for example, if you have
2156 workspace 1, 3, 4 and 9 and you want to cycle through them with a single key
2157 combination. To restrict those to the current output, use +workspace
2158 next_on_output+ and +workspace prev_on_output+. Similarly, you can use +move
2159 container to workspace next+, +move container to workspace prev+ to move a
2160 container to the next/previous workspace and +move container to workspace current+
2161 (the last one makes sense only when used with criteria).
2162
2163 +workspace next+ cycles through either numbered or named workspaces. But when it
2164 reaches the last numbered/named workspace, it looks for named workspaces after
2165 exhausting numbered ones and looks for numbered ones after exhausting named ones.
2166
2167 See <<move_to_outputs>> for how to move a container/workspace to a different
2168 RandR output.
2169
2170 Workspace names are parsed as
2171 https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
2172 by i3bar.
2173
2174 [[back_and_forth]]
2175 To switch back to the previously focused workspace, use +workspace
2176 back_and_forth+; likewise, you can move containers to the previously focused
2177 workspace using +move container to workspace back_and_forth+.
2178
2179 *Syntax*:
2180 --------------------------------------------------------------------------------
2181 workspace next|prev|next_on_output|prev_on_output
2182 workspace back_and_forth
2183 workspace [--no-auto-back-and-forth] <name>
2184 workspace [--no-auto-back-and-forth] number <name>
2185
2186 move [--no-auto-back-and-forth] [window|container] [to] workspace <name>
2187 move [--no-auto-back-and-forth] [window|container] [to] workspace number <name>
2188 move [window|container] [to] workspace prev|next|current
2189 --------------------------------------------------------------------------------
2190
2191 *Examples*:
2192 -------------------------
2193 bindsym $mod+1 workspace 1
2194 bindsym $mod+2 workspace 2
2195 bindsym $mod+3 workspace 3:<span foreground="red">vim</span>
2196 ...
2197
2198 bindsym $mod+Shift+1 move container to workspace 1
2199 bindsym $mod+Shift+2 move container to workspace 2
2200 ...
2201
2202 # switch between the current and the previously focused one
2203 bindsym $mod+b workspace back_and_forth
2204 bindsym $mod+Shift+b move container to workspace back_and_forth
2205
2206 # move the whole workspace to the next output
2207 bindsym $mod+x move workspace to output right
2208
2209 # move firefox to current workspace
2210 bindsym $mod+F1 [class="Firefox"] move workspace current
2211 -------------------------
2212
2213 ==== Named workspaces
2214
2215 Workspaces are identified by their name. So, instead of using numbers in the
2216 workspace command, you can use an arbitrary name:
2217
2218 *Example*:
2219 -------------------------
2220 bindsym $mod+1 workspace mail
2221 ...
2222 -------------------------
2223
2224 If you want the workspace to have a number *and* a name, just prefix the
2225 number, like this:
2226
2227 *Example*:
2228 -------------------------
2229 bindsym $mod+1 workspace 1: mail
2230 bindsym $mod+2 workspace 2: www
2231 ...
2232 -------------------------
2233
2234 Note that the workspace will really be named "1: mail". i3 treats workspace
2235 names beginning with a number in a slightly special way. Normally, named
2236 workspaces are ordered the way they appeared. When they start with a number, i3
2237 will order them numerically. Also, you will be able to use +workspace number 1+
2238 to switch to the workspace which begins with number 1, regardless of which name
2239 it has. This is useful in case you are changing the workspace’s name
2240 dynamically. To combine both commands you can use +workspace number 1: mail+ to
2241 specify a default name if there's currently no workspace starting with a "1".
2242
2243 ==== Renaming workspaces
2244
2245 You can rename workspaces. This might be useful to start with the default
2246 numbered workspaces, do your work, and rename the workspaces afterwards to
2247 reflect what’s actually on them. You can also omit the old name to rename
2248 the currently focused workspace. This is handy if you want to use the
2249 rename command with +i3-input+.
2250
2251 *Syntax*:
2252 ----------------------------------------------------
2253 rename workspace <old_name> to <new_name>
2254 rename workspace to <new_name>
2255 ----------------------------------------------------
2256
2257 *Examples*:
2258 --------------------------------------------------------------------------
2259 i3-msg 'rename workspace 5 to 6'
2260 i3-msg 'rename workspace 1 to "1: www"'
2261 i3-msg 'rename workspace "1: www" to "10: www"'
2262 i3-msg 'rename workspace to "2: mail"'
2263 bindsym $mod+r exec i3-input -F 'rename workspace to "%s"' -P 'New name: '
2264 --------------------------------------------------------------------------
2265
2266 If you want to rename workspaces on demand while keeping the navigation stable,
2267 you can use a setup like this:
2268
2269 *Example*:
2270 -------------------------
2271 bindsym $mod+1 workspace number "1: www"
2272 bindsym $mod+2 workspace number "2: mail"
2273 ...
2274 -------------------------
2275
2276 If a workspace does not exist, the command +workspace number "1: mail"+ will
2277 create workspace "1: mail".
2278
2279 If a workspace with number 1 does already exist, the command will switch to this
2280 workspace and ignore the text part. So even when the workspace has been renamed
2281 to "1: web", the above command will still switch to it.
2282
2283 === Moving workspaces to a different screen
2284
2285 See <<move_to_outputs>> for how to move a container/workspace to a different
2286 RandR output.
2287
2288 [[move_to_outputs]]
2289 === [[_moving_containers_workspaces_to_randr_outputs]]Moving containers/workspaces to RandR outputs
2290
2291 To move a container to another RandR output (addressed by names like +LVDS1+ or
2292 +VGA1+) or to a RandR output identified by a specific direction (like +left+,
2293 +right+, +up+ or +down+), there are two commands:
2294
2295 *Syntax*:
2296 ------------------------------------------------------------
2297 move container to output left|right|down|up|current|primary|<output>
2298 move workspace to output left|right|down|up|current|primary|<output>
2299 ------------------------------------------------------------
2300
2301 *Examples*:
2302 --------------------------------------------------------
2303 # Move the current workspace to the next output
2304 # (effectively toggles when you only have two outputs)
2305 bindsym $mod+x move workspace to output right
2306
2307 # Put this window on the presentation output.
2308 bindsym $mod+x move container to output VGA1
2309
2310 # Put this window on the primary output.
2311 bindsym $mod+x move container to output primary
2312 --------------------------------------------------------
2313
2314 Note that you might not have a primary output configured yet. To do so, run:
2315 -------------------------
2316 xrandr --output <output> --primary
2317 -------------------------
2318
2319 === Moving containers/windows to marks
2320
2321 To move a container to another container with a specific mark (see <<vim_like_marks>>),
2322 you can use the following command.
2323
2324 The window will be moved right after the marked container in the tree, i.e., it ends up
2325 in the same position as if you had opened a new window when the marked container was
2326 focused. If the mark is on a split container, the window will appear as a new child
2327 after the currently focused child within that container.
2328
2329 *Syntax*:
2330 ------------------------------------
2331 move window|container to mark <mark>
2332 ------------------------------------
2333
2334 *Example*:
2335 --------------------------------------------------------
2336 for_window [instance="tabme"] move window to mark target
2337 --------------------------------------------------------
2338
2339 [[resizingconfig]]
2340 === Resizing containers/windows
2341
2342 If you want to resize containers/windows using your keyboard, you can use the
2343 +resize+ command:
2344
2345 *Syntax*:
2346 -------------------------------------------------------
2347 resize grow|shrink <direction> [<px> px [or <ppt> ppt]]
2348 resize set [width] <width> [px | ppt]
2349 resize set height <height> [px | ppt]
2350 resize set [width] <width> [px | ppt] [height] <height> [px | ppt]
2351 -------------------------------------------------------
2352
2353 Direction can either be one of +up+, +down+, +left+ or +right+. Or you can be
2354 less specific and use +width+ or +height+, in which case i3 will take/give space
2355 from all the other containers. The optional pixel argument specifies by how many
2356 pixels a container should be grown or shrunk (the default is 10 pixels). The
2357 optional ppt argument means "percentage points", and if specified it indicates
2358 that a *tiling container* should be grown or shrunk by that many points, instead
2359 of by the +px+ value.
2360
2361 Note about +resize set+: a value of 0 for <width> or <height> means "do not
2362 resize in this direction".
2363
2364 It is recommended to define bindings for resizing in a dedicated binding mode.
2365 See <<binding_modes>> and the example in the i3
2366 https://github.com/i3/i3/blob/next/etc/config.keycodes[default config] for more
2367 context.
2368
2369 *Example*:
2370 ------------------------------------------------
2371 for_window [class="urxvt"] resize set 640 480
2372 ------------------------------------------------
2373
2374 === Jumping to specific windows
2375
2376 Often when in a multi-monitor environment, you want to quickly jump to a
2377 specific window. For example, while working on workspace 3 you may want to
2378 jump to your mail client to email your boss that you’ve achieved some
2379 important goal. Instead of figuring out how to navigate to your mail client,
2380 it would be more convenient to have a shortcut. You can use the +focus+ command
2381 with criteria for that.
2382
2383 *Syntax*:
2384 ----------------------------------------------------
2385 [class="class"] focus
2386 [title="title"] focus
2387 ----------------------------------------------------
2388
2389 *Examples*:
2390 ------------------------------------------------
2391 # Get me to the next open VIM instance
2392 bindsym $mod+a [class="urxvt" title="VIM"] focus
2393 ------------------------------------------------
2394
2395 [[vim_like_marks]]
2396 === VIM-like marks (mark/goto)
2397
2398 This feature is like the jump feature: It allows you to directly jump to a
2399 specific window (this means switching to the appropriate workspace and setting
2400 focus to the windows). However, you can directly mark a specific window with
2401 an arbitrary label and use it afterwards. You can unmark the label in the same
2402 way, using the unmark command. If you don't specify a label, unmark removes all
2403 marks. You do not need to ensure that your windows have unique classes or
2404 titles, and you do not need to change your configuration file.
2405
2406 As the command needs to include the label with which you want to mark the
2407 window, you cannot simply bind it to a key. +i3-input+ is a tool created
2408 for this purpose: It lets you input a command and sends the command to i3. It
2409 can also prefix this command and display a custom prompt for the input dialog.
2410
2411 The additional +--toggle+ option will remove the mark if the window already has
2412 this mark or add it otherwise. Note that you may need to use this in
2413 combination with +--add+ (see below) as any other marks will otherwise be
2414 removed.
2415
2416 By default, a window can only have one mark. You can use the +--add+ flag to
2417 put more than one mark on a window.
2418
2419 Refer to <<show_marks>> if you don't want marks to be shown in the window decoration.
2420
2421 *Syntax*:
2422 ----------------------------------------------
2423 mark [--add|--replace] [--toggle] <identifier>
2424 [con_mark="identifier"] focus
2425 unmark <identifier>
2426 ----------------------------------------------
2427
2428 *Example (in a terminal)*:
2429 ---------------------------------------------------------
2430 # marks the focused container
2431 mark irssi
2432
2433 # focus the container with the mark "irssi"
2434 '[con_mark="irssi"] focus'
2435
2436 # remove the mark "irssi" from whichever container has it
2437 unmark irssi
2438
2439 # remove all marks on all firefox windows
2440 [class="(?i)firefox"] unmark
2441 ---------------------------------------------------------
2442
2443 ///////////////////////////////////////////////////////////////////
2444 TODO: make i3-input replace %s
2445 *Examples*:
2446 ---------------------------------------
2447 # Read 1 character and mark the current window with this character
2448 bindsym $mod+m exec i3-input -F 'mark %s' -l 1 -P 'Mark: '
2449
2450 # Read 1 character and go to the window with the character
2451 bindsym $mod+g exec i3-input -F '[con_mark="%s"] focus' -l 1 -P 'Goto: '
2452 ---------------------------------------
2453
2454 Alternatively, if you do not want to mess with +i3-input+, you could create
2455 separate bindings for a specific set of labels and then only use those labels.
2456 ///////////////////////////////////////////////////////////////////
2457
2458 [[pango_markup]]
2459 === Window title format
2460
2461 By default, i3 will simply print the X11 window title. Using +title_format+,
2462 this can be customized by setting the format to the desired output. This
2463 directive supports
2464 https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
2465 and the following placeholders which will be replaced:
2466
2467 +%title+::
2468 For normal windows, this is the X11 window title (_NET_WM_NAME or WM_NAME
2469 as fallback). When used on containers without a window (e.g., a split
2470 container inside a tabbed/stacked layout), this will be the tree
2471 representation of the container (e.g., "H[xterm xterm]").
2472 +%class+::
2473 The X11 window class (second part of WM_CLASS). This corresponds to the
2474 +class+ criterion, see <<command_criteria>>.
2475 +%instance+::
2476 The X11 window instance (first part of WM_CLASS). This corresponds to the
2477 +instance+ criterion, see <<command_criteria>>.
2478
2479 Using the <<for_window>> directive, you can set the title format for any window
2480 based on <<command_criteria>>.
2481
2482 *Syntax*:
2483 ---------------------
2484 title_format <format>
2485 ---------------------
2486
2487 *Examples*:
2488 -------------------------------------------------------------------------------------
2489 # give the focused window a prefix
2490 bindsym $mod+p title_format "Important | %title"
2491
2492 # print all window titles bold
2493 for_window [class=".*"] title_format "<b>%title</b>"
2494
2495 # print window titles of firefox windows red
2496 for_window [class="(?i)firefox"] title_format "<span foreground='red'>%title</span>"
2497 -------------------------------------------------------------------------------------
2498
2499 === Changing border style
2500
2501 To change the border of the current client, you can use +border normal+ to use the normal
2502 border (including window title), +border pixel 1+ to use a 1-pixel border (no window title)
2503 and +border none+ to make the client borderless.
2504
2505 There is also +border toggle+ which will toggle the different border styles. The
2506 optional pixel argument can be used to specify the border width when switching
2507 to the normal and pixel styles.
2508
2509 Note that "pixel" refers to logical pixel. On HiDPI displays, a logical pixel
2510 may be represented by multiple physical pixels, so +pixel 1+ might not
2511 necessarily translate into a single pixel row wide border.
2512
2513 *Syntax*:
2514 -----------------------------------------------
2515 border normal|pixel|toggle [<n>]
2516 border none
2517
2518 # legacy syntax, equivalent to "border pixel 1"
2519 border 1pixel
2520 -----------------------------------------------
2521
2522 *Examples*:
2523 ----------------------------------------------
2524 # use window title, but no border
2525 bindsym $mod+t border normal 0
2526 # use no window title and a thick border
2527 bindsym $mod+y border pixel 3
2528 # use neither window title nor border
2529 bindsym $mod+u border none
2530 ----------------------------------------------
2531
2532 [[shmlog]]
2533 === Enabling shared memory logging
2534
2535 As described in https://i3wm.org/docs/debugging.html, i3 can log to a shared
2536 memory buffer, which you can dump using +i3-dump-log+. The +shmlog+ command
2537 allows you to enable or disable the shared memory logging at runtime.
2538
2539 Note that when using +shmlog <size_in_bytes>+, the current log will be
2540 discarded and a new one will be started.
2541
2542 *Syntax*:
2543 ------------------------------
2544 shmlog <size_in_bytes>
2545 shmlog on|off|toggle
2546 ------------------------------
2547
2548 *Examples*:
2549 ---------------
2550 # Enable/disable logging
2551 bindsym $mod+x shmlog toggle
2552
2553 # or, from a terminal:
2554 # increase the shared memory log buffer to 50 MiB
2555 i3-msg shmlog $((50*1024*1024))
2556 ---------------
2557
2558 === Enabling debug logging
2559
2560 The +debuglog+ command allows you to enable or disable debug logging at
2561 runtime. Debug logging is much more verbose than non-debug logging. This
2562 command does not activate shared memory logging (shmlog), and as such is most
2563 likely useful in combination with the above-described <<shmlog>> command.
2564
2565 *Syntax*:
2566 ----------------------
2567 debuglog on|off|toggle
2568 ----------------------
2569
2570 *Examples*:
2571 ------------------------
2572 # Enable/disable logging
2573 bindsym $mod+x debuglog toggle
2574 ------------------------
2575
2576 === Reloading/Restarting/Exiting
2577
2578 You can make i3 reload its configuration file with +reload+. You can also
2579 restart i3 inplace with the +restart+ command to get it out of some weird state
2580 (if that should ever happen) or to perform an upgrade without having to restart
2581 your X session. To exit i3 properly, you can use the +exit+ command,
2582 however you don’t need to (simply killing your X session is fine as well).
2583
2584 *Examples*:
2585 ----------------------------
2586 bindsym $mod+Shift+r restart
2587 bindsym $mod+Shift+w reload
2588 bindsym $mod+Shift+e exit
2589 ----------------------------
2590
2591 === Scratchpad
2592
2593 There are two commands to use any existing window as scratchpad window. +move
2594 scratchpad+ will move a window to the scratchpad workspace. This will make it
2595 invisible until you show it again. There is no way to open that workspace.
2596 Instead, when using +scratchpad show+, the window will be shown again, as a
2597 floating window, centered on your current workspace (using +scratchpad show+ on
2598 a visible scratchpad window will make it hidden again, so you can have a
2599 keybinding to toggle). Note that this is just a normal floating window, so if
2600 you want to "remove it from scratchpad", you can simple make it tiling again
2601 (+floating toggle+).
2602
2603 As the name indicates, this is useful for having a window with your favorite
2604 editor always at hand. However, you can also use this for other permanently
2605 running applications which you don’t want to see all the time: Your music
2606 player, alsamixer, maybe even your mail client…?
2607
2608 *Syntax*:
2609 ---------------
2610 move scratchpad
2611
2612 scratchpad show
2613 ---------------
2614
2615 *Examples*:
2616 ------------------------------------------------
2617 # Make the currently focused window a scratchpad
2618 bindsym $mod+Shift+minus move scratchpad
2619
2620 # Show the first scratchpad window
2621 bindsym $mod+minus scratchpad show
2622
2623 # Show the sup-mail scratchpad window, if any.
2624 bindsym mod4+s [title="^Sup ::"] scratchpad show
2625 ------------------------------------------------
2626
2627 === Nop
2628
2629 There is a no operation command +nop+ which allows you to override default
2630 behavior. This can be useful for, e.g., disabling a focus change on clicks with
2631 the middle mouse button.
2632
2633 The optional +comment+ argument is ignored, but will be printed to the log file
2634 for debugging purposes.
2635
2636 *Syntax*:
2637 ---------------
2638 nop [<comment>]
2639 ---------------
2640
2641 *Example*:
2642 ----------------------------------------------
2643 # Disable focus change for clicks on titlebars
2644 # with the middle mouse button
2645 bindsym button2 nop
2646 ----------------------------------------------
2647
2648 === i3bar control
2649
2650 There are two options in the configuration of each i3bar instance that can be
2651 changed during runtime by invoking a command through i3. The commands +bar
2652 hidden_state+ and +bar mode+ allow setting the current hidden_state
2653 respectively mode option of each bar. It is also possible to toggle between
2654 hide state and show state as well as between dock mode and hide mode. Each
2655 i3bar instance can be controlled individually by specifying a bar_id, if none
2656 is given, the command is executed for all bar instances.
2657
2658 *Syntax*:
2659 ---------------
2660 bar hidden_state hide|show|toggle [<bar_id>]
2661
2662 bar mode dock|hide|invisible|toggle [<bar_id>]
2663 ---------------
2664
2665 *Examples*:
2666 ------------------------------------------------
2667 # Toggle between hide state and show state
2668 bindsym $mod+m bar hidden_state toggle
2669
2670 # Toggle between dock mode and hide mode
2671 bindsym $mod+n bar mode toggle
2672
2673 # Set the bar instance with id 'bar-1' to switch to hide mode
2674 bindsym $mod+b bar mode hide bar-1
2675
2676 # Set the bar instance with id 'bar-1' to always stay hidden
2677 bindsym $mod+Shift+b bar mode invisible bar-1
2678 ------------------------------------------------
2679
2680 [[multi_monitor]]
2681 == Multiple monitors
2682
2683 As you can see in the goal list on the website, i3 was specifically developed
2684 with support for multiple monitors in mind. This section will explain how to
2685 handle multiple monitors.
2686
2687 When you have only one monitor, things are simple. You usually start with
2688 workspace 1 on your monitor and open new ones as you need them.
2689
2690 When you have more than one monitor, each monitor will get an initial
2691 workspace. The first monitor gets 1, the second gets 2 and a possible third
2692 would get 3. When you switch to a workspace on a different monitor, i3 will
2693 switch to that monitor and then switch to the workspace. This way, you don’t
2694 need shortcuts to switch to a specific monitor, and you don’t need to remember
2695 where you put which workspace. New workspaces will be opened on the currently
2696 active monitor. It is not possible to have a monitor without a workspace.
2697
2698 The idea of making workspaces global is based on the observation that most
2699 users have a very limited set of workspaces on their additional monitors.
2700 They are often used for a specific task (browser, shell) or for monitoring
2701 several things (mail, IRC, syslog, …). Thus, using one workspace on one monitor
2702 and "the rest" on the other monitors often makes sense. However, as you can
2703 create an unlimited number of workspaces in i3 and tie them to specific
2704 screens, you can have the "traditional" approach of having X workspaces per
2705 screen by changing your configuration (using modes, for example).
2706
2707 === Configuring your monitors
2708
2709 To help you get going if you have never used multiple monitors before, here is
2710 a short overview of the xrandr options which will probably be of interest to
2711 you. It is always useful to get an overview of the current screen configuration.
2712 Just run "xrandr" and you will get an output like the following:
2713 -------------------------------------------------------------------------------
2714 $ xrandr
2715 Screen 0: minimum 320 x 200, current 1280 x 800, maximum 8192 x 8192
2716 VGA1 disconnected (normal left inverted right x axis y axis)
2717 LVDS1 connected 1280x800+0+0 (normal left inverted right x axis y axis) 261mm x 163mm
2718 1280x800 60.0*+ 50.0
2719 1024x768 85.0 75.0 70.1 60.0
2720 832x624 74.6
2721 800x600 85.1 72.2 75.0 60.3 56.2
2722 640x480 85.0 72.8 75.0 59.9
2723 720x400 85.0
2724 640x400 85.1
2725 640x350 85.1
2726 --------------------------------------------------------------------------------------
2727
2728 Several things are important here: You can see that +LVDS1+ is connected (of
2729 course, it is the internal flat panel) but +VGA1+ is not. If you have a monitor
2730 connected to one of the ports but xrandr still says "disconnected", you should
2731 check your cable, monitor or graphics driver.
2732
2733 The maximum resolution you can see at the end of the first line is the maximum
2734 combined resolution of your monitors. By default, it is usually too low and has
2735 to be increased by editing +/etc/X11/xorg.conf+.
2736
2737 So, say you connected VGA1 and want to use it as an additional screen:
2738 -------------------------------------------
2739 xrandr --output VGA1 --auto --left-of LVDS1
2740 -------------------------------------------
2741 This command makes xrandr try to find the native resolution of the device
2742 connected to +VGA1+ and configures it to the left of your internal flat panel.
2743 When running "xrandr" again, the output looks like this:
2744 -------------------------------------------------------------------------------
2745 $ xrandr
2746 Screen 0: minimum 320 x 200, current 2560 x 1024, maximum 8192 x 8192
2747 VGA1 connected 1280x1024+0+0 (normal left inverted right x axis y axis) 338mm x 270mm
2748 1280x1024 60.0*+ 75.0
2749 1280x960 60.0
2750 1152x864 75.0
2751 1024x768 75.1 70.1 60.0
2752 832x624 74.6
2753 800x600 72.2 75.0 60.3 56.2
2754 640x480 72.8 75.0 66.7 60.0
2755 720x400 70.1
2756 LVDS1 connected 1280x800+1280+0 (normal left inverted right x axis y axis) 261mm x 163mm
2757 1280x800 60.0*+ 50.0
2758 1024x768 85.0 75.0 70.1 60.0
2759 832x624 74.6
2760 800x600 85.1 72.2 75.0 60.3 56.2
2761 640x480 85.0 72.8 75.0 59.9
2762 720x400 85.0
2763 640x400 85.1
2764 640x350 85.1
2765 -------------------------------------------------------------------------------
2766 Please note that i3 uses exactly the same API as xrandr does, so it will see
2767 only what you can see in xrandr.
2768
2769 See also <<presentations>> for more examples of multi-monitor setups.
2770
2771 === Interesting configuration for multi-monitor environments
2772
2773 There are several things to configure in i3 which may be interesting if you
2774 have more than one monitor:
2775
2776 1. You can specify which workspace should be put on which screen. This
2777 allows you to have a different set of workspaces when starting than just
2778 1 for the first monitor, 2 for the second and so on. See
2779 <<workspace_screen>>.
2780 2. If you want some applications to generally open on the bigger screen
2781 (MPlayer, Firefox, …), you can assign them to a specific workspace, see
2782 <<assign_workspace>>.
2783 3. If you have many workspaces on many monitors, it might get hard to keep
2784 track of which window you put where. Thus, you can use vim-like marks to
2785 quickly switch between windows. See <<vim_like_marks>>.
2786 4. For information on how to move existing workspaces between monitors,
2787 see <<move_to_outputs>>.
2788
2789 == i3 and the rest of your software world
2790
2791 === Displaying a status line
2792
2793 A very common thing amongst users of exotic window managers is a status line at
2794 some corner of the screen. It is an often superior replacement to the widget
2795 approach you have in the task bar of a traditional desktop environment.
2796
2797 If you don’t already have your favorite way of generating such a status line
2798 (self-written scripts, conky, …), then i3status is the recommended tool for
2799 this task. It was written in C with the goal of using as few syscalls as
2800 possible to reduce the time your CPU is woken up from sleep states. Because
2801 i3status only spits out text, you need to combine it with some other tool, like
2802 i3bar. See <<status_command>> for how to display i3status in i3bar.
2803
2804 Regardless of which application you use to display the status line, you
2805 want to make sure that it registers as a dock window using EWMH hints. i3 will
2806 position the window either at the top or at the bottom of the screen, depending
2807 on which hint the application sets. With i3bar, you can configure its position,
2808 see <<i3bar_position>>.
2809
2810 [[presentations]]
2811 === Giving presentations (multi-monitor)
2812
2813 When giving a presentation, you typically want the audience to see what you see
2814 on your screen and then go through a series of slides (if the presentation is
2815 simple). For more complex presentations, you might want to have some notes
2816 which only you can see on your screen, while the audience can only see the
2817 slides.
2818
2819 ==== Case 1: everybody gets the same output
2820 This is the simple case. You connect your computer to the video projector,
2821 turn on both (computer and video projector) and configure your X server to
2822 clone the internal flat panel of your computer to the video output:
2823 -----------------------------------------------------
2824 xrandr --output VGA1 --mode 1024x768 --same-as LVDS1
2825 -----------------------------------------------------
2826 i3 will then use the lowest common subset of screen resolutions, the rest of
2827 your screen will be left untouched (it will show the X background). So, in
2828 our example, this would be 1024x768 (my notebook has 1280x800).
2829
2830 ==== Case 2: you can see more than your audience
2831 This case is a bit harder. First of all, you should configure the VGA output
2832 somewhere near your internal flat panel, say right of it:
2833 -----------------------------------------------------
2834 xrandr --output VGA1 --mode 1024x768 --right-of LVDS1
2835 -----------------------------------------------------
2836 Now, i3 will put a new workspace (depending on your settings) on the new screen
2837 and you are in multi-monitor mode (see <<multi_monitor>>).
2838
2839 Because i3 is not a compositing window manager, there is no ability to
2840 display a window on two screens at the same time. Instead, your presentation
2841 software needs to do this job (that is, open a window on each screen).
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>i3 User’s Guide</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>i3 User’s Guide</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <div id="toc">
740 <div id="toctitle">Table of Contents</div>
741 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
742 </div>
743 </div>
744 <div id="content">
745 <div id="preamble">
746 <div class="sectionbody">
747 <div class="paragraph"><p>This document contains all the information you need to configure and use the i3
748 window manager. If it does not, please check <a href="https://www.reddit.com/r/i3wm/">https://www.reddit.com/r/i3wm/</a>
749 first, then contact us on IRC (preferred) or post your question(s) on the
750 mailing list.</p></div>
751 </div>
752 </div>
753 <div class="sect1">
754 <h2 id="_default_keybindings">1. Default keybindings</h2>
755 <div class="sectionbody">
756 <div class="paragraph"><p>For the "too long; didn’t read" people, here is an overview of the default
757 keybindings (click to see the full-size image):</p></div>
758 <div class="paragraph"><p><strong>Keys to use with $mod (Alt):</strong></p></div>
759 <div class="paragraph"><p><span class="image">
760 <a class="image" href="keyboard-layer1.png">
761 <img src="keyboard-layer1.png" alt="Keys to use with $mod (Alt)" width="600" />
762 </a>
763 </span></p></div>
764 <div class="paragraph"><p><strong>Keys to use with Shift+$mod:</strong></p></div>
765 <div class="paragraph"><p><span class="image">
766 <a class="image" href="keyboard-layer2.png">
767 <img src="keyboard-layer2.png" alt="Keys to use with Shift+$mod" width="600" />
768 </a>
769 </span></p></div>
770 <div class="paragraph"><p>The red keys are the modifiers you need to press (by default), the blue keys
771 are your homerow.</p></div>
772 <div class="paragraph"><p>Note that when starting i3 without a config file, i3-config-wizard will offer
773 you to create a config file in which the key positions (!) match what you see
774 in the image above, regardless of the keyboard layout you are using. If you
775 prefer to use a config file where the key letters match what you are seeing
776 above, just decline i3-config-wizard’s offer and base your config on
777 <code>/etc/i3/config</code>.</p></div>
778 </div>
779 </div>
780 <div class="sect1">
781 <h2 id="_using_i3">2. Using i3</h2>
782 <div class="sectionbody">
783 <div class="paragraph"><p>Throughout this guide, the keyword <code>$mod</code> will be used to refer to the
784 configured modifier. This is the Alt key (<code>Mod1</code>) by default, with the Windows
785 key (<code>Mod4</code>) being a popular alternative that largely prevents conflicts with
786 application-defined shortcuts.</p></div>
787 <div class="sect2">
788 <h3 id="_opening_terminals_and_moving_around">2.1. Opening terminals and moving around</h3>
789 <div class="paragraph"><p>One very basic operation is opening a new terminal. By default, the keybinding
790 for this is <code>$mod+Enter</code>, that is Alt+Enter (<code>Mod1+Enter</code>) in the default
791 configuration. By pressing <code>$mod+Enter</code>, a new terminal will be opened. It
792 will fill the whole space available on your screen.</p></div>
793 <div class="paragraph"><p><span class="image">
794 <img src="single_terminal.png" alt="Single terminal" />
795 </span></p></div>
796 <div class="paragraph"><p>If you now open another terminal, i3 will place it next to the current one,
797 splitting the screen size in half. Depending on your monitor, i3 will put the
798 created window beside the existing window (on wide displays) or below the
799 existing window (rotated displays).</p></div>
800 <div class="paragraph"><p><span class="image">
801 <img src="two_terminals.png" alt="Two terminals" />
802 </span></p></div>
803 <div class="paragraph"><p>To move the focus between the two terminals, you can use the direction keys
804 which you may know from the editor <code>vi</code>. However, in i3, your homerow is used
805 for these keys (in <code>vi</code>, the keys are shifted to the left by one for
806 compatibility with most keyboard layouts). Therefore, <code>$mod+j</code> is left, <code>$mod+k</code>
807 is down, <code>$mod+l</code> is up and <code>$mod+;</code> is right. So, to switch between the
808 terminals, use <code>$mod+k</code> or <code>$mod+l</code>. Of course, you can also use the arrow keys.</p></div>
809 <div class="paragraph"><p>At the moment, your workspace is split (it contains two terminals) in a
810 specific direction (horizontal by default). Every window can be split
811 horizontally or vertically again, just like the workspace. The terminology is
812 "window" for a container that actually contains an X11 window (like a terminal
813 or browser) and "split container" for containers that consist of one or more
814 windows.</p></div>
815 <div class="paragraph"><p>TODO: picture of the tree</p></div>
816 <div class="paragraph"><p>To split a window vertically, press <code>$mod+v</code> before you create the new window.
817 To split it horizontally, press <code>$mod+h</code>.</p></div>
818 </div>
819 <div class="sect2">
820 <h3 id="_changing_the_container_layout">2.2. Changing the container layout</h3>
821 <div class="paragraph"><p>A split container can have one of the following layouts:</p></div>
822 <div class="dlist"><dl>
823 <dt class="hdlist1">
824 splith/splitv
825 </dt>
826 <dd>
827 <p>
828 Windows are sized so that every window gets an equal amount of space in the
829 container. splith distributes the windows horizontally (windows are right next
830 to each other), splitv distributes them vertically (windows are on top of each
831 other).
832 </p>
833 </dd>
834 <dt class="hdlist1">
835 stacking
836 </dt>
837 <dd>
838 <p>
839 Only the focused window in the container is displayed. You get a list of
840 windows at the top of the container.
841 </p>
842 </dd>
843 <dt class="hdlist1">
844 tabbed
845 </dt>
846 <dd>
847 <p>
848 The same principle as <code>stacking</code>, but the list of windows at the top is only
849 a single line which is vertically split.
850 </p>
851 </dd>
852 </dl></div>
853 <div class="paragraph"><p>To switch modes, press <code>$mod+e</code> for splith/splitv (it toggles), <code>$mod+s</code> for
854 stacking and <code>$mod+w</code> for tabbed.</p></div>
855 <div class="paragraph"><p><span class="image">
856 <img src="modes.png" alt="Container modes" />
857 </span></p></div>
858 </div>
859 <div class="sect2">
860 <h3 id="_toggling_fullscreen_mode_for_a_window">2.3. Toggling fullscreen mode for a window</h3>
861 <div class="paragraph"><p>To display a window in fullscreen mode or to go out of fullscreen mode again,
862 press <code>$mod+f</code>.</p></div>
863 <div class="paragraph"><p>There is also a global fullscreen mode in i3 in which the client will span all
864 available outputs (the command is <code>fullscreen toggle global</code>).</p></div>
865 </div>
866 <div class="sect2">
867 <h3 id="_opening_other_applications">2.4. Opening other applications</h3>
868 <div class="paragraph"><p>Aside from opening applications from a terminal, you can also use the handy
869 <code>dmenu</code> which is opened by pressing <code>$mod+d</code> by default. Just type the name
870 (or a part of it) of the application which you want to open. The corresponding
871 application has to be in your <code>$PATH</code> for this to work.</p></div>
872 <div class="paragraph"><p>Additionally, if you have applications you open very frequently, you can
873 create a keybinding for starting the application directly. See the section
874 <a href="#configuring">[configuring]</a> for details.</p></div>
875 </div>
876 <div class="sect2">
877 <h3 id="_closing_windows">2.5. Closing windows</h3>
878 <div class="paragraph"><p>If an application does not provide a mechanism for closing (most applications
879 provide a menu, the escape key or a shortcut like <code>Control+w</code> to close), you
880 can press <code>$mod+Shift+q</code> to kill a window. For applications which support
881 the WM_DELETE protocol, this will correctly close the application (saving
882 any modifications or doing other cleanup). If the application doesn’t support
883 the WM_DELETE protocol your X server will kill the window and the behaviour
884 depends on the application.</p></div>
885 </div>
886 <div class="sect2">
887 <h3 id="_using_workspaces">2.6. Using workspaces</h3>
888 <div class="paragraph"><p>Workspaces are an easy way to group a set of windows. By default, you are on
889 the first workspace, as the bar on the bottom left indicates. To switch to
890 another workspace, press <code>$mod+num</code> where <code>num</code> is the number of the workspace
891 you want to use. If the workspace does not exist yet, it will be created.</p></div>
892 <div class="paragraph"><p>A common paradigm is to put the web browser on one workspace, communication
893 applications (<code>mutt</code>, <code>irssi</code>, &#8230;) on another one, and the ones with which you
894 work, on the third one. Of course, there is no need to follow this approach.</p></div>
895 <div class="paragraph"><p>If you have multiple screens, a workspace will be created on each screen at
896 startup. If you open a new workspace, it will be bound to the screen you
897 created it on. When you switch to a workspace on another screen, i3 will set
898 focus to that screen.</p></div>
899 </div>
900 <div class="sect2">
901 <h3 id="_moving_windows_to_workspaces">2.7. Moving windows to workspaces</h3>
902 <div class="paragraph"><p>To move a window to another workspace, simply press <code>$mod+Shift+num</code> where
903 <code>num</code> is (like when switching workspaces) the number of the target workspace.
904 Similarly to switching workspaces, the target workspace will be created if
905 it does not yet exist.</p></div>
906 </div>
907 <div class="sect2">
908 <h3 id="_resizing">2.8. Resizing</h3>
909 <div class="paragraph"><p>The easiest way to resize a container is by using the mouse: Grab the border
910 and move it to the wanted size.</p></div>
911 <div class="paragraph"><p>You can also use <a href="#binding_modes">[binding_modes]</a> to define a mode for resizing via the
912 keyboard. To see an example for this, look at the
913 <a href="https://github.com/i3/i3/blob/next/etc/config.keycodes">default config</a> provided
914 by i3.</p></div>
915 </div>
916 <div class="sect2">
917 <h3 id="_restarting_i3_inplace">2.9. Restarting i3 inplace</h3>
918 <div class="paragraph"><p>To restart i3 in place (and thus get into a clean state if there is a bug, or
919 to upgrade to a newer version of i3) you can use <code>$mod+Shift+r</code>.</p></div>
920 </div>
921 <div class="sect2">
922 <h3 id="_exiting_i3">2.10. Exiting i3</h3>
923 <div class="paragraph"><p>To cleanly exit i3 without killing your X server, you can use <code>$mod+Shift+e</code>.
924 By default, a dialog will ask you to confirm if you really want to quit.</p></div>
925 </div>
926 <div class="sect2">
927 <h3 id="_floating">2.11. Floating</h3>
928 <div class="paragraph"><p>Floating mode is the opposite of tiling mode. The position and size of
929 a window are not managed automatically by i3, but manually by
930 you. Using this mode violates the tiling paradigm but can be useful
931 for some corner cases like "Save as" dialog windows, or toolbar
932 windows (GIMP or similar). Those windows usually set the appropriate
933 hint and are opened in floating mode by default.</p></div>
934 <div class="paragraph"><p>You can toggle floating mode for a window by pressing <code>$mod+Shift+Space</code>. By
935 dragging the window’s titlebar with your mouse you can move the window
936 around. By grabbing the borders and moving them you can resize the window. You
937 can also do that by using the <a href="#floating_modifier">[floating_modifier]</a>. Another way to resize
938 floating windows using the mouse is to right-click on the titlebar and drag.</p></div>
939 <div class="paragraph"><p>For resizing floating windows with your keyboard, see the resizing binding mode
940 provided by the i3 <a href="https://github.com/i3/i3/blob/next/etc/config.keycodes">default config</a>.</p></div>
941 <div class="paragraph"><p>Floating windows are always on top of tiling windows.</p></div>
942 </div>
943 </div>
944 </div>
945 <div class="sect1">
946 <h2 id="_tree">3. Tree</h2>
947 <div class="sectionbody">
948 <div class="paragraph"><p>i3 stores all information about the X11 outputs, workspaces and layout of the
949 windows on them in a tree. The root node is the X11 root window, followed by
950 the X11 outputs, then dock areas and a content container, then workspaces and
951 finally the windows themselves. In previous versions of i3 we had multiple lists
952 (of outputs, workspaces) and a table for each workspace. That approach turned
953 out to be complicated to use (snapping), understand and implement.</p></div>
954 <div class="sect2">
955 <h3 id="_the_tree_consists_of_containers">3.1. The tree consists of Containers</h3>
956 <div class="paragraph"><p>The building blocks of our tree are so-called <code>Containers</code>. A <code>Container</code> can
957 host a window (meaning an X11 window, one that you can actually see and use,
958 like a browser). Alternatively, it could contain one or more <code>Containers</code>. A
959 simple example is the workspace: When you start i3 with a single monitor, a
960 single workspace and you open two terminal windows, you will end up with a tree
961 like this:</p></div>
962 <div class="imageblock" style="float:right;">
963 <div class="content">
964 <img src="tree-layout2.png" alt="layout2" />
965 </div>
966 </div>
967 <div class="imageblock">
968 <div class="content">
969 <img src="tree-shot4.png" alt="shot4" />
970 </div>
971 <div class="title">Figure 1. Two terminals on standard workspace</div>
972 </div>
973 </div>
974 <div class="sect2">
975 <h3 id="OrientationSplit">3.2. Orientation and Split Containers</h3>
976 <div class="paragraph"><p>It is only natural to use so-called <code>Split Containers</code> in order to build a
977 layout when using a tree as data structure. In i3, every <code>Container</code> has an
978 orientation (horizontal, vertical or unspecified) and the orientation depends
979 on the layout the container is in (vertical for splitv and stacking, horizontal
980 for splith and tabbed). So, in our example with the workspace, the default
981 layout of the workspace <code>Container</code> is splith (most monitors are widescreen
982 nowadays). If you change the layout to splitv (<code>$mod+v</code> in the default config)
983 and <strong>then</strong> open two terminals, i3 will configure your windows like this:</p></div>
984 <div class="imageblock">
985 <div class="content">
986 <img src="tree-shot2.png" alt="shot2" />
987 </div>
988 <div class="title">Figure 2. Vertical Workspace Orientation</div>
989 </div>
990 <div class="paragraph"><p>An interesting new feature of i3 since version 4 is the ability to split anything:
991 Let’s assume you have two terminals on a workspace (with splith layout, that is
992 horizontal orientation), focus is on the right terminal. Now you want to open
993 another terminal window below the current one. If you would just open a new
994 terminal window, it would show up to the right due to the splith layout.
995 Instead, press <code>$mod+v</code> to split the container with the splitv layout (to
996 open a <code>Horizontal Split Container</code>, use <code>$mod+h</code>). Now you can open a new
997 terminal and it will open below the current one:</p></div>
998 <div class="imageblock" style="float:right;">
999 <div class="content">
1000 <img src="tree-layout1.png" alt="Layout" />
1001 </div>
1002 </div>
1003 <div class="imageblock">
1004 <div class="content">
1005 <img src="tree-shot1.png" alt="shot" />
1006 </div>
1007 <div class="title">Figure 3. Vertical Split Container</div>
1008 </div>
1009 <div style="clear:both;"></div>
1010 <div class="paragraph"><p>You probably guessed it already: There is no limit on how deep your hierarchy
1011 of splits can be.</p></div>
1012 </div>
1013 <div class="sect2">
1014 <h3 id="_focus_parent">3.3. Focus parent</h3>
1015 <div class="paragraph"><p>Let’s stay with our example from above. We have a terminal on the left and two
1016 vertically split terminals on the right, focus is on the bottom right one. When
1017 you open a new terminal, it will open below the current one.</p></div>
1018 <div class="paragraph"><p>So, how can you open a new terminal window to the <strong>right</strong> of the current one?
1019 The solution is to use <code>focus parent</code>, which will focus the <code>Parent Container</code> of
1020 the current <code>Container</code>. In default configuration, use <code>$mod+a</code> to navigate one
1021 <code>Container</code> up the tree (you can repeat this multiple times until you get to the
1022 <code>Workspace Container</code>). In this case, you would focus the <code>Vertical Split Container</code>
1023 which is <strong>inside</strong> the horizontally oriented workspace. Thus, now new windows will be
1024 opened to the right of the <code>Vertical Split Container</code>:</p></div>
1025 <div class="imageblock">
1026 <div class="content">
1027 <img src="tree-shot3.png" alt="shot3" />
1028 </div>
1029 <div class="title">Figure 4. Focus parent, then open new terminal</div>
1030 </div>
1031 </div>
1032 <div class="sect2">
1033 <h3 id="_implicit_containers">3.4. Implicit containers</h3>
1034 <div class="paragraph"><p>In some cases, i3 needs to implicitly create a container to fulfill your
1035 command.</p></div>
1036 <div class="paragraph"><p>One example is the following scenario: You start i3 with a single monitor and a
1037 single workspace on which you open three terminal windows. All these terminal
1038 windows are directly attached to one node inside i3’s layout tree, the
1039 workspace node. By default, the workspace node’s orientation is <code>horizontal</code>.</p></div>
1040 <div class="paragraph"><p>Now you move one of these terminals down (<code>$mod+Shift+k</code> by default). The
1041 workspace node’s orientation will be changed to <code>vertical</code>. The terminal window
1042 you moved down is directly attached to the workspace and appears on the bottom
1043 of the screen. A new (horizontal) container was created to accommodate the
1044 other two terminal windows. You will notice this when switching to tabbed mode
1045 (for example). You would end up having one tab with a representation of the split
1046 container (e.g., "H[urxvt firefox]") and the other one being the terminal window
1047 you moved down.</p></div>
1048 </div>
1049 </div>
1050 </div>
1051 <div class="sect1">
1052 <h2 id="configuring">4. Configuring i3</h2>
1053 <div class="sectionbody">
1054 <div class="paragraph"><p>This is where the real fun begins ;-). Most things are very dependent on your
1055 ideal working environment so we can’t make reasonable defaults for them.</p></div>
1056 <div class="paragraph"><p>While not using a programming language for the configuration, i3 stays
1057 quite flexible in regards to the things you usually want your window manager
1058 to do.</p></div>
1059 <div class="paragraph"><p>For example, you can configure bindings to jump to specific windows,
1060 you can set specific applications to start on specific workspaces, you can
1061 automatically start applications, you can change the colors of i3, and you
1062 can bind your keys to do useful things.</p></div>
1063 <div class="paragraph"><p>To change the configuration of i3, copy <code>/etc/i3/config</code> to <code>~/.i3/config</code>
1064 (or <code>~/.config/i3/config</code> if you like the XDG directory scheme) and edit it
1065 with a text editor.</p></div>
1066 <div class="paragraph"><p>On first start (and on all following starts, unless you have a configuration
1067 file), i3 will offer you to create a configuration file. You can tell the
1068 wizard to use either Alt (<code>Mod1</code>) or Windows (<code>Mod4</code>) as modifier in the config
1069 file. Also, the created config file will use the key symbols of your current
1070 keyboard layout. To start the wizard, use the command <code>i3-config-wizard</code>.
1071 Please note that you must not have <code>~/.i3/config</code>, otherwise the wizard will
1072 exit.</p></div>
1073 <div class="paragraph"><p>Since i3 4.0, a new configuration format is used. i3 will try to automatically
1074 detect the format version of a config file based on a few different keywords,
1075 but if you want to make sure that your config is read with the new format,
1076 include the following line in your config file:</p></div>
1077 <div class="listingblock">
1078 <div class="content">
1079 <pre><code># i3 config file (v4)</code></pre>
1080 </div></div>
1081 <div class="sect2">
1082 <h3 id="_comments">4.1. Comments</h3>
1083 <div class="paragraph"><p>It is possible and recommended to use comments in your configuration file to
1084 properly document your setup for later reference. Comments are started with
1085 a # and can only be used at the beginning of a line:</p></div>
1086 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1087 <div class="listingblock">
1088 <div class="content">
1089 <pre><code># This is a comment</code></pre>
1090 </div></div>
1091 </div>
1092 <div class="sect2">
1093 <h3 id="fonts">4.2. Fonts</h3>
1094 <div class="paragraph"><p>i3 has support for both X core fonts and FreeType fonts (through Pango) to
1095 render window titles.</p></div>
1096 <div class="paragraph"><p>To generate an X core font description, you can use <code>xfontsel(1)</code>. To see
1097 special characters (Unicode), you need to use a font which supports the
1098 ISO-10646 encoding.</p></div>
1099 <div class="paragraph"><p>A FreeType font description is composed by a font family, a style, a weight,
1100 a variant, a stretch and a size.
1101 FreeType fonts support right-to-left rendering and contain often more
1102 Unicode glyphs than X core fonts.</p></div>
1103 <div class="paragraph"><p>If i3 cannot open the configured font, it will output an error in the logfile
1104 and fall back to a working font.</p></div>
1105 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1106 <div class="listingblock">
1107 <div class="content">
1108 <pre><code>font &lt;X core font description&gt;
1109 font pango:&lt;family list&gt; [&lt;style options&gt;] &lt;size&gt;</code></pre>
1110 </div></div>
1111 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1112 <div class="listingblock">
1113 <div class="content">
1114 <pre><code>font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
1115 font pango:DejaVu Sans Mono 10
1116 font pango:DejaVu Sans Mono, Terminus Bold Semi-Condensed 11
1117 font pango:Terminus 11px</code></pre>
1118 </div></div>
1119 </div>
1120 <div class="sect2">
1121 <h3 id="keybindings">4.3. Keyboard bindings</h3>
1122 <div class="paragraph"><p>A keyboard binding makes i3 execute a command (see below) upon pressing a
1123 specific key. i3 allows you to bind either on keycodes or on keysyms (you can
1124 also mix your bindings, though i3 will not protect you from overlapping ones).</p></div>
1125 <div class="ulist"><ul>
1126 <li>
1127 <p>
1128 A keysym (key symbol) is a description for a specific symbol, like "a"
1129 or "b", but also more strange ones like "underscore" instead of "_". These
1130 are the ones you use in Xmodmap to remap your keys. To get the current
1131 mapping of your keys, use <code>xmodmap -pke</code>. To interactively enter a key and
1132 see what keysym it is configured to, use <code>xev</code>.
1133 </p>
1134 </li>
1135 <li>
1136 <p>
1137 Keycodes do not need to have a symbol assigned (handy for custom vendor
1138 hotkeys on some notebooks) and they will not change their meaning as you
1139 switch to a different keyboard layout (when using <code>xmodmap</code>).
1140 </p>
1141 </li>
1142 </ul></div>
1143 <div class="paragraph"><p>My recommendation is: If you often switch keyboard layouts but you want to keep
1144 your bindings in the same physical location on the keyboard, use keycodes.
1145 If you don’t switch layouts, and want a clean and simple config file, use
1146 keysyms.</p></div>
1147 <div class="paragraph"><p>Some tools (such as <code>import</code> or <code>xdotool</code>) might be unable to run upon a
1148 KeyPress event, because the keyboard/pointer is still grabbed. For these
1149 situations, the <code>--release</code> flag can be used, which will execute the command
1150 after the keys have been released.</p></div>
1151 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1152 <div class="listingblock">
1153 <div class="content">
1154 <pre><code>bindsym [--release] [&lt;Group&gt;+][&lt;Modifiers&gt;+]&lt;keysym&gt; command
1155 bindcode [--release] [&lt;Group&gt;+][&lt;Modifiers&gt;+]&lt;keycode&gt; command</code></pre>
1156 </div></div>
1157 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1158 <div class="listingblock">
1159 <div class="content">
1160 <pre><code># Fullscreen
1161 bindsym $mod+f fullscreen toggle
1162
1163 # Restart
1164 bindsym $mod+Shift+r restart
1165
1166 # Notebook-specific hotkeys
1167 bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh
1168
1169 # Simulate ctrl+v upon pressing $mod+x
1170 bindsym --release $mod+x exec --no-startup-id xdotool key --clearmodifiers ctrl+v
1171
1172 # Take a screenshot upon pressing $mod+x (select an area)
1173 bindsym --release $mod+x exec --no-startup-id import /tmp/latest-screenshot.png</code></pre>
1174 </div></div>
1175 <div class="paragraph"><p>Available Modifiers:</p></div>
1176 <div class="dlist"><dl>
1177 <dt class="hdlist1">
1178 Mod1-Mod5, Shift, Control
1179 </dt>
1180 <dd>
1181 <p>
1182 Standard modifiers, see <code>xmodmap(1)</code>
1183 </p>
1184 </dd>
1185 <dt class="hdlist1">
1186 Group1, Group2, Group3, Group4
1187 </dt>
1188 <dd>
1189 <p>
1190 When using multiple keyboard layouts (e.g. with <code>setxkbmap -layout us,ru</code>), you
1191 can specify in which XKB group (also called “layout”) a keybinding should be
1192 active. By default, keybindings are translated in Group1 and are active in all
1193 groups. If you want to override keybindings in one of your layouts, specify the
1194 corresponding group. For backwards compatibility, the group “Mode_switch” is an
1195 alias for Group2.
1196 </p>
1197 </dd>
1198 </dl></div>
1199 </div>
1200 <div class="sect2">
1201 <h3 id="mousebindings">4.4. Mouse bindings</h3>
1202 <div class="paragraph"><p>A mouse binding makes i3 execute a command upon pressing a specific mouse
1203 button in the scope of the clicked container (see <a href="#command_criteria">[command_criteria]</a>). You
1204 can configure mouse bindings in a similar way to key bindings.</p></div>
1205 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1206 <div class="listingblock">
1207 <div class="content">
1208 <pre><code>bindsym [--release] [--border] [--whole-window] [--exclude-titlebar] [&lt;Modifiers&gt;+]button&lt;n&gt; command</code></pre>
1209 </div></div>
1210 <div class="paragraph"><p>By default, the binding will only run when you click on the titlebar of the
1211 window. If the <code>--release</code> flag is given, it will run when the mouse button
1212 is released.</p></div>
1213 <div class="paragraph"><p>If the <code>--whole-window</code> flag is given, the binding will also run when any part
1214 of the window is clicked, with the exception of the border. To have a bind run
1215 when the border is clicked, specify the <code>--border</code> flag.</p></div>
1216 <div class="paragraph"><p>If the <code>--exclude-titlebar</code> flag is given, the titlebar will not be considered
1217 for the keybinding.</p></div>
1218 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1219 <div class="listingblock">
1220 <div class="content">
1221 <pre><code># The middle button over a titlebar kills the window
1222 bindsym --release button2 kill
1223
1224 # The middle button and a modifer over any part of the window kills the window
1225 bindsym --whole-window $mod+button2 kill
1226
1227 # The right button toggles floating
1228 bindsym button3 floating toggle
1229 bindsym $mod+button3 floating toggle
1230
1231 # The side buttons move the window around
1232 bindsym button9 move left
1233 bindsym button8 move right</code></pre>
1234 </div></div>
1235 </div>
1236 <div class="sect2">
1237 <h3 id="binding_modes">4.5. Binding modes</h3>
1238 <div class="paragraph"><p>You can have multiple sets of bindings by using different binding modes. When
1239 you switch to another binding mode, all bindings from the current mode are
1240 released and only the bindings defined in the new mode are valid for as long as
1241 you stay in that binding mode. The only predefined binding mode is <code>default</code>,
1242 which is the mode i3 starts out with and to which all bindings not defined in a
1243 specific binding mode belong.</p></div>
1244 <div class="paragraph"><p>Working with binding modes consists of two parts: defining a binding mode and
1245 switching to it. For these purposes, there are one config directive and one
1246 command, both of which are called <code>mode</code>. The directive is used to define the
1247 bindings belonging to a certain binding mode, while the command will switch to
1248 the specified mode.</p></div>
1249 <div class="paragraph"><p>It is recommended to use binding modes in combination with <a href="#variables">[variables]</a> in
1250 order to make maintenance easier. Below is an example of how to use a binding
1251 mode.</p></div>
1252 <div class="paragraph"><p>Note that it is advisable to define bindings for switching back to the default
1253 mode.</p></div>
1254 <div class="paragraph"><p>Note that it is possible to use <a href="#pango_markup">[pango_markup]</a> for binding modes, but you
1255 need to enable it explicitly by passing the <code>--pango_markup</code> flag to the mode
1256 definition.</p></div>
1257 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1258 <div class="listingblock">
1259 <div class="content">
1260 <pre><code># config directive
1261 mode [--pango_markup] &lt;name&gt;
1262
1263 # command
1264 mode &lt;name&gt;</code></pre>
1265 </div></div>
1266 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1267 <div class="listingblock">
1268 <div class="content">
1269 <pre><code># Press $mod+o followed by either f, t, Escape or Return to launch firefox,
1270 # thunderbird or return to the default mode, respectively.
1271 set $mode_launcher Launch: [f]irefox [t]hunderbird
1272 bindsym $mod+o mode "$mode_launcher"
1273
1274 mode "$mode_launcher" {
1275 bindsym f exec firefox
1276 bindsym t exec thunderbird
1277
1278 bindsym Escape mode "default"
1279 bindsym Return mode "default"
1280 }</code></pre>
1281 </div></div>
1282 </div>
1283 <div class="sect2">
1284 <h3 id="floating_modifier">4.6. The floating modifier</h3>
1285 <div class="paragraph"><p>To move floating windows with your mouse, you can either grab their titlebar
1286 or configure the so-called floating modifier which you can then press and
1287 click anywhere in the window itself to move it. The most common setup is to
1288 use the same key you use for managing windows (Mod1 for example). Then
1289 you can press Mod1, click into a window using your left mouse button, and drag
1290 it to the position you want.</p></div>
1291 <div class="paragraph"><p>When holding the floating modifier, you can resize a floating window by
1292 pressing the right mouse button on it and moving around while holding it. If
1293 you hold the shift button as well, the resize will be proportional (the aspect
1294 ratio will be preserved).</p></div>
1295 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1296 <div class="listingblock">
1297 <div class="content">
1298 <pre><code>floating_modifier &lt;Modifier&gt;</code></pre>
1299 </div></div>
1300 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1301 <div class="listingblock">
1302 <div class="content">
1303 <pre><code>floating_modifier Mod1</code></pre>
1304 </div></div>
1305 </div>
1306 <div class="sect2">
1307 <h3 id="_constraining_floating_window_size">4.7. Constraining floating window size</h3>
1308 <div class="paragraph"><p>The maximum and minimum dimensions of floating windows can be specified. If
1309 either dimension of <code>floating_maximum_size</code> is specified as -1, that dimension
1310 will be unconstrained with respect to its maximum value. If either dimension of
1311 <code>floating_maximum_size</code> is undefined, or specified as 0, i3 will use a default
1312 value to constrain the maximum size. <code>floating_minimum_size</code> is treated in a
1313 manner analogous to <code>floating_maximum_size</code>.</p></div>
1314 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1315 <div class="listingblock">
1316 <div class="content">
1317 <pre><code>floating_minimum_size &lt;width&gt; x &lt;height&gt;
1318 floating_maximum_size &lt;width&gt; x &lt;height&gt;</code></pre>
1319 </div></div>
1320 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1321 <div class="listingblock">
1322 <div class="content">
1323 <pre><code>floating_minimum_size 75 x 50
1324 floating_maximum_size -1 x -1</code></pre>
1325 </div></div>
1326 </div>
1327 <div class="sect2">
1328 <h3 id="_orientation_for_new_workspaces">4.8. Orientation for new workspaces</h3>
1329 <div class="paragraph"><p>New workspaces get a reasonable default orientation: Wide-screen monitors
1330 (anything wider than high) get horizontal orientation, rotated monitors
1331 (anything higher than wide) get vertical orientation.</p></div>
1332 <div class="paragraph"><p>With the <code>default_orientation</code> configuration directive, you can override that
1333 behavior.</p></div>
1334 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1335 <div class="listingblock">
1336 <div class="content">
1337 <pre><code>default_orientation horizontal|vertical|auto</code></pre>
1338 </div></div>
1339 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1340 <div class="listingblock">
1341 <div class="content">
1342 <pre><code>default_orientation vertical</code></pre>
1343 </div></div>
1344 </div>
1345 <div class="sect2">
1346 <h3 id="_layout_mode_for_new_containers">4.9. Layout mode for new containers</h3>
1347 <div class="paragraph"><p>This option determines in which mode new containers on workspace level will
1348 start.</p></div>
1349 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1350 <div class="listingblock">
1351 <div class="content">
1352 <pre><code>workspace_layout default|stacking|tabbed</code></pre>
1353 </div></div>
1354 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1355 <div class="listingblock">
1356 <div class="content">
1357 <pre><code>workspace_layout tabbed</code></pre>
1358 </div></div>
1359 </div>
1360 <div class="sect2">
1361 <h3 id="_window_title_alignment">4.10. Window title alignment</h3>
1362 <div class="paragraph"><p>This option determines the window title&#8217;s text alignment.
1363 Default is <code>left</code></p></div>
1364 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1365 <div class="listingblock">
1366 <div class="content">
1367 <pre><code>title_align left|center|right</code></pre>
1368 </div></div>
1369 </div>
1370 <div class="sect2">
1371 <h3 id="_default_border_style_for_new_windows">4.11. Default border style for new windows</h3>
1372 <div class="paragraph"><p>This option determines which border style new windows will have. The default is
1373 <code>normal</code>. Note that default_floating_border applies only to windows which are starting out as
1374 floating windows, e.g., dialog windows, but not windows that are floated later on.</p></div>
1375 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1376 <div class="listingblock">
1377 <div class="content">
1378 <pre><code>default_border normal|none|pixel
1379 default_border normal|pixel &lt;px&gt;
1380 default_floating_border normal|none|pixel
1381 default_floating_border normal|pixel &lt;px&gt;</code></pre>
1382 </div></div>
1383 <div class="paragraph"><p>Please note that <code>new_window</code> and <code>new_float</code> have been deprecated in favor of the above options
1384 and will be removed in a future release. We strongly recommend using the new options instead.</p></div>
1385 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1386 <div class="listingblock">
1387 <div class="content">
1388 <pre><code>default_border pixel</code></pre>
1389 </div></div>
1390 <div class="paragraph"><p>The "normal" and "pixel" border styles support an optional border width in
1391 pixels:</p></div>
1392 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1393 <div class="listingblock">
1394 <div class="content">
1395 <pre><code># The same as default_border none
1396 default_border pixel 0
1397
1398 # A 3 px border
1399 default_border pixel 3</code></pre>
1400 </div></div>
1401 </div>
1402 <div class="sect2">
1403 <h3 id="_hiding_vertical_borders">4.12. Hiding borders adjacent to the screen edges</h3>
1404 <div class="paragraph"><p>You can hide container borders adjacent to the screen edges using
1405 <code>hide_edge_borders</code>. This is useful if you are using scrollbars, or do not want
1406 to waste even two pixels in displayspace. The "smart" setting hides borders on
1407 workspaces with only one window visible, but keeps them on workspaces with
1408 multiple windows visible. Default is none.</p></div>
1409 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1410 <div class="listingblock">
1411 <div class="content">
1412 <pre><code>hide_edge_borders none|vertical|horizontal|both|smart</code></pre>
1413 </div></div>
1414 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1415 <div class="listingblock">
1416 <div class="content">
1417 <pre><code>hide_edge_borders vertical</code></pre>
1418 </div></div>
1419 </div>
1420 <div class="sect2">
1421 <h3 id="for_window">4.13. Arbitrary commands for specific windows (for_window)</h3>
1422 <div class="paragraph"><p>With the <code>for_window</code> command, you can let i3 execute any command when it
1423 encounters a specific window. This can be used to set windows to floating or to
1424 change their border style, for example.</p></div>
1425 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1426 <div class="listingblock">
1427 <div class="content">
1428 <pre><code>for_window &lt;criteria&gt; &lt;command&gt;</code></pre>
1429 </div></div>
1430 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1431 <div class="listingblock">
1432 <div class="content">
1433 <pre><code># enable floating mode for all XTerm windows
1434 for_window [class="XTerm"] floating enable
1435
1436 # Make all urxvts use a 1-pixel border:
1437 for_window [class="urxvt"] border pixel 1
1438
1439 # A less useful, but rather funny example:
1440 # makes the window floating as soon as I change
1441 # directory to ~/work
1442 for_window [title="x200: ~/work"] floating enable</code></pre>
1443 </div></div>
1444 <div class="paragraph"><p>The valid criteria are the same as those for commands, see <a href="#command_criteria">[command_criteria]</a>.</p></div>
1445 </div>
1446 <div class="sect2">
1447 <h3 id="no_focus">4.14. Don&#8217;t focus window upon opening</h3>
1448 <div class="paragraph"><p>When a new window appears, it will be focused. The <code>no_focus</code> directive allows preventing
1449 this from happening and must be used in combination with <a href="#command_criteria">[command_criteria]</a>.</p></div>
1450 <div class="paragraph"><p>Note that this does not apply to all cases, e.g., when feeding data into a running application
1451 causing it to request being focused. To configure the behavior in such cases, refer to
1452 <a href="#focus_on_window_activation">[focus_on_window_activation]</a>.</p></div>
1453 <div class="paragraph"><p><code>no_focus</code> will also be ignored for the first window on a workspace as there shouldn&#8217;t be
1454 a reason to not focus the window in this case. This allows for better usability in
1455 combination with <code>workspace_layout</code>.</p></div>
1456 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1457 <div class="listingblock">
1458 <div class="content">
1459 <pre><code>no_focus &lt;criteria&gt;</code></pre>
1460 </div></div>
1461 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1462 <div class="listingblock">
1463 <div class="content">
1464 <pre><code>no_focus [window_role="pop-up"]</code></pre>
1465 </div></div>
1466 </div>
1467 <div class="sect2">
1468 <h3 id="variables">4.15. Variables</h3>
1469 <div class="paragraph"><p>As you learned in the section about keyboard bindings, you will have
1470 to configure lots of bindings containing modifier keys. If you want to save
1471 yourself some typing and be able to change the modifier you use later,
1472 variables can be handy.</p></div>
1473 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1474 <div class="listingblock">
1475 <div class="content">
1476 <pre><code>set $&lt;name&gt; &lt;value&gt;</code></pre>
1477 </div></div>
1478 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1479 <div class="listingblock">
1480 <div class="content">
1481 <pre><code>set $m Mod1
1482 bindsym $m+Shift+r restart</code></pre>
1483 </div></div>
1484 <div class="paragraph"><p>Variables are directly replaced in the file when parsing. Variables expansion
1485 is not recursive so it is not possible to define a variable with a value
1486 containing another variable. There is no fancy handling and there are
1487 absolutely no plans to change this. If you need a more dynamic configuration
1488 you should create a little script which generates a configuration file and run
1489 it before starting i3 (for example in your <code>~/.xsession</code> file).</p></div>
1490 <div class="paragraph"><p>Also see <a href="#xresources">[xresources]</a> to learn how to create variables based on resources
1491 loaded from the X resource database.</p></div>
1492 </div>
1493 <div class="sect2">
1494 <h3 id="xresources">4.16. X resources</h3>
1495 <div class="paragraph"><p><a href="#variables">[variables]</a> can also be created using a value configured in the X resource
1496 database. This is useful, for example, to avoid configuring color values within
1497 the i3 configuration. Instead, the values can be configured, once, in the X
1498 resource database to achieve an easily maintainable, consistent color theme
1499 across many X applications.</p></div>
1500 <div class="paragraph"><p>Defining a resource will load this resource from the resource database and
1501 assign its value to the specified variable. This is done verbatim and the value
1502 must therefore be in the format that i3 uses. A fallback must be specified in
1503 case the resource cannot be loaded from the database.</p></div>
1504 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1505 <div class="listingblock">
1506 <div class="content">
1507 <pre><code>set_from_resource $&lt;name&gt; &lt;resource_name&gt; &lt;fallback&gt;</code></pre>
1508 </div></div>
1509 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1510 <div class="listingblock">
1511 <div class="content">
1512 <pre><code># The ~/.Xresources should contain a line such as
1513 # *color0: #121212
1514 # and must be loaded properly, e.g., by using
1515 # xrdb ~/.Xresources
1516 # This value is picked up on by other applications (e.g., the URxvt terminal
1517 # emulator) and can be used in i3 like this:
1518 set_from_resource $black i3wm.color0 #000000</code></pre>
1519 </div></div>
1520 </div>
1521 <div class="sect2">
1522 <h3 id="assign_workspace">4.17. Automatically putting clients on specific workspaces</h3>
1523 <div class="paragraph"><p>To automatically make a specific window show up on a specific workspace, you
1524 can use an <strong>assignment</strong>. You can match windows by using any criteria,
1525 see <a href="#command_criteria">[command_criteria]</a>. The difference between <code>assign</code> and
1526 <code>for_window &lt;criteria&gt; move to workspace</code> is that the former will only be
1527 executed when the application maps the window (mapping means actually displaying
1528 it on the screen) but the latter will be executed whenever a window changes its
1529 properties to something that matches the specified criteria.</p></div>
1530 <div class="paragraph"><p>Thus, it is recommended that you match on window classes (and instances, when
1531 appropriate) instead of window titles whenever possible because some
1532 applications first create their window, and then worry about setting the correct
1533 title. Firefox with Vimperator comes to mind. The window starts up being named
1534 Firefox, and only when Vimperator is loaded does the title change. As i3 will
1535 get the title as soon as the application maps the window, you’d need to have to
1536 match on <em>Firefox</em> in this case.
1537 Another known issue is with Spotify, which doesn&#8217;t set the class hints when
1538 mapping the window, meaning you&#8217;ll have to use a <code>for_window</code> rule to assign
1539 Spotify to a specific workspace.
1540 Finally, using <code>assign [tiling]</code> and <code>assign [floating]</code> is not supported.</p></div>
1541 <div class="paragraph"><p>You can also assign a window to show up on a specific output. You can use RandR
1542 names such as <code>VGA1</code> or names relative to the output with the currently focused
1543 workspace such as <code>left</code> and <code>down</code>.</p></div>
1544 <div class="paragraph"><p>Assignments are processed by i3 in the order in which they appear in the config
1545 file. The first one which matches the window wins and later assignments are not
1546 considered.</p></div>
1547 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1548 <div class="listingblock">
1549 <div class="content">
1550 <pre><code>assign &lt;criteria&gt; [→] [workspace] [number] &lt;workspace&gt;
1551 assign &lt;criteria&gt; [→] output left|right|up|down|primary|&lt;output&gt;</code></pre>
1552 </div></div>
1553 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1554 <div class="listingblock">
1555 <div class="content">
1556 <pre><code># Assign URxvt terminals to workspace 2
1557 assign [class="URxvt"] 2
1558
1559 # Same thing, but more precise (exact match instead of substring)
1560 assign [class="^URxvt$"] 2
1561
1562 # Same thing, but with a beautiful arrow :)
1563 assign [class="^URxvt$"] → 2
1564
1565 # Assignment to a named workspace
1566 assign [class="^URxvt$"] → work
1567
1568 # Assign to the workspace with number 2, regardless of name
1569 assign [class="^URxvt$"] → number 2
1570
1571 # You can also specify a number + name. If the workspace with number 2 exists, assign will skip the text part.
1572 assign [class="^URxvt$"] → number "2: work"
1573
1574 # Start urxvt -name irssi
1575 assign [class="^URxvt$" instance="^irssi$"] → 3
1576
1577 # Assign urxvt to the output right of the current one
1578 assign [class="^URxvt$"] → output right
1579
1580 # Assign urxvt to the primary output
1581 assign [class="^URxvt$"] → output primary</code></pre>
1582 </div></div>
1583 <div class="paragraph"><p>Note that you might not have a primary output configured yet. To do so, run:</p></div>
1584 <div class="listingblock">
1585 <div class="content">
1586 <pre><code>xrandr --output &lt;output&gt; --primary</code></pre>
1587 </div></div>
1588 <div class="paragraph"><p>Also, the arrow is not required, it just looks good :-). If you decide to
1589 use it, it has to be a UTF-8 encoded arrow, not <code>-&gt;</code> or something like that.</p></div>
1590 <div class="paragraph"><p>To get the class and instance, you can use <code>xprop</code>. After clicking on the
1591 window, you will see the following output:</p></div>
1592 <div class="paragraph"><p><strong>xprop</strong>:</p></div>
1593 <div class="listingblock">
1594 <div class="content">
1595 <pre><code>WM_CLASS(STRING) = "irssi", "URxvt"</code></pre>
1596 </div></div>
1597 <div class="paragraph"><p>The first part of the WM_CLASS is the instance ("irssi" in this example), the
1598 second part is the class ("URxvt" in this example).</p></div>
1599 <div class="paragraph"><p>Should you have any problems with assignments, make sure to check the i3
1600 logfile first (see <a href="https://i3wm.org/docs/debugging.html">https://i3wm.org/docs/debugging.html</a>). It includes more
1601 details about the matching process and the window’s actual class, instance and
1602 title when starting up.</p></div>
1603 <div class="paragraph"><p>Note that if you want to start an application just once on a specific
1604 workspace, but you don’t want to assign all instances of it permanently, you
1605 can make use of i3’s startup-notification support (see <a href="#exec">[exec]</a>) in your config
1606 file in the following way:</p></div>
1607 <div class="paragraph"><p><strong>Start iceweasel on workspace 3 (once)</strong>:</p></div>
1608 <div class="listingblock">
1609 <div class="content">
1610 <pre><code># Start iceweasel on workspace 3, then switch back to workspace 1
1611 # (Being a command-line utility, i3-msg does not support startup notifications,
1612 # hence the exec --no-startup-id.)
1613 # (Starting iceweasel with i3’s exec command is important in order to make i3
1614 # create a startup notification context, without which the iceweasel window(s)
1615 # cannot be matched onto the workspace on which the command was started.)
1616 exec --no-startup-id i3-msg 'workspace 3; exec iceweasel; workspace 1'</code></pre>
1617 </div></div>
1618 </div>
1619 <div class="sect2">
1620 <h3 id="_automatically_starting_applications_on_i3_startup">4.18. Automatically starting applications on i3 startup</h3>
1621 <div class="paragraph"><p>By using the <code>exec</code> keyword outside a keybinding, you can configure
1622 which commands will be performed by i3 on initial startup. <code>exec</code>
1623 commands will not run when restarting i3, if you need a command to run
1624 also when restarting i3 you should use the <code>exec_always</code>
1625 keyword. These commands will be run in order.</p></div>
1626 <div class="paragraph"><p>See <a href="#command_chaining">[command_chaining]</a> for details on the special meaning of <code>;</code> (semicolon)
1627 and <code>,</code> (comma): they chain commands together in i3, so you need to use quoted
1628 strings (as shown in <a href="#exec_quoting">[exec_quoting]</a>) if they appear in your command.</p></div>
1629 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1630 <div class="listingblock">
1631 <div class="content">
1632 <pre><code>exec [--no-startup-id] &lt;command&gt;
1633 exec_always [--no-startup-id] &lt;command&gt;</code></pre>
1634 </div></div>
1635 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1636 <div class="listingblock">
1637 <div class="content">
1638 <pre><code>exec chromium
1639 exec_always ~/my_script.sh
1640
1641 # Execute the terminal emulator urxvt, which is not yet startup-notification aware.
1642 exec --no-startup-id urxvt</code></pre>
1643 </div></div>
1644 <div class="paragraph"><p>The flag --no-startup-id is explained in <a href="#exec">[exec]</a>.</p></div>
1645 </div>
1646 <div class="sect2">
1647 <h3 id="workspace_screen">4.19. Automatically putting workspaces on specific screens</h3>
1648 <div class="paragraph"><p>If you assign clients to workspaces, it might be handy to put the
1649 workspaces on specific screens. Also, the assignment of workspaces to screens
1650 will determine which workspace i3 uses for a new screen when adding screens
1651 or when starting (e.g., by default it will use 1 for the first screen, 2 for
1652 the second screen and so on).</p></div>
1653 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1654 <div class="listingblock">
1655 <div class="content">
1656 <pre><code>workspace &lt;workspace&gt; output &lt;output1&gt; [output2]…</code></pre>
1657 </div></div>
1658 <div class="paragraph"><p>The <em>output</em> is the name of the RandR output you attach your screen to. On a
1659 laptop, you might have VGA1 and LVDS1 as output names. You can see the
1660 available outputs by running <code>xrandr --current</code>.</p></div>
1661 <div class="paragraph"><p>If your X server supports RandR 1.5 or newer, i3 will use RandR monitor objects
1662 instead of output objects. Run <code>xrandr --listmonitors</code> to see a list. Usually,
1663 a monitor object contains exactly one output, and has the same name as the
1664 output; but should that not be the case, you may specify the name of either the
1665 monitor or the output in i3&#8217;s configuration. For example, the Dell UP2414Q uses
1666 two scalers internally, so its output names might be “DP1” and “DP2”, but the
1667 monitor name is “Dell UP2414Q”.</p></div>
1668 <div class="paragraph"><p>(Note that even if you specify the name of an output which doesn&#8217;t span the
1669 entire monitor, i3 will still use the entire area of the containing monitor
1670 rather than that of just the output&#8217;s.)</p></div>
1671 <div class="paragraph"><p>You can specify multiple outputs. The first available will be used.</p></div>
1672 <div class="paragraph"><p>If you use named workspaces, they must be quoted:</p></div>
1673 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1674 <div class="listingblock">
1675 <div class="content">
1676 <pre><code>workspace 1 output LVDS1
1677 workspace 2 output primary
1678 workspace 5 output VGA1 LVDS1
1679 workspace "2: vim" output VGA1</code></pre>
1680 </div></div>
1681 </div>
1682 <div class="sect2">
1683 <h3 id="_changing_colors">4.20. Changing colors</h3>
1684 <div class="paragraph"><p>You can change all colors which i3 uses to draw the window decorations.</p></div>
1685 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1686 <div class="listingblock">
1687 <div class="content">
1688 <pre><code>&lt;colorclass&gt; &lt;border&gt; &lt;background&gt; &lt;text&gt; &lt;indicator&gt; &lt;child_border&gt;</code></pre>
1689 </div></div>
1690 <div class="paragraph"><p>Where colorclass can be one of:</p></div>
1691 <div class="dlist"><dl>
1692 <dt class="hdlist1">
1693 client.focused
1694 </dt>
1695 <dd>
1696 <p>
1697 A client which currently has the focus.
1698 </p>
1699 </dd>
1700 <dt class="hdlist1">
1701 client.focused_inactive
1702 </dt>
1703 <dd>
1704 <p>
1705 A client which is the focused one of its container, but it does not have
1706 the focus at the moment.
1707 </p>
1708 </dd>
1709 <dt class="hdlist1">
1710 client.unfocused
1711 </dt>
1712 <dd>
1713 <p>
1714 A client which is not the focused one of its container.
1715 </p>
1716 </dd>
1717 <dt class="hdlist1">
1718 client.urgent
1719 </dt>
1720 <dd>
1721 <p>
1722 A client which has its urgency hint activated.
1723 </p>
1724 </dd>
1725 <dt class="hdlist1">
1726 client.placeholder
1727 </dt>
1728 <dd>
1729 <p>
1730 Background and text color are used to draw placeholder window contents
1731 (when restoring layouts). Border and indicator are ignored.
1732 </p>
1733 </dd>
1734 <dt class="hdlist1">
1735 client.background
1736 </dt>
1737 <dd>
1738 <p>
1739 Background color which will be used to paint the background of the
1740 client window on top of which the client will be rendered. Only clients
1741 which do not cover the whole area of this window expose the color. Note
1742 that this colorclass only takes a single color.
1743 </p>
1744 </dd>
1745 </dl></div>
1746 <div class="paragraph"><p>Colors are in HTML hex format (#rrggbb), see the following example:</p></div>
1747 <div class="paragraph"><p><strong>Examples (default colors)</strong>:</p></div>
1748 <div class="listingblock">
1749 <div class="content">
1750 <pre><code># class border backgr. text indicator child_border
1751 client.focused #4c7899 #285577 #ffffff #2e9ef4 #285577
1752 client.focused_inactive #333333 #5f676a #ffffff #484e50 #5f676a
1753 client.unfocused #333333 #222222 #888888 #292d2e #222222
1754 client.urgent #2f343a #900000 #ffffff #900000 #900000
1755 client.placeholder #000000 #0c0c0c #ffffff #000000 #0c0c0c
1756
1757 client.background #ffffff</code></pre>
1758 </div></div>
1759 <div class="paragraph"><p>Note that for the window decorations, the color around the child window is the
1760 "child_border", and "border" color is only the two thin lines around the
1761 titlebar.</p></div>
1762 <div class="paragraph"><p>The indicator color is used for indicating where a new window will be opened.
1763 For horizontal split containers, the right border will be painted in indicator
1764 color, for vertical split containers, the bottom border. This only applies to
1765 single windows within a split container, which are otherwise indistinguishable
1766 from single windows outside of a split container.</p></div>
1767 </div>
1768 <div class="sect2">
1769 <h3 id="_interprocess_communication">4.21. Interprocess communication</h3>
1770 <div class="paragraph"><p>i3 uses Unix sockets to provide an IPC interface. This allows third-party
1771 programs to get information from i3, such as the current workspaces
1772 (to display a workspace bar), and to control i3.</p></div>
1773 <div class="paragraph"><p>The IPC socket is enabled by default and will be created in
1774 <code>/tmp/i3-%u.XXXXXX/ipc-socket.%p</code> where <code>%u</code> is your UNIX username, <code>%p</code> is
1775 the PID of i3 and XXXXXX is a string of random characters from the portable
1776 filename character set (see mkdtemp(3)).</p></div>
1777 <div class="paragraph"><p>You can override the default path through the environment-variable <code>I3SOCK</code> or
1778 by specifying the <code>ipc-socket</code> directive. This is discouraged, though, since i3
1779 does the right thing by default. If you decide to change it, it is strongly
1780 recommended to set this to a location in your home directory so that no other
1781 user can create that directory.</p></div>
1782 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1783 <div class="listingblock">
1784 <div class="content">
1785 <pre><code>ipc-socket ~/.i3/i3-ipc.sock</code></pre>
1786 </div></div>
1787 <div class="paragraph"><p>You can then use the <code>i3-msg</code> application to perform any command listed in
1788 <a href="#list_of_commands">[list_of_commands]</a>.</p></div>
1789 </div>
1790 <div class="sect2">
1791 <h3 id="_focus_follows_mouse">4.22. Focus follows mouse</h3>
1792 <div class="paragraph"><p>By default, window focus follows your mouse movements as the mouse crosses
1793 window borders. However, if you have a setup where your mouse usually is in your
1794 way (like a touchpad on your laptop which you do not want to disable
1795 completely), you might want to disable <em>focus follows mouse</em> and control focus
1796 only by using your keyboard. The mouse will still be useful inside the
1797 currently active window (for example to click on links in your browser window).</p></div>
1798 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1799 <div class="listingblock">
1800 <div class="content">
1801 <pre><code>focus_follows_mouse yes|no</code></pre>
1802 </div></div>
1803 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1804 <div class="listingblock">
1805 <div class="content">
1806 <pre><code>focus_follows_mouse no</code></pre>
1807 </div></div>
1808 </div>
1809 <div class="sect2">
1810 <h3 id="_mouse_warping">4.23. Mouse warping</h3>
1811 <div class="paragraph"><p>By default, when switching focus to a window on a different output (e.g.
1812 focusing a window on workspace 3 on output VGA-1, coming from workspace 2 on
1813 LVDS-1), the mouse cursor is warped to the center of that window.</p></div>
1814 <div class="paragraph"><p>With the <code>mouse_warping</code> option, you can control when the mouse cursor should
1815 be warped. <code>none</code> disables warping entirely, whereas <code>output</code> is the default
1816 behavior described above.</p></div>
1817 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1818 <div class="listingblock">
1819 <div class="content">
1820 <pre><code>mouse_warping output|none</code></pre>
1821 </div></div>
1822 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1823 <div class="listingblock">
1824 <div class="content">
1825 <pre><code>mouse_warping none</code></pre>
1826 </div></div>
1827 </div>
1828 <div class="sect2">
1829 <h3 id="_popups_during_fullscreen_mode">4.24. Popups during fullscreen mode</h3>
1830 <div class="paragraph"><p>When you are in fullscreen mode, some applications still open popup windows
1831 (take Xpdf for example). This is because these applications may not be aware
1832 that they are in fullscreen mode (they do not check the corresponding hint).
1833 There are three things which are possible to do in this situation:</p></div>
1834 <div class="olist arabic"><ol class="arabic">
1835 <li>
1836 <p>
1837 Display the popup if it belongs to the fullscreen application only. This is
1838 the default and should be reasonable behavior for most users.
1839 </p>
1840 </li>
1841 <li>
1842 <p>
1843 Just ignore the popup (don’t map it). This won’t interrupt you while you are
1844 in fullscreen. However, some apps might react badly to this (deadlock until
1845 you go out of fullscreen).
1846 </p>
1847 </li>
1848 <li>
1849 <p>
1850 Leave fullscreen mode.
1851 </p>
1852 </li>
1853 </ol></div>
1854 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1855 <div class="listingblock">
1856 <div class="content">
1857 <pre><code>popup_during_fullscreen smart|ignore|leave_fullscreen</code></pre>
1858 </div></div>
1859 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1860 <div class="listingblock">
1861 <div class="content">
1862 <pre><code>popup_during_fullscreen smart</code></pre>
1863 </div></div>
1864 </div>
1865 <div class="sect2">
1866 <h3 id="_focus_wrapping">4.25. Focus wrapping</h3>
1867 <div class="paragraph"><p>By default, when in a container with several windows or child containers, the
1868 opposite window will be focused when trying to move the focus over the edge of
1869 a container (and there are no other containers in that direction)&#8201;&#8212;&#8201;the focus
1870 wraps.</p></div>
1871 <div class="paragraph"><p>If desired, you can disable this behavior by setting the <code>focus_wrapping</code>
1872 configuration directive to the value <code>no</code>.</p></div>
1873 <div class="paragraph"><p>When enabled, focus wrapping does not occur by default if there is another
1874 window or container in the specified direction, and focus will instead be set
1875 on that window or container. This is the default behavior so you can navigate
1876 to all your windows without having to use <code>focus parent</code>.</p></div>
1877 <div class="paragraph"><p>If you want the focus to <strong>always</strong> wrap and you are aware of using <code>focus
1878 parent</code> to switch to different containers, you can instead set <code>focus_wrapping</code>
1879 to the value <code>force</code>.</p></div>
1880 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1881 <div class="listingblock">
1882 <div class="content">
1883 <pre><code>focus_wrapping yes|no|force
1884
1885 # Legacy syntax, equivalent to "focus_wrapping force"
1886 force_focus_wrapping yes</code></pre>
1887 </div></div>
1888 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
1889 <div class="listingblock">
1890 <div class="content">
1891 <pre><code># Disable focus wrapping
1892 focus_wrapping no
1893
1894 # Force focus wrapping
1895 focus_wrapping force</code></pre>
1896 </div></div>
1897 </div>
1898 <div class="sect2">
1899 <h3 id="_forcing_xinerama">4.26. Forcing Xinerama</h3>
1900 <div class="paragraph"><p>As explained in-depth in <a href="https://i3wm.org/docs/multi-monitor.html">https://i3wm.org/docs/multi-monitor.html</a>, some X11
1901 video drivers (especially the nVidia binary driver) only provide support for
1902 Xinerama instead of RandR. In such a situation, i3 must be told to use the
1903 inferior Xinerama API explicitly and therefore don’t provide support for
1904 reconfiguring your screens on the fly (they are read only once on startup and
1905 that’s it).</p></div>
1906 <div class="paragraph"><p>For people who cannot modify their <code>~/.xsession</code> to add the
1907 <code>--force-xinerama</code> commandline parameter, a configuration option is provided:</p></div>
1908 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1909 <div class="listingblock">
1910 <div class="content">
1911 <pre><code>force_xinerama yes|no</code></pre>
1912 </div></div>
1913 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1914 <div class="listingblock">
1915 <div class="content">
1916 <pre><code>force_xinerama yes</code></pre>
1917 </div></div>
1918 <div class="paragraph"><p>Also note that your output names are not descriptive (like <code>HDMI1</code>) when using
1919 Xinerama, instead they are counted up, starting at 0: <code>xinerama-0</code>, <code>xinerama-1</code>, …</p></div>
1920 </div>
1921 <div class="sect2">
1922 <h3 id="workspace_auto_back_and_forth">4.27. Automatic back-and-forth when switching to the current workspace</h3>
1923 <div class="paragraph"><p>This configuration directive enables automatic <code>workspace back_and_forth</code> (see
1924 <a href="#back_and_forth">[back_and_forth]</a>) when switching to the workspace that is currently focused.</p></div>
1925 <div class="paragraph"><p>For instance: Assume you are on workspace "1: www" and switch to "2: IM" using
1926 mod+2 because somebody sent you a message. You don’t need to remember where you
1927 came from now, you can just press $mod+2 again to switch back to "1: www".</p></div>
1928 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1929 <div class="listingblock">
1930 <div class="content">
1931 <pre><code>workspace_auto_back_and_forth yes|no</code></pre>
1932 </div></div>
1933 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1934 <div class="listingblock">
1935 <div class="content">
1936 <pre><code>workspace_auto_back_and_forth yes</code></pre>
1937 </div></div>
1938 </div>
1939 <div class="sect2">
1940 <h3 id="_delaying_urgency_hint_reset_on_workspace_change">4.28. Delaying urgency hint reset on workspace change</h3>
1941 <div class="paragraph"><p>If an application on another workspace sets an urgency hint, switching to this
1942 workspace may lead to immediate focus of the application, which also means the
1943 window decoration color would be immediately reset to <code>client.focused</code>. This
1944 may make it unnecessarily hard to tell which window originally raised the
1945 event.</p></div>
1946 <div class="paragraph"><p>In order to prevent this, you can tell i3 to delay resetting the urgency state
1947 by a certain time using the <code>force_display_urgency_hint</code> directive. Setting the
1948 value to 0 disables this feature.</p></div>
1949 <div class="paragraph"><p>The default is 500ms.</p></div>
1950 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1951 <div class="listingblock">
1952 <div class="content">
1953 <pre><code>force_display_urgency_hint &lt;timeout&gt; ms</code></pre>
1954 </div></div>
1955 <div class="paragraph"><p><strong>Example</strong>:</p></div>
1956 <div class="listingblock">
1957 <div class="content">
1958 <pre><code>force_display_urgency_hint 500 ms</code></pre>
1959 </div></div>
1960 </div>
1961 <div class="sect2">
1962 <h3 id="focus_on_window_activation">4.29. Focus on window activation</h3>
1963 <div class="paragraph"><p>If a window is activated, e.g., via <code>google-chrome www.google.com</code>, it may request
1964 to take focus. Since this may not preferable, different reactions can be configured.</p></div>
1965 <div class="paragraph"><p>Note that this may not affect windows that are being opened. To prevent new windows
1966 from being focused, see <a href="#no_focus">[no_focus]</a>.</p></div>
1967 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
1968 <div class="listingblock">
1969 <div class="content">
1970 <pre><code>focus_on_window_activation smart|urgent|focus|none</code></pre>
1971 </div></div>
1972 <div class="paragraph"><p>The different modes will act as follows:</p></div>
1973 <div class="dlist"><dl>
1974 <dt class="hdlist1">
1975 smart
1976 </dt>
1977 <dd>
1978 <p>
1979 This is the default behavior. If the window requesting focus is on an active
1980 workspace, it will receive the focus. Otherwise, the urgency hint will be set.
1981 </p>
1982 </dd>
1983 <dt class="hdlist1">
1984 urgent
1985 </dt>
1986 <dd>
1987 <p>
1988 The window will always be marked urgent, but the focus will not be stolen.
1989 </p>
1990 </dd>
1991 <dt class="hdlist1">
1992 focus
1993 </dt>
1994 <dd>
1995 <p>
1996 The window will always be focused and not be marked urgent.
1997 </p>
1998 </dd>
1999 <dt class="hdlist1">
2000 none
2001 </dt>
2002 <dd>
2003 <p>
2004 The window will neither be focused, nor be marked urgent.
2005 </p>
2006 </dd>
2007 </dl></div>
2008 </div>
2009 <div class="sect2">
2010 <h3 id="show_marks">4.30. Drawing marks on window decoration</h3>
2011 <div class="paragraph"><p>If activated, marks (see <a href="#vim_like_marks">[vim_like_marks]</a>) on windows are drawn in their window
2012 decoration. However, any mark starting with an underscore in its name (<code>_</code>) will
2013 not be drawn even if this option is activated.</p></div>
2014 <div class="paragraph"><p>The default for this option is <code>yes</code>.</p></div>
2015 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2016 <div class="listingblock">
2017 <div class="content">
2018 <pre><code>show_marks yes|no</code></pre>
2019 </div></div>
2020 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2021 <div class="listingblock">
2022 <div class="content">
2023 <pre><code>show_marks yes</code></pre>
2024 </div></div>
2025 </div>
2026 <div class="sect2">
2027 <h3 id="line_continuation">4.31. Line continuation</h3>
2028 <div class="paragraph"><p>Config files support line continuation, meaning when you end a line in a
2029 backslash character (<code>\</code>), the line-break will be ignored by the parser. This
2030 feature can be used to create more readable configuration files.
2031 Commented lines are not continued.</p></div>
2032 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
2033 <div class="listingblock">
2034 <div class="content">
2035 <pre><code>bindsym Mod1+f \
2036 fullscreen toggle
2037
2038 # this line is not continued \
2039 bindsym Mod1+F fullscreen toggle</code></pre>
2040 </div></div>
2041 </div>
2042 </div>
2043 </div>
2044 <div class="sect1">
2045 <h2 id="_configuring_i3bar">5. Configuring i3bar</h2>
2046 <div class="sectionbody">
2047 <div class="paragraph"><p>The bar at the bottom of your monitor is drawn by a separate process called
2048 i3bar. Having this part of "the i3 user interface" in a separate process has
2049 several advantages:</p></div>
2050 <div class="olist arabic"><ol class="arabic">
2051 <li>
2052 <p>
2053 It is a modular approach. If you don’t need a workspace bar at all, or if
2054 you prefer a different one (dzen2, xmobar, maybe even gnome-panel?), you can
2055 just remove the i3bar configuration and start your favorite bar instead.
2056 </p>
2057 </li>
2058 <li>
2059 <p>
2060 It follows the UNIX philosophy of "Make each program do one thing well".
2061 While i3 manages your windows well, i3bar is good at displaying a bar on
2062 each monitor (unless you configure it otherwise).
2063 </p>
2064 </li>
2065 <li>
2066 <p>
2067 It leads to two separate, clean codebases. If you want to understand i3, you
2068 don’t need to bother with the details of i3bar and vice versa.
2069 </p>
2070 </li>
2071 </ol></div>
2072 <div class="paragraph"><p>That said, i3bar is configured in the same configuration file as i3. This is
2073 because it is tightly coupled with i3 (in contrary to i3lock or i3status which
2074 are useful for people using other window managers). Therefore, it makes no
2075 sense to use a different configuration place when we already have a good
2076 configuration infrastructure in place.</p></div>
2077 <div class="paragraph"><p>Configuring your workspace bar starts with opening a <code>bar</code> block. You can have
2078 multiple bar blocks to use different settings for different outputs (monitors):</p></div>
2079 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2080 <div class="listingblock">
2081 <div class="content">
2082 <pre><code>bar {
2083 status_command i3status
2084 }</code></pre>
2085 </div></div>
2086 <div class="sect2">
2087 <h3 id="_i3bar_command">5.1. i3bar command</h3>
2088 <div class="paragraph"><p>By default i3 will just pass <code>i3bar</code> and let your shell handle the execution,
2089 searching your <code>$PATH</code> for a correct version.
2090 If you have a different <code>i3bar</code> somewhere or the binary is not in your <code>$PATH</code> you can
2091 tell i3 what to execute.</p></div>
2092 <div class="paragraph"><p>The specified command will be passed to <code>sh -c</code>, so you can use globbing and
2093 have to have correct quoting etc.</p></div>
2094 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2095 <div class="listingblock">
2096 <div class="content">
2097 <pre><code>i3bar_command &lt;command&gt;</code></pre>
2098 </div></div>
2099 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2100 <div class="listingblock">
2101 <div class="content">
2102 <pre><code>bar {
2103 i3bar_command /home/user/bin/i3bar
2104 }</code></pre>
2105 </div></div>
2106 </div>
2107 <div class="sect2">
2108 <h3 id="status_command">5.2. Statusline command</h3>
2109 <div class="paragraph"><p>i3bar can run a program and display every line of its <code>stdout</code> output on the
2110 right hand side of the bar. This is useful to display system information like
2111 your current IP address, battery status or date/time.</p></div>
2112 <div class="paragraph"><p>The specified command will be passed to <code>sh -c</code>, so you can use globbing and
2113 have to have correct quoting etc. Note that for signal handling, depending on
2114 your shell (users of dash(1) are known to be affected), you have to use the
2115 shell’s exec command so that signals are passed to your program, not to the
2116 shell.</p></div>
2117 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2118 <div class="listingblock">
2119 <div class="content">
2120 <pre><code>status_command &lt;command&gt;</code></pre>
2121 </div></div>
2122 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2123 <div class="listingblock">
2124 <div class="content">
2125 <pre><code>bar {
2126 status_command i3status --config ~/.i3status.conf
2127
2128 # For dash(1) users who want signal handling to work:
2129 status_command exec ~/.bin/my_status_command
2130 }</code></pre>
2131 </div></div>
2132 </div>
2133 <div class="sect2">
2134 <h3 id="_display_mode">5.3. Display mode</h3>
2135 <div class="paragraph"><p>You can either have i3bar be visible permanently at one edge of the screen
2136 (<code>dock</code> mode) or make it show up when you press your modifier key (<code>hide</code> mode).
2137 It is also possible to force i3bar to always stay hidden (<code>invisible</code>
2138 mode). The modifier key can be configured using the <code>modifier</code> option.</p></div>
2139 <div class="paragraph"><p>The mode option can be changed during runtime through the <code>bar mode</code> command.
2140 On reload the mode will be reverted to its configured value.</p></div>
2141 <div class="paragraph"><p>The hide mode maximizes screen space that can be used for actual windows. Also,
2142 i3bar sends the <code>SIGSTOP</code> and <code>SIGCONT</code> signals to the statusline process to
2143 save battery power.</p></div>
2144 <div class="paragraph"><p>Invisible mode allows to permanently maximize screen space, as the bar is never
2145 shown. Thus, you can configure i3bar to not disturb you by popping up because
2146 of an urgency hint or because the modifier key is pressed.</p></div>
2147 <div class="paragraph"><p>In order to control whether i3bar is hidden or shown in hide mode, there exists
2148 the hidden_state option, which has no effect in dock mode or invisible mode. It
2149 indicates the current hidden_state of the bar: (1) The bar acts like in normal
2150 hide mode, it is hidden and is only unhidden in case of urgency hints or by
2151 pressing the modifier key (<code>hide</code> state), or (2) it is drawn on top of the
2152 currently visible workspace (<code>show</code> state).</p></div>
2153 <div class="paragraph"><p>Like the mode, the hidden_state can also be controlled through i3, this can be
2154 done by using the <code>bar hidden_state</code> command.</p></div>
2155 <div class="paragraph"><p>The default mode is dock mode; in hide mode, the default modifier is Mod4 (usually
2156 the windows key). The default value for the hidden_state is hide.</p></div>
2157 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2158 <div class="listingblock">
2159 <div class="content">
2160 <pre><code>mode dock|hide|invisible
2161 hidden_state hide|show
2162 modifier &lt;Modifier&gt;|none</code></pre>
2163 </div></div>
2164 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2165 <div class="listingblock">
2166 <div class="content">
2167 <pre><code>bar {
2168 mode hide
2169 hidden_state hide
2170 modifier Mod1
2171 }</code></pre>
2172 </div></div>
2173 <div class="paragraph"><p>Available modifiers are Mod1-Mod5, Shift, Control (see <code>xmodmap(1)</code>). You can
2174 also use "none" if you don&#8217;t want any modifier to trigger this behavior.</p></div>
2175 </div>
2176 <div class="sect2">
2177 <h3 id="_mouse_button_commands">5.4. Mouse button commands</h3>
2178 <div class="paragraph"><p>Specifies a command to run when a button was pressed on i3bar to override the
2179 default behavior. This is useful, e.g., for disabling the scroll wheel action
2180 or running scripts that implement custom behavior for these buttons.</p></div>
2181 <div class="paragraph"><p>A button is always named <code>button&lt;n&gt;</code>, where 1 to 5 are default buttons as follows and higher
2182 numbers can be special buttons on devices offering more buttons:</p></div>
2183 <div class="dlist"><dl>
2184 <dt class="hdlist1">
2185 button1
2186 </dt>
2187 <dd>
2188 <p>
2189 Left mouse button.
2190 </p>
2191 </dd>
2192 <dt class="hdlist1">
2193 button2
2194 </dt>
2195 <dd>
2196 <p>
2197 Middle mouse button.
2198 </p>
2199 </dd>
2200 <dt class="hdlist1">
2201 button3
2202 </dt>
2203 <dd>
2204 <p>
2205 Right mouse button.
2206 </p>
2207 </dd>
2208 <dt class="hdlist1">
2209 button4
2210 </dt>
2211 <dd>
2212 <p>
2213 Scroll wheel up.
2214 </p>
2215 </dd>
2216 <dt class="hdlist1">
2217 button5
2218 </dt>
2219 <dd>
2220 <p>
2221 Scroll wheel down.
2222 </p>
2223 </dd>
2224 </dl></div>
2225 <div class="paragraph"><p>Please note that the old <code>wheel_up_cmd</code> and <code>wheel_down_cmd</code> commands are deprecated
2226 and will be removed in a future release. We strongly recommend using the more general
2227 <code>bindsym</code> with <code>button4</code> and <code>button5</code> instead.</p></div>
2228 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2229 <div class="listingblock">
2230 <div class="content">
2231 <pre><code>bindsym [--release] button&lt;n&gt; &lt;command&gt;</code></pre>
2232 </div></div>
2233 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2234 <div class="listingblock">
2235 <div class="content">
2236 <pre><code>bar {
2237 # disable clicking on workspace buttons
2238 bindsym button1 nop
2239 # Take a screenshot by right clicking on the bar
2240 bindsym --release button3 exec --no-startup-id import /tmp/latest-screenshot.png
2241 # execute custom script when scrolling downwards
2242 bindsym button5 exec ~/.i3/scripts/custom_wheel_down
2243 }</code></pre>
2244 </div></div>
2245 </div>
2246 <div class="sect2">
2247 <h3 id="_bar_id">5.5. Bar ID</h3>
2248 <div class="paragraph"><p>Specifies the bar ID for the configured bar instance. If this option is missing,
2249 the ID is set to <em>bar-x</em>, where x corresponds to the position of the embedding
2250 bar block in the config file (<em>bar-0</em>, <em>bar-1</em>, &#8230;).</p></div>
2251 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2252 <div class="listingblock">
2253 <div class="content">
2254 <pre><code>id &lt;bar_id&gt;</code></pre>
2255 </div></div>
2256 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2257 <div class="listingblock">
2258 <div class="content">
2259 <pre><code>bar {
2260 id bar-1
2261 }</code></pre>
2262 </div></div>
2263 </div>
2264 <div class="sect2">
2265 <h3 id="i3bar_position">5.6. Position</h3>
2266 <div class="paragraph"><p>This option determines in which edge of the screen i3bar should show up.</p></div>
2267 <div class="paragraph"><p>The default is bottom.</p></div>
2268 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2269 <div class="listingblock">
2270 <div class="content">
2271 <pre><code>position top|bottom</code></pre>
2272 </div></div>
2273 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2274 <div class="listingblock">
2275 <div class="content">
2276 <pre><code>bar {
2277 position top
2278 }</code></pre>
2279 </div></div>
2280 </div>
2281 <div class="sect2">
2282 <h3 id="_output_s">5.7. Output(s)</h3>
2283 <div class="paragraph"><p>You can restrict i3bar to one or more outputs (monitors). The default is to
2284 handle all outputs. Restricting the outputs is useful for using different
2285 options for different outputs by using multiple <em>bar</em> blocks.</p></div>
2286 <div class="paragraph"><p>To make a particular i3bar instance handle multiple outputs, specify the output
2287 directive multiple times.</p></div>
2288 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2289 <div class="listingblock">
2290 <div class="content">
2291 <pre><code>output primary|&lt;output&gt;</code></pre>
2292 </div></div>
2293 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2294 <div class="listingblock">
2295 <div class="content">
2296 <pre><code># big monitor: everything
2297 bar {
2298 # The display is connected either via HDMI or via DisplayPort
2299 output HDMI2
2300 output DP2
2301 status_command i3status
2302 }
2303
2304 # laptop monitor: bright colors and i3status with less modules.
2305 bar {
2306 output LVDS1
2307 status_command i3status --config ~/.i3status-small.conf
2308 colors {
2309 background #000000
2310 statusline #ffffff
2311 }
2312 }
2313
2314 # show bar on the primary monitor and on HDMI2
2315 bar {
2316 output primary
2317 output HDMI2
2318 status_command i3status
2319 }</code></pre>
2320 </div></div>
2321 <div class="paragraph"><p>Note that you might not have a primary output configured yet. To do so, run:</p></div>
2322 <div class="listingblock">
2323 <div class="content">
2324 <pre><code>xrandr --output &lt;output&gt; --primary</code></pre>
2325 </div></div>
2326 </div>
2327 <div class="sect2">
2328 <h3 id="_tray_output">5.8. Tray output</h3>
2329 <div class="paragraph"><p>i3bar by default provides a system tray area where programs such as
2330 NetworkManager, VLC, Pidgin, etc. can place little icons.</p></div>
2331 <div class="paragraph"><p>You can configure on which output (monitor) the icons should be displayed or
2332 you can turn off the functionality entirely.</p></div>
2333 <div class="paragraph"><p>You can use multiple <code>tray_output</code> directives in your config to specify a list
2334 of outputs on which you want the tray to appear. The first available output in
2335 that list as defined by the order of the directives will be used for the tray
2336 output.</p></div>
2337 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2338 <div class="listingblock">
2339 <div class="content">
2340 <pre><code>tray_output none|primary|&lt;output&gt;</code></pre>
2341 </div></div>
2342 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2343 <div class="listingblock">
2344 <div class="content">
2345 <pre><code># disable system tray
2346 bar {
2347 tray_output none
2348 }
2349
2350 # show tray icons on the primary monitor
2351 bar {
2352 tray_output primary
2353 }
2354
2355 # show tray icons on the big monitor
2356 bar {
2357 tray_output HDMI2
2358 }</code></pre>
2359 </div></div>
2360 <div class="paragraph"><p>Note that you might not have a primary output configured yet. To do so, run:</p></div>
2361 <div class="listingblock">
2362 <div class="content">
2363 <pre><code>xrandr --output &lt;output&gt; --primary</code></pre>
2364 </div></div>
2365 <div class="paragraph"><p>Note that when you use multiple bar configuration blocks, either specify
2366 <code>tray_output primary</code> in all of them or explicitly specify <code>tray_output none</code>
2367 in bars which should not display the tray, otherwise the different instances
2368 might race each other in trying to display tray icons.</p></div>
2369 </div>
2370 <div class="sect2">
2371 <h3 id="_tray_padding">5.9. Tray padding</h3>
2372 <div class="paragraph"><p>The tray is shown on the right-hand side of the bar. By default, a padding of 2
2373 pixels is used for the upper, lower and right-hand side of the tray area and
2374 between the individual icons.</p></div>
2375 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2376 <div class="listingblock">
2377 <div class="content">
2378 <pre><code>tray_padding &lt;px&gt; [px]</code></pre>
2379 </div></div>
2380 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2381 <div class="listingblock">
2382 <div class="content">
2383 <pre><code># Obey Fitts's law
2384 tray_padding 0</code></pre>
2385 </div></div>
2386 </div>
2387 <div class="sect2">
2388 <h3 id="_font">5.10. Font</h3>
2389 <div class="paragraph"><p>Specifies the font to be used in the bar. See <a href="#fonts">[fonts]</a>.</p></div>
2390 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2391 <div class="listingblock">
2392 <div class="content">
2393 <pre><code>font &lt;font&gt;</code></pre>
2394 </div></div>
2395 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2396 <div class="listingblock">
2397 <div class="content">
2398 <pre><code>bar {
2399 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
2400 font pango:DejaVu Sans Mono 10
2401 }</code></pre>
2402 </div></div>
2403 </div>
2404 <div class="sect2">
2405 <h3 id="_custom_separator_symbol">5.11. Custom separator symbol</h3>
2406 <div class="paragraph"><p>Specifies a custom symbol to be used for the separator as opposed to the vertical,
2407 one pixel thick separator.</p></div>
2408 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2409 <div class="listingblock">
2410 <div class="content">
2411 <pre><code>separator_symbol &lt;symbol&gt;</code></pre>
2412 </div></div>
2413 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2414 <div class="listingblock">
2415 <div class="content">
2416 <pre><code>bar {
2417 separator_symbol ":|:"
2418 }</code></pre>
2419 </div></div>
2420 </div>
2421 <div class="sect2">
2422 <h3 id="_workspace_buttons">5.12. Workspace buttons</h3>
2423 <div class="paragraph"><p>Specifies whether workspace buttons should be shown or not. This is useful if
2424 you want to display a statusline-only bar containing additional information.</p></div>
2425 <div class="paragraph"><p>The default is to show workspace buttons.</p></div>
2426 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2427 <div class="listingblock">
2428 <div class="content">
2429 <pre><code>workspace_buttons yes|no</code></pre>
2430 </div></div>
2431 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2432 <div class="listingblock">
2433 <div class="content">
2434 <pre><code>bar {
2435 workspace_buttons no
2436 }</code></pre>
2437 </div></div>
2438 </div>
2439 <div class="sect2">
2440 <h3 id="_strip_workspace_numbers_name">5.13. Strip workspace numbers/name</h3>
2441 <div class="paragraph"><p>Specifies whether workspace numbers should be displayed within the workspace
2442 buttons. This is useful if you want to have a named workspace that stays in
2443 order on the bar according to its number without displaying the number prefix.</p></div>
2444 <div class="paragraph"><p>When <code>strip_workspace_numbers</code> is set to <code>yes</code>, any workspace that has a name of
2445 the form "[n]:[NAME]" will display only the name. You could use this, for
2446 instance, to display Roman numerals rather than digits by naming your
2447 workspaces to "1:I", "2:II", "3:III", "4:IV", &#8230;</p></div>
2448 <div class="paragraph"><p>When <code>strip_workspace_name</code> is set to <code>yes</code>, any workspace that has a name of
2449 the form "[n]:[NAME]" will display only the number.</p></div>
2450 <div class="paragraph"><p>The default is to display the full name within the workspace button.</p></div>
2451 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2452 <div class="listingblock">
2453 <div class="content">
2454 <pre><code>strip_workspace_numbers yes|no
2455 strip_workspace_name yes|no</code></pre>
2456 </div></div>
2457 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2458 <div class="listingblock">
2459 <div class="content">
2460 <pre><code>bar {
2461 strip_workspace_numbers yes
2462 }</code></pre>
2463 </div></div>
2464 </div>
2465 <div class="sect2">
2466 <h3 id="_binding_mode_indicator">5.14. Binding Mode indicator</h3>
2467 <div class="paragraph"><p>Specifies whether the current binding mode indicator should be shown or not.
2468 This is useful if you want to hide the workspace buttons but still be able
2469 to see the current binding mode indicator. See <a href="#binding_modes">[binding_modes]</a> to learn what
2470 modes are and how to use them.</p></div>
2471 <div class="paragraph"><p>The default is to show the mode indicator.</p></div>
2472 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2473 <div class="listingblock">
2474 <div class="content">
2475 <pre><code>binding_mode_indicator yes|no</code></pre>
2476 </div></div>
2477 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2478 <div class="listingblock">
2479 <div class="content">
2480 <pre><code>bar {
2481 binding_mode_indicator no
2482 }</code></pre>
2483 </div></div>
2484 </div>
2485 <div class="sect2">
2486 <h3 id="_colors">5.15. Colors</h3>
2487 <div class="paragraph"><p>As with i3, colors are in HTML hex format (#rrggbb). The following colors can
2488 be configured at the moment:</p></div>
2489 <div class="dlist"><dl>
2490 <dt class="hdlist1">
2491 background
2492 </dt>
2493 <dd>
2494 <p>
2495 Background color of the bar.
2496 </p>
2497 </dd>
2498 <dt class="hdlist1">
2499 statusline
2500 </dt>
2501 <dd>
2502 <p>
2503 Text color to be used for the statusline.
2504 </p>
2505 </dd>
2506 <dt class="hdlist1">
2507 separator
2508 </dt>
2509 <dd>
2510 <p>
2511 Text color to be used for the separator.
2512 </p>
2513 </dd>
2514 <dt class="hdlist1">
2515 focused_background
2516 </dt>
2517 <dd>
2518 <p>
2519 Background color of the bar on the currently focused monitor output. If
2520 not used, the color will be taken from <code>background</code>.
2521 </p>
2522 </dd>
2523 <dt class="hdlist1">
2524 focused_statusline
2525 </dt>
2526 <dd>
2527 <p>
2528 Text color to be used for the statusline on the currently focused
2529 monitor output. If not used, the color will be taken from <code>statusline</code>.
2530 </p>
2531 </dd>
2532 <dt class="hdlist1">
2533 focused_separator
2534 </dt>
2535 <dd>
2536 <p>
2537 Text color to be used for the separator on the currently focused
2538 monitor output. If not used, the color will be taken from <code>separator</code>.
2539 </p>
2540 </dd>
2541 <dt class="hdlist1">
2542 focused_workspace
2543 </dt>
2544 <dd>
2545 <p>
2546 Border, background and text color for a workspace button when the workspace
2547 has focus.
2548 </p>
2549 </dd>
2550 <dt class="hdlist1">
2551 active_workspace
2552 </dt>
2553 <dd>
2554 <p>
2555 Border, background and text color for a workspace button when the workspace
2556 is active (visible) on some output, but the focus is on another one.
2557 You can only tell this apart from the focused workspace when you are
2558 using multiple monitors.
2559 </p>
2560 </dd>
2561 <dt class="hdlist1">
2562 inactive_workspace
2563 </dt>
2564 <dd>
2565 <p>
2566 Border, background and text color for a workspace button when the workspace
2567 does not have focus and is not active (visible) on any output. This
2568 will be the case for most workspaces.
2569 </p>
2570 </dd>
2571 <dt class="hdlist1">
2572 urgent_workspace
2573 </dt>
2574 <dd>
2575 <p>
2576 Border, background and text color for a workspace button when the workspace
2577 contains a window with the urgency hint set.
2578 </p>
2579 </dd>
2580 <dt class="hdlist1">
2581 binding_mode
2582 </dt>
2583 <dd>
2584 <p>
2585 Border, background and text color for the binding mode indicator. If not used,
2586 the colors will be taken from <code>urgent_workspace</code>.
2587 </p>
2588 </dd>
2589 </dl></div>
2590 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2591 <div class="listingblock">
2592 <div class="content">
2593 <pre><code>colors {
2594 background &lt;color&gt;
2595 statusline &lt;color&gt;
2596 separator &lt;color&gt;
2597
2598 &lt;colorclass&gt; &lt;border&gt; &lt;background&gt; &lt;text&gt;
2599 }</code></pre>
2600 </div></div>
2601 <div class="paragraph"><p><strong>Example (default colors)</strong>:</p></div>
2602 <div class="listingblock">
2603 <div class="content">
2604 <pre><code>bar {
2605 colors {
2606 background #000000
2607 statusline #ffffff
2608 separator #666666
2609
2610 focused_workspace #4c7899 #285577 #ffffff
2611 active_workspace #333333 #5f676a #ffffff
2612 inactive_workspace #333333 #222222 #888888
2613 urgent_workspace #2f343a #900000 #ffffff
2614 binding_mode #2f343a #900000 #ffffff
2615 }
2616 }</code></pre>
2617 </div></div>
2618 </div>
2619 </div>
2620 </div>
2621 <div class="sect1">
2622 <h2 id="list_of_commands">6. List of commands</h2>
2623 <div class="sectionbody">
2624 <div class="paragraph"><p>Commands are what you bind to specific keypresses. You can also issue commands
2625 at runtime without pressing a key by using the IPC interface. An easy way to
2626 do this is to use the <code>i3-msg</code> utility:</p></div>
2627 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2628 <div class="listingblock">
2629 <div class="content">
2630 <pre><code># execute this on your shell to make the current container borderless
2631 i3-msg border none</code></pre>
2632 </div></div>
2633 <div class="paragraph" id="command_chaining"><p>Commands can be chained by using <code>;</code> (a semicolon). So, to move a window to a
2634 specific workspace and immediately switch to that workspace, you can configure
2635 the following keybinding:</p></div>
2636 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2637 <div class="listingblock">
2638 <div class="content">
2639 <pre><code>bindsym $mod+x move container to workspace 3; workspace 3</code></pre>
2640 </div></div>
2641 <div class="paragraph" id="command_criteria"><p>Furthermore, you can change the scope of a command - that is, which containers
2642 should be affected by that command, by using various criteria. The criteria
2643 are specified before any command in a pair of square brackets and are separated
2644 by space.</p></div>
2645 <div class="paragraph"><p>When using multiple commands, separate them by using a <code>,</code> (a comma) instead of
2646 a semicolon. Criteria apply only until the next semicolon, so if you use a
2647 semicolon to separate commands, only the first one will be executed for the
2648 matched window(s).</p></div>
2649 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2650 <div class="listingblock">
2651 <div class="content">
2652 <pre><code># if you want to kill all windows which have the class Firefox, use:
2653 bindsym $mod+x [class="Firefox"] kill
2654
2655 # same thing, but case-insensitive
2656 bindsym $mod+x [class="(?i)firefox"] kill
2657
2658 # kill only the About dialog from Firefox
2659 bindsym $mod+x [class="Firefox" window_role="About"] kill
2660
2661 # enable floating mode and move container to workspace 4
2662 for_window [class="^evil-app$"] floating enable, move container to workspace 4
2663
2664 # move all floating windows to the scratchpad
2665 bindsym $mod+x [floating] move scratchpad</code></pre>
2666 </div></div>
2667 <div class="paragraph"><p>The criteria which are currently implemented are:</p></div>
2668 <div class="dlist"><dl>
2669 <dt class="hdlist1">
2670 class
2671 </dt>
2672 <dd>
2673 <p>
2674 Compares the window class (the second part of WM_CLASS). Use the
2675 special value <code>__focused__</code> to match all windows having the same window
2676 class as the currently focused window.
2677 </p>
2678 </dd>
2679 <dt class="hdlist1">
2680 instance
2681 </dt>
2682 <dd>
2683 <p>
2684 Compares the window instance (the first part of WM_CLASS). Use the
2685 special value <code>__focused__</code> to match all windows having the same window
2686 instance as the currently focused window.
2687 </p>
2688 </dd>
2689 <dt class="hdlist1">
2690 window_role
2691 </dt>
2692 <dd>
2693 <p>
2694 Compares the window role (WM_WINDOW_ROLE). Use the special value
2695 <code>__focused__</code> to match all windows having the same window role as the
2696 currently focused window.
2697 </p>
2698 </dd>
2699 <dt class="hdlist1">
2700 window_type
2701 </dt>
2702 <dd>
2703 <p>
2704 Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
2705 <code>normal</code>, <code>dialog</code>, <code>utility</code>, <code>toolbar</code>, <code>splash</code>, <code>menu</code>, <code>dropdown_menu</code>,
2706 <code>popup_menu</code>, <code>tooltip</code> and <code>notification</code>.
2707 </p>
2708 </dd>
2709 <dt class="hdlist1">
2710 id
2711 </dt>
2712 <dd>
2713 <p>
2714 Compares the X11 window ID, which you can get via <code>xwininfo</code> for example.
2715 </p>
2716 </dd>
2717 <dt class="hdlist1">
2718 title
2719 </dt>
2720 <dd>
2721 <p>
2722 Compares the X11 window title (_NET_WM_NAME or WM_NAME as fallback).
2723 Use the special value <code>__focused__</code> to match all windows having the
2724 same window title as the currently focused window.
2725 </p>
2726 </dd>
2727 <dt class="hdlist1">
2728 urgent
2729 </dt>
2730 <dd>
2731 <p>
2732 Compares the urgent state of the window. Can be "latest" or "oldest".
2733 Matches the latest or oldest urgent window, respectively.
2734 (The following aliases are also available: newest, last, recent, first)
2735 </p>
2736 </dd>
2737 <dt class="hdlist1">
2738 workspace
2739 </dt>
2740 <dd>
2741 <p>
2742 Compares the workspace name of the workspace the window belongs to. Use
2743 the special value <code>__focused__</code> to match all windows in the currently
2744 focused workspace.
2745 </p>
2746 </dd>
2747 <dt class="hdlist1">
2748 con_mark
2749 </dt>
2750 <dd>
2751 <p>
2752 Compares the marks set for this container, see <a href="#vim_like_marks">[vim_like_marks]</a>. A
2753 match is made if any of the container&#8217;s marks matches the specified
2754 mark.
2755 </p>
2756 </dd>
2757 <dt class="hdlist1">
2758 con_id
2759 </dt>
2760 <dd>
2761 <p>
2762 Compares the i3-internal container ID, which you can get via the IPC
2763 interface. Handy for scripting. Use the special value <code>__focused__</code>
2764 to match only the currently focused window.
2765 </p>
2766 </dd>
2767 <dt class="hdlist1">
2768 floating
2769 </dt>
2770 <dd>
2771 <p>
2772 Only matches floating windows. This criterion requires no value.
2773 </p>
2774 </dd>
2775 <dt class="hdlist1">
2776 tiling
2777 </dt>
2778 <dd>
2779 <p>
2780 Only matches tiling windows. This criterion requires no value.
2781 </p>
2782 </dd>
2783 </dl></div>
2784 <div class="paragraph"><p>The criteria <code>class</code>, <code>instance</code>, <code>role</code>, <code>title</code>, <code>workspace</code> and <code>mark</code> are
2785 actually regular expressions (PCRE). See <code>pcresyntax(3)</code> or <code>perldoc perlre</code> for
2786 information on how to use them.</p></div>
2787 <div class="sect2">
2788 <h3 id="exec">6.1. Executing applications (exec)</h3>
2789 <div class="paragraph"><p>What good is a window manager if you can’t actually start any applications?
2790 The exec command starts an application by passing the command you specify to a
2791 shell. This implies that you can use globbing (wildcards) and programs will be
2792 searched in your <code>$PATH</code>.</p></div>
2793 <div class="paragraph"><p>See <a href="#command_chaining">[command_chaining]</a> for details on the special meaning of <code>;</code> (semicolon)
2794 and <code>,</code> (comma): they chain commands together in i3, so you need to use quoted
2795 strings (as shown in <a href="#exec_quoting">[exec_quoting]</a>) if they appear in your command.</p></div>
2796 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2797 <div class="listingblock">
2798 <div class="content">
2799 <pre><code>exec [--no-startup-id] &lt;command&gt;</code></pre>
2800 </div></div>
2801 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2802 <div class="listingblock">
2803 <div class="content">
2804 <pre><code># Start the GIMP
2805 bindsym $mod+g exec gimp
2806
2807 # Start the terminal emulator urxvt which is not yet startup-notification-aware
2808 bindsym $mod+Return exec --no-startup-id urxvt</code></pre>
2809 </div></div>
2810 <div class="paragraph"><p>The <code>--no-startup-id</code> parameter disables startup-notification support for this
2811 particular exec command. With startup-notification, i3 can make sure that a
2812 window appears on the workspace on which you used the exec command. Also, it
2813 will change the X11 cursor to <code>watch</code> (a clock) while the application is
2814 launching. So, if an application is not startup-notification aware (most GTK
2815 and Qt using applications seem to be, though), you will end up with a watch
2816 cursor for 60 seconds.</p></div>
2817 <div class="paragraph" id="exec_quoting"><p>If the command to be executed contains a <code>;</code> (semicolon) and/or a <code>,</code> (comma),
2818 the entire command must be quoted. For example, to have a keybinding for the
2819 shell command <code>notify-send Hello, i3</code>, you would add an entry to your
2820 configuration file like this:</p></div>
2821 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2822 <div class="listingblock">
2823 <div class="content">
2824 <pre><code># Execute a command with a comma in it
2825 bindsym $mod+p exec "notify-send Hello, i3"</code></pre>
2826 </div></div>
2827 <div class="paragraph"><p>If however a command with a comma and/or semicolon itself requires quotes, you
2828 must escape the internal quotation marks with double backslashes, like this:</p></div>
2829 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2830 <div class="listingblock">
2831 <div class="content">
2832 <pre><code># Execute a command with a comma, semicolon and internal quotes
2833 bindsym $mod+p exec "notify-send \\"Hello, i3; from $USER\\""</code></pre>
2834 </div></div>
2835 </div>
2836 <div class="sect2">
2837 <h3 id="_splitting_containers">6.2. Splitting containers</h3>
2838 <div class="paragraph"><p>The split command makes the current window a split container. Split containers
2839 can contain multiple windows. Depending on the layout of the split container,
2840 new windows get placed to the right of the current one (splith) or new windows
2841 get placed below the current one (splitv).</p></div>
2842 <div class="paragraph"><p>If you apply this command to a split container with the same orientation,
2843 nothing will happen. If you use a different orientation, the split container’s
2844 orientation will be changed (if it does not have more than one window).
2845 The <code>toggle</code> option will toggle the orientation of the split container if it
2846 contains a single window. Otherwise it makes the current window a split
2847 container with opposite orientation compared to the parent container.
2848 Use <code>layout toggle split</code> to change the layout of any split container from
2849 splitv to splith or vice-versa. You can also define a custom sequence of layouts
2850 to cycle through with <code>layout toggle</code>, see <a href="#manipulating_layout">[manipulating_layout]</a>.</p></div>
2851 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2852 <div class="listingblock">
2853 <div class="content">
2854 <pre><code>split vertical|horizontal|toggle</code></pre>
2855 </div></div>
2856 <div class="paragraph"><p><strong>Example</strong>:</p></div>
2857 <div class="listingblock">
2858 <div class="content">
2859 <pre><code>bindsym $mod+v split vertical
2860 bindsym $mod+h split horizontal
2861 bindsym $mod+t split toggle</code></pre>
2862 </div></div>
2863 </div>
2864 <div class="sect2">
2865 <h3 id="_manipulating_layout">6.3. Manipulating layout</h3>
2866 <div class="paragraph"><p>Use <code>layout toggle split</code>, <code>layout stacking</code>, <code>layout tabbed</code>, <code>layout splitv</code>
2867 or <code>layout splith</code> to change the current container layout to splith/splitv,
2868 stacking, tabbed layout, splitv or splith, respectively.</p></div>
2869 <div class="paragraph"><p>Specify up to four layouts after <code>layout toggle</code> to cycle through them. Every
2870 time the command is executed, the layout specified after the currently active
2871 one will be applied. If the currently active layout is not in the list, the
2872 first layout in the list will be activated.</p></div>
2873 <div class="paragraph"><p>To make the current window (!) fullscreen, use <code>fullscreen enable</code> (or
2874 <code>fullscreen enable global</code> for the global mode), to leave either fullscreen
2875 mode use <code>fullscreen disable</code>, and to toggle between these two states use
2876 <code>fullscreen toggle</code> (or <code>fullscreen toggle global</code>).</p></div>
2877 <div class="paragraph"><p>Likewise, to make the current window floating (or tiling again) use <code>floating
2878 enable</code> respectively <code>floating disable</code> (or <code>floating toggle</code>):</p></div>
2879 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2880 <div class="listingblock">
2881 <div class="content">
2882 <pre><code>layout default|tabbed|stacking|splitv|splith
2883 layout toggle [split|all]
2884 layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]…</code></pre>
2885 </div></div>
2886 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
2887 <div class="listingblock">
2888 <div class="content">
2889 <pre><code>bindsym $mod+s layout stacking
2890 bindsym $mod+l layout toggle split
2891 bindsym $mod+w layout tabbed
2892
2893 # Toggle between stacking/tabbed/split:
2894 bindsym $mod+x layout toggle
2895
2896 # Toggle between stacking/tabbed/splith/splitv:
2897 bindsym $mod+x layout toggle all
2898
2899 # Toggle between stacking/tabbed/splith:
2900 bindsym $mod+x layout toggle stacking tabbed splith
2901
2902 # Toggle between splitv/tabbed
2903 bindsym $mod+x layout toggle splitv tabbed
2904
2905 # Toggle between last split layout/tabbed/stacking
2906 bindsym $mod+x layout toggle split tabbed stacking
2907
2908 # Toggle fullscreen
2909 bindsym $mod+f fullscreen toggle
2910
2911 # Toggle floating/tiling
2912 bindsym $mod+t floating toggle</code></pre>
2913 </div></div>
2914 </div>
2915 <div class="sect2">
2916 <h3 id="_focusing_moving_containers">6.4. Focusing containers</h3>
2917 <div class="paragraph"><p>To change focus, you can use the <code>focus</code> command. The following options are
2918 available:</p></div>
2919 <div class="dlist"><dl>
2920 <dt class="hdlist1">
2921 &lt;criteria&gt;
2922 </dt>
2923 <dd>
2924 <p>
2925 Sets focus to the container that matches the specified criteria.
2926 See <a href="#command_criteria">[command_criteria]</a>.
2927 </p>
2928 </dd>
2929 <dt class="hdlist1">
2930 left|right|up|down
2931 </dt>
2932 <dd>
2933 <p>
2934 Sets focus to the nearest container in the given direction.
2935 </p>
2936 </dd>
2937 <dt class="hdlist1">
2938 parent
2939 </dt>
2940 <dd>
2941 <p>
2942 Sets focus to the parent container of the current container.
2943 </p>
2944 </dd>
2945 <dt class="hdlist1">
2946 child
2947 </dt>
2948 <dd>
2949 <p>
2950 The opposite of <code>focus parent</code>, sets the focus to the last focused
2951 child container.
2952 </p>
2953 </dd>
2954 <dt class="hdlist1">
2955 floating
2956 </dt>
2957 <dd>
2958 <p>
2959 Sets focus to the last focused floating container.
2960 </p>
2961 </dd>
2962 <dt class="hdlist1">
2963 tiling
2964 </dt>
2965 <dd>
2966 <p>
2967 Sets focus to the last focused tiling container.
2968 </p>
2969 </dd>
2970 <dt class="hdlist1">
2971 mode_toggle
2972 </dt>
2973 <dd>
2974 <p>
2975 Toggles between floating/tiling containers.
2976 </p>
2977 </dd>
2978 <dt class="hdlist1">
2979 output
2980 </dt>
2981 <dd>
2982 <p>
2983 Followed by a direction or an output name, this will focus the
2984 corresponding output.
2985 </p>
2986 </dd>
2987 </dl></div>
2988 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
2989 <div class="listingblock">
2990 <div class="content">
2991 <pre><code>&lt;criteria&gt; focus
2992 focus left|right|down|up
2993 focus parent|child|floating|tiling|mode_toggle
2994 focus output left|right|up|down|primary|&lt;output&gt;</code></pre>
2995 </div></div>
2996 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
2997 <div class="listingblock">
2998 <div class="content">
2999 <pre><code># Focus firefox
3000 bindsym $mod+F1 [class="Firefox"] focus
3001
3002 # Focus container on the left, bottom, top, right
3003 bindsym $mod+j focus left
3004 bindsym $mod+k focus down
3005 bindsym $mod+l focus up
3006 bindsym $mod+semicolon focus right
3007
3008 # Focus parent container
3009 bindsym $mod+u focus parent
3010
3011 # Focus last floating/tiling container
3012 bindsym $mod+g focus mode_toggle
3013
3014 # Focus the output right to the current one
3015 bindsym $mod+x focus output right
3016
3017 # Focus the big output
3018 bindsym $mod+x focus output HDMI-2
3019
3020 # Focus the primary output
3021 bindsym $mod+x focus output primary</code></pre>
3022 </div></div>
3023 <div class="paragraph"><p>Note that you might not have a primary output configured yet. To do so, run:</p></div>
3024 <div class="listingblock">
3025 <div class="content">
3026 <pre><code>xrandr --output &lt;output&gt; --primary</code></pre>
3027 </div></div>
3028 </div>
3029 <div class="sect2">
3030 <h3 id="_moving_containers">6.5. Moving containers</h3>
3031 <div class="paragraph"><p>Use the <code>move</code> command to move a container.</p></div>
3032 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3033 <div class="listingblock">
3034 <div class="content">
3035 <pre><code># Moves the container into the given direction.
3036 # The optional pixel argument specifies how far the
3037 # container should be moved if it is floating and
3038 # defaults to 10 pixels.
3039 move &lt;left|right|down|up&gt; [&lt;px&gt; px]
3040
3041 # Moves the container to the specified pos_x and pos_y
3042 # coordinates on the screen.
3043 move position &lt;pos_x&gt; [px] &lt;pos_y&gt; [px]
3044
3045 # Moves the container to the center of the screen.
3046 # If 'absolute' is used, it is moved to the center of
3047 # all outputs.
3048 move [absolute] position center
3049
3050 # Moves the container to the current position of the
3051 # mouse cursor. Only affects floating containers.
3052 move position mouse</code></pre>
3053 </div></div>
3054 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3055 <div class="listingblock">
3056 <div class="content">
3057 <pre><code># Move container to the left, bottom, top, right
3058 bindsym $mod+j move left
3059 bindsym $mod+k move down
3060 bindsym $mod+l move up
3061 bindsym $mod+semicolon move right
3062
3063 # Move container, but make floating containers
3064 # move more than the default
3065 bindsym $mod+j move left 20 px
3066
3067 # Move floating container to the center of all outputs
3068 bindsym $mod+c move absolute position center
3069
3070 # Move container to the current position of the cursor
3071 bindsym $mod+m move position mouse</code></pre>
3072 </div></div>
3073 </div>
3074 <div class="sect2">
3075 <h3 id="_swapping_containers">6.6. Swapping containers</h3>
3076 <div class="paragraph"><p>Two containers can be swapped (i.e., move to each other&#8217;s position) by using
3077 the <code>swap</code> command. They will assume the position and geometry of the container
3078 they are swapped with.</p></div>
3079 <div class="paragraph"><p>The first container to participate in the swapping can be selected through the
3080 normal command criteria process with the focused window being the usual
3081 fallback if no criteria are specified. The second container can be selected
3082 using one of the following methods:</p></div>
3083 <div class="dlist"><dl>
3084 <dt class="hdlist1">
3085 <code>id</code>
3086 </dt>
3087 <dd>
3088 <p>
3089 The X11 window ID of a client window.
3090 </p>
3091 </dd>
3092 <dt class="hdlist1">
3093 <code>con_id</code>
3094 </dt>
3095 <dd>
3096 <p>
3097 The i3 container ID of a container.
3098 </p>
3099 </dd>
3100 <dt class="hdlist1">
3101 <code>mark</code>
3102 </dt>
3103 <dd>
3104 <p>
3105 A container with the specified mark, see <a href="#vim_like_marks">[vim_like_marks]</a>.
3106 </p>
3107 </dd>
3108 </dl></div>
3109 <div class="paragraph"><p>Note that swapping does not work with all containers. Most notably, swapping
3110 floating containers or containers that have a parent-child relationship to one
3111 another does not work.</p></div>
3112 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3113 <div class="listingblock">
3114 <div class="content">
3115 <pre><code>swap container with id|con_id|mark &lt;arg&gt;</code></pre>
3116 </div></div>
3117 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3118 <div class="listingblock">
3119 <div class="content">
3120 <pre><code># Swaps the focused container with the container marked »swapee«.
3121 swap container with mark swapee
3122
3123 # Swaps container marked »A« and »B«
3124 [con_mark="^A$"] swap container with mark B</code></pre>
3125 </div></div>
3126 </div>
3127 <div class="sect2">
3128 <h3 id="_sticky_floating_windows">6.7. Sticky floating windows</h3>
3129 <div class="paragraph"><p>If you want a window to stick to the glass, i.e., have it stay on screen even
3130 if you switch to another workspace, you can use the <code>sticky</code> command. For
3131 example, this can be useful for notepads, a media player or a video chat
3132 window.</p></div>
3133 <div class="paragraph"><p>Note that while any window can be made sticky through this command, it will
3134 only take effect if the window is floating.</p></div>
3135 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3136 <div class="listingblock">
3137 <div class="content">
3138 <pre><code>sticky enable|disable|toggle</code></pre>
3139 </div></div>
3140 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3141 <div class="listingblock">
3142 <div class="content">
3143 <pre><code># make a terminal sticky that was started as a notepad
3144 for_window [instance=notepad] sticky enable</code></pre>
3145 </div></div>
3146 </div>
3147 <div class="sect2">
3148 <h3 id="_changing_named_workspaces_moving_to_workspaces">6.8. Changing (named) workspaces/moving to workspaces</h3>
3149 <div class="paragraph"><p>To change to a specific workspace, use the <code>workspace</code> command, followed by the
3150 number or name of the workspace. Pass the optional flag
3151 <code>--no-auto-back-and-forth</code> to disable <a href="#workspace_auto_back_and_forth">[workspace_auto_back_and_forth]</a> for this
3152 specific call only.</p></div>
3153 <div class="paragraph"><p>To move containers to specific workspaces, use <code>move container to workspace</code>.</p></div>
3154 <div class="paragraph"><p>You can also switch to the next and previous workspace with the commands
3155 <code>workspace next</code> and <code>workspace prev</code>, which is handy, for example, if you have
3156 workspace 1, 3, 4 and 9 and you want to cycle through them with a single key
3157 combination. To restrict those to the current output, use <code>workspace
3158 next_on_output</code> and <code>workspace prev_on_output</code>. Similarly, you can use <code>move
3159 container to workspace next</code>, <code>move container to workspace prev</code> to move a
3160 container to the next/previous workspace and <code>move container to workspace current</code>
3161 (the last one makes sense only when used with criteria).</p></div>
3162 <div class="paragraph"><p><code>workspace next</code> cycles through either numbered or named workspaces. But when it
3163 reaches the last numbered/named workspace, it looks for named workspaces after
3164 exhausting numbered ones and looks for numbered ones after exhausting named ones.</p></div>
3165 <div class="paragraph"><p>See <a href="#move_to_outputs">[move_to_outputs]</a> for how to move a container/workspace to a different
3166 RandR output.</p></div>
3167 <div class="paragraph"><p>Workspace names are parsed as
3168 <a href="https://developer.gnome.org/pango/stable/PangoMarkupFormat.html">Pango markup</a>
3169 by i3bar.</p></div>
3170 <div class="paragraph" id="back_and_forth"><p>To switch back to the previously focused workspace, use <code>workspace
3171 back_and_forth</code>; likewise, you can move containers to the previously focused
3172 workspace using <code>move container to workspace back_and_forth</code>.</p></div>
3173 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3174 <div class="listingblock">
3175 <div class="content">
3176 <pre><code>workspace next|prev|next_on_output|prev_on_output
3177 workspace back_and_forth
3178 workspace [--no-auto-back-and-forth] &lt;name&gt;
3179 workspace [--no-auto-back-and-forth] number &lt;name&gt;
3180
3181 move [--no-auto-back-and-forth] [window|container] [to] workspace &lt;name&gt;
3182 move [--no-auto-back-and-forth] [window|container] [to] workspace number &lt;name&gt;
3183 move [window|container] [to] workspace prev|next|current</code></pre>
3184 </div></div>
3185 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3186 <div class="listingblock">
3187 <div class="content">
3188 <pre><code>bindsym $mod+1 workspace 1
3189 bindsym $mod+2 workspace 2
3190 bindsym $mod+3 workspace 3:&lt;span foreground="red"&gt;vim&lt;/span&gt;
3191 ...
3192
3193 bindsym $mod+Shift+1 move container to workspace 1
3194 bindsym $mod+Shift+2 move container to workspace 2
3195 ...
3196
3197 # switch between the current and the previously focused one
3198 bindsym $mod+b workspace back_and_forth
3199 bindsym $mod+Shift+b move container to workspace back_and_forth
3200
3201 # move the whole workspace to the next output
3202 bindsym $mod+x move workspace to output right
3203
3204 # move firefox to current workspace
3205 bindsym $mod+F1 [class="Firefox"] move workspace current</code></pre>
3206 </div></div>
3207 <div class="sect3">
3208 <h4 id="_named_workspaces">6.8.1. Named workspaces</h4>
3209 <div class="paragraph"><p>Workspaces are identified by their name. So, instead of using numbers in the
3210 workspace command, you can use an arbitrary name:</p></div>
3211 <div class="paragraph"><p><strong>Example</strong>:</p></div>
3212 <div class="listingblock">
3213 <div class="content">
3214 <pre><code>bindsym $mod+1 workspace mail
3215 ...</code></pre>
3216 </div></div>
3217 <div class="paragraph"><p>If you want the workspace to have a number <strong>and</strong> a name, just prefix the
3218 number, like this:</p></div>
3219 <div class="paragraph"><p><strong>Example</strong>:</p></div>
3220 <div class="listingblock">
3221 <div class="content">
3222 <pre><code>bindsym $mod+1 workspace 1: mail
3223 bindsym $mod+2 workspace 2: www
3224 ...</code></pre>
3225 </div></div>
3226 <div class="paragraph"><p>Note that the workspace will really be named "1: mail". i3 treats workspace
3227 names beginning with a number in a slightly special way. Normally, named
3228 workspaces are ordered the way they appeared. When they start with a number, i3
3229 will order them numerically. Also, you will be able to use <code>workspace number 1</code>
3230 to switch to the workspace which begins with number 1, regardless of which name
3231 it has. This is useful in case you are changing the workspace’s name
3232 dynamically. To combine both commands you can use <code>workspace number 1: mail</code> to
3233 specify a default name if there&#8217;s currently no workspace starting with a "1".</p></div>
3234 </div>
3235 <div class="sect3">
3236 <h4 id="_renaming_workspaces">6.8.2. Renaming workspaces</h4>
3237 <div class="paragraph"><p>You can rename workspaces. This might be useful to start with the default
3238 numbered workspaces, do your work, and rename the workspaces afterwards to
3239 reflect what’s actually on them. You can also omit the old name to rename
3240 the currently focused workspace. This is handy if you want to use the
3241 rename command with <code>i3-input</code>.</p></div>
3242 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3243 <div class="listingblock">
3244 <div class="content">
3245 <pre><code>rename workspace &lt;old_name&gt; to &lt;new_name&gt;
3246 rename workspace to &lt;new_name&gt;</code></pre>
3247 </div></div>
3248 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3249 <div class="listingblock">
3250 <div class="content">
3251 <pre><code>i3-msg 'rename workspace 5 to 6'
3252 i3-msg 'rename workspace 1 to "1: www"'
3253 i3-msg 'rename workspace "1: www" to "10: www"'
3254 i3-msg 'rename workspace to "2: mail"'
3255 bindsym $mod+r exec i3-input -F 'rename workspace to "%s"' -P 'New name: '</code></pre>
3256 </div></div>
3257 <div class="paragraph"><p>If you want to rename workspaces on demand while keeping the navigation stable,
3258 you can use a setup like this:</p></div>
3259 <div class="paragraph"><p><strong>Example</strong>:</p></div>
3260 <div class="listingblock">
3261 <div class="content">
3262 <pre><code>bindsym $mod+1 workspace number "1: www"
3263 bindsym $mod+2 workspace number "2: mail"
3264 ...</code></pre>
3265 </div></div>
3266 <div class="paragraph"><p>If a workspace does not exist, the command <code>workspace number "1: mail"</code> will
3267 create workspace "1: mail".</p></div>
3268 <div class="paragraph"><p>If a workspace with number 1 does already exist, the command will switch to this
3269 workspace and ignore the text part. So even when the workspace has been renamed
3270 to "1: web", the above command will still switch to it.</p></div>
3271 </div>
3272 </div>
3273 <div class="sect2">
3274 <h3 id="_moving_workspaces_to_a_different_screen">6.9. Moving workspaces to a different screen</h3>
3275 <div class="paragraph"><p>See <a href="#move_to_outputs">[move_to_outputs]</a> for how to move a container/workspace to a different
3276 RandR output.</p></div>
3277 </div>
3278 <div class="sect2">
3279 <h3 id="move_to_outputs">6.10. <a id="_moving_containers_workspaces_to_randr_outputs"></a>Moving containers/workspaces to RandR outputs</h3>
3280 <div class="paragraph"><p>To move a container to another RandR output (addressed by names like <code>LVDS1</code> or
3281 <code>VGA1</code>) or to a RandR output identified by a specific direction (like <code>left</code>,
3282 <code>right</code>, <code>up</code> or <code>down</code>), there are two commands:</p></div>
3283 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3284 <div class="listingblock">
3285 <div class="content">
3286 <pre><code>move container to output left|right|down|up|current|primary|&lt;output&gt;
3287 move workspace to output left|right|down|up|current|primary|&lt;output&gt;</code></pre>
3288 </div></div>
3289 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3290 <div class="listingblock">
3291 <div class="content">
3292 <pre><code># Move the current workspace to the next output
3293 # (effectively toggles when you only have two outputs)
3294 bindsym $mod+x move workspace to output right
3295
3296 # Put this window on the presentation output.
3297 bindsym $mod+x move container to output VGA1
3298
3299 # Put this window on the primary output.
3300 bindsym $mod+x move container to output primary</code></pre>
3301 </div></div>
3302 <div class="paragraph"><p>Note that you might not have a primary output configured yet. To do so, run:</p></div>
3303 <div class="listingblock">
3304 <div class="content">
3305 <pre><code>xrandr --output &lt;output&gt; --primary</code></pre>
3306 </div></div>
3307 </div>
3308 <div class="sect2">
3309 <h3 id="_moving_containers_windows_to_marks">6.11. Moving containers/windows to marks</h3>
3310 <div class="paragraph"><p>To move a container to another container with a specific mark (see <a href="#vim_like_marks">[vim_like_marks]</a>),
3311 you can use the following command.</p></div>
3312 <div class="paragraph"><p>The window will be moved right after the marked container in the tree, i.e., it ends up
3313 in the same position as if you had opened a new window when the marked container was
3314 focused. If the mark is on a split container, the window will appear as a new child
3315 after the currently focused child within that container.</p></div>
3316 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3317 <div class="listingblock">
3318 <div class="content">
3319 <pre><code>move window|container to mark &lt;mark&gt;</code></pre>
3320 </div></div>
3321 <div class="paragraph"><p><strong>Example</strong>:</p></div>
3322 <div class="listingblock">
3323 <div class="content">
3324 <pre><code>for_window [instance="tabme"] move window to mark target</code></pre>
3325 </div></div>
3326 </div>
3327 <div class="sect2">
3328 <h3 id="resizingconfig">6.12. Resizing containers/windows</h3>
3329 <div class="paragraph"><p>If you want to resize containers/windows using your keyboard, you can use the
3330 <code>resize</code> command:</p></div>
3331 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3332 <div class="listingblock">
3333 <div class="content">
3334 <pre><code>resize grow|shrink &lt;direction&gt; [&lt;px&gt; px [or &lt;ppt&gt; ppt]]
3335 resize set [width] &lt;width&gt; [px | ppt]
3336 resize set height &lt;height&gt; [px | ppt]
3337 resize set [width] &lt;width&gt; [px | ppt] [height] &lt;height&gt; [px | ppt]</code></pre>
3338 </div></div>
3339 <div class="paragraph"><p>Direction can either be one of <code>up</code>, <code>down</code>, <code>left</code> or <code>right</code>. Or you can be
3340 less specific and use <code>width</code> or <code>height</code>, in which case i3 will take/give space
3341 from all the other containers. The optional pixel argument specifies by how many
3342 pixels a container should be grown or shrunk (the default is 10 pixels). The
3343 optional ppt argument means "percentage points", and if specified it indicates
3344 that a <strong>tiling container</strong> should be grown or shrunk by that many points, instead
3345 of by the <code>px</code> value.</p></div>
3346 <div class="paragraph"><p>Note about <code>resize set</code>: a value of 0 for &lt;width&gt; or &lt;height&gt; means "do not
3347 resize in this direction".</p></div>
3348 <div class="paragraph"><p>It is recommended to define bindings for resizing in a dedicated binding mode.
3349 See <a href="#binding_modes">[binding_modes]</a> and the example in the i3
3350 <a href="https://github.com/i3/i3/blob/next/etc/config.keycodes">default config</a> for more
3351 context.</p></div>
3352 <div class="paragraph"><p><strong>Example</strong>:</p></div>
3353 <div class="listingblock">
3354 <div class="content">
3355 <pre><code>for_window [class="urxvt"] resize set 640 480</code></pre>
3356 </div></div>
3357 </div>
3358 <div class="sect2">
3359 <h3 id="_jumping_to_specific_windows">6.13. Jumping to specific windows</h3>
3360 <div class="paragraph"><p>Often when in a multi-monitor environment, you want to quickly jump to a
3361 specific window. For example, while working on workspace 3 you may want to
3362 jump to your mail client to email your boss that you’ve achieved some
3363 important goal. Instead of figuring out how to navigate to your mail client,
3364 it would be more convenient to have a shortcut. You can use the <code>focus</code> command
3365 with criteria for that.</p></div>
3366 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3367 <div class="listingblock">
3368 <div class="content">
3369 <pre><code>[class="class"] focus
3370 [title="title"] focus</code></pre>
3371 </div></div>
3372 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3373 <div class="listingblock">
3374 <div class="content">
3375 <pre><code># Get me to the next open VIM instance
3376 bindsym $mod+a [class="urxvt" title="VIM"] focus</code></pre>
3377 </div></div>
3378 </div>
3379 <div class="sect2">
3380 <h3 id="vim_like_marks">6.14. VIM-like marks (mark/goto)</h3>
3381 <div class="paragraph"><p>This feature is like the jump feature: It allows you to directly jump to a
3382 specific window (this means switching to the appropriate workspace and setting
3383 focus to the windows). However, you can directly mark a specific window with
3384 an arbitrary label and use it afterwards. You can unmark the label in the same
3385 way, using the unmark command. If you don&#8217;t specify a label, unmark removes all
3386 marks. You do not need to ensure that your windows have unique classes or
3387 titles, and you do not need to change your configuration file.</p></div>
3388 <div class="paragraph"><p>As the command needs to include the label with which you want to mark the
3389 window, you cannot simply bind it to a key. <code>i3-input</code> is a tool created
3390 for this purpose: It lets you input a command and sends the command to i3. It
3391 can also prefix this command and display a custom prompt for the input dialog.</p></div>
3392 <div class="paragraph"><p>The additional <code>--toggle</code> option will remove the mark if the window already has
3393 this mark or add it otherwise. Note that you may need to use this in
3394 combination with <code>--add</code> (see below) as any other marks will otherwise be
3395 removed.</p></div>
3396 <div class="paragraph"><p>By default, a window can only have one mark. You can use the <code>--add</code> flag to
3397 put more than one mark on a window.</p></div>
3398 <div class="paragraph"><p>Refer to <a href="#show_marks">[show_marks]</a> if you don&#8217;t want marks to be shown in the window decoration.</p></div>
3399 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3400 <div class="listingblock">
3401 <div class="content">
3402 <pre><code>mark [--add|--replace] [--toggle] &lt;identifier&gt;
3403 [con_mark="identifier"] focus
3404 unmark &lt;identifier&gt;</code></pre>
3405 </div></div>
3406 <div class="paragraph"><p><strong>Example (in a terminal)</strong>:</p></div>
3407 <div class="listingblock">
3408 <div class="content">
3409 <pre><code># marks the focused container
3410 mark irssi
3411
3412 # focus the container with the mark "irssi"
3413 '[con_mark="irssi"] focus'
3414
3415 # remove the mark "irssi" from whichever container has it
3416 unmark irssi
3417
3418 # remove all marks on all firefox windows
3419 [class="(?i)firefox"] unmark</code></pre>
3420 </div></div>
3421 </div>
3422 <div class="sect2">
3423 <h3 id="pango_markup">6.15. Window title format</h3>
3424 <div class="paragraph"><p>By default, i3 will simply print the X11 window title. Using <code>title_format</code>,
3425 this can be customized by setting the format to the desired output. This
3426 directive supports
3427 <a href="https://developer.gnome.org/pango/stable/PangoMarkupFormat.html">Pango markup</a>
3428 and the following placeholders which will be replaced:</p></div>
3429 <div class="dlist"><dl>
3430 <dt class="hdlist1">
3431 <code>%title</code>
3432 </dt>
3433 <dd>
3434 <p>
3435 For normal windows, this is the X11 window title (_NET_WM_NAME or WM_NAME
3436 as fallback). When used on containers without a window (e.g., a split
3437 container inside a tabbed/stacked layout), this will be the tree
3438 representation of the container (e.g., "H[xterm xterm]").
3439 </p>
3440 </dd>
3441 <dt class="hdlist1">
3442 <code>%class</code>
3443 </dt>
3444 <dd>
3445 <p>
3446 The X11 window class (second part of WM_CLASS). This corresponds to the
3447 <code>class</code> criterion, see <a href="#command_criteria">[command_criteria]</a>.
3448 </p>
3449 </dd>
3450 <dt class="hdlist1">
3451 <code>%instance</code>
3452 </dt>
3453 <dd>
3454 <p>
3455 The X11 window instance (first part of WM_CLASS). This corresponds to the
3456 <code>instance</code> criterion, see <a href="#command_criteria">[command_criteria]</a>.
3457 </p>
3458 </dd>
3459 </dl></div>
3460 <div class="paragraph"><p>Using the <a href="#for_window">[for_window]</a> directive, you can set the title format for any window
3461 based on <a href="#command_criteria">[command_criteria]</a>.</p></div>
3462 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3463 <div class="listingblock">
3464 <div class="content">
3465 <pre><code>title_format &lt;format&gt;</code></pre>
3466 </div></div>
3467 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3468 <div class="listingblock">
3469 <div class="content">
3470 <pre><code># give the focused window a prefix
3471 bindsym $mod+p title_format "Important | %title"
3472
3473 # print all window titles bold
3474 for_window [class=".*"] title_format "&lt;b&gt;%title&lt;/b&gt;"
3475
3476 # print window titles of firefox windows red
3477 for_window [class="(?i)firefox"] title_format "&lt;span foreground='red'&gt;%title&lt;/span&gt;"</code></pre>
3478 </div></div>
3479 </div>
3480 <div class="sect2">
3481 <h3 id="_changing_border_style">6.16. Changing border style</h3>
3482 <div class="paragraph"><p>To change the border of the current client, you can use <code>border normal</code> to use the normal
3483 border (including window title), <code>border pixel 1</code> to use a 1-pixel border (no window title)
3484 and <code>border none</code> to make the client borderless.</p></div>
3485 <div class="paragraph"><p>There is also <code>border toggle</code> which will toggle the different border styles. The
3486 optional pixel argument can be used to specify the border width when switching
3487 to the normal and pixel styles.</p></div>
3488 <div class="paragraph"><p>Note that "pixel" refers to logical pixel. On HiDPI displays, a logical pixel
3489 may be represented by multiple physical pixels, so <code>pixel 1</code> might not
3490 necessarily translate into a single pixel row wide border.</p></div>
3491 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3492 <div class="listingblock">
3493 <div class="content">
3494 <pre><code>border normal|pixel|toggle [&lt;n&gt;]
3495 border none
3496
3497 # legacy syntax, equivalent to "border pixel 1"
3498 border 1pixel</code></pre>
3499 </div></div>
3500 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3501 <div class="listingblock">
3502 <div class="content">
3503 <pre><code># use window title, but no border
3504 bindsym $mod+t border normal 0
3505 # use no window title and a thick border
3506 bindsym $mod+y border pixel 3
3507 # use neither window title nor border
3508 bindsym $mod+u border none</code></pre>
3509 </div></div>
3510 </div>
3511 <div class="sect2">
3512 <h3 id="shmlog">6.17. Enabling shared memory logging</h3>
3513 <div class="paragraph"><p>As described in <a href="https://i3wm.org/docs/debugging.html">https://i3wm.org/docs/debugging.html</a>, i3 can log to a shared
3514 memory buffer, which you can dump using <code>i3-dump-log</code>. The <code>shmlog</code> command
3515 allows you to enable or disable the shared memory logging at runtime.</p></div>
3516 <div class="paragraph"><p>Note that when using <code>shmlog &lt;size_in_bytes&gt;</code>, the current log will be
3517 discarded and a new one will be started.</p></div>
3518 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3519 <div class="listingblock">
3520 <div class="content">
3521 <pre><code>shmlog &lt;size_in_bytes&gt;
3522 shmlog on|off|toggle</code></pre>
3523 </div></div>
3524 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3525 <div class="listingblock">
3526 <div class="content">
3527 <pre><code># Enable/disable logging
3528 bindsym $mod+x shmlog toggle
3529
3530 # or, from a terminal:
3531 # increase the shared memory log buffer to 50 MiB
3532 i3-msg shmlog $((50*1024*1024))</code></pre>
3533 </div></div>
3534 </div>
3535 <div class="sect2">
3536 <h3 id="_enabling_debug_logging">6.18. Enabling debug logging</h3>
3537 <div class="paragraph"><p>The <code>debuglog</code> command allows you to enable or disable debug logging at
3538 runtime. Debug logging is much more verbose than non-debug logging. This
3539 command does not activate shared memory logging (shmlog), and as such is most
3540 likely useful in combination with the above-described <a href="#shmlog">[shmlog]</a> command.</p></div>
3541 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3542 <div class="listingblock">
3543 <div class="content">
3544 <pre><code>debuglog on|off|toggle</code></pre>
3545 </div></div>
3546 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3547 <div class="listingblock">
3548 <div class="content">
3549 <pre><code># Enable/disable logging
3550 bindsym $mod+x debuglog toggle</code></pre>
3551 </div></div>
3552 </div>
3553 <div class="sect2">
3554 <h3 id="_reloading_restarting_exiting">6.19. Reloading/Restarting/Exiting</h3>
3555 <div class="paragraph"><p>You can make i3 reload its configuration file with <code>reload</code>. You can also
3556 restart i3 inplace with the <code>restart</code> command to get it out of some weird state
3557 (if that should ever happen) or to perform an upgrade without having to restart
3558 your X session. To exit i3 properly, you can use the <code>exit</code> command,
3559 however you don’t need to (simply killing your X session is fine as well).</p></div>
3560 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3561 <div class="listingblock">
3562 <div class="content">
3563 <pre><code>bindsym $mod+Shift+r restart
3564 bindsym $mod+Shift+w reload
3565 bindsym $mod+Shift+e exit</code></pre>
3566 </div></div>
3567 </div>
3568 <div class="sect2">
3569 <h3 id="_scratchpad">6.20. Scratchpad</h3>
3570 <div class="paragraph"><p>There are two commands to use any existing window as scratchpad window. <code>move
3571 scratchpad</code> will move a window to the scratchpad workspace. This will make it
3572 invisible until you show it again. There is no way to open that workspace.
3573 Instead, when using <code>scratchpad show</code>, the window will be shown again, as a
3574 floating window, centered on your current workspace (using <code>scratchpad show</code> on
3575 a visible scratchpad window will make it hidden again, so you can have a
3576 keybinding to toggle). Note that this is just a normal floating window, so if
3577 you want to "remove it from scratchpad", you can simple make it tiling again
3578 (<code>floating toggle</code>).</p></div>
3579 <div class="paragraph"><p>As the name indicates, this is useful for having a window with your favorite
3580 editor always at hand. However, you can also use this for other permanently
3581 running applications which you don’t want to see all the time: Your music
3582 player, alsamixer, maybe even your mail client…?</p></div>
3583 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3584 <div class="listingblock">
3585 <div class="content">
3586 <pre><code>move scratchpad
3587
3588 scratchpad show</code></pre>
3589 </div></div>
3590 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3591 <div class="listingblock">
3592 <div class="content">
3593 <pre><code># Make the currently focused window a scratchpad
3594 bindsym $mod+Shift+minus move scratchpad
3595
3596 # Show the first scratchpad window
3597 bindsym $mod+minus scratchpad show
3598
3599 # Show the sup-mail scratchpad window, if any.
3600 bindsym mod4+s [title="^Sup ::"] scratchpad show</code></pre>
3601 </div></div>
3602 </div>
3603 <div class="sect2">
3604 <h3 id="_nop">6.21. Nop</h3>
3605 <div class="paragraph"><p>There is a no operation command <code>nop</code> which allows you to override default
3606 behavior. This can be useful for, e.g., disabling a focus change on clicks with
3607 the middle mouse button.</p></div>
3608 <div class="paragraph"><p>The optional <code>comment</code> argument is ignored, but will be printed to the log file
3609 for debugging purposes.</p></div>
3610 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3611 <div class="listingblock">
3612 <div class="content">
3613 <pre><code>nop [&lt;comment&gt;]</code></pre>
3614 </div></div>
3615 <div class="paragraph"><p><strong>Example</strong>:</p></div>
3616 <div class="listingblock">
3617 <div class="content">
3618 <pre><code># Disable focus change for clicks on titlebars
3619 # with the middle mouse button
3620 bindsym button2 nop</code></pre>
3621 </div></div>
3622 </div>
3623 <div class="sect2">
3624 <h3 id="_i3bar_control">6.22. i3bar control</h3>
3625 <div class="paragraph"><p>There are two options in the configuration of each i3bar instance that can be
3626 changed during runtime by invoking a command through i3. The commands <code>bar
3627 hidden_state</code> and <code>bar mode</code> allow setting the current hidden_state
3628 respectively mode option of each bar. It is also possible to toggle between
3629 hide state and show state as well as between dock mode and hide mode. Each
3630 i3bar instance can be controlled individually by specifying a bar_id, if none
3631 is given, the command is executed for all bar instances.</p></div>
3632 <div class="paragraph"><p><strong>Syntax</strong>:</p></div>
3633 <div class="listingblock">
3634 <div class="content">
3635 <pre><code>bar hidden_state hide|show|toggle [&lt;bar_id&gt;]
3636
3637 bar mode dock|hide|invisible|toggle [&lt;bar_id&gt;]</code></pre>
3638 </div></div>
3639 <div class="paragraph"><p><strong>Examples</strong>:</p></div>
3640 <div class="listingblock">
3641 <div class="content">
3642 <pre><code># Toggle between hide state and show state
3643 bindsym $mod+m bar hidden_state toggle
3644
3645 # Toggle between dock mode and hide mode
3646 bindsym $mod+n bar mode toggle
3647
3648 # Set the bar instance with id 'bar-1' to switch to hide mode
3649 bindsym $mod+b bar mode hide bar-1
3650
3651 # Set the bar instance with id 'bar-1' to always stay hidden
3652 bindsym $mod+Shift+b bar mode invisible bar-1</code></pre>
3653 </div></div>
3654 </div>
3655 </div>
3656 </div>
3657 <div class="sect1">
3658 <h2 id="multi_monitor">7. Multiple monitors</h2>
3659 <div class="sectionbody">
3660 <div class="paragraph"><p>As you can see in the goal list on the website, i3 was specifically developed
3661 with support for multiple monitors in mind. This section will explain how to
3662 handle multiple monitors.</p></div>
3663 <div class="paragraph"><p>When you have only one monitor, things are simple. You usually start with
3664 workspace 1 on your monitor and open new ones as you need them.</p></div>
3665 <div class="paragraph"><p>When you have more than one monitor, each monitor will get an initial
3666 workspace. The first monitor gets 1, the second gets 2 and a possible third
3667 would get 3. When you switch to a workspace on a different monitor, i3 will
3668 switch to that monitor and then switch to the workspace. This way, you don’t
3669 need shortcuts to switch to a specific monitor, and you don’t need to remember
3670 where you put which workspace. New workspaces will be opened on the currently
3671 active monitor. It is not possible to have a monitor without a workspace.</p></div>
3672 <div class="paragraph"><p>The idea of making workspaces global is based on the observation that most
3673 users have a very limited set of workspaces on their additional monitors.
3674 They are often used for a specific task (browser, shell) or for monitoring
3675 several things (mail, IRC, syslog, …). Thus, using one workspace on one monitor
3676 and "the rest" on the other monitors often makes sense. However, as you can
3677 create an unlimited number of workspaces in i3 and tie them to specific
3678 screens, you can have the "traditional" approach of having X workspaces per
3679 screen by changing your configuration (using modes, for example).</p></div>
3680 <div class="sect2">
3681 <h3 id="_configuring_your_monitors">7.1. Configuring your monitors</h3>
3682 <div class="paragraph"><p>To help you get going if you have never used multiple monitors before, here is
3683 a short overview of the xrandr options which will probably be of interest to
3684 you. It is always useful to get an overview of the current screen configuration.
3685 Just run "xrandr" and you will get an output like the following:</p></div>
3686 <div class="listingblock">
3687 <div class="content">
3688 <pre><code>$ xrandr
3689 Screen 0: minimum 320 x 200, current 1280 x 800, maximum 8192 x 8192
3690 VGA1 disconnected (normal left inverted right x axis y axis)
3691 LVDS1 connected 1280x800+0+0 (normal left inverted right x axis y axis) 261mm x 163mm
3692 1280x800 60.0*+ 50.0
3693 1024x768 85.0 75.0 70.1 60.0
3694 832x624 74.6
3695 800x600 85.1 72.2 75.0 60.3 56.2
3696 640x480 85.0 72.8 75.0 59.9
3697 720x400 85.0
3698 640x400 85.1
3699 640x350 85.1</code></pre>
3700 </div></div>
3701 <div class="paragraph"><p>Several things are important here: You can see that <code>LVDS1</code> is connected (of
3702 course, it is the internal flat panel) but <code>VGA1</code> is not. If you have a monitor
3703 connected to one of the ports but xrandr still says "disconnected", you should
3704 check your cable, monitor or graphics driver.</p></div>
3705 <div class="paragraph"><p>The maximum resolution you can see at the end of the first line is the maximum
3706 combined resolution of your monitors. By default, it is usually too low and has
3707 to be increased by editing <code>/etc/X11/xorg.conf</code>.</p></div>
3708 <div class="paragraph"><p>So, say you connected VGA1 and want to use it as an additional screen:</p></div>
3709 <div class="listingblock">
3710 <div class="content">
3711 <pre><code>xrandr --output VGA1 --auto --left-of LVDS1</code></pre>
3712 </div></div>
3713 <div class="paragraph"><p>This command makes xrandr try to find the native resolution of the device
3714 connected to <code>VGA1</code> and configures it to the left of your internal flat panel.
3715 When running "xrandr" again, the output looks like this:</p></div>
3716 <div class="listingblock">
3717 <div class="content">
3718 <pre><code>$ xrandr
3719 Screen 0: minimum 320 x 200, current 2560 x 1024, maximum 8192 x 8192
3720 VGA1 connected 1280x1024+0+0 (normal left inverted right x axis y axis) 338mm x 270mm
3721 1280x1024 60.0*+ 75.0
3722 1280x960 60.0
3723 1152x864 75.0
3724 1024x768 75.1 70.1 60.0
3725 832x624 74.6
3726 800x600 72.2 75.0 60.3 56.2
3727 640x480 72.8 75.0 66.7 60.0
3728 720x400 70.1
3729 LVDS1 connected 1280x800+1280+0 (normal left inverted right x axis y axis) 261mm x 163mm
3730 1280x800 60.0*+ 50.0
3731 1024x768 85.0 75.0 70.1 60.0
3732 832x624 74.6
3733 800x600 85.1 72.2 75.0 60.3 56.2
3734 640x480 85.0 72.8 75.0 59.9
3735 720x400 85.0
3736 640x400 85.1
3737 640x350 85.1</code></pre>
3738 </div></div>
3739 <div class="paragraph"><p>Please note that i3 uses exactly the same API as xrandr does, so it will see
3740 only what you can see in xrandr.</p></div>
3741 <div class="paragraph"><p>See also <a href="#presentations">[presentations]</a> for more examples of multi-monitor setups.</p></div>
3742 </div>
3743 <div class="sect2">
3744 <h3 id="_interesting_configuration_for_multi_monitor_environments">7.2. Interesting configuration for multi-monitor environments</h3>
3745 <div class="paragraph"><p>There are several things to configure in i3 which may be interesting if you
3746 have more than one monitor:</p></div>
3747 <div class="olist arabic"><ol class="arabic">
3748 <li>
3749 <p>
3750 You can specify which workspace should be put on which screen. This
3751 allows you to have a different set of workspaces when starting than just
3752 1 for the first monitor, 2 for the second and so on. See
3753 <a href="#workspace_screen">[workspace_screen]</a>.
3754 </p>
3755 </li>
3756 <li>
3757 <p>
3758 If you want some applications to generally open on the bigger screen
3759 (MPlayer, Firefox, …), you can assign them to a specific workspace, see
3760 <a href="#assign_workspace">[assign_workspace]</a>.
3761 </p>
3762 </li>
3763 <li>
3764 <p>
3765 If you have many workspaces on many monitors, it might get hard to keep
3766 track of which window you put where. Thus, you can use vim-like marks to
3767 quickly switch between windows. See <a href="#vim_like_marks">[vim_like_marks]</a>.
3768 </p>
3769 </li>
3770 <li>
3771 <p>
3772 For information on how to move existing workspaces between monitors,
3773 see <a href="#move_to_outputs">[move_to_outputs]</a>.
3774 </p>
3775 </li>
3776 </ol></div>
3777 </div>
3778 </div>
3779 </div>
3780 <div class="sect1">
3781 <h2 id="_i3_and_the_rest_of_your_software_world">8. i3 and the rest of your software world</h2>
3782 <div class="sectionbody">
3783 <div class="sect2">
3784 <h3 id="_displaying_a_status_line">8.1. Displaying a status line</h3>
3785 <div class="paragraph"><p>A very common thing amongst users of exotic window managers is a status line at
3786 some corner of the screen. It is an often superior replacement to the widget
3787 approach you have in the task bar of a traditional desktop environment.</p></div>
3788 <div class="paragraph"><p>If you don’t already have your favorite way of generating such a status line
3789 (self-written scripts, conky, …), then i3status is the recommended tool for
3790 this task. It was written in C with the goal of using as few syscalls as
3791 possible to reduce the time your CPU is woken up from sleep states. Because
3792 i3status only spits out text, you need to combine it with some other tool, like
3793 i3bar. See <a href="#status_command">[status_command]</a> for how to display i3status in i3bar.</p></div>
3794 <div class="paragraph"><p>Regardless of which application you use to display the status line, you
3795 want to make sure that it registers as a dock window using EWMH hints. i3 will
3796 position the window either at the top or at the bottom of the screen, depending
3797 on which hint the application sets. With i3bar, you can configure its position,
3798 see <a href="#i3bar_position">[i3bar_position]</a>.</p></div>
3799 </div>
3800 <div class="sect2">
3801 <h3 id="presentations">8.2. Giving presentations (multi-monitor)</h3>
3802 <div class="paragraph"><p>When giving a presentation, you typically want the audience to see what you see
3803 on your screen and then go through a series of slides (if the presentation is
3804 simple). For more complex presentations, you might want to have some notes
3805 which only you can see on your screen, while the audience can only see the
3806 slides.</p></div>
3807 <div class="sect3">
3808 <h4 id="_case_1_everybody_gets_the_same_output">8.2.1. Case 1: everybody gets the same output</h4>
3809 <div class="paragraph"><p>This is the simple case. You connect your computer to the video projector,
3810 turn on both (computer and video projector) and configure your X server to
3811 clone the internal flat panel of your computer to the video output:</p></div>
3812 <div class="listingblock">
3813 <div class="content">
3814 <pre><code>xrandr --output VGA1 --mode 1024x768 --same-as LVDS1</code></pre>
3815 </div></div>
3816 <div class="paragraph"><p>i3 will then use the lowest common subset of screen resolutions, the rest of
3817 your screen will be left untouched (it will show the X background). So, in
3818 our example, this would be 1024x768 (my notebook has 1280x800).</p></div>
3819 </div>
3820 <div class="sect3">
3821 <h4 id="_case_2_you_can_see_more_than_your_audience">8.2.2. Case 2: you can see more than your audience</h4>
3822 <div class="paragraph"><p>This case is a bit harder. First of all, you should configure the VGA output
3823 somewhere near your internal flat panel, say right of it:</p></div>
3824 <div class="listingblock">
3825 <div class="content">
3826 <pre><code>xrandr --output VGA1 --mode 1024x768 --right-of LVDS1</code></pre>
3827 </div></div>
3828 <div class="paragraph"><p>Now, i3 will put a new workspace (depending on your settings) on the new screen
3829 and you are in multi-monitor mode (see <a href="#multi_monitor">[multi_monitor]</a>).</p></div>
3830 <div class="paragraph"><p>Because i3 is not a compositing window manager, there is no ability to
3831 display a window on two screens at the same time. Instead, your presentation
3832 software needs to do this job (that is, open a window on each screen).</p></div>
3833 </div>
3834 </div>
3835 </div>
3836 </div>
3837 </div>
3838 <div id="footnotes"><hr /></div>
3839 <div id="footer">
3840 <div id="footer-text">
3841 Last updated
3842 2019-01-27 16:45:19 CET
3843 </div>
3844 </div>
3845 </body>
3846 </html>
0 External workspace bars
1 =======================
2 Michael Stapelberg <michael@i3wm.org>
3 April 2013
4
5 i3 comes with i3bar by default, a simple bar that is sufficient for most users.
6 In case you are unhappy with it, this document explains how to use a different,
7 external workspace bar. Note that we do not provide support for external
8 programs.
9
10 == Internal and external bars
11
12 The internal workspace bar of i3 is meant to be a reasonable default so that
13 you can use i3 without having too much hassle when setting it up. It is quite
14 simple and intended to stay this way.
15
16 == dock mode
17
18 You typically want to see the same workspace bar on every workspace on a
19 specific screen. Also, you don’t want to place the workspace bar somewhere
20 in your layout by hand. This is where dock mode comes in: When a program sets
21 the appropriate hint (_NET_WM_WINDOW_TYPE_DOCK), it will be managed in dock
22 mode by i3. That means it will be placed at the bottom or top of the screen
23 (while other edges of the screen are possible in the NetWM standard, this is
24 not yet implemented in i3), it will not overlap any other window and it will be
25 on every workspace for the specific screen it was placed on initially.
26
27 == The IPC interface
28
29 In the context of using an external workspace bar, the IPC interface needs to
30 provide the bar program with the current workspaces and output (as in VGA-1,
31 LVDS-1, …) configuration. In the other direction, the program has to be able
32 to switch to specific workspaces.
33
34 By default, the IPC interface is enabled and you can get the path to the socket
35 by calling +i3 --get-socketpath+.
36
37 To learn more about the protocol which is used for IPC, see +docs/ipc+.
38
39 == Output changes (on-the-fly)
40
41 i3 implements the RandR API and can handle changing outputs quite well. So, an
42 external workspace bar implementation needs to make sure that when you change
43 the resolution of any of your screens (or enable/disable an output), the bars
44 will be adjusted properly.
45
46 == i3-wsbar, an example implementation
47
48 +i3-wsbar+ used to be the reference implementation before we had +i3bar+.
49 Nowadays, it is not shipped with release tarballs, but you can still get it at
50 https://github.com/i3/i3/blob/next/contrib/i3-wsbar
51
52 === The big picture
53
54 The most common reason to use an external workspace bar is to integrate system
55 information such as what +i3status+ or +conky+ provide into the workspace bar.
56 So, we have +i3status+ or a similar program, which only provides
57 text output (formatted in some way). To display this text nicely on the screen,
58 there are programs such as dzen2, xmobar and similar. We will stick to dzen2
59 from here on. So, we have the output of i3status, which needs to go into dzen2
60 somehow. But we also want to display the list of workspaces. +i3-wsbar+ takes
61 input on stdin, combines it with a formatted workspace list and pipes it to
62 dzen2.
63
64 Please note that +i3-wsbar+ does not print its output to stdout. Instead, it
65 launches the dzen2 instances on its own. This is necessary to handle changes
66 in the available outputs (to place a new dzen2 on a new screen for example).
67
68 image:wsbar.png["Overview",link="wsbar.png"]
69
70 === Running i3-wsbar
71
72 The most simple usage of i3-wsbar looks like this:
73 -------------------------------
74 i3-wsbar -c "dzen2 -x %x -dock"
75 -------------------------------
76
77 The +%x+ in the command name will be replaced by the X position of the output
78 for which this workspace bar is running. i3 will automatically place the
79 workspace bar on the correct output when dzen2 is started in dock mode. The
80 bar which you will see should look exactly like the internal bar of i3.
81
82 To actually get a benefit, you want to give +i3-wsbar+ some input:
83 ------------------------------------------
84 i3status | i3-wsbar -c "dzen2 -x %x -dock"
85 ------------------------------------------
Binary diff not shown
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4 <head>
5 <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
6 <meta name="generator" content="AsciiDoc 8.6.10" />
7 <title>External workspace bars</title>
8 <style type="text/css">
9 /* Shared CSS for AsciiDoc xhtml11 and html5 backends */
10
11 /* Default font. */
12 body {
13 font-family: Georgia,serif;
14 }
15
16 /* Title font. */
17 h1, h2, h3, h4, h5, h6,
18 div.title, caption.title,
19 thead, p.table.header,
20 #toctitle,
21 #author, #revnumber, #revdate, #revremark,
22 #footer {
23 font-family: Arial,Helvetica,sans-serif;
24 }
25
26 body {
27 margin: 1em 5% 1em 5%;
28 }
29
30 a {
31 color: blue;
32 text-decoration: underline;
33 }
34 a:visited {
35 color: fuchsia;
36 }
37
38 em {
39 font-style: italic;
40 color: navy;
41 }
42
43 strong {
44 font-weight: bold;
45 color: #083194;
46 }
47
48 h1, h2, h3, h4, h5, h6 {
49 color: #527bbd;
50 margin-top: 1.2em;
51 margin-bottom: 0.5em;
52 line-height: 1.3;
53 }
54
55 h1, h2, h3 {
56 border-bottom: 2px solid silver;
57 }
58 h2 {
59 padding-top: 0.5em;
60 }
61 h3 {
62 float: left;
63 }
64 h3 + * {
65 clear: left;
66 }
67 h5 {
68 font-size: 1.0em;
69 }
70
71 div.sectionbody {
72 margin-left: 0;
73 }
74
75 hr {
76 border: 1px solid silver;
77 }
78
79 p {
80 margin-top: 0.5em;
81 margin-bottom: 0.5em;
82 }
83
84 ul, ol, li > p {
85 margin-top: 0;
86 }
87 ul > li { color: #aaa; }
88 ul > li > * { color: black; }
89
90 .monospaced, code, pre {
91 font-family: "Courier New", Courier, monospace;
92 font-size: inherit;
93 color: navy;
94 padding: 0;
95 margin: 0;
96 }
97 pre {
98 white-space: pre-wrap;
99 }
100
101 #author {
102 color: #527bbd;
103 font-weight: bold;
104 font-size: 1.1em;
105 }
106 #email {
107 }
108 #revnumber, #revdate, #revremark {
109 }
110
111 #footer {
112 font-size: small;
113 border-top: 2px solid silver;
114 padding-top: 0.5em;
115 margin-top: 4.0em;
116 }
117 #footer-text {
118 float: left;
119 padding-bottom: 0.5em;
120 }
121 #footer-badges {
122 float: right;
123 padding-bottom: 0.5em;
124 }
125
126 #preamble {
127 margin-top: 1.5em;
128 margin-bottom: 1.5em;
129 }
130 div.imageblock, div.exampleblock, div.verseblock,
131 div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
132 div.admonitionblock {
133 margin-top: 1.0em;
134 margin-bottom: 1.5em;
135 }
136 div.admonitionblock {
137 margin-top: 2.0em;
138 margin-bottom: 2.0em;
139 margin-right: 10%;
140 color: #606060;
141 }
142
143 div.content { /* Block element content. */
144 padding: 0;
145 }
146
147 /* Block element titles. */
148 div.title, caption.title {
149 color: #527bbd;
150 font-weight: bold;
151 text-align: left;
152 margin-top: 1.0em;
153 margin-bottom: 0.5em;
154 }
155 div.title + * {
156 margin-top: 0;
157 }
158
159 td div.title:first-child {
160 margin-top: 0.0em;
161 }
162 div.content div.title:first-child {
163 margin-top: 0.0em;
164 }
165 div.content + div.title {
166 margin-top: 0.0em;
167 }
168
169 div.sidebarblock > div.content {
170 background: #ffffee;
171 border: 1px solid #dddddd;
172 border-left: 4px solid #f0f0f0;
173 padding: 0.5em;
174 }
175
176 div.listingblock > div.content {
177 border: 1px solid #dddddd;
178 border-left: 5px solid #f0f0f0;
179 background: #f8f8f8;
180 padding: 0.5em;
181 }
182
183 div.quoteblock, div.verseblock {
184 padding-left: 1.0em;
185 margin-left: 1.0em;
186 margin-right: 10%;
187 border-left: 5px solid #f0f0f0;
188 color: #888;
189 }
190
191 div.quoteblock > div.attribution {
192 padding-top: 0.5em;
193 text-align: right;
194 }
195
196 div.verseblock > pre.content {
197 font-family: inherit;
198 font-size: inherit;
199 }
200 div.verseblock > div.attribution {
201 padding-top: 0.75em;
202 text-align: left;
203 }
204 /* DEPRECATED: Pre version 8.2.7 verse style literal block. */
205 div.verseblock + div.attribution {
206 text-align: left;
207 }
208
209 div.admonitionblock .icon {
210 vertical-align: top;
211 font-size: 1.1em;
212 font-weight: bold;
213 text-decoration: underline;
214 color: #527bbd;
215 padding-right: 0.5em;
216 }
217 div.admonitionblock td.content {
218 padding-left: 0.5em;
219 border-left: 3px solid #dddddd;
220 }
221
222 div.exampleblock > div.content {
223 border-left: 3px solid #dddddd;
224 padding-left: 0.5em;
225 }
226
227 div.imageblock div.content { padding-left: 0; }
228 span.image img { border-style: none; vertical-align: text-bottom; }
229 a.image:visited { color: white; }
230
231 dl {
232 margin-top: 0.8em;
233 margin-bottom: 0.8em;
234 }
235 dt {
236 margin-top: 0.5em;
237 margin-bottom: 0;
238 font-style: normal;
239 color: navy;
240 }
241 dd > *:first-child {
242 margin-top: 0.1em;
243 }
244
245 ul, ol {
246 list-style-position: outside;
247 }
248 ol.arabic {
249 list-style-type: decimal;
250 }
251 ol.loweralpha {
252 list-style-type: lower-alpha;
253 }
254 ol.upperalpha {
255 list-style-type: upper-alpha;
256 }
257 ol.lowerroman {
258 list-style-type: lower-roman;
259 }
260 ol.upperroman {
261 list-style-type: upper-roman;
262 }
263
264 div.compact ul, div.compact ol,
265 div.compact p, div.compact p,
266 div.compact div, div.compact div {
267 margin-top: 0.1em;
268 margin-bottom: 0.1em;
269 }
270
271 tfoot {
272 font-weight: bold;
273 }
274 td > div.verse {
275 white-space: pre;
276 }
277
278 div.hdlist {
279 margin-top: 0.8em;
280 margin-bottom: 0.8em;
281 }
282 div.hdlist tr {
283 padding-bottom: 15px;
284 }
285 dt.hdlist1.strong, td.hdlist1.strong {
286 font-weight: bold;
287 }
288 td.hdlist1 {
289 vertical-align: top;
290 font-style: normal;
291 padding-right: 0.8em;
292 color: navy;
293 }
294 td.hdlist2 {
295 vertical-align: top;
296 }
297 div.hdlist.compact tr {
298 margin: 0;
299 padding-bottom: 0;
300 }
301
302 .comment {
303 background: yellow;
304 }
305
306 .footnote, .footnoteref {
307 font-size: 0.8em;
308 }
309
310 span.footnote, span.footnoteref {
311 vertical-align: super;
312 }
313
314 #footnotes {
315 margin: 20px 0 20px 0;
316 padding: 7px 0 0 0;
317 }
318
319 #footnotes div.footnote {
320 margin: 0 0 5px 0;
321 }
322
323 #footnotes hr {
324 border: none;
325 border-top: 1px solid silver;
326 height: 1px;
327 text-align: left;
328 margin-left: 0;
329 width: 20%;
330 min-width: 100px;
331 }
332
333 div.colist td {
334 padding-right: 0.5em;
335 padding-bottom: 0.3em;
336 vertical-align: top;
337 }
338 div.colist td img {
339 margin-top: 0.3em;
340 }
341
342 @media print {
343 #footer-badges { display: none; }
344 }
345
346 #toc {
347 margin-bottom: 2.5em;
348 }
349
350 #toctitle {
351 color: #527bbd;
352 font-size: 1.1em;
353 font-weight: bold;
354 margin-top: 1.0em;
355 margin-bottom: 0.1em;
356 }
357
358 div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
359 margin-top: 0;
360 margin-bottom: 0;
361 }
362 div.toclevel2 {
363 margin-left: 2em;
364 font-size: 0.9em;
365 }
366 div.toclevel3 {
367 margin-left: 4em;
368 font-size: 0.9em;
369 }
370 div.toclevel4 {
371 margin-left: 6em;
372 font-size: 0.9em;
373 }
374
375 span.aqua { color: aqua; }
376 span.black { color: black; }
377 span.blue { color: blue; }
378 span.fuchsia { color: fuchsia; }
379 span.gray { color: gray; }
380 span.green { color: green; }
381 span.lime { color: lime; }
382 span.maroon { color: maroon; }
383 span.navy { color: navy; }
384 span.olive { color: olive; }
385 span.purple { color: purple; }
386 span.red { color: red; }
387 span.silver { color: silver; }
388 span.teal { color: teal; }
389 span.white { color: white; }
390 span.yellow { color: yellow; }
391
392 span.aqua-background { background: aqua; }
393 span.black-background { background: black; }
394 span.blue-background { background: blue; }
395 span.fuchsia-background { background: fuchsia; }
396 span.gray-background { background: gray; }
397 span.green-background { background: green; }
398 span.lime-background { background: lime; }
399 span.maroon-background { background: maroon; }
400 span.navy-background { background: navy; }
401 span.olive-background { background: olive; }
402 span.purple-background { background: purple; }
403 span.red-background { background: red; }
404 span.silver-background { background: silver; }
405 span.teal-background { background: teal; }
406 span.white-background { background: white; }
407 span.yellow-background { background: yellow; }
408
409 span.big { font-size: 2em; }
410 span.small { font-size: 0.6em; }
411
412 span.underline { text-decoration: underline; }
413 span.overline { text-decoration: overline; }
414 span.line-through { text-decoration: line-through; }
415
416 div.unbreakable { page-break-inside: avoid; }
417
418
419 /*
420 * xhtml11 specific
421 *
422 * */
423
424 div.tableblock {
425 margin-top: 1.0em;
426 margin-bottom: 1.5em;
427 }
428 div.tableblock > table {
429 border: 3px solid #527bbd;
430 }
431 thead, p.table.header {
432 font-weight: bold;
433 color: #527bbd;
434 }
435 p.table {
436 margin-top: 0;
437 }
438 /* Because the table frame attribute is overriden by CSS in most browsers. */
439 div.tableblock > table[frame="void"] {
440 border-style: none;
441 }
442 div.tableblock > table[frame="hsides"] {
443 border-left-style: none;
444 border-right-style: none;
445 }
446 div.tableblock > table[frame="vsides"] {
447 border-top-style: none;
448 border-bottom-style: none;
449 }
450
451
452 /*
453 * html5 specific
454 *
455 * */
456
457 table.tableblock {
458 margin-top: 1.0em;
459 margin-bottom: 1.5em;
460 }
461 thead, p.tableblock.header {
462 font-weight: bold;
463 color: #527bbd;
464 }
465 p.tableblock {
466 margin-top: 0;
467 }
468 table.tableblock {
469 border-width: 3px;
470 border-spacing: 0px;
471 border-style: solid;
472 border-color: #527bbd;
473 border-collapse: collapse;
474 }
475 th.tableblock, td.tableblock {
476 border-width: 1px;
477 padding: 4px;
478 border-style: solid;
479 border-color: #527bbd;
480 }
481
482 table.tableblock.frame-topbot {
483 border-left-style: hidden;
484 border-right-style: hidden;
485 }
486 table.tableblock.frame-sides {
487 border-top-style: hidden;
488 border-bottom-style: hidden;
489 }
490 table.tableblock.frame-none {
491 border-style: hidden;
492 }
493
494 th.tableblock.halign-left, td.tableblock.halign-left {
495 text-align: left;
496 }
497 th.tableblock.halign-center, td.tableblock.halign-center {
498 text-align: center;
499 }
500 th.tableblock.halign-right, td.tableblock.halign-right {
501 text-align: right;
502 }
503
504 th.tableblock.valign-top, td.tableblock.valign-top {
505 vertical-align: top;
506 }
507 th.tableblock.valign-middle, td.tableblock.valign-middle {
508 vertical-align: middle;
509 }
510 th.tableblock.valign-bottom, td.tableblock.valign-bottom {
511 vertical-align: bottom;
512 }
513
514
515 /*
516 * manpage specific
517 *
518 * */
519
520 body.manpage h1 {
521 padding-top: 0.5em;
522 padding-bottom: 0.5em;
523 border-top: 2px solid silver;
524 border-bottom: 2px solid silver;
525 }
526 body.manpage h2 {
527 border-style: none;
528 }
529 body.manpage div.sectionbody {
530 margin-left: 3em;
531 }
532
533 @media print {
534 body.manpage div#toc { display: none; }
535 }
536
537
538 </style>
539 <script type="text/javascript">
540 /*<![CDATA[*/
541 var asciidoc = { // Namespace.
542
543 /////////////////////////////////////////////////////////////////////
544 // Table Of Contents generator
545 /////////////////////////////////////////////////////////////////////
546
547 /* Author: Mihai Bazon, September 2002
548 * http://students.infoiasi.ro/~mishoo
549 *
550 * Table Of Content generator
551 * Version: 0.4
552 *
553 * Feel free to use this script under the terms of the GNU General Public
554 * License, as long as you do not remove or alter this notice.
555 */
556
557 /* modified by Troy D. Hanson, September 2006. License: GPL */
558 /* modified by Stuart Rackham, 2006, 2009. License: GPL */
559
560 // toclevels = 1..4.
561 toc: function (toclevels) {
562
563 function getText(el) {
564 var text = "";
565 for (var i = el.firstChild; i != null; i = i.nextSibling) {
566 if (i.nodeType == 3 /* Node.TEXT_NODE */) // IE doesn't speak constants.
567 text += i.data;
568 else if (i.firstChild != null)
569 text += getText(i);
570 }
571 return text;
572 }
573
574 function TocEntry(el, text, toclevel) {
575 this.element = el;
576 this.text = text;
577 this.toclevel = toclevel;
578 }
579
580 function tocEntries(el, toclevels) {
581 var result = new Array;
582 var re = new RegExp('[hH]([1-'+(toclevels+1)+'])');
583 // Function that scans the DOM tree for header elements (the DOM2
584 // nodeIterator API would be a better technique but not supported by all
585 // browsers).
586 var iterate = function (el) {
587 for (var i = el.firstChild; i != null; i = i.nextSibling) {
588 if (i.nodeType == 1 /* Node.ELEMENT_NODE */) {
589 var mo = re.exec(i.tagName);
590 if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") {
591 result[result.length] = new TocEntry(i, getText(i), mo[1]-1);
592 }
593 iterate(i);
594 }
595 }
596 }
597 iterate(el);
598 return result;
599 }
600
601 var toc = document.getElementById("toc");
602 if (!toc) {
603 return;
604 }
605
606 // Delete existing TOC entries in case we're reloading the TOC.
607 var tocEntriesToRemove = [];
608 var i;
609 for (i = 0; i < toc.childNodes.length; i++) {
610 var entry = toc.childNodes[i];
611 if (entry.nodeName.toLowerCase() == 'div'
612 && entry.getAttribute("class")
613 && entry.getAttribute("class").match(/^toclevel/))
614 tocEntriesToRemove.push(entry);
615 }
616 for (i = 0; i < tocEntriesToRemove.length; i++) {
617 toc.removeChild(tocEntriesToRemove[i]);
618 }
619
620 // Rebuild TOC entries.
621 var entries = tocEntries(document.getElementById("content"), toclevels);
622 for (var i = 0; i < entries.length; ++i) {
623 var entry = entries[i];
624 if (entry.element.id == "")
625 entry.element.id = "_toc_" + i;
626 var a = document.createElement("a");
627 a.href = "#" + entry.element.id;
628 a.appendChild(document.createTextNode(entry.text));
629 var div = document.createElement("div");
630 div.appendChild(a);
631 div.className = "toclevel" + entry.toclevel;
632 toc.appendChild(div);
633 }
634 if (entries.length == 0)
635 toc.parentNode.removeChild(toc);
636 },
637
638
639 /////////////////////////////////////////////////////////////////////
640 // Footnotes generator
641 /////////////////////////////////////////////////////////////////////
642
643 /* Based on footnote generation code from:
644 * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html
645 */
646
647 footnotes: function () {
648 // Delete existing footnote entries in case we're reloading the footnodes.
649 var i;
650 var noteholder = document.getElementById("footnotes");
651 if (!noteholder) {
652 return;
653 }
654 var entriesToRemove = [];
655 for (i = 0; i < noteholder.childNodes.length; i++) {
656 var entry = noteholder.childNodes[i];
657 if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote")
658 entriesToRemove.push(entry);
659 }
660 for (i = 0; i < entriesToRemove.length; i++) {
661 noteholder.removeChild(entriesToRemove[i]);
662 }
663
664 // Rebuild footnote entries.
665 var cont = document.getElementById("content");
666 var spans = cont.getElementsByTagName("span");
667 var refs = {};
668 var n = 0;
669 for (i=0; i<spans.length; i++) {
670 if (spans[i].className == "footnote") {
671 n++;
672 var note = spans[i].getAttribute("data-note");
673 if (!note) {
674 // Use [\s\S] in place of . so multi-line matches work.
675 // Because JavaScript has no s (dotall) regex flag.
676 note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1];
677 spans[i].innerHTML =
678 "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n +
679 "' title='View footnote' class='footnote'>" + n + "</a>]";
680 spans[i].setAttribute("data-note", note);
681 }
682 noteholder.innerHTML +=
683 "<div class='footnote' id='_footnote_" + n + "'>" +
684 "<a href='#_footnoteref_" + n + "' title='Return to text'>" +
685 n + "</a>. " + note + "</div>";
686 var id =spans[i].getAttribute("id");
687 if (id != null) refs["#"+id] = n;
688 }
689 }
690 if (n == 0)
691 noteholder.parentNode.removeChild(noteholder);
692 else {
693 // Process footnoterefs.
694 for (i=0; i<spans.length; i++) {
695 if (spans[i].className == "footnoteref") {
696 var href = spans[i].getElementsByTagName("a")[0].getAttribute("href");
697 href = href.match(/#.*/)[0]; // Because IE return full URL.
698 n = refs[href];
699 spans[i].innerHTML =
700 "[<a href='#_footnote_" + n +
701 "' title='View footnote' class='footnote'>" + n + "</a>]";
702 }
703 }
704 }
705 },
706
707 install: function(toclevels) {
708 var timerId;
709
710 function reinstall() {
711 asciidoc.footnotes();
712 if (toclevels) {
713 asciidoc.toc(toclevels);
714 }
715 }
716
717 function reinstallAndRemoveTimer() {
718 clearInterval(timerId);
719 reinstall();
720 }
721
722 timerId = setInterval(reinstall, 500);
723 if (document.addEventListener)
724 document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false);
725 else
726 window.onload = reinstallAndRemoveTimer;
727 }
728
729 }
730 asciidoc.install(2);
731 /*]]>*/
732 </script>
733 </head>
734 <body class="article">
735 <div id="header">
736 <h1>External workspace bars</h1>
737 <span id="author">Michael Stapelberg</span><br />
738 <span id="email"><code>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</code></span><br />
739 <span id="revdate">April 2013</span>
740 <div id="toc">
741 <div id="toctitle">Table of Contents</div>
742 <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
743 </div>
744 </div>
745 <div id="content">
746 <div id="preamble">
747 <div class="sectionbody">
748 <div class="paragraph"><p>i3 comes with i3bar by default, a simple bar that is sufficient for most users.
749 In case you are unhappy with it, this document explains how to use a different,
750 external workspace bar. Note that we do not provide support for external
751 programs.</p></div>
752 </div>
753 </div>
754 <div class="sect1">
755 <h2 id="_internal_and_external_bars">1. Internal and external bars</h2>
756 <div class="sectionbody">
757 <div class="paragraph"><p>The internal workspace bar of i3 is meant to be a reasonable default so that
758 you can use i3 without having too much hassle when setting it up. It is quite
759 simple and intended to stay this way.</p></div>
760 </div>
761 </div>
762 <div class="sect1">
763 <h2 id="_dock_mode">2. dock mode</h2>
764 <div class="sectionbody">
765 <div class="paragraph"><p>You typically want to see the same workspace bar on every workspace on a
766 specific screen. Also, you don’t want to place the workspace bar somewhere
767 in your layout by hand. This is where dock mode comes in: When a program sets
768 the appropriate hint (_NET_WM_WINDOW_TYPE_DOCK), it will be managed in dock
769 mode by i3. That means it will be placed at the bottom or top of the screen
770 (while other edges of the screen are possible in the NetWM standard, this is
771 not yet implemented in i3), it will not overlap any other window and it will be
772 on every workspace for the specific screen it was placed on initially.</p></div>
773 </div>
774 </div>
775 <div class="sect1">
776 <h2 id="_the_ipc_interface">3. The IPC interface</h2>
777 <div class="sectionbody">
778 <div class="paragraph"><p>In the context of using an external workspace bar, the IPC interface needs to
779 provide the bar program with the current workspaces and output (as in VGA-1,
780 LVDS-1, …) configuration. In the other direction, the program has to be able
781 to switch to specific workspaces.</p></div>
782 <div class="paragraph"><p>By default, the IPC interface is enabled and you can get the path to the socket
783 by calling <code>i3 --get-socketpath</code>.</p></div>
784 <div class="paragraph"><p>To learn more about the protocol which is used for IPC, see <code>docs/ipc</code>.</p></div>
785 </div>
786 </div>
787 <div class="sect1">
788 <h2 id="_output_changes_on_the_fly">4. Output changes (on-the-fly)</h2>
789 <div class="sectionbody">
790 <div class="paragraph"><p>i3 implements the RandR API and can handle changing outputs quite well. So, an
791 external workspace bar implementation needs to make sure that when you change
792 the resolution of any of your screens (or enable/disable an output), the bars
793 will be adjusted properly.</p></div>
794 </div>
795 </div>
796 <div class="sect1">
797 <h2 id="_i3_wsbar_an_example_implementation">5. i3-wsbar, an example implementation</h2>
798 <div class="sectionbody">
799 <div class="paragraph"><p><code>i3-wsbar</code> used to be the reference implementation before we had <code>i3bar</code>.
800 Nowadays, it is not shipped with release tarballs, but you can still get it at
801 <a href="https://github.com/i3/i3/blob/next/contrib/i3-wsbar">https://github.com/i3/i3/blob/next/contrib/i3-wsbar</a></p></div>
802 <div class="sect2">
803 <h3 id="_the_big_picture">5.1. The big picture</h3>
804 <div class="paragraph"><p>The most common reason to use an external workspace bar is to integrate system
805 information such as what <code>i3status</code> or <code>conky</code> provide into the workspace bar.
806 So, we have <code>i3status</code> or a similar program, which only provides
807 text output (formatted in some way). To display this text nicely on the screen,
808 there are programs such as dzen2, xmobar and similar. We will stick to dzen2
809 from here on. So, we have the output of i3status, which needs to go into dzen2
810 somehow. But we also want to display the list of workspaces. <code>i3-wsbar</code> takes
811 input on stdin, combines it with a formatted workspace list and pipes it to
812 dzen2.</p></div>
813 <div class="paragraph"><p>Please note that <code>i3-wsbar</code> does not print its output to stdout. Instead, it
814 launches the dzen2 instances on its own. This is necessary to handle changes
815 in the available outputs (to place a new dzen2 on a new screen for example).</p></div>
816 <div class="paragraph"><p><span class="image">
817 <a class="image" href="wsbar.png">
818 <img src="wsbar.png" alt="Overview" />
819 </a>
820 </span></p></div>
821 </div>
822 <div class="sect2">
823 <h3 id="_running_i3_wsbar">5.2. Running i3-wsbar</h3>
824 <div class="paragraph"><p>The most simple usage of i3-wsbar looks like this:</p></div>
825 <div class="listingblock">
826 <div class="content">
827 <pre><code>i3-wsbar -c "dzen2 -x %x -dock"</code></pre>
828 </div></div>
829 <div class="paragraph"><p>The <code>%x</code> in the command name will be replaced by the X position of the output
830 for which this workspace bar is running. i3 will automatically place the
831 workspace bar on the correct output when dzen2 is started in dock mode. The
832 bar which you will see should look exactly like the internal bar of i3.</p></div>
833 <div class="paragraph"><p>To actually get a benefit, you want to give <code>i3-wsbar</code> some input:</p></div>
834 <div class="listingblock">
835 <div class="content">
836 <pre><code>i3status | i3-wsbar -c "dzen2 -x %x -dock"</code></pre>
837 </div></div>
838 </div>
839 </div>
840 </div>
841 </div>
842 <div id="footnotes"><hr /></div>
843 <div id="footer">
844 <div id="footer-text">
845 Last updated
846 2019-01-27 16:45:19 CET
847 </div>
848 </div>
849 </body>
850 </html>
Binary diff not shown
0 # i3 config file (v4)
1 #
2 # Please see https://i3wm.org/docs/userguide.html for a complete reference!
3 #
4 # This config file uses keycodes (bindsym) and was written for the QWERTY
5 # layout.
6 #
7 # To get a config file with the same key positions, but for your current
8 # layout, use the i3-config-wizard
9 #
10
11 # Font for window titles. Will also be used by the bar unless a different font
12 # is used in the bar {} block below.
13 font pango:monospace 8
14
15 # This font is widely installed, provides lots of unicode glyphs, right-to-left
16 # text rendering and scalability on retina/hidpi displays (thanks to pango).
17 #font pango:DejaVu Sans Mono 8
18
19 # Before i3 v4.8, we used to recommend this one as the default:
20 # font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21 # The font above is very space-efficient, that is, it looks good, sharp and
22 # clear in small sizes. However, its unicode glyph coverage is limited, the old
23 # X core fonts rendering does not support right-to-left and this being a bitmap
24 # font, it doesn't scale on retina/hidpi displays.
25
26 # use these keys for focus, movement, and resize directions when reaching for
27 # the arrows is not convenient
28 set $up l
29 set $down k
30 set $left j
31 set $right semicolon
32
33 # use Mouse+Mod1 to drag floating windows to their wanted position
34 floating_modifier Mod1
35
36 # start a terminal
37 bindsym Mod1+Return exec i3-sensible-terminal
38
39 # kill focused window
40 bindsym Mod1+Shift+q kill
41
42 # start dmenu (a program launcher)
43 bindsym Mod1+d exec dmenu_run
44 # There also is the (new) i3-dmenu-desktop which only displays applications
45 # shipping a .desktop file. It is a wrapper around dmenu, so you need that
46 # installed.
47 # bindsym Mod1+d exec --no-startup-id i3-dmenu-desktop
48
49 # change focus
50 bindsym Mod1+$left focus left
51 bindsym Mod1+$down focus down
52 bindsym Mod1+$up focus up
53 bindsym Mod1+$right focus right
54
55 # alternatively, you can use the cursor keys:
56 bindsym Mod1+Left focus left
57 bindsym Mod1+Down focus down
58 bindsym Mod1+Up focus up
59 bindsym Mod1+Right focus right
60
61 # move focused window
62 bindsym Mod1+Shift+$left move left
63 bindsym Mod1+Shift+$down move down
64 bindsym Mod1+Shift+$up move up
65 bindsym Mod1+Shift+$right move right
66
67 # alternatively, you can use the cursor keys:
68 bindsym Mod1+Shift+Left move left
69 bindsym Mod1+Shift+Down move down
70 bindsym Mod1+Shift+Up move up
71 bindsym Mod1+Shift+Right move right
72
73 # split in horizontal orientation
74 bindsym Mod1+h split h
75
76 # split in vertical orientation
77 bindsym Mod1+v split v
78
79 # enter fullscreen mode for the focused container
80 bindsym Mod1+f fullscreen toggle
81
82 # change container layout (stacked, tabbed, toggle split)
83 bindsym Mod1+s layout stacking
84 bindsym Mod1+w layout tabbed
85 bindsym Mod1+e layout toggle split
86
87 # toggle tiling / floating
88 bindsym Mod1+Shift+space floating toggle
89
90 # change focus between tiling / floating windows
91 bindsym Mod1+space focus mode_toggle
92
93 # focus the parent container
94 bindsym Mod1+a focus parent
95
96 # focus the child container
97 #bindsym Mod1+d focus child
98
99 # move the currently focused window to the scratchpad
100 bindsym Mod1+Shift+minus move scratchpad
101
102 # Show the next scratchpad window or hide the focused scratchpad window.
103 # If there are multiple scratchpad windows, this command cycles through them.
104 bindsym Mod1+minus scratchpad show
105
106 # Define names for default workspaces for which we configure key bindings later on.
107 # We use variables to avoid repeating the names in multiple places.
108 set $ws1 "1"
109 set $ws2 "2"
110 set $ws3 "3"
111 set $ws4 "4"
112 set $ws5 "5"
113 set $ws6 "6"
114 set $ws7 "7"
115 set $ws8 "8"
116 set $ws9 "9"
117 set $ws10 "10"
118
119
120 # switch to workspace
121 bindsym Mod1+1 workspace $ws1
122 bindsym Mod1+2 workspace $ws2
123 bindsym Mod1+3 workspace $ws3
124 bindsym Mod1+4 workspace $ws4
125 bindsym Mod1+5 workspace $ws5
126 bindsym Mod1+6 workspace $ws6
127 bindsym Mod1+7 workspace $ws7
128 bindsym Mod1+8 workspace $ws8
129 bindsym Mod1+9 workspace $ws9
130 bindsym Mod1+0 workspace $ws10
131
132 # move focused container to workspace
133 bindsym Mod1+Shift+1 move container to workspace $ws1
134 bindsym Mod1+Shift+2 move container to workspace $ws2
135 bindsym Mod1+Shift+3 move container to workspace $ws3
136 bindsym Mod1+Shift+4 move container to workspace $ws4
137 bindsym Mod1+Shift+5 move container to workspace $ws5
138 bindsym Mod1+Shift+6 move container to workspace $ws6
139 bindsym Mod1+Shift+7 move container to workspace $ws7
140 bindsym Mod1+Shift+8 move container to workspace $ws8
141 bindsym Mod1+Shift+9 move container to workspace $ws9
142 bindsym Mod1+Shift+0 move container to workspace $ws10
143
144 # reload the configuration file
145 bindsym Mod1+Shift+c reload
146 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
147 bindsym Mod1+Shift+r restart
148 # exit i3 (logs you out of your X session)
149 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'"
150
151 # resize window (you can also use the mouse for that)
152 mode "resize" {
153 # These bindings trigger as soon as you enter the resize mode
154
155 # Pressing left will shrink the window’s width.
156 # Pressing right will grow the window’s width.
157 # Pressing up will shrink the window’s height.
158 # Pressing down will grow the window’s height.
159 bindsym $left resize shrink width 10 px or 10 ppt
160 bindsym $down resize grow height 10 px or 10 ppt
161 bindsym $up resize shrink height 10 px or 10 ppt
162 bindsym $right resize grow width 10 px or 10 ppt
163
164 # same bindings, but for the arrow keys
165 bindsym Left resize shrink width 10 px or 10 ppt
166 bindsym Down resize grow height 10 px or 10 ppt
167 bindsym Up resize shrink height 10 px or 10 ppt
168 bindsym Right resize grow width 10 px or 10 ppt
169
170 # back to normal: Enter or Escape or Mod1+r
171 bindsym Return mode "default"
172 bindsym Escape mode "default"
173 bindsym Mod1+r mode "default"
174 }
175
176 bindsym Mod1+r mode "resize"
177
178 # Start i3bar to display a workspace bar (plus the system information i3status
179 # finds out, if available)
180 bar {
181 status_command i3status
182 }
183
184 #######################################################################
185 # automatically start i3-config-wizard to offer the user to create a
186 # keysym-based config which used their favorite modifier (alt or windows)
187 #
188 # i3-config-wizard will not launch if there already is a config file
189 # in ~/.i3/config.
190 #
191 # Please remove the following exec line:
192 #######################################################################
193 exec i3-config-wizard
0 # WARNING
1 # WARNING: This configuration file is a template for the i3-config-wizard to
2 # WARNING: generate a config which uses keysyms in your current layout. It does
3 # WARNING: not get loaded by i3. Please do not change it.
4 # WARNING
5
6 # i3 config file (v4)
7 #
8 # Please see https://i3wm.org/docs/userguide.html for a complete reference!
9
10 set $mod Mod1
11
12 # Font for window titles. Will also be used by the bar unless a different font
13 # is used in the bar {} block below.
14 font pango:monospace 8
15
16 # This font is widely installed, provides lots of unicode glyphs, right-to-left
17 # text rendering and scalability on retina/hidpi displays (thanks to pango).
18 #font pango:DejaVu Sans Mono 8
19
20 # Before i3 v4.8, we used to recommend this one as the default:
21 # font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22 # The font above is very space-efficient, that is, it looks good, sharp and
23 # clear in small sizes. However, its unicode glyph coverage is limited, the old
24 # X core fonts rendering does not support right-to-left and this being a bitmap
25 # font, it doesn’t scale on retina/hidpi displays.
26
27 # Use Mouse+$mod to drag floating windows to their wanted position
28 floating_modifier $mod
29
30 # start a terminal
31 bindcode $mod+36 exec i3-sensible-terminal
32
33 # kill focused window
34 bindcode $mod+Shift+24 kill
35
36 # start dmenu (a program launcher)
37 bindcode $mod+40 exec dmenu_run
38 # There also is the (new) i3-dmenu-desktop which only displays applications
39 # shipping a .desktop file. It is a wrapper around dmenu, so you need that
40 # installed.
41 # bindsym $mod+d exec --no-startup-id i3-dmenu-desktop
42
43 # change focus
44 bindcode $mod+44 focus left
45 bindcode $mod+45 focus down
46 bindcode $mod+46 focus up
47 bindcode $mod+47 focus right
48
49 # alternatively, you can use the cursor keys:
50 bindcode $mod+113 focus left
51 bindcode $mod+116 focus down
52 bindcode $mod+111 focus up
53 bindcode $mod+114 focus right
54
55 # move focused window
56 bindcode $mod+Shift+44 move left
57 bindcode $mod+Shift+45 move down
58 bindcode $mod+Shift+46 move up
59 bindcode $mod+Shift+47 move right
60
61 # alternatively, you can use the cursor keys:
62 bindcode $mod+Shift+113 move left
63 bindcode $mod+Shift+116 move down
64 bindcode $mod+Shift+111 move up
65 bindcode $mod+Shift+114 move right
66
67 # split in horizontal orientation
68 bindcode $mod+43 split h
69
70 # split in vertical orientation
71 bindcode $mod+55 split v
72
73 # enter fullscreen mode for the focused container
74 bindcode $mod+41 fullscreen toggle
75
76 # change container layout (stacked, tabbed, toggle split)
77 bindcode $mod+39 layout stacking
78 bindcode $mod+25 layout tabbed
79 bindcode $mod+26 layout toggle split
80
81 # toggle tiling / floating
82 bindcode $mod+Shift+65 floating toggle
83
84 # change focus between tiling / floating windows
85 bindcode $mod+65 focus mode_toggle
86
87 # focus the parent container
88 bindcode $mod+38 focus parent
89
90 # focus the child container
91 #bindsym $mod+d focus child
92
93 # Define names for default workspaces for which we configure key bindings later on.
94 # We use variables to avoid repeating the names in multiple places.
95 set $ws1 "1"
96 set $ws2 "2"
97 set $ws3 "3"
98 set $ws4 "4"
99 set $ws5 "5"
100 set $ws6 "6"
101 set $ws7 "7"
102 set $ws8 "8"
103 set $ws9 "9"
104 set $ws10 "10"
105
106 # switch to workspace
107 bindcode $mod+10 workspace $ws1
108 bindcode $mod+11 workspace $ws2
109 bindcode $mod+12 workspace $ws3
110 bindcode $mod+13 workspace $ws4
111 bindcode $mod+14 workspace $ws5
112 bindcode $mod+15 workspace $ws6
113 bindcode $mod+16 workspace $ws7
114 bindcode $mod+17 workspace $ws8
115 bindcode $mod+18 workspace $ws9
116 bindcode $mod+19 workspace $ws10
117
118 # move focused container to workspace
119 bindcode $mod+Shift+10 move container to workspace $ws1
120 bindcode $mod+Shift+11 move container to workspace $ws2
121 bindcode $mod+Shift+12 move container to workspace $ws3
122 bindcode $mod+Shift+13 move container to workspace $ws4
123 bindcode $mod+Shift+14 move container to workspace $ws5
124 bindcode $mod+Shift+15 move container to workspace $ws6
125 bindcode $mod+Shift+16 move container to workspace $ws7
126 bindcode $mod+Shift+17 move container to workspace $ws8
127 bindcode $mod+Shift+18 move container to workspace $ws9
128 bindcode $mod+Shift+19 move container to workspace $ws10
129
130 # reload the configuration file
131 bindcode $mod+Shift+54 reload
132 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
133 bindcode $mod+Shift+27 restart
134 # exit i3 (logs you out of your X session)
135 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'"
136
137 # resize window (you can also use the mouse for that)
138 mode "resize" {
139 # These bindings trigger as soon as you enter the resize mode
140
141 # Pressing left will shrink the window’s width.
142 # Pressing right will grow the window’s width.
143 # Pressing up will shrink the window’s height.
144 # Pressing down will grow the window’s height.
145 bindcode 44 resize shrink width 10 px or 10 ppt
146 bindcode 45 resize grow height 10 px or 10 ppt
147 bindcode 46 resize shrink height 10 px or 10 ppt
148 bindcode 47 resize grow width 10 px or 10 ppt
149
150 # same bindings, but for the arrow keys
151 bindcode 113 resize shrink width 10 px or 10 ppt
152 bindcode 116 resize grow height 10 px or 10 ppt
153 bindcode 111 resize shrink height 10 px or 10 ppt
154 bindcode 114 resize grow width 10 px or 10 ppt
155
156 # back to normal: Enter or Escape or $mod+r
157 bindcode 36 mode "default"
158 bindcode 9 mode "default"
159 bindcode $mod+27 mode "default"
160 }
161
162 bindcode $mod+27 mode "resize"
163
164 # Start i3bar to display a workspace bar (plus the system information i3status
165 # finds out, if available)
166 bar {
167 status_command i3status
168 }
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # i3 - an improved dynamic tiling window manager
4 # © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 #
6 # generate-command-parser.pl: script to generate parts of the command parser
7 # from its specification file parser-specs/commands.spec.
8 #
9 # Requires only perl >= 5.10, no modules.
10
11 use strict;
12 use warnings;
13 use Data::Dumper;
14 use Getopt::Long;
15 use v5.10;
16
17 my $input = '';
18 my $prefix = '';
19 my $result = GetOptions(
20 'input=s' => \$input,
21 'prefix=s' => \$prefix
22 );
23
24 die qq|Input file "$input" does not exist!| unless -e $input;
25
26 # reads in a whole file
27 sub slurp {
28 open my $fh, '<', shift;
29 local $/;
30 <$fh>;
31 }
32
33 # Stores the different states.
34 my %states;
35
36 my @raw_lines = split("\n", slurp($input));
37 my @lines;
38
39 # XXX: In the future, we might switch to a different way of parsing this. The
40 # parser is in many ways not good — one obvious one is that it is hand-crafted
41 # without a good reason, also it preprocesses lines and forgets about line
42 # numbers. Luckily, this is just an implementation detail and the specification
43 # for the i3 command parser is in-tree (not user input).
44 # -- michael, 2012-01-12
45
46 # First step of preprocessing:
47 # Join token definitions which are spread over multiple lines.
48 for my $line (@raw_lines) {
49 next if $line =~ /^\s*#/ || $line =~ /^\s*$/;
50
51 if ($line =~ /^\s+->/) {
52 # This is a continued token definition, append this line to the
53 # previous one.
54 $lines[$#lines] = $lines[$#lines] . $line;
55 } else {
56 push @lines, $line;
57 next;
58 }
59 }
60
61 # First step: We build up the data structure containing all states and their
62 # token rules.
63
64 my $current_state;
65
66 for my $line (@lines) {
67 if (my ($state) = ($line =~ /^state ([A-Z0-9_]+):$/)) {
68 #say "got a new state: $state";
69 $current_state = $state;
70 } else {
71 # Must be a token definition:
72 # [identifier = ] <tokens> -> <action>
73 #say "token definition: $line";
74
75 my ($identifier, $tokens, $action) =
76 ($line =~ /
77 ^\s* # skip leading whitespace
78 ([a-z_]+ \s* = \s*|) # optional identifier
79 (.*?) -> \s* # token
80 (.*) # optional action
81 /x);
82
83 # Cleanup the identifier (if any).
84 $identifier =~ s/^\s*(\S+)\s*=\s*$/$1/g;
85
86 # The default action is to stay in the current state.
87 $action = $current_state if length($action) == 0;
88
89 #say "identifier = *$identifier*, token = *$tokens*, action = *$action*";
90 for my $token (split(',', $tokens)) {
91 # Cleanup trailing/leading whitespace.
92 $token =~ s/^\s*//g;
93 $token =~ s/\s*$//g;
94 my $store_token = {
95 token => $token,
96 identifier => $identifier,
97 next_state => $action,
98 };
99 if (exists $states{$current_state}) {
100 push @{$states{$current_state}}, $store_token;
101 } else {
102 $states{$current_state} = [ $store_token ];
103 }
104 }
105 }
106 }
107
108 # Second step: Generate the enum values for all states.
109
110 # It is important to keep the order the same, so we store the keys once.
111 # We sort descendingly by length to be able to replace occurrences of the state
112 # name even when one state’s name is included in another one’s (like FOR_WINDOW
113 # is in FOR_WINDOW_COMMAND).
114 my @keys = sort { (length($b) <=> length($a)) or ($a cmp $b) } keys %states;
115
116 open(my $enumfh, '>', "GENERATED_${prefix}_enums.h");
117
118 my %statenum;
119 say $enumfh 'typedef enum {';
120 my $cnt = 0;
121 for my $state (@keys, '__CALL') {
122 say $enumfh ',' if $cnt > 0;
123 print $enumfh " $state = $cnt";
124 $statenum{$state} = $cnt;
125 $cnt++;
126 }
127 say $enumfh "\n} cmdp_state;";
128 close($enumfh);
129
130 # Third step: Generate the call function.
131 open(my $callfh, '>', "GENERATED_${prefix}_call.h");
132 my $resultname = uc(substr($prefix, 0, 1)) . substr($prefix, 1) . 'ResultIR';
133 say $callfh "static void GENERATED_call(const int call_identifier, struct $resultname *result) {";
134 say $callfh ' switch (call_identifier) {';
135 my $call_id = 0;
136 for my $state (@keys) {
137 my $tokens = $states{$state};
138 for my $token (@$tokens) {
139 next unless $token->{next_state} =~ /^call /;
140 my ($cmd) = ($token->{next_state} =~ /^call (.*)/);
141 my ($next_state) = ($cmd =~ /; ([A-Z_]+)$/);
142 $cmd =~ s/; ([A-Z_]+)$//;
143 # Go back to the INITIAL state unless told otherwise.
144 $next_state ||= 'INITIAL';
145 my $fmt = $cmd;
146 # Replace the references to identified literals (like $workspace) with
147 # calls to get_string(). Also replaces state names (like FOR_WINDOW)
148 # with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.).
149 $cmd =~ s/$_/$statenum{$_}/g for @keys;
150 $cmd =~ s/\$([a-z_]+)/get_string("$1")/g;
151 $cmd =~ s/\&([a-z_]+)/get_long("$1")/g;
152 # For debugging/testing, we print the call using printf() and thus need
153 # to generate a format string. The format uses %d for <number>s,
154 # literal numbers or state IDs and %s for NULL, <string>s and literal
155 # strings.
156
157 # remove the function name temporarily, so that the following
158 # replacements only apply to the arguments.
159 my ($funcname) = ($fmt =~ /^(.+)\(/);
160 $fmt =~ s/^$funcname//;
161
162 $fmt =~ s/$_/%d/g for @keys;
163 $fmt =~ s/\$([a-z_]+)/%s/g;
164 $fmt =~ s/\&([a-z_]+)/%ld/g;
165 $fmt =~ s/"([a-z0-9_]+)"/%s/g;
166 $fmt =~ s/(?:-?|\b)[0-9]+\b/%d/g;
167
168 $fmt = $funcname . $fmt;
169
170 say $callfh " case $call_id:";
171 say $callfh " result->next_state = $next_state;";
172 say $callfh '#ifndef TEST_PARSER';
173 my $real_cmd = $cmd;
174 if ($real_cmd =~ /\(\)/) {
175 $real_cmd =~ s/\(/(&current_match, result/;
176 } else {
177 $real_cmd =~ s/\(/(&current_match, result, /;
178 }
179 say $callfh " $real_cmd;";
180 say $callfh '#else';
181 # debug
182 $cmd =~ s/[^(]+\(//;
183 $cmd =~ s/\)$//;
184 $cmd = ", $cmd" if length($cmd) > 0;
185 $cmd =~ s/, NULL//g;
186 say $callfh qq| fprintf(stderr, "$fmt\\n"$cmd);|;
187 # The cfg_criteria functions have side-effects which are important for
188 # testing. They are implemented as stubs in the test parser code.
189 if ($real_cmd =~ /^cfg_criteria/) {
190 say $callfh qq| $real_cmd;|;
191 }
192 say $callfh '#endif';
193 say $callfh " break;";
194 $token->{next_state} = "call $call_id";
195 $call_id++;
196 }
197 }
198 say $callfh ' default:';
199 say $callfh ' printf("BUG in the parser. state = %d\n", call_identifier);';
200 say $callfh ' assert(false);';
201 say $callfh ' }';
202 say $callfh '}';
203 close($callfh);
204
205 # Fourth step: Generate the token datastructures.
206
207 open(my $tokfh, '>', "GENERATED_${prefix}_tokens.h");
208
209 for my $state (@keys) {
210 my $tokens = $states{$state};
211 say $tokfh 'static cmdp_token tokens_' . $state . '[' . scalar @$tokens . '] = {';
212 for my $token (@$tokens) {
213 my $call_identifier = 0;
214 my $token_name = $token->{token};
215 if ($token_name =~ /^'/) {
216 # To make the C code simpler, we leave out the trailing single
217 # quote of the literal. We can do strdup(literal + 1); then :).
218 $token_name =~ s/'$//;
219 }
220 my $next_state = $token->{next_state};
221 if ($next_state =~ /^call /) {
222 ($call_identifier) = ($next_state =~ /^call ([0-9]+)$/);
223 $next_state = '__CALL';
224 }
225 my $identifier = $token->{identifier};
226 say $tokfh qq| { "$token_name", "$identifier", $next_state, { $call_identifier } },|;
227 }
228 say $tokfh '};';
229 }
230
231 say $tokfh 'static cmdp_token_ptr tokens[' . scalar @keys . '] = {';
232 for my $state (@keys) {
233 my $tokens = $states{$state};
234 say $tokfh ' { tokens_' . $state . ', ' . scalar @$tokens . ' },';
235 }
236 say $tokfh '};';
237
238 close($tokfh);
0 xmacro(_NET_WM_NAME)
1 xmacro(_NET_WM_WINDOW_TYPE)
2 xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
3 xmacro(ATOM)
4 xmacro(CARDINAL)
5 xmacro(UTF8_STRING)
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * i3-config-wizard: Program to convert configs using keycodes to configs using
7 * keysyms.
8 *
9 */
10 #include <config.h>
11
12 #if defined(__FreeBSD__)
13 #include <sys/param.h>
14 #endif
15
16 /* For systems without getline, fall back to fgetln */
17 #if defined(__APPLE__)
18 #define USE_FGETLN
19 #elif defined(__FreeBSD__)
20 /* Defining this macro before including stdio.h is necessary in order to have
21 * a prototype for getline in FreeBSD. */
22 #define _WITH_GETLINE
23 #endif
24
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <stdlib.h>
28 #include <stdbool.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <err.h>
34 #include <stdint.h>
35 #include <getopt.h>
36 #include <limits.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <glob.h>
40 #include <assert.h>
41
42 #include <xcb/xcb.h>
43 #include <xcb/xcb_aux.h>
44 #include <xcb/xcb_event.h>
45 #include <xcb/xcb_keysyms.h>
46
47 #include <xkbcommon/xkbcommon.h>
48 #include <xkbcommon/xkbcommon-x11.h>
49
50 #define SN_API_NOT_YET_FROZEN 1
51 #include <libsn/sn-launchee.h>
52
53 #include <X11/Xlib.h>
54 #include <X11/keysym.h>
55 #include <X11/XKBlib.h>
56
57 /* We need SYSCONFDIR for the path to the keycode config template, so raise an
58 * error if it’s not defined for whatever reason */
59 #ifndef SYSCONFDIR
60 #error "SYSCONFDIR not defined"
61 #endif
62
63 #define FREE(pointer) \
64 do { \
65 free(pointer); \
66 pointer = NULL; \
67 } while (0)
68
69 #include "xcb.h"
70 #include "libi3.h"
71
72 #define TEXT_PADDING logical_px(4)
73 #define WIN_POS_X logical_px(490)
74 #define WIN_POS_Y logical_px(297)
75 #define WIN_WIDTH logical_px(300)
76 #define WIN_HEIGHT (15 * font.height + TEXT_PADDING)
77
78 #define col_x(col) \
79 (((col)-1) * char_width + TEXT_PADDING)
80 #define row_y(row) \
81 (((row)-1) * font.height + TEXT_PADDING)
82
83 enum { STEP_WELCOME,
84 STEP_GENERATE } current_step = STEP_WELCOME;
85 enum { MOD_Mod1,
86 MOD_Mod4 } modifier = MOD_Mod4;
87
88 static char *config_path;
89 static uint32_t xcb_numlock_mask;
90 xcb_connection_t *conn;
91 static xcb_key_symbols_t *keysyms;
92 xcb_screen_t *root_screen;
93 static xcb_get_modifier_mapping_reply_t *modmap_reply;
94 static i3Font font;
95 static i3Font bold_font;
96 static int char_width;
97 static char *socket_path = NULL;
98 static xcb_window_t win;
99 static surface_t surface;
100 static xcb_key_symbols_t *symbols;
101 xcb_window_t root;
102 static struct xkb_keymap *xkb_keymap;
103 static uint8_t xkb_base_event;
104 static uint8_t xkb_base_error;
105
106 static void finish(void);
107
108 #include "GENERATED_config_enums.h"
109
110 typedef struct token {
111 char *name;
112 char *identifier;
113 /* This might be __CALL */
114 cmdp_state next_state;
115 union {
116 uint16_t call_identifier;
117 } extra;
118 } cmdp_token;
119
120 typedef struct tokenptr {
121 cmdp_token *array;
122 int n;
123 } cmdp_token_ptr;
124
125 #include "GENERATED_config_tokens.h"
126
127 static cmdp_state state;
128 /* A list which contains the states that lead to the current state, e.g.
129 * INITIAL, WORKSPACE_LAYOUT.
130 * When jumping back to INITIAL, statelist_idx will simply be set to 1
131 * (likewise for other states, e.g. MODE or BAR).
132 * This list is used to process the nearest error token. */
133 static cmdp_state statelist[10] = {INITIAL};
134 /* NB: statelist_idx points to where the next entry will be inserted */
135 static int statelist_idx = 1;
136
137 struct stack_entry {
138 /* Just a pointer, not dynamically allocated. */
139 const char *identifier;
140 enum {
141 STACK_STR = 0,
142 STACK_LONG = 1,
143 } type;
144 union {
145 char *str;
146 long num;
147 } val;
148 };
149
150 /* 10 entries should be enough for everybody. */
151 static struct stack_entry stack[10];
152
153 /*
154 * Pushes a string (identified by 'identifier') on the stack. We simply use a
155 * single array, since the number of entries we have to store is very small.
156 *
157 */
158 static void push_string(const char *identifier, const char *str) {
159 for (int c = 0; c < 10; c++) {
160 if (stack[c].identifier != NULL &&
161 strcmp(stack[c].identifier, identifier) != 0)
162 continue;
163 if (stack[c].identifier == NULL) {
164 /* Found a free slot, let’s store it here. */
165 stack[c].identifier = identifier;
166 stack[c].val.str = sstrdup(str);
167 stack[c].type = STACK_STR;
168 } else {
169 /* Append the value. */
170 char *prev = stack[c].val.str;
171 sasprintf(&(stack[c].val.str), "%s,%s", prev, str);
172 free(prev);
173 }
174 return;
175 }
176
177 /* When we arrive here, the stack is full. This should not happen and
178 * means there’s either a bug in this parser or the specification
179 * contains a command with more than 10 identified tokens. */
180 fprintf(stderr, "BUG: commands_parser stack full. This means either a bug "
181 "in the code, or a new command which contains more than "
182 "10 identified tokens.\n");
183 exit(1);
184 }
185
186 static void push_long(const char *identifier, long num) {
187 for (int c = 0; c < 10; c++) {
188 if (stack[c].identifier != NULL)
189 continue;
190 /* Found a free slot, let’s store it here. */
191 stack[c].identifier = identifier;
192 stack[c].val.num = num;
193 stack[c].type = STACK_LONG;
194 return;
195 }
196
197 /* When we arrive here, the stack is full. This should not happen and
198 * means there’s either a bug in this parser or the specification
199 * contains a command with more than 10 identified tokens. */
200 fprintf(stderr, "BUG: commands_parser stack full. This means either a bug "
201 "in the code, or a new command which contains more than "
202 "10 identified tokens.\n");
203 exit(1);
204 }
205
206 static const char *get_string(const char *identifier) {
207 for (int c = 0; c < 10; c++) {
208 if (stack[c].identifier == NULL)
209 break;
210 if (strcmp(identifier, stack[c].identifier) == 0)
211 return stack[c].val.str;
212 }
213 return NULL;
214 }
215
216 static void clear_stack(void) {
217 for (int c = 0; c < 10; c++) {
218 if (stack[c].type == STACK_STR)
219 free(stack[c].val.str);
220 stack[c].identifier = NULL;
221 stack[c].val.str = NULL;
222 stack[c].val.num = 0;
223 }
224 }
225
226 /*
227 * Returns true if sym is bound to any key except for 'except_keycode' on the
228 * first four layers (normal, shift, mode_switch, mode_switch + shift).
229 *
230 */
231 static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) {
232 xcb_keycode_t i,
233 min_keycode = xcb_get_setup(conn)->min_keycode,
234 max_keycode = xcb_get_setup(conn)->max_keycode;
235
236 for (i = min_keycode; i && i <= max_keycode; i++) {
237 if (i == except_keycode)
238 continue;
239 for (int level = 0; level < 4; level++) {
240 if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym)
241 continue;
242 return true;
243 }
244 }
245 return false;
246 }
247
248 static char *next_state(const cmdp_token *token) {
249 cmdp_state _next_state = token->next_state;
250
251 if (token->next_state == __CALL) {
252 const char *modifiers = get_string("modifiers");
253 int keycode = atoi(get_string("key"));
254 int level = 0;
255 if (modifiers != NULL &&
256 strstr(modifiers, "Shift") != NULL) {
257 /* When shift is included, we really need to use the second-level
258 * symbol (upper-case). The lower-case symbol could be on a
259 * different key than the upper-case one (unlikely for letters, but
260 * more likely for special characters). */
261 level = 1;
262
263 /* Try to use the keysym on the first level (lower-case). In case
264 * this doesn’t make it ambiguous (think of a keyboard layout
265 * having '1' on two different keys, but '!' only on keycode 10),
266 * we’ll stick with the keysym of the first level.
267 *
268 * This reduces a lot of confusion for users who switch keyboard
269 * layouts from qwerty to qwertz or other slight variations of
270 * qwerty (yes, that happens quite often). */
271 const xkb_keysym_t *syms;
272 int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
273 if (num == 0)
274 errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
275 if (!keysym_used_on_other_key(syms[0], keycode))
276 level = 0;
277 }
278
279 const xkb_keysym_t *syms;
280 int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
281 if (num == 0)
282 errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
283 if (num > 1)
284 printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
285
286 char str[4096];
287 if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
288 errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
289 const char *release = get_string("release");
290 char *res;
291 char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
292 char *comma;
293 while ((comma = strchr(modrep, ',')) != NULL) {
294 *comma = '+';
295 }
296 sasprintf(&res, "bindsym %s%s%s %s%s\n", (modifiers == NULL ? "" : modrep), (modifiers == NULL ? "" : "+"), str, (release == NULL ? "" : release), get_string("command"));
297 clear_stack();
298 free(modrep);
299 return res;
300 }
301
302 state = _next_state;
303
304 /* See if we are jumping back to a state in which we were in previously
305 * (statelist contains INITIAL) and just move statelist_idx accordingly. */
306 for (int i = 0; i < statelist_idx; i++) {
307 if (statelist[i] != _next_state)
308 continue;
309 statelist_idx = i + 1;
310 return NULL;
311 }
312
313 /* Otherwise, the state is new and we add it to the list */
314 statelist[statelist_idx++] = _next_state;
315 return NULL;
316 }
317
318 static char *rewrite_binding(const char *input) {
319 state = INITIAL;
320 statelist_idx = 1;
321
322 const char *walk = input;
323 const size_t len = strlen(input);
324 int c;
325 const cmdp_token *token;
326 char *result = NULL;
327
328 /* The "<=" operator is intentional: We also handle the terminating 0-byte
329 * explicitly by looking for an 'end' token. */
330 while ((size_t)(walk - input) <= len) {
331 /* Skip whitespace before every token, newlines are relevant since they
332 * separate configuration directives. */
333 while ((*walk == ' ' || *walk == '\t') && *walk != '\0')
334 walk++;
335
336 //printf("remaining input: %s\n", walk);
337
338 cmdp_token_ptr *ptr = &(tokens[state]);
339 for (c = 0; c < ptr->n; c++) {
340 token = &(ptr->array[c]);
341
342 /* A literal. */
343 if (token->name[0] == '\'') {
344 if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
345 if (token->identifier != NULL)
346 push_string(token->identifier, token->name + 1);
347 walk += strlen(token->name) - 1;
348 if ((result = next_state(token)) != NULL)
349 return result;
350 break;
351 }
352 continue;
353 }
354
355 if (strcmp(token->name, "number") == 0) {
356 /* Handle numbers. We only accept decimal numbers for now. */
357 char *end = NULL;
358 errno = 0;
359 long int num = strtol(walk, &end, 10);
360 if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
361 (errno != 0 && num == 0))
362 continue;
363
364 /* No valid numbers found */
365 if (end == walk)
366 continue;
367
368 if (token->identifier != NULL)
369 push_long(token->identifier, num);
370
371 /* Set walk to the first non-number character */
372 walk = end;
373 if ((result = next_state(token)) != NULL)
374 return result;
375 break;
376 }
377
378 if (strcmp(token->name, "string") == 0 ||
379 strcmp(token->name, "word") == 0) {
380 const char *beginning = walk;
381 /* Handle quoted strings (or words). */
382 if (*walk == '"') {
383 beginning++;
384 walk++;
385 while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
386 walk++;
387 } else {
388 if (token->name[0] == 's') {
389 while (*walk != '\0' && *walk != '\r' && *walk != '\n')
390 walk++;
391 } else {
392 /* For a word, the delimiters are white space (' ' or
393 * '\t'), closing square bracket (]), comma (,) and
394 * semicolon (;). */
395 while (*walk != ' ' && *walk != '\t' &&
396 *walk != ']' && *walk != ',' &&
397 *walk != ';' && *walk != '\r' &&
398 *walk != '\n' && *walk != '\0')
399 walk++;
400 }
401 }
402 if (walk != beginning) {
403 char *str = scalloc(walk - beginning + 1, 1);
404 /* We copy manually to handle escaping of characters. */
405 int inpos, outpos;
406 for (inpos = 0, outpos = 0;
407 inpos < (walk - beginning);
408 inpos++, outpos++) {
409 /* We only handle escaped double quotes to not break
410 * backwards compatibility with people using \w in
411 * regular expressions etc. */
412 if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
413 inpos++;
414 str[outpos] = beginning[inpos];
415 }
416 if (token->identifier)
417 push_string(token->identifier, str);
418 free(str);
419 /* If we are at the end of a quoted string, skip the ending
420 * double quote. */
421 if (*walk == '"')
422 walk++;
423 if ((result = next_state(token)) != NULL)
424 return result;
425 break;
426 }
427 }
428
429 if (strcmp(token->name, "end") == 0) {
430 //printf("checking for end: *%s*\n", walk);
431 if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
432 if ((result = next_state(token)) != NULL)
433 return result;
434 /* To make sure we start with an appropriate matching
435 * datastructure for commands which do *not* specify any
436 * criteria, we re-initialize the criteria system after
437 * every command. */
438 // TODO: make this testable
439 walk++;
440 break;
441 }
442 }
443 }
444 }
445
446 return NULL;
447 }
448
449 /*
450 * Having verboselog(), errorlog() and debuglog() is necessary when using libi3.
451 *
452 */
453 void verboselog(char *fmt, ...) {
454 va_list args;
455
456 va_start(args, fmt);
457 vfprintf(stdout, fmt, args);
458 va_end(args);
459 }
460
461 void errorlog(char *fmt, ...) {
462 va_list args;
463
464 va_start(args, fmt);
465 vfprintf(stderr, fmt, args);
466 va_end(args);
467 }
468
469 void debuglog(char *fmt, ...) {
470 }
471
472 static void txt(int col, int row, char *text, color_t fg, color_t bg) {
473 int x = col_x(col);
474 int y = row_y(row);
475 i3String *string = i3string_from_utf8(text);
476 draw_util_text(string, &surface, fg, bg, x, y, WIN_WIDTH - x - TEXT_PADDING);
477 i3string_free(string);
478 }
479
480 /*
481 * Handles expose events, that is, draws the window contents.
482 *
483 */
484 static int handle_expose(void) {
485 const color_t black = draw_util_hex_to_color("#000000");
486 const color_t white = draw_util_hex_to_color("#FFFFFF");
487 const color_t green = draw_util_hex_to_color("#00FF00");
488 const color_t red = draw_util_hex_to_color("#FF0000");
489
490 /* draw background */
491 draw_util_clear_surface(&surface, black);
492
493 set_font(&font);
494
495 if (current_step == STEP_WELCOME) {
496 txt(2, 2, "You have not configured i3 yet.", white, black);
497 txt(2, 3, "Do you want me to generate a config at", white, black);
498
499 char *msg;
500 sasprintf(&msg, "%s?", config_path);
501 txt(2, 4, msg, white, black);
502 free(msg);
503
504 txt(13, 6, "Yes, generate the config", white, black);
505 txt(13, 8, "No, I will use the defaults", white, black);
506
507 txt(4, 6, "<Enter>", green, black);
508
509 txt(5, 8, "<ESC>", red, black);
510 }
511
512 if (current_step == STEP_GENERATE) {
513 txt(2, 2, "Please choose either:", white, black);
514 txt(13, 4, "Win as default modifier", white, black);
515 txt(13, 5, "Alt as default modifier", white, black);
516 txt(2, 7, "Afterwards, press", white, black);
517 txt(13, 9, "to write the config", white, black);
518 txt(13, 10, "to abort", white, black);
519
520 /* the not-selected modifier */
521 if (modifier == MOD_Mod4)
522 txt(5, 5, "<Alt>", white, black);
523 else
524 txt(5, 4, "<Win>", white, black);
525
526 /* the selected modifier */
527 set_font(&bold_font);
528 if (modifier == MOD_Mod4)
529 txt(2, 4, "-> <Win>", white, black);
530 else
531 txt(2, 5, "-> <Alt>", white, black);
532
533 set_font(&font);
534 txt(4, 9, "<Enter>", green, black);
535
536 txt(5, 10, "<ESC>", red, black);
537 }
538
539 xcb_flush(conn);
540
541 return 1;
542 }
543
544 static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
545 printf("Keypress %d, state raw = %d\n", event->detail, event->state);
546
547 /* Remove the numlock bit, all other bits are modifiers we can bind to */
548 uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK);
549 /* Only use the lower 8 bits of the state (modifier masks) so that mouse
550 * button masks are filtered out */
551 state_filtered &= 0xFF;
552
553 xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, state_filtered);
554
555 printf("sym = %c (%d)\n", sym, sym);
556
557 if (sym == XK_Return || sym == XK_KP_Enter) {
558 if (current_step == STEP_WELCOME) {
559 current_step = STEP_GENERATE;
560 /* Set window title */
561 xcb_change_property(conn,
562 XCB_PROP_MODE_REPLACE,
563 win,
564 A__NET_WM_NAME,
565 A_UTF8_STRING,
566 8,
567 strlen("i3: generate config"),
568 "i3: generate config");
569 xcb_flush(conn);
570 } else
571 finish();
572 }
573
574 /* Swap between modifiers when up or down is pressed. */
575 if (sym == XK_Up || sym == XK_Down) {
576 modifier = (modifier == MOD_Mod1) ? MOD_Mod4 : MOD_Mod1;
577 handle_expose();
578 }
579
580 /* cancel any time */
581 if (sym == XK_Escape)
582 exit(0);
583
584 /* Check if this is Mod1 or Mod4. The modmap contains Shift, Lock, Control,
585 * Mod1, Mod2, Mod3, Mod4, Mod5 (in that order) */
586 xcb_keycode_t *modmap = xcb_get_modifier_mapping_keycodes(modmap_reply);
587 /* Mod1? */
588 int mask = 3;
589 for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) {
590 xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i];
591 if (code == XCB_NONE)
592 continue;
593 printf("Modifier keycode for Mod1: 0x%02x\n", code);
594 if (code == event->detail) {
595 modifier = MOD_Mod1;
596 printf("This is Mod1!\n");
597 }
598 }
599
600 /* Mod4? */
601 mask = 6;
602 for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) {
603 xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i];
604 if (code == XCB_NONE)
605 continue;
606 printf("Modifier keycode for Mod4: 0x%02x\n", code);
607 if (code == event->detail) {
608 modifier = MOD_Mod4;
609 printf("This is Mod4!\n");
610 }
611 }
612
613 handle_expose();
614 return 1;
615 }
616
617 /*
618 * Handle button presses to make clicking on "<win>" and "<alt>" work
619 *
620 */
621 static void handle_button_press(xcb_button_press_event_t *event) {
622 if (current_step != STEP_GENERATE)
623 return;
624
625 if (event->event_x < col_x(5) || event->event_x > col_x(10))
626 return;
627
628 if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) {
629 modifier = MOD_Mod4;
630 handle_expose();
631 }
632
633 if (event->event_y >= row_y(5) && event->event_y <= (row_y(5) + font.height)) {
634 modifier = MOD_Mod1;
635 handle_expose();
636 }
637 }
638
639 /*
640 * Creates the config file and tells i3 to reload.
641 *
642 */
643 static void finish(void) {
644 printf("creating \"%s\"...\n", config_path);
645
646 struct xkb_context *xkb_context;
647
648 if ((xkb_context = xkb_context_new(0)) == NULL)
649 errx(1, "could not create xkbcommon context");
650
651 int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
652 if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
653 errx(1, "xkb_x11_keymap_new_from_device failed");
654
655 FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
656 if (kc_config == NULL)
657 err(1, "Could not open input file \"%s\"", SYSCONFDIR "/i3/config.keycodes");
658
659 FILE *ks_config = fopen(config_path, "w");
660 if (ks_config == NULL)
661 err(1, "Could not open output config file \"%s\"", config_path);
662 free(config_path);
663
664 char *line = NULL;
665 size_t len = 0;
666 #ifndef USE_FGETLN
667 ssize_t read;
668 #endif
669 bool head_of_file = true;
670
671 /* write a header about auto-generation to the output file */
672 fputs("# This file has been auto-generated by i3-config-wizard(1).\n", ks_config);
673 fputs("# It will not be overwritten, so edit it as you like.\n", ks_config);
674 fputs("#\n", ks_config);
675 fputs("# Should you change your keyboard layout some time, delete\n", ks_config);
676 fputs("# this file and re-run i3-config-wizard(1).\n", ks_config);
677 fputs("#\n", ks_config);
678
679 #ifdef USE_FGETLN
680 char *buf = NULL;
681 while ((buf = fgetln(kc_config, &len)) != NULL) {
682 /* fgetln does not return null-terminated strings */
683 FREE(line);
684 sasprintf(&line, "%.*s", len, buf);
685 #else
686 size_t linecap = 0;
687 while ((read = getline(&line, &linecap, kc_config)) != -1) {
688 len = strlen(line);
689 #endif
690 /* skip the warning block at the beginning of the input file */
691 if (head_of_file &&
692 strncmp("# WARNING", line, strlen("# WARNING")) == 0)
693 continue;
694
695 head_of_file = false;
696
697 /* Skip leading whitespace */
698 char *walk = line;
699 while (isspace(*walk) && walk < (line + len)) {
700 /* Pre-output the skipped whitespaces to keep proper indentation */
701 fputc(*walk, ks_config);
702 walk++;
703 }
704
705 /* Set the modifier the user chose */
706 if (strncmp(walk, "set $mod ", strlen("set $mod ")) == 0) {
707 if (modifier == MOD_Mod1)
708 fputs("set $mod Mod1\n", ks_config);
709 else
710 fputs("set $mod Mod4\n", ks_config);
711 continue;
712 }
713
714 /* Check for 'bindcode'. If it’s not a bindcode line, we
715 * just copy it to the output file */
716 if (strncmp(walk, "bindcode", strlen("bindcode")) != 0) {
717 fputs(walk, ks_config);
718 continue;
719 }
720 char *result = rewrite_binding(walk);
721 fputs(result, ks_config);
722 free(result);
723 }
724
725 /* sync to do our best in order to have the file really stored on disk */
726 fflush(ks_config);
727 fsync(fileno(ks_config));
728
729 #ifndef USE_FGETLN
730 free(line);
731 #endif
732
733 fclose(kc_config);
734 fclose(ks_config);
735
736 /* tell i3 to reload the config file */
737 int sockfd = ipc_connect(socket_path);
738 ipc_send_message(sockfd, strlen("reload"), 0, (uint8_t *)"reload");
739 close(sockfd);
740
741 exit(0);
742 }
743
744 int main(int argc, char *argv[]) {
745 char *xdg_config_home;
746 char *pattern = "pango:monospace 8";
747 char *patternbold = "pango:monospace bold 8";
748 int o, option_index = 0;
749 bool headless_run = false;
750
751 static struct option long_options[] = {
752 {"socket", required_argument, 0, 's'},
753 {"version", no_argument, 0, 'v'},
754 {"modifier", required_argument, 0, 'm'},
755 {"limit", required_argument, 0, 'l'},
756 {"prompt", required_argument, 0, 'P'},
757 {"prefix", required_argument, 0, 'p'},
758 {"font", required_argument, 0, 'f'},
759 {"help", no_argument, 0, 'h'},
760 {0, 0, 0, 0}};
761
762 char *options_string = "sm:vh";
763
764 while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
765 switch (o) {
766 case 's':
767 FREE(socket_path);
768 socket_path = sstrdup(optarg);
769 break;
770 case 'v':
771 printf("i3-config-wizard " I3_VERSION "\n");
772 return 0;
773 case 'm':
774 headless_run = true;
775 if (strcmp(optarg, "alt") == 0)
776 modifier = MOD_Mod1;
777 else if (strcmp(optarg, "win") == 0)
778 modifier = MOD_Mod4;
779 else
780 err(EXIT_FAILURE, "Invalid modifier key %s", optarg);
781 break;
782 case 'h':
783 printf("i3-config-wizard " I3_VERSION "\n");
784 printf("i3-config-wizard [-s <socket>] [-m win|alt] [-v] [-h]\n");
785 return 0;
786 }
787 }
788
789 char *path = get_config_path(NULL, false);
790 if (path != NULL) {
791 printf("The config file \"%s\" already exists. Exiting.\n", path);
792 free(path);
793 return 0;
794 }
795
796 /* Always write to $XDG_CONFIG_HOME/i3/config by default. */
797 if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
798 xdg_config_home = "~/.config";
799
800 xdg_config_home = resolve_tilde(xdg_config_home);
801 sasprintf(&config_path, "%s/i3/config", xdg_config_home);
802
803 /* Create $XDG_CONFIG_HOME/i3 if it does not yet exist */
804 char *config_dir;
805 struct stat stbuf;
806 sasprintf(&config_dir, "%s/i3", xdg_config_home);
807 if (stat(config_dir, &stbuf) != 0)
808 if (mkdirp(config_dir, DEFAULT_DIR_MODE) != 0)
809 err(EXIT_FAILURE, "mkdirp(%s) failed", config_dir);
810 free(config_dir);
811 free(xdg_config_home);
812
813 int fd;
814 if ((fd = open(config_path, O_CREAT | O_RDWR, 0644)) == -1) {
815 printf("Cannot open file \"%s\" for writing: %s. Exiting.\n", config_path, strerror(errno));
816 return 0;
817 }
818 close(fd);
819 unlink(config_path);
820
821 int screen;
822 if ((conn = xcb_connect(NULL, &screen)) == NULL ||
823 xcb_connection_has_error(conn))
824 errx(1, "Cannot open display\n");
825
826 if (xkb_x11_setup_xkb_extension(conn,
827 XKB_X11_MIN_MAJOR_XKB_VERSION,
828 XKB_X11_MIN_MINOR_XKB_VERSION,
829 0,
830 NULL,
831 NULL,
832 &xkb_base_event,
833 &xkb_base_error) != 1)
834 errx(EXIT_FAILURE, "Could not setup XKB extension.");
835
836 keysyms = xcb_key_symbols_alloc(conn);
837 xcb_get_modifier_mapping_cookie_t modmap_cookie;
838 modmap_cookie = xcb_get_modifier_mapping(conn);
839 symbols = xcb_key_symbols_alloc(conn);
840
841 if (headless_run) {
842 finish();
843 return 0;
844 }
845
846 /* Place requests for the atoms we need as soon as possible */
847 #define xmacro(atom) \
848 xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
849 #include "atoms.xmacro"
850 #undef xmacro
851
852 /* Init startup notification. */
853 SnDisplay *sndisplay = sn_xcb_display_new(conn, NULL, NULL);
854 SnLauncheeContext *sncontext = sn_launchee_context_new_from_environment(sndisplay, screen);
855 sn_display_unref(sndisplay);
856
857 root_screen = xcb_aux_get_screen(conn, screen);
858 root = root_screen->root;
859
860 if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
861 errx(EXIT_FAILURE, "Could not get modifier mapping\n");
862
863 xcb_numlock_mask = get_mod_mask_for(XCB_NUM_LOCK, symbols, modmap_reply);
864
865 init_dpi();
866 font = load_font(pattern, true);
867 bold_font = load_font(patternbold, true);
868
869 /* Determine character width in the default font. */
870 set_font(&font);
871 char_width = predict_text_width(i3string_from_utf8("a"));
872
873 /* Open an input window */
874 win = xcb_generate_id(conn);
875 xcb_create_window(
876 conn,
877 XCB_COPY_FROM_PARENT,
878 win, /* the window id */
879 root, /* parent == root */
880 WIN_POS_X, WIN_POS_Y, WIN_WIDTH, WIN_HEIGHT, /* dimensions */
881 0, /* X11 border = 0, we draw our own */
882 XCB_WINDOW_CLASS_INPUT_OUTPUT,
883 XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
884 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
885 (uint32_t[]){
886 0, /* back pixel: black */
887 XCB_EVENT_MASK_EXPOSURE |
888 XCB_EVENT_MASK_BUTTON_PRESS});
889 if (sncontext) {
890 sn_launchee_context_setup_window(sncontext, win);
891 }
892
893 /* Map the window (make it visible) */
894 xcb_map_window(conn, win);
895
896 /* Setup NetWM atoms */
897 #define xmacro(name) \
898 do { \
899 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
900 if (!reply) \
901 errx(EXIT_FAILURE, "Could not get atom " #name "\n"); \
902 \
903 A_##name = reply->atom; \
904 free(reply); \
905 } while (0);
906 #include "atoms.xmacro"
907 #undef xmacro
908
909 /* Set dock mode */
910 xcb_change_property(conn,
911 XCB_PROP_MODE_REPLACE,
912 win,
913 A__NET_WM_WINDOW_TYPE,
914 A_ATOM,
915 32,
916 1,
917 (unsigned char *)&A__NET_WM_WINDOW_TYPE_DIALOG);
918
919 /* Set window title */
920 xcb_change_property(conn,
921 XCB_PROP_MODE_REPLACE,
922 win,
923 A__NET_WM_NAME,
924 A_UTF8_STRING,
925 8,
926 strlen("i3: first configuration"),
927 "i3: first configuration");
928
929 /* Initialize drawable surface */
930 draw_util_surface_init(conn, &surface, win, get_visualtype(root_screen), WIN_WIDTH, WIN_HEIGHT);
931
932 /* Grab the keyboard to get all input */
933 xcb_flush(conn);
934
935 /* Try (repeatedly, if necessary) to grab the keyboard. We might not
936 * get the keyboard at the first attempt because of the keybinding
937 * still being active when started via a wm’s keybinding. */
938 xcb_grab_keyboard_cookie_t cookie;
939 xcb_grab_keyboard_reply_t *reply = NULL;
940
941 int count = 0;
942 while ((reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) && (count++ < 500)) {
943 cookie = xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
944 reply = xcb_grab_keyboard_reply(conn, cookie, NULL);
945 usleep(1000);
946 }
947
948 if (reply->status != XCB_GRAB_STATUS_SUCCESS) {
949 fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status);
950 exit(-1);
951 }
952
953 /* Startup complete. */
954 if (sncontext) {
955 sn_launchee_context_complete(sncontext);
956 sn_launchee_context_unref(sncontext);
957 }
958
959 xcb_flush(conn);
960
961 xcb_generic_event_t *event;
962 while ((event = xcb_wait_for_event(conn)) != NULL) {
963 if (event->response_type == 0) {
964 fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
965 continue;
966 }
967
968 /* Strip off the highest bit (set if the event is generated) */
969 int type = (event->response_type & 0x7F);
970
971 /* TODO: handle mappingnotify */
972 switch (type) {
973 case XCB_KEY_PRESS:
974 handle_key_press(NULL, conn, (xcb_key_press_event_t *)event);
975 break;
976
977 case XCB_BUTTON_PRESS:
978 handle_button_press((xcb_button_press_event_t *)event);
979 break;
980
981 case XCB_EXPOSE:
982 if (((xcb_expose_event_t *)event)->count == 0) {
983 handle_expose();
984 }
985
986 break;
987 }
988
989 free(event);
990 }
991
992 /* Dismiss drawable surface */
993 draw_util_surface_free(conn, &surface);
994
995 return 0;
996 }
0 #pragma once
1
2 /* from X11/keysymdef.h */
3 #define XCB_NUM_LOCK 0xff7f
4
5 #define xmacro(atom) xcb_atom_t A_##atom;
6 #include "atoms.xmacro"
7 #undef xmacro
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # © 2012 Michael Stapelberg
4 #
5 # No dependencies except for perl ≥ v5.10
6
7 use strict;
8 use warnings qw(FATAL utf8);
9 use Data::Dumper;
10 use IPC::Open2;
11 use POSIX qw(locale_h);
12 use File::Find;
13 use File::Basename qw(basename);
14 use File::Temp qw(tempfile);
15 use Getopt::Long;
16 use Pod::Usage;
17 use v5.10;
18 use utf8;
19 use open ':encoding(UTF-8)';
20
21 binmode STDOUT, ':utf8';
22 binmode STDERR, ':utf8';
23
24 # reads in a whole file
25 sub slurp {
26 my ($filename) = @_;
27 my $fh;
28 if (!open($fh, '<', $filename)) {
29 warn "Could not open $filename: $!";
30 return undef;
31 }
32 local $/;
33 my $result;
34 eval {
35 $result = <$fh>;
36 };
37 if ($@) {
38 warn "Could not read $filename: $@";
39 return undef;
40 } else {
41 return $result;
42 }
43 }
44
45 my @entry_types;
46 my $dmenu_cmd = 'dmenu -i';
47 my $result = GetOptions(
48 'dmenu=s' => \$dmenu_cmd,
49 'entry-type=s' => \@entry_types,
50 'version' => sub {
51 say "dmenu-desktop 1.5 © 2012 Michael Stapelberg";
52 exit 0;
53 },
54 'help' => sub {
55 pod2usage(-exitval => 0);
56 });
57
58 die "Could not parse command line options" unless $result;
59
60 # Filter entry types and set default type(s) if none selected
61 my $valid_types = {
62 name => 1,
63 command => 1,
64 filename => 1,
65 };
66 @entry_types = grep { exists($valid_types->{$_}) } @entry_types;
67 @entry_types = ('name', 'command') unless @entry_types;
68
69 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
70 # ┃ Convert LC_MESSAGES into an ordered list of suffixes to search for in the ┃
71 # ┃ .desktop files (e.g. “Name[de_DE@euro]” for LC_MESSAGES=de_DE.UTF-8@euro ┃
72 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
73
74 # For details on how the transformation of LC_MESSAGES to a list of keys that
75 # should be looked up works, refer to “Localized values for keys” of the
76 # “Desktop Entry Specification”:
77 # https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html
78 my $lc_messages = setlocale(LC_MESSAGES);
79
80 # Ignore the encoding (e.g. .UTF-8)
81 $lc_messages =~ s/\.[^@]+//g;
82
83 my @suffixes = ($lc_messages);
84
85 # _COUNTRY and @MODIFIER are present
86 if ($lc_messages =~ /_[^@]+@/) {
87 my $no_modifier = $lc_messages;
88 $no_modifier =~ s/@.*//g;
89 push @suffixes, $no_modifier;
90
91 my $no_country = $lc_messages;
92 $no_country =~ s/_[^@]+//g;
93 push @suffixes, $no_country;
94 }
95
96 # Strip _COUNTRY and @MODIFIER if present
97 $lc_messages =~ s/[_@].*//g;
98 push @suffixes, $lc_messages;
99
100 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
101 # ┃ Read all .desktop files and store the values in which we are interested. ┃
102 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
103
104 my %desktops;
105 # See https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
106 my $xdg_data_home = $ENV{XDG_DATA_HOME};
107 $xdg_data_home = $ENV{HOME} . '/.local/share' if
108 !defined($xdg_data_home) ||
109 $xdg_data_home eq '' ||
110 ! -d $xdg_data_home;
111
112 my $xdg_data_dirs = $ENV{XDG_DATA_DIRS};
113 $xdg_data_dirs = '/usr/local/share/:/usr/share/' if
114 !defined($xdg_data_dirs) ||
115 $xdg_data_dirs eq '';
116
117 my @searchdirs = ("$xdg_data_home/applications/");
118 for my $dir (split(':', $xdg_data_dirs)) {
119 push @searchdirs, "$dir/applications/";
120 }
121
122 # Cleanup the paths, maybe some application does not cope with double slashes
123 # (the field code %k is replaced with the .desktop file location).
124 @searchdirs = map { s,//,/,g; $_ } @searchdirs;
125
126 # To avoid errors by File::Find’s find(), only pass existing directories.
127 @searchdirs = grep { -d $_ } @searchdirs;
128
129 find(
130 {
131 wanted => sub {
132 return unless substr($_, -1 * length('.desktop')) eq '.desktop';
133 my $relative = $File::Find::name;
134
135 # + 1 for the trailing /, which is missing in ::topdir.
136 substr($relative, 0, length($File::Find::topdir) + 1) = '';
137
138 # Don’t overwrite files with the same relative path, we search in
139 # descending order of importance.
140 return if exists($desktops{$relative});
141
142 $desktops{$relative} = $File::Find::name;
143 },
144 no_chdir => 1,
145 },
146 @searchdirs
147 );
148
149 my %apps;
150
151 for my $file (values %desktops) {
152 my $base = basename($file);
153
154 # _ is an invalid character for a key, so we can use it for our own keys.
155 $apps{$base}->{_Location} = $file;
156
157 # Extract all “Name” and “Exec” keys from the [Desktop Entry] group
158 # and store them in $apps{$base}.
159 my %names;
160 my $content = slurp($file);
161 next unless defined($content);
162 my @lines = split("\n", $content);
163 for my $line (@lines) {
164 my $first = substr($line, 0, 1);
165 next if $line eq '' || $first eq '#';
166 next unless ($line eq '[Desktop Entry]' ..
167 ($first eq '[' &&
168 substr($line, -1) eq ']' &&
169 $line ne '[Desktop Entry]'));
170 next if $first eq '[';
171
172 my ($key, $value) = ($line =~ /^
173 (
174 [A-Za-z0-9-]+ # the spec specifies these as valid key characters
175 (?:\[[^]]+\])? # possibly, there as a locale suffix
176 )
177 \s* = \s* # whitespace around = should be ignored
178 (.*) # no restrictions on the values
179 $/x);
180
181 if ($key =~ /^Name/) {
182 $names{$key} = $value;
183 } elsif ($key eq 'Exec' ||
184 $key eq 'TryExec' ||
185 $key eq 'Path' ||
186 $key eq 'Type') {
187 $apps{$base}->{$key} = $value;
188 } elsif ($key eq 'NoDisplay' ||
189 $key eq 'Hidden' ||
190 $key eq 'StartupNotify' ||
191 $key eq 'Terminal') {
192 # Values of type boolean must either be string true or false,
193 # see “Possible value types”:
194 # https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html
195 $apps{$base}->{$key} = ($value eq 'true');
196 }
197 }
198
199 for my $suffix (@suffixes) {
200 next unless exists($names{"Name[$suffix]"});
201 $apps{$base}->{Name} = $names{"Name[$suffix]"};
202 last;
203 }
204
205 # Fallback to unlocalized “Name”.
206 $apps{$base}->{Name} = $names{Name} unless exists($apps{$base}->{Name});
207 }
208
209 # %apps now looks like this:
210 #
211 # %apps = {
212 # 'evince.desktop' => {
213 # 'Exec' => 'evince %U',
214 # 'Name' => 'Dokumentenbetrachter',
215 # '_Location' => '/usr/share/applications/evince.desktop'
216 # },
217 # 'gedit.desktop' => {
218 # 'Exec' => 'gedit %U',
219 # 'Name' => 'gedit',
220 # '_Location' => '/usr/share/applications/gedit.desktop'
221 # }
222 # };
223
224 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
225 # ┃ Turn %apps inside out to provide Name → filename lookup. ┃
226 # ┃ The Name is what we display in dmenu later. ┃
227 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
228
229 my %choices;
230 for my $app (keys %apps) {
231 my $name = $apps{$app}->{Name};
232
233 # Don’t try to use .desktop files which don’t have Type=application
234 next if (!exists($apps{$app}->{Type}) ||
235 $apps{$app}->{Type} ne 'Application');
236
237 # Skip broken files (Type=application, but no Exec key).
238 if (!exists($apps{$app}->{Exec}) ||
239 $apps{$app}->{Exec} eq '') {
240 warn 'File ' . $apps{$app}->{_Location} . ' is broken: it contains Type=Application, but no Exec key/value pair.';
241 next;
242 }
243
244 # Don’t offer apps which have NoDisplay == true or Hidden == true.
245 # See https://wiki.xfce.org/howto/customize-menu#hide_menu_entries
246 # for the difference between NoDisplay and Hidden.
247 next if (exists($apps{$app}->{NoDisplay}) && $apps{$app}->{NoDisplay}) ||
248 (exists($apps{$app}->{Hidden}) && $apps{$app}->{Hidden});
249
250 if (exists($apps{$app}->{TryExec})) {
251 my $tryexec = $apps{$app}->{TryExec};
252 if (substr($tryexec, 0, 1) eq '/') {
253 # Skip if absolute path is not executable.
254 next unless -x $tryexec;
255 } else {
256 # Search in $PATH for the executable.
257 my $found = 0;
258 for my $path (split(':', $ENV{PATH})) {
259 next unless -x "$path/$tryexec";
260 $found = 1;
261 last;
262 }
263 next unless $found;
264 }
265 }
266
267 if ((scalar grep { $_ eq 'name' } @entry_types) > 0) {
268 if (exists($choices{$name})) {
269 # There are two .desktop files which contain the same “Name” value.
270 # I’m not sure if that is allowed to happen, but we disambiguate the
271 # situation by appending “ (2)”, “ (3)”, etc. to the name.
272 #
273 # An example of this happening is exo-file-manager.desktop and
274 # thunar-settings.desktop, both of which contain “Name=File Manager”.
275 my $inc = 2;
276 $inc++ while exists($choices{"$name ($inc)"});
277 $name = "$name ($inc)";
278 }
279
280 $choices{$name} = $app;
281 next;
282 }
283
284 if ((scalar grep { $_ eq 'command' } @entry_types) > 0) {
285 my $command = $apps{$app}->{Exec};
286
287 # Handle escape sequences (should be done for all string values, but does
288 # matter here).
289 my %escapes = (
290 '\\s' => ' ',
291 '\\n' => '\n',
292 '\\t' => '\t',
293 '\\r' => '\r',
294 '\\\\' => '\\',
295 );
296 $command =~ s/(\\[sntr\\])/$escapes{$1}/go;
297
298 # Extract executable
299 if ($command =~ m/^\s*([^\s\"]+)(?:\s|$)/) {
300 # No quotes
301 $command = $1;
302 } elsif ($command =~ m/^\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"(?:\s|$)/) {
303 # Quoted, remove quotes and fix escaped characters
304 $command = $1;
305 $command =~ s/\\([\"\`\$\\])/$1/g;
306 } else {
307 # Invalid quotes, fallback to whitespace
308 ($command) = split(' ', $command);
309 }
310
311 # Don’t add “geany” if “Geany” is already present.
312 my @keys = map { lc } keys %choices;
313 if (!(scalar grep { $_ eq lc(basename($command)) } @keys) > 0) {
314 $choices{basename($command)} = $app;
315 }
316 next;
317 }
318
319 if ((scalar grep { $_ eq 'filename' } @entry_types) > 0) {
320 my $filename = basename($app, '.desktop');
321
322 # Don’t add “geany” if “Geany” is already present.
323 my @keys = map { lc } keys %choices;
324 next if (scalar grep { $_ eq lc($filename) } @keys) > 0;
325
326 $choices{$filename} = $app;
327 }
328 }
329
330 # %choices now looks like this:
331 #
332 # %choices = {
333 # 'Dokumentenbetrachter' => 'evince.desktop',
334 # 'gedit' => 'gedit.desktop'
335 # };
336
337 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
338 # ┃ Run dmenu to ask the user for their choice ┃
339 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
340
341 # open2 will just make dmenu’s STDERR go to our own STDERR.
342 my ($dmenu_out, $dmenu_in);
343 my $pid = eval {
344 open2($dmenu_out, $dmenu_in, $dmenu_cmd);
345 } or do {
346 print STDERR "$@";
347 say STDERR "Running dmenu failed. Is dmenu installed at all? Try running dmenu -v";
348 exit 1;
349 };
350
351 binmode $dmenu_in, ':utf8';
352 binmode $dmenu_out, ':utf8';
353
354 # Feed dmenu the possible choices.
355 say $dmenu_in $_ for sort keys %choices;
356 close($dmenu_in);
357
358 waitpid($pid, 0);
359 my $status = ($? >> 8);
360
361 # Pass on dmenu’s exit status if there was an error.
362 exit $status unless $status == 0;
363
364 my $choice = <$dmenu_out>;
365 # dmenu ≥ 4.4 adds a newline after the choice
366 chomp($choice);
367 my $app;
368 # Exact match: the user chose “Avidemux (GTK+)”
369 if (exists($choices{$choice})) {
370 $app = $apps{$choices{$choice}};
371 $choice = '';
372 } else {
373 # Not an exact match: the user entered “Avidemux (GTK+) ~/movie.mp4”
374 for my $possibility (keys %choices) {
375 next unless substr($choice, 0, length($possibility)) eq $possibility;
376 $app = $apps{$choices{$possibility}};
377 substr($choice, 0, length($possibility)) = '';
378 # Remove whitespace separating the entry and arguments.
379 $choice =~ s/^\s//g;
380 last;
381 }
382 if (!defined($app)) {
383 warn "Invalid input: “$choice” does not match any application. Trying to execute nevertheless.";
384 $app->{Name} = '';
385 $app->{Exec} = $choice;
386 # We assume that the app is old and does not support startup
387 # notifications because it doesn’t ship a desktop file.
388 $app->{StartupNotify} = 0;
389 $app->{_Location} = '';
390 }
391 }
392
393 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
394 # ┃ Make i3 start the chosen application. ┃
395 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
396
397 my $name = $app->{Name};
398 my $exec = $app->{Exec};
399 my $location = $app->{_Location};
400
401 # Quote as described by “The Exec key”:
402 # https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
403 sub quote {
404 my ($str) = @_;
405 $str =~ s/("|`|\$|\\)/\\$1/g;
406 $str = qq|"$str"| if $str ne "";
407 return $str;
408 }
409
410 $choice = quote($choice);
411 $location = quote($location);
412 $name = quote($name);
413
414 # Remove deprecated field codes, as the spec dictates.
415 $exec =~ s/%[dDnNvm]//g;
416
417 # Replace filename field codes with the rest of the command line.
418 # Note that we assume the user uses precisely one file name,
419 # not multiple file names.
420 $exec =~ s/%[fF]/$choice/g;
421
422 # If the program works with URLs,
423 # we assume the user provided a URL instead of a filename.
424 # As per the spec, there must be at most one of %f, %u, %F or %U present.
425 $exec =~ s/%[uU]/$choice/g;
426
427 # The translated name of the application.
428 $exec =~ s/%c/$name/g;
429
430 # XXX: Icons are not implemented. Is the complexity (looking up the path if
431 # only a name is given) actually worth it?
432 #$exec =~ s/%i/--icon $icon/g;
433 $exec =~ s/%i//g;
434
435 # location of .desktop file
436 $exec =~ s/%k/$location/g;
437
438 # Literal % characters are represented as %%.
439 $exec =~ s/%%/%/g;
440
441 if (exists($app->{Path}) && $app->{Path} ne '') {
442 $exec = 'cd ' . quote($app->{Path}) . ' && ' . $exec;
443 }
444
445 my $nosn = '';
446 my $cmd;
447 if (exists($app->{Terminal}) && $app->{Terminal}) {
448 # For applications which specify “Terminal=true” (e.g. htop.desktop),
449 # we need to create a temporary script that contains the full command line
450 # as the syntax for starting commands with arguments varies from terminal
451 # emulator to terminal emulator.
452 # Then, we launch that script with i3-sensible-terminal.
453 my ($fh, $filename) = tempfile();
454 binmode($fh, ':utf8');
455 say $fh <<EOT;
456 #!/bin/sh
457 rm $filename
458 exec $exec
459 EOT
460 close($fh);
461 chmod 0755, $filename;
462
463 $cmd = qq|exec i3-sensible-terminal -e "$filename"|;
464 } else {
465 # i3 executes applications by passing the argument to i3’s “exec” command
466 # as-is to $SHELL -c. The i3 parser supports quoted strings: When a string
467 # starts with a double quote ("), everything is parsed as-is until the next
468 # double quote which is NOT preceded by a backslash (\).
469 #
470 # Therefore, we escape all double quotes (") by replacing them with \"
471 $exec =~ s/"/\\"/g;
472
473 if (exists($app->{StartupNotify}) && !$app->{StartupNotify}) {
474 $nosn = '--no-startup-id';
475 }
476 $cmd = qq|exec $nosn "$exec"|;
477 }
478
479 system('i3-msg', $cmd) == 0 or die "Could not launch i3-msg: $?";
480
481 =encoding utf-8
482
483 =head1 NAME
484
485 i3-dmenu-desktop - run .desktop files with dmenu
486
487 =head1 SYNOPSIS
488
489 i3-dmenu-desktop [--dmenu='dmenu -i'] [--entry-type=name]
490
491 =head1 DESCRIPTION
492
493 i3-dmenu-desktop is a script which extracts the (localized) name from
494 application .desktop files, offers the user a choice via dmenu(1) and then
495 starts the chosen application via i3 (for startup notification support).
496 The advantage of using .desktop files instead of dmenu_run(1) is that dmenu_run
497 offers B<all> binaries in your $PATH, including non-interactive utilities like
498 "sed". Also, .desktop files contain a proper name, information about whether
499 the application runs in a terminal and whether it supports startup
500 notifications.
501
502 The .desktop files are searched in $XDG_DATA_HOME/applications (by default
503 $HOME/.local/share/applications) and in the "applications" subdirectory of each
504 entry of $XDG_DATA_DIRS (by default /usr/local/share/:/usr/share/).
505
506 Files with the same name in $XDG_DATA_HOME/applications take precedence over
507 files in $XDG_DATA_DIRS, so that you can overwrite parts of the system-wide
508 .desktop files by copying them to your local directory and making changes.
509
510 i3-dmenu-desktop displays the "Name" value in the localized version depending
511 on LC_MESSAGES as specified in the Desktop Entry Specification.
512
513 You can pass a filename or URL (%f/%F and %u/%U field codes in the .desktop
514 file respectively) by appending it to the name of the application. E.g., if you
515 want to launch "GNU Emacs 24" with the patch /tmp/foobar.txt, you would type
516 "emacs", press TAB, type " /tmp/foobar.txt" and press ENTER.
517
518 .desktop files with Terminal=true are started using i3-sensible-terminal(1).
519
520 .desktop files with NoDisplay=true or Hidden=true are skipped.
521
522 UTF-8 is supported, of course, but dmenu does not support displaying all
523 glyphs. E.g., xfce4-terminal.desktop's Name[fi]=Pääte will be displayed just
524 fine, but not its Name[ru]=Терминал.
525
526 =head1 OPTIONS
527
528 =over
529
530 =item B<--dmenu=command>
531
532 Execute command instead of 'dmenu -i'. This option can be used to pass custom
533 parameters to dmenu, or to make i3-dmenu-desktop start a custom (patched?)
534 version of dmenu.
535
536 =item B<--entry-type=type>
537
538 Display the (localized) "Name" (type = name), the command (type = command) or
539 the (*.desktop) filename (type = filename) in dmenu. This option can be
540 specified multiple times.
541
542 Examples are "GNU Image Manipulation Program" (type = name), "gimp" (type =
543 command), and "libreoffice-writer" (type = filename).
544
545 =back
546
547 =head1 VERSION
548
549 Version 1.5
550
551 =head1 AUTHOR
552
553 Michael Stapelberg, C<< <michael at i3wm.org> >>
554
555 =head1 LICENSE AND COPYRIGHT
556
557 Copyright 2012 Michael Stapelberg.
558
559 This program is free software; you can redistribute it and/or modify it
560 under the terms of the BSD license.
561
562 =cut
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
7 *
8 */
9 #include <config.h>
10
11 #include <stdio.h>
12 #include <stdbool.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <err.h>
21 #include <stdint.h>
22 #include <getopt.h>
23 #include <limits.h>
24 #include <fcntl.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <signal.h>
28
29 #include "libi3.h"
30 #include "shmlog.h"
31 #include <i3/ipc.h>
32
33 #if !defined(__OpenBSD__)
34 static uint32_t offset_next_write;
35 #endif
36 static uint32_t wrap_count;
37
38 static i3_shmlog_header *header;
39 static char *logbuffer,
40 *walk;
41 static int ipcfd = -1;
42
43 static volatile bool interrupted = false;
44
45 static void sighandler(int signal) {
46 interrupted = true;
47 }
48
49 static void disable_shmlog(void) {
50 const char *disablecmd = "debuglog off; shmlog off";
51 if (ipc_send_message(ipcfd, strlen(disablecmd),
52 I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)disablecmd) != 0)
53 err(EXIT_FAILURE, "IPC send");
54
55 /* Ensure the command was sent by waiting for the reply: */
56 uint32_t reply_length = 0;
57 uint8_t *reply = NULL;
58 if (ipc_recv_message(ipcfd, I3_IPC_REPLY_TYPE_COMMAND,
59 &reply_length, &reply) != 0) {
60 err(EXIT_FAILURE, "IPC recv");
61 }
62 free(reply);
63 }
64
65 static int check_for_wrap(void) {
66 if (wrap_count == header->wrap_count)
67 return 0;
68
69 /* The log wrapped. Print the remaining content and reset walk to the top
70 * of the log. */
71 wrap_count = header->wrap_count;
72 const int len = (logbuffer + header->offset_last_wrap) - walk;
73 swrite(STDOUT_FILENO, walk, len);
74 walk = logbuffer + sizeof(i3_shmlog_header);
75 return 1;
76 }
77
78 static void print_till_end(void) {
79 check_for_wrap();
80 const int len = (logbuffer + header->offset_next_write) - walk;
81 swrite(STDOUT_FILENO, walk, len);
82 walk += len;
83 }
84
85 void errorlog(char *fmt, ...) {
86 va_list args;
87
88 va_start(args, fmt);
89 vfprintf(stderr, fmt, args);
90 va_end(args);
91 }
92
93 int main(int argc, char *argv[]) {
94 int o, option_index = 0;
95 bool verbose = false;
96 #if !defined(__OpenBSD__)
97 bool follow = false;
98 #endif
99
100 static struct option long_options[] = {
101 {"version", no_argument, 0, 'v'},
102 {"verbose", no_argument, 0, 'V'},
103 #if !defined(__OpenBSD__)
104 {"follow", no_argument, 0, 'f'},
105 #endif
106 {"help", no_argument, 0, 'h'},
107 {0, 0, 0, 0}
108 };
109
110 #if !defined(__OpenBSD__)
111 char *options_string = "s:vfVh";
112 #else
113 char *options_string = "vVh";
114 #endif
115
116 while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
117 if (o == 'v') {
118 printf("i3-dump-log " I3_VERSION "\n");
119 return 0;
120 } else if (o == 'V') {
121 verbose = true;
122 #if !defined(__OpenBSD__)
123 } else if (o == 'f') {
124 follow = true;
125 #endif
126 } else if (o == 'h') {
127 printf("i3-dump-log " I3_VERSION "\n");
128 #if !defined(__OpenBSD__)
129 printf("i3-dump-log [-fhVv]\n");
130 #else
131 printf("i3-dump-log [-hVv]\n");
132 #endif
133 return 0;
134 }
135 }
136
137 char *shmname = root_atom_contents("I3_SHMLOG_PATH", NULL, 0);
138 if (shmname == NULL) {
139 /* Something failed. Let’s invest a little effort to find out what it
140 * is. This is hugely helpful for users who want to debug i3 but are
141 * not used to the procedure yet. */
142 xcb_connection_t *conn;
143 int screen;
144 if ((conn = xcb_connect(NULL, &screen)) == NULL ||
145 xcb_connection_has_error(conn)) {
146 fprintf(stderr, "i3-dump-log: ERROR: Cannot connect to X11.\n\n");
147 if (getenv("DISPLAY") == NULL) {
148 fprintf(stderr, "Your DISPLAY environment variable is not set.\n");
149 fprintf(stderr, "Are you running i3-dump-log via SSH or on a virtual console?\n");
150 fprintf(stderr, "Try DISPLAY=:0 i3-dump-log\n");
151 exit(1);
152 }
153 fprintf(stderr, "FYI: The DISPLAY environment variable is set to \"%s\".\n", getenv("DISPLAY"));
154 exit(1);
155 }
156 if (root_atom_contents("I3_CONFIG_PATH", conn, screen) != NULL) {
157 fprintf(stderr, "i3-dump-log: ERROR: i3 is running, but SHM logging is not enabled. Enabling SHM log until cancelled\n\n");
158 ipcfd = ipc_connect(NULL);
159 const char *enablecmd = "debuglog on; shmlog 5242880";
160 if (ipc_send_message(ipcfd, strlen(enablecmd),
161 I3_IPC_MESSAGE_TYPE_COMMAND, (uint8_t *)enablecmd) != 0)
162 err(EXIT_FAILURE, "IPC send");
163 /* By the time we receive a reply, I3_SHMLOG_PATH is set: */
164 uint32_t reply_length = 0;
165 uint8_t *reply = NULL;
166 if (ipc_recv_message(ipcfd, I3_IPC_REPLY_TYPE_COMMAND,
167 &reply_length, &reply) != 0) {
168 err(EXIT_FAILURE, "IPC recv");
169 }
170 free(reply);
171
172 atexit(disable_shmlog);
173
174 /* Retry: */
175 shmname = root_atom_contents("I3_SHMLOG_PATH", NULL, 0);
176 if (shmname == NULL && !is_debug_build()) {
177 fprintf(stderr, "You seem to be using a release version of i3:\n %s\n\n", I3_VERSION);
178 fprintf(stderr, "Release versions do not use SHM logging by default,\ntherefore i3-dump-log does not work.\n\n");
179 fprintf(stderr, "Please follow this guide instead:\nhttps://i3wm.org/docs/debugging-release-version.html\n");
180 exit(1);
181 }
182 }
183 if (shmname == NULL) {
184 errx(EXIT_FAILURE, "Cannot get I3_SHMLOG_PATH atom contents. Is i3 running on this display?");
185 }
186 }
187
188 if (*shmname == '\0')
189 errx(EXIT_FAILURE, "Cannot dump log: SHM logging is disabled in i3.");
190
191 struct stat statbuf;
192
193 /* NB: While we must never write, we need O_RDWR for the pthread condvar. */
194 int logbuffer_shm = shm_open(shmname, O_RDWR, 0);
195 if (logbuffer_shm == -1)
196 err(EXIT_FAILURE, "Could not shm_open SHM segment for the i3 log (%s)", shmname);
197
198 if (fstat(logbuffer_shm, &statbuf) != 0)
199 err(EXIT_FAILURE, "stat(%s)", shmname);
200
201 /* NB: While we must never write, we need PROT_WRITE for the pthread condvar. */
202 logbuffer = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
203 if (logbuffer == MAP_FAILED)
204 err(EXIT_FAILURE, "Could not mmap SHM segment for the i3 log");
205
206 header = (i3_shmlog_header *)logbuffer;
207
208 if (verbose)
209 printf("next_write = %d, last_wrap = %d, logbuffer_size = %d, shmname = %s\n",
210 header->offset_next_write, header->offset_last_wrap, header->size, shmname);
211 free(shmname);
212 walk = logbuffer + header->offset_next_write;
213
214 /* We first need to print old content in case there was at least one
215 * wrapping already. */
216
217 if (*walk != '\0') {
218 /* In case there was a write to the buffer already, skip the first
219 * old line, it very likely is mangled. Not a problem, though, the log
220 * is chatty enough to have plenty lines left. */
221 while (*walk != '\n')
222 walk++;
223 walk++;
224 }
225
226 /* In case there was no wrapping, this is a no-op, otherwise it prints the
227 * old lines. */
228 wrap_count = 0;
229 check_for_wrap();
230
231 /* Then start from the beginning and print the newer lines */
232 walk = logbuffer + sizeof(i3_shmlog_header);
233 print_till_end();
234
235 #if !defined(__OpenBSD__)
236 if (!follow) {
237 return 0;
238 }
239
240 /* Handle SIGINT gracefully to invoke atexit handlers, if any. */
241 struct sigaction action;
242 action.sa_handler = sighandler;
243 sigemptyset(&action.sa_mask);
244 action.sa_flags = 0;
245 sigaction(SIGINT, &action, NULL);
246
247 /* Since pthread_cond_wait() expects a mutex, we need to provide one.
248 * To not lock i3 (that’s bad, mhkay?) we just define one outside of
249 * the shared memory. */
250 pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER;
251 pthread_mutex_lock(&dummy_mutex);
252 while (!interrupted) {
253 pthread_cond_wait(&(header->condvar), &dummy_mutex);
254 /* If this was not a spurious wakeup, print the new lines. */
255 if (header->offset_next_write != offset_next_write) {
256 offset_next_write = header->offset_next_write;
257 print_till_end();
258 }
259 }
260
261 #endif
262 exit(0);
263 return 0;
264 }
0 #pragma once
1
2 #include <config.h>
3
4 #include <err.h>
5
6 #define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
7 #define FREE(pointer) \
8 do { \
9 free(pointer); \
10 pointer = NULL; \
11 } while (0)
12
13 extern xcb_window_t root;
0 /* $XFree86$
1 * This module converts keysym values into the corresponding ISO 10646-1
2 * (UCS, Unicode) values.
3 *
4 * The array keysymtab[] contains pairs of X11 keysym values for graphical
5 * characters and the corresponding Unicode value. The function
6 * keysym2ucs() maps a keysym onto a Unicode value using a binary search,
7 * therefore keysymtab[] must remain SORTED by keysym value.
8 *
9 * The keysym -> UTF-8 conversion will hopefully one day be provided
10 * by Xlib via XmbLookupString() and should ideally not have to be
11 * done in X applications. But we are not there yet.
12 *
13 * We allow to represent any UCS character in the range U+00000000 to
14 * U+00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
15 * This admittedly does not cover the entire 31-bit space of UCS, but
16 * it does cover all of the characters up to U+10FFFF, which can be
17 * represented by UTF-16, and more, and it is very unlikely that higher
18 * UCS codes will ever be assigned by ISO. So to get Unicode character
19 * U+ABCD you can directly use keysym 0x1000abcd.
20 *
21 * NOTE: The comments in the table below contain the actual character
22 * encoded in UTF-8, so for viewing and editing best use an editor in
23 * UTF-8 mode.
24 *
25 * Author: Markus G. Kuhn <mkuhn@acm.org>, University of Cambridge, June 1999
26 *
27 * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
28 * an initial draft of the mapping table.
29 *
30 * This software is in the public domain. Share and enjoy!
31 */
32
33 #include <xcb/xcb.h>
34 #include "keysym2ucs.h"
35
36 struct codepair {
37 unsigned short keysym;
38 unsigned short ucs;
39 } keysymtab[] = {
40 {0x01a1, 0x0104}, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
41 {0x01a2, 0x02d8}, /* breve ˘ BREVE */
42 {0x01a3, 0x0141}, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */
43 {0x01a5, 0x013d}, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */
44 {0x01a6, 0x015a}, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */
45 {0x01a9, 0x0160}, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */
46 {0x01aa, 0x015e}, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */
47 {0x01ab, 0x0164}, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */
48 {0x01ac, 0x0179}, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */
49 {0x01ae, 0x017d}, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */
50 {0x01af, 0x017b}, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */
51 {0x01b1, 0x0105}, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */
52 {0x01b2, 0x02db}, /* ogonek ˛ OGONEK */
53 {0x01b3, 0x0142}, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */
54 {0x01b5, 0x013e}, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */
55 {0x01b6, 0x015b}, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */
56 {0x01b7, 0x02c7}, /* caron ˇ CARON */
57 {0x01b9, 0x0161}, /* scaron š LATIN SMALL LETTER S WITH CARON */
58 {0x01ba, 0x015f}, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */
59 {0x01bb, 0x0165}, /* tcaron ť LATIN SMALL LETTER T WITH CARON */
60 {0x01bc, 0x017a}, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */
61 {0x01bd, 0x02dd}, /* doubleacute ˝ DOUBLE ACUTE ACCENT */
62 {0x01be, 0x017e}, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */
63 {0x01bf, 0x017c}, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */
64 {0x01c0, 0x0154}, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */
65 {0x01c3, 0x0102}, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */
66 {0x01c5, 0x0139}, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */
67 {0x01c6, 0x0106}, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */
68 {0x01c8, 0x010c}, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */
69 {0x01ca, 0x0118}, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */
70 {0x01cc, 0x011a}, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */
71 {0x01cf, 0x010e}, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */
72 {0x01d0, 0x0110}, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */
73 {0x01d1, 0x0143}, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */
74 {0x01d2, 0x0147}, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */
75 {0x01d5, 0x0150}, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
76 {0x01d8, 0x0158}, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */
77 {0x01d9, 0x016e}, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */
78 {0x01db, 0x0170}, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
79 {0x01de, 0x0162}, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */
80 {0x01e0, 0x0155}, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */
81 {0x01e3, 0x0103}, /* abreve ă LATIN SMALL LETTER A WITH BREVE */
82 {0x01e5, 0x013a}, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */
83 {0x01e6, 0x0107}, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */
84 {0x01e8, 0x010d}, /* ccaron č LATIN SMALL LETTER C WITH CARON */
85 {0x01ea, 0x0119}, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */
86 {0x01ec, 0x011b}, /* ecaron ě LATIN SMALL LETTER E WITH CARON */
87 {0x01ef, 0x010f}, /* dcaron ď LATIN SMALL LETTER D WITH CARON */
88 {0x01f0, 0x0111}, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */
89 {0x01f1, 0x0144}, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */
90 {0x01f2, 0x0148}, /* ncaron ň LATIN SMALL LETTER N WITH CARON */
91 {0x01f5, 0x0151}, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */
92 {0x01f8, 0x0159}, /* rcaron ř LATIN SMALL LETTER R WITH CARON */
93 {0x01f9, 0x016f}, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */
94 {0x01fb, 0x0171}, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */
95 {0x01fe, 0x0163}, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */
96 {0x01ff, 0x02d9}, /* abovedot ˙ DOT ABOVE */
97 {0x02a1, 0x0126}, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */
98 {0x02a6, 0x0124}, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
99 {0x02a9, 0x0130}, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */
100 {0x02ab, 0x011e}, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */
101 {0x02ac, 0x0134}, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
102 {0x02b1, 0x0127}, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */
103 {0x02b6, 0x0125}, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */
104 {0x02b9, 0x0131}, /* idotless ı LATIN SMALL LETTER DOTLESS I */
105 {0x02bb, 0x011f}, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */
106 {0x02bc, 0x0135}, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */
107 {0x02c5, 0x010a}, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */
108 {0x02c6, 0x0108}, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
109 {0x02d5, 0x0120}, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */
110 {0x02d8, 0x011c}, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
111 {0x02dd, 0x016c}, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */
112 {0x02de, 0x015c}, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
113 {0x02e5, 0x010b}, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */
114 {0x02e6, 0x0109}, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */
115 {0x02f5, 0x0121}, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */
116 {0x02f8, 0x011d}, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */
117 {0x02fd, 0x016d}, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */
118 {0x02fe, 0x015d}, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */
119 {0x03a2, 0x0138}, /* kra ĸ LATIN SMALL LETTER KRA */
120 {0x03a3, 0x0156}, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */
121 {0x03a5, 0x0128}, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */
122 {0x03a6, 0x013b}, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */
123 {0x03aa, 0x0112}, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */
124 {0x03ab, 0x0122}, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */
125 {0x03ac, 0x0166}, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */
126 {0x03b3, 0x0157}, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */
127 {0x03b5, 0x0129}, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */
128 {0x03b6, 0x013c}, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */
129 {0x03ba, 0x0113}, /* emacron ē LATIN SMALL LETTER E WITH MACRON */
130 {0x03bb, 0x0123}, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */
131 {0x03bc, 0x0167}, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */
132 {0x03bd, 0x014a}, /* ENG Ŋ LATIN CAPITAL LETTER ENG */
133 {0x03bf, 0x014b}, /* eng ŋ LATIN SMALL LETTER ENG */
134 {0x03c0, 0x0100}, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */
135 {0x03c7, 0x012e}, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */
136 {0x03cc, 0x0116}, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */
137 {0x03cf, 0x012a}, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */
138 {0x03d1, 0x0145}, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */
139 {0x03d2, 0x014c}, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */
140 {0x03d3, 0x0136}, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */
141 {0x03d9, 0x0172}, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */
142 {0x03dd, 0x0168}, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */
143 {0x03de, 0x016a}, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */
144 {0x03e0, 0x0101}, /* amacron ā LATIN SMALL LETTER A WITH MACRON */
145 {0x03e7, 0x012f}, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */
146 {0x03ec, 0x0117}, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */
147 {0x03ef, 0x012b}, /* imacron ī LATIN SMALL LETTER I WITH MACRON */
148 {0x03f1, 0x0146}, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */
149 {0x03f2, 0x014d}, /* omacron ō LATIN SMALL LETTER O WITH MACRON */
150 {0x03f3, 0x0137}, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */
151 {0x03f9, 0x0173}, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */
152 {0x03fd, 0x0169}, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */
153 {0x03fe, 0x016b}, /* umacron ū LATIN SMALL LETTER U WITH MACRON */
154 {0x047e, 0x203e}, /* overline ‾ OVERLINE */
155 {0x04a1, 0x3002}, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */
156 {0x04a2, 0x300c}, /* kana_openingbracket 「 LEFT CORNER BRACKET */
157 {0x04a3, 0x300d}, /* kana_closingbracket 」 RIGHT CORNER BRACKET */
158 {0x04a4, 0x3001}, /* kana_comma 、 IDEOGRAPHIC COMMA */
159 {0x04a5, 0x30fb}, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */
160 {0x04a6, 0x30f2}, /* kana_WO ヲ KATAKANA LETTER WO */
161 {0x04a7, 0x30a1}, /* kana_a ァ KATAKANA LETTER SMALL A */
162 {0x04a8, 0x30a3}, /* kana_i ィ KATAKANA LETTER SMALL I */
163 {0x04a9, 0x30a5}, /* kana_u ゥ KATAKANA LETTER SMALL U */
164 {0x04aa, 0x30a7}, /* kana_e ェ KATAKANA LETTER SMALL E */
165 {0x04ab, 0x30a9}, /* kana_o ォ KATAKANA LETTER SMALL O */
166 {0x04ac, 0x30e3}, /* kana_ya ャ KATAKANA LETTER SMALL YA */
167 {0x04ad, 0x30e5}, /* kana_yu ュ KATAKANA LETTER SMALL YU */
168 {0x04ae, 0x30e7}, /* kana_yo ョ KATAKANA LETTER SMALL YO */
169 {0x04af, 0x30c3}, /* kana_tsu ッ KATAKANA LETTER SMALL TU */
170 {0x04b0, 0x30fc}, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */
171 {0x04b1, 0x30a2}, /* kana_A ア KATAKANA LETTER A */
172 {0x04b2, 0x30a4}, /* kana_I イ KATAKANA LETTER I */
173 {0x04b3, 0x30a6}, /* kana_U ウ KATAKANA LETTER U */
174 {0x04b4, 0x30a8}, /* kana_E エ KATAKANA LETTER E */
175 {0x04b5, 0x30aa}, /* kana_O オ KATAKANA LETTER O */
176 {0x04b6, 0x30ab}, /* kana_KA カ KATAKANA LETTER KA */
177 {0x04b7, 0x30ad}, /* kana_KI キ KATAKANA LETTER KI */
178 {0x04b8, 0x30af}, /* kana_KU ク KATAKANA LETTER KU */
179 {0x04b9, 0x30b1}, /* kana_KE ケ KATAKANA LETTER KE */
180 {0x04ba, 0x30b3}, /* kana_KO コ KATAKANA LETTER KO */
181 {0x04bb, 0x30b5}, /* kana_SA サ KATAKANA LETTER SA */
182 {0x04bc, 0x30b7}, /* kana_SHI シ KATAKANA LETTER SI */
183 {0x04bd, 0x30b9}, /* kana_SU ス KATAKANA LETTER SU */
184 {0x04be, 0x30bb}, /* kana_SE セ KATAKANA LETTER SE */
185 {0x04bf, 0x30bd}, /* kana_SO ソ KATAKANA LETTER SO */
186 {0x04c0, 0x30bf}, /* kana_TA タ KATAKANA LETTER TA */
187 {0x04c1, 0x30c1}, /* kana_CHI チ KATAKANA LETTER TI */
188 {0x04c2, 0x30c4}, /* kana_TSU ツ KATAKANA LETTER TU */
189 {0x04c3, 0x30c6}, /* kana_TE テ KATAKANA LETTER TE */
190 {0x04c4, 0x30c8}, /* kana_TO ト KATAKANA LETTER TO */
191 {0x04c5, 0x30ca}, /* kana_NA ナ KATAKANA LETTER NA */
192 {0x04c6, 0x30cb}, /* kana_NI ニ KATAKANA LETTER NI */
193 {0x04c7, 0x30cc}, /* kana_NU ヌ KATAKANA LETTER NU */
194 {0x04c8, 0x30cd}, /* kana_NE ネ KATAKANA LETTER NE */
195 {0x04c9, 0x30ce}, /* kana_NO ノ KATAKANA LETTER NO */
196 {0x04ca, 0x30cf}, /* kana_HA ハ KATAKANA LETTER HA */
197 {0x04cb, 0x30d2}, /* kana_HI ヒ KATAKANA LETTER HI */
198 {0x04cc, 0x30d5}, /* kana_FU フ KATAKANA LETTER HU */
199 {0x04cd, 0x30d8}, /* kana_HE ヘ KATAKANA LETTER HE */
200 {0x04ce, 0x30db}, /* kana_HO ホ KATAKANA LETTER HO */
201 {0x04cf, 0x30de}, /* kana_MA マ KATAKANA LETTER MA */
202 {0x04d0, 0x30df}, /* kana_MI ミ KATAKANA LETTER MI */
203 {0x04d1, 0x30e0}, /* kana_MU ム KATAKANA LETTER MU */
204 {0x04d2, 0x30e1}, /* kana_ME メ KATAKANA LETTER ME */
205 {0x04d3, 0x30e2}, /* kana_MO モ KATAKANA LETTER MO */
206 {0x04d4, 0x30e4}, /* kana_YA ヤ KATAKANA LETTER YA */
207 {0x04d5, 0x30e6}, /* kana_YU ユ KATAKANA LETTER YU */
208 {0x04d6, 0x30e8}, /* kana_YO ヨ KATAKANA LETTER YO */
209 {0x04d7, 0x30e9}, /* kana_RA ラ KATAKANA LETTER RA */
210 {0x04d8, 0x30ea}, /* kana_RI リ KATAKANA LETTER RI */
211 {0x04d9, 0x30eb}, /* kana_RU ル KATAKANA LETTER RU */
212 {0x04da, 0x30ec}, /* kana_RE レ KATAKANA LETTER RE */
213 {0x04db, 0x30ed}, /* kana_RO ロ KATAKANA LETTER RO */
214 {0x04dc, 0x30ef}, /* kana_WA ワ KATAKANA LETTER WA */
215 {0x04dd, 0x30f3}, /* kana_N ン KATAKANA LETTER N */
216 {0x04de, 0x309b}, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */
217 {0x04df, 0x309c}, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
218 {0x05ac, 0x060c}, /* Arabic_comma ، ARABIC COMMA */
219 {0x05bb, 0x061b}, /* Arabic_semicolon ؛ ARABIC SEMICOLON */
220 {0x05bf, 0x061f}, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */
221 {0x05c1, 0x0621}, /* Arabic_hamza ء ARABIC LETTER HAMZA */
222 {0x05c2, 0x0622}, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */
223 {0x05c3, 0x0623}, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */
224 {0x05c4, 0x0624}, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */
225 {0x05c5, 0x0625}, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */
226 {0x05c6, 0x0626}, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */
227 {0x05c7, 0x0627}, /* Arabic_alef ا ARABIC LETTER ALEF */
228 {0x05c8, 0x0628}, /* Arabic_beh ب ARABIC LETTER BEH */
229 {0x05c9, 0x0629}, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */
230 {0x05ca, 0x062a}, /* Arabic_teh ت ARABIC LETTER TEH */
231 {0x05cb, 0x062b}, /* Arabic_theh ث ARABIC LETTER THEH */
232 {0x05cc, 0x062c}, /* Arabic_jeem ج ARABIC LETTER JEEM */
233 {0x05cd, 0x062d}, /* Arabic_hah ح ARABIC LETTER HAH */
234 {0x05ce, 0x062e}, /* Arabic_khah خ ARABIC LETTER KHAH */
235 {0x05cf, 0x062f}, /* Arabic_dal د ARABIC LETTER DAL */
236 {0x05d0, 0x0630}, /* Arabic_thal ذ ARABIC LETTER THAL */
237 {0x05d1, 0x0631}, /* Arabic_ra ر ARABIC LETTER REH */
238 {0x05d2, 0x0632}, /* Arabic_zain ز ARABIC LETTER ZAIN */
239 {0x05d3, 0x0633}, /* Arabic_seen س ARABIC LETTER SEEN */
240 {0x05d4, 0x0634}, /* Arabic_sheen ش ARABIC LETTER SHEEN */
241 {0x05d5, 0x0635}, /* Arabic_sad ص ARABIC LETTER SAD */
242 {0x05d6, 0x0636}, /* Arabic_dad ض ARABIC LETTER DAD */
243 {0x05d7, 0x0637}, /* Arabic_tah ط ARABIC LETTER TAH */
244 {0x05d8, 0x0638}, /* Arabic_zah ظ ARABIC LETTER ZAH */
245 {0x05d9, 0x0639}, /* Arabic_ain ع ARABIC LETTER AIN */
246 {0x05da, 0x063a}, /* Arabic_ghain غ ARABIC LETTER GHAIN */
247 {0x05e0, 0x0640}, /* Arabic_tatweel ـ ARABIC TATWEEL */
248 {0x05e1, 0x0641}, /* Arabic_feh ف ARABIC LETTER FEH */
249 {0x05e2, 0x0642}, /* Arabic_qaf ق ARABIC LETTER QAF */
250 {0x05e3, 0x0643}, /* Arabic_kaf ك ARABIC LETTER KAF */
251 {0x05e4, 0x0644}, /* Arabic_lam ل ARABIC LETTER LAM */
252 {0x05e5, 0x0645}, /* Arabic_meem م ARABIC LETTER MEEM */
253 {0x05e6, 0x0646}, /* Arabic_noon ن ARABIC LETTER NOON */
254 {0x05e7, 0x0647}, /* Arabic_ha ه ARABIC LETTER HEH */
255 {0x05e8, 0x0648}, /* Arabic_waw و ARABIC LETTER WAW */
256 {0x05e9, 0x0649}, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */
257 {0x05ea, 0x064a}, /* Arabic_yeh ي ARABIC LETTER YEH */
258 {0x05eb, 0x064b}, /* Arabic_fathatan ً ARABIC FATHATAN */
259 {0x05ec, 0x064c}, /* Arabic_dammatan ٌ ARABIC DAMMATAN */
260 {0x05ed, 0x064d}, /* Arabic_kasratan ٍ ARABIC KASRATAN */
261 {0x05ee, 0x064e}, /* Arabic_fatha َ ARABIC FATHA */
262 {0x05ef, 0x064f}, /* Arabic_damma ُ ARABIC DAMMA */
263 {0x05f0, 0x0650}, /* Arabic_kasra ِ ARABIC KASRA */
264 {0x05f1, 0x0651}, /* Arabic_shadda ّ ARABIC SHADDA */
265 {0x05f2, 0x0652}, /* Arabic_sukun ْ ARABIC SUKUN */
266 {0x06a1, 0x0452}, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */
267 {0x06a2, 0x0453}, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */
268 {0x06a3, 0x0451}, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */
269 {0x06a4, 0x0454}, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */
270 {0x06a5, 0x0455}, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */
271 {0x06a6, 0x0456}, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
272 {0x06a7, 0x0457}, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */
273 {0x06a8, 0x0458}, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */
274 {0x06a9, 0x0459}, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */
275 {0x06aa, 0x045a}, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */
276 {0x06ab, 0x045b}, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */
277 {0x06ac, 0x045c}, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */
278 /* 0x06ad Ukrainian_ghe_with_upturn ? ??? */
279 {0x06ae, 0x045e}, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */
280 {0x06af, 0x045f}, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */
281 {0x06b0, 0x2116}, /* numerosign № NUMERO SIGN */
282 {0x06b1, 0x0402}, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */
283 {0x06b2, 0x0403}, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */
284 {0x06b3, 0x0401}, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */
285 {0x06b4, 0x0404}, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */
286 {0x06b5, 0x0405}, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */
287 {0x06b6, 0x0406}, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
288 {0x06b7, 0x0407}, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */
289 {0x06b8, 0x0408}, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */
290 {0x06b9, 0x0409}, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */
291 {0x06ba, 0x040a}, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */
292 {0x06bb, 0x040b}, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */
293 {0x06bc, 0x040c}, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */
294 /* 0x06bd Ukrainian_GHE_WITH_UPTURN ? ??? */
295 {0x06be, 0x040e}, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */
296 {0x06bf, 0x040f}, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */
297 {0x06c0, 0x044e}, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */
298 {0x06c1, 0x0430}, /* Cyrillic_a а CYRILLIC SMALL LETTER A */
299 {0x06c2, 0x0431}, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */
300 {0x06c3, 0x0446}, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */
301 {0x06c4, 0x0434}, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */
302 {0x06c5, 0x0435}, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */
303 {0x06c6, 0x0444}, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */
304 {0x06c7, 0x0433}, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */
305 {0x06c8, 0x0445}, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */
306 {0x06c9, 0x0438}, /* Cyrillic_i и CYRILLIC SMALL LETTER I */
307 {0x06ca, 0x0439}, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */
308 {0x06cb, 0x043a}, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */
309 {0x06cc, 0x043b}, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */
310 {0x06cd, 0x043c}, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */
311 {0x06ce, 0x043d}, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */
312 {0x06cf, 0x043e}, /* Cyrillic_o о CYRILLIC SMALL LETTER O */
313 {0x06d0, 0x043f}, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */
314 {0x06d1, 0x044f}, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */
315 {0x06d2, 0x0440}, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */
316 {0x06d3, 0x0441}, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */
317 {0x06d4, 0x0442}, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */
318 {0x06d5, 0x0443}, /* Cyrillic_u у CYRILLIC SMALL LETTER U */
319 {0x06d6, 0x0436}, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */
320 {0x06d7, 0x0432}, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */
321 {0x06d8, 0x044c}, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */
322 {0x06d9, 0x044b}, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */
323 {0x06da, 0x0437}, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */
324 {0x06db, 0x0448}, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */
325 {0x06dc, 0x044d}, /* Cyrillic_e э CYRILLIC SMALL LETTER E */
326 {0x06dd, 0x0449}, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */
327 {0x06de, 0x0447}, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */
328 {0x06df, 0x044a}, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */
329 {0x06e0, 0x042e}, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */
330 {0x06e1, 0x0410}, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */
331 {0x06e2, 0x0411}, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */
332 {0x06e3, 0x0426}, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */
333 {0x06e4, 0x0414}, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */
334 {0x06e5, 0x0415}, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */
335 {0x06e6, 0x0424}, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */
336 {0x06e7, 0x0413}, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */
337 {0x06e8, 0x0425}, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */
338 {0x06e9, 0x0418}, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */
339 {0x06ea, 0x0419}, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */
340 {0x06eb, 0x041a}, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */
341 {0x06ec, 0x041b}, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */
342 {0x06ed, 0x041c}, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */
343 {0x06ee, 0x041d}, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */
344 {0x06ef, 0x041e}, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */
345 {0x06f0, 0x041f}, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */
346 {0x06f1, 0x042f}, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */
347 {0x06f2, 0x0420}, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */
348 {0x06f3, 0x0421}, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */
349 {0x06f4, 0x0422}, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */
350 {0x06f5, 0x0423}, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */
351 {0x06f6, 0x0416}, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */
352 {0x06f7, 0x0412}, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */
353 {0x06f8, 0x042c}, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */
354 {0x06f9, 0x042b}, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */
355 {0x06fa, 0x0417}, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */
356 {0x06fb, 0x0428}, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */
357 {0x06fc, 0x042d}, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */
358 {0x06fd, 0x0429}, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */
359 {0x06fe, 0x0427}, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */
360 {0x06ff, 0x042a}, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */
361 {0x07a1, 0x0386}, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */
362 {0x07a2, 0x0388}, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */
363 {0x07a3, 0x0389}, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */
364 {0x07a4, 0x038a}, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */
365 {0x07a5, 0x03aa}, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
366 {0x07a7, 0x038c}, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */
367 {0x07a8, 0x038e}, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */
368 {0x07a9, 0x03ab}, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
369 {0x07ab, 0x038f}, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */
370 {0x07ae, 0x0385}, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */
371 {0x07af, 0x2015}, /* Greek_horizbar ― HORIZONTAL BAR */
372 {0x07b1, 0x03ac}, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */
373 {0x07b2, 0x03ad}, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */
374 {0x07b3, 0x03ae}, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */
375 {0x07b4, 0x03af}, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */
376 {0x07b5, 0x03ca}, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */
377 {0x07b6, 0x0390}, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
378 {0x07b7, 0x03cc}, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */
379 {0x07b8, 0x03cd}, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */
380 {0x07b9, 0x03cb}, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
381 {0x07ba, 0x03b0}, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
382 {0x07bb, 0x03ce}, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */
383 {0x07c1, 0x0391}, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */
384 {0x07c2, 0x0392}, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */
385 {0x07c3, 0x0393}, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */
386 {0x07c4, 0x0394}, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */
387 {0x07c5, 0x0395}, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */
388 {0x07c6, 0x0396}, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */
389 {0x07c7, 0x0397}, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */
390 {0x07c8, 0x0398}, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */
391 {0x07c9, 0x0399}, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */
392 {0x07ca, 0x039a}, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */
393 {0x07cb, 0x039b}, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */
394 {0x07cc, 0x039c}, /* Greek_MU Μ GREEK CAPITAL LETTER MU */
395 {0x07cd, 0x039d}, /* Greek_NU Ν GREEK CAPITAL LETTER NU */
396 {0x07ce, 0x039e}, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */
397 {0x07cf, 0x039f}, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */
398 {0x07d0, 0x03a0}, /* Greek_PI Π GREEK CAPITAL LETTER PI */
399 {0x07d1, 0x03a1}, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */
400 {0x07d2, 0x03a3}, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */
401 {0x07d4, 0x03a4}, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */
402 {0x07d5, 0x03a5}, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */
403 {0x07d6, 0x03a6}, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */
404 {0x07d7, 0x03a7}, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */
405 {0x07d8, 0x03a8}, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */
406 {0x07d9, 0x03a9}, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */
407 {0x07e1, 0x03b1}, /* Greek_alpha α GREEK SMALL LETTER ALPHA */
408 {0x07e2, 0x03b2}, /* Greek_beta β GREEK SMALL LETTER BETA */
409 {0x07e3, 0x03b3}, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */
410 {0x07e4, 0x03b4}, /* Greek_delta δ GREEK SMALL LETTER DELTA */
411 {0x07e5, 0x03b5}, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */
412 {0x07e6, 0x03b6}, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */
413 {0x07e7, 0x03b7}, /* Greek_eta η GREEK SMALL LETTER ETA */
414 {0x07e8, 0x03b8}, /* Greek_theta θ GREEK SMALL LETTER THETA */
415 {0x07e9, 0x03b9}, /* Greek_iota ι GREEK SMALL LETTER IOTA */
416 {0x07ea, 0x03ba}, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */
417 {0x07eb, 0x03bb}, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */
418 {0x07ec, 0x03bc}, /* Greek_mu μ GREEK SMALL LETTER MU */
419 {0x07ed, 0x03bd}, /* Greek_nu ν GREEK SMALL LETTER NU */
420 {0x07ee, 0x03be}, /* Greek_xi ξ GREEK SMALL LETTER XI */
421 {0x07ef, 0x03bf}, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */
422 {0x07f0, 0x03c0}, /* Greek_pi π GREEK SMALL LETTER PI */
423 {0x07f1, 0x03c1}, /* Greek_rho ρ GREEK SMALL LETTER RHO */
424 {0x07f2, 0x03c3}, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */
425 {0x07f3, 0x03c2}, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */
426 {0x07f4, 0x03c4}, /* Greek_tau τ GREEK SMALL LETTER TAU */
427 {0x07f5, 0x03c5}, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */
428 {0x07f6, 0x03c6}, /* Greek_phi φ GREEK SMALL LETTER PHI */
429 {0x07f7, 0x03c7}, /* Greek_chi χ GREEK SMALL LETTER CHI */
430 {0x07f8, 0x03c8}, /* Greek_psi ψ GREEK SMALL LETTER PSI */
431 {0x07f9, 0x03c9}, /* Greek_omega ω GREEK SMALL LETTER OMEGA */
432 /* 0x08a1 leftradical ? ??? */
433 /* 0x08a2 topleftradical ? ??? */
434 /* 0x08a3 horizconnector ? ??? */
435 {0x08a4, 0x2320}, /* topintegral ⌠ TOP HALF INTEGRAL */
436 {0x08a5, 0x2321}, /* botintegral ⌡ BOTTOM HALF INTEGRAL */
437 {0x08a6, 0x2502}, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */
438 /* 0x08a7 topleftsqbracket ? ??? */
439 /* 0x08a8 botleftsqbracket ? ??? */
440 /* 0x08a9 toprightsqbracket ? ??? */
441 /* 0x08aa botrightsqbracket ? ??? */
442 /* 0x08ab topleftparens ? ??? */
443 /* 0x08ac botleftparens ? ??? */
444 /* 0x08ad toprightparens ? ??? */
445 /* 0x08ae botrightparens ? ??? */
446 /* 0x08af leftmiddlecurlybrace ? ??? */
447 /* 0x08b0 rightmiddlecurlybrace ? ??? */
448 /* 0x08b1 topleftsummation ? ??? */
449 /* 0x08b2 botleftsummation ? ??? */
450 /* 0x08b3 topvertsummationconnector ? ??? */
451 /* 0x08b4 botvertsummationconnector ? ??? */
452 /* 0x08b5 toprightsummation ? ??? */
453 /* 0x08b6 botrightsummation ? ??? */
454 /* 0x08b7 rightmiddlesummation ? ??? */
455 {0x08bc, 0x2264}, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */
456 {0x08bd, 0x2260}, /* notequal ≠ NOT EQUAL TO */
457 {0x08be, 0x2265}, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */
458 {0x08bf, 0x222b}, /* integral ∫ INTEGRAL */
459 {0x08c0, 0x2234}, /* therefore ∴ THEREFORE */
460 {0x08c1, 0x221d}, /* variation ∝ PROPORTIONAL TO */
461 {0x08c2, 0x221e}, /* infinity ∞ INFINITY */
462 {0x08c5, 0x2207}, /* nabla ∇ NABLA */
463 {0x08c8, 0x2245}, /* approximate ≅ APPROXIMATELY EQUAL TO */
464 /* 0x08c9 similarequal ? ??? */
465 {0x08cd, 0x21d4}, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */
466 {0x08ce, 0x21d2}, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */
467 {0x08cf, 0x2261}, /* identical ≡ IDENTICAL TO */
468 {0x08d6, 0x221a}, /* radical √ SQUARE ROOT */
469 {0x08da, 0x2282}, /* includedin ⊂ SUBSET OF */
470 {0x08db, 0x2283}, /* includes ⊃ SUPERSET OF */
471 {0x08dc, 0x2229}, /* intersection ∩ INTERSECTION */
472 {0x08dd, 0x222a}, /* union ∪ UNION */
473 {0x08de, 0x2227}, /* logicaland ∧ LOGICAL AND */
474 {0x08df, 0x2228}, /* logicalor ∨ LOGICAL OR */
475 {0x08ef, 0x2202}, /* partialderivative ∂ PARTIAL DIFFERENTIAL */
476 {0x08f6, 0x0192}, /* function ƒ LATIN SMALL LETTER F WITH HOOK */
477 {0x08fb, 0x2190}, /* leftarrow ← LEFTWARDS ARROW */
478 {0x08fc, 0x2191}, /* uparrow ↑ UPWARDS ARROW */
479 {0x08fd, 0x2192}, /* rightarrow → RIGHTWARDS ARROW */
480 {0x08fe, 0x2193}, /* downarrow ↓ DOWNWARDS ARROW */
481 {0x09df, 0x2422}, /* blank ␢ BLANK SYMBOL */
482 {0x09e0, 0x25c6}, /* soliddiamond ◆ BLACK DIAMOND */
483 {0x09e1, 0x2592}, /* checkerboard ▒ MEDIUM SHADE */
484 {0x09e2, 0x2409}, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */
485 {0x09e3, 0x240c}, /* ff ␌ SYMBOL FOR FORM FEED */
486 {0x09e4, 0x240d}, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */
487 {0x09e5, 0x240a}, /* lf ␊ SYMBOL FOR LINE FEED */
488 {0x09e8, 0x2424}, /* nl ␤ SYMBOL FOR NEWLINE */
489 {0x09e9, 0x240b}, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */
490 {0x09ea, 0x2518}, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */
491 {0x09eb, 0x2510}, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
492 {0x09ec, 0x250c}, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
493 {0x09ed, 0x2514}, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */
494 {0x09ee, 0x253c}, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
495 /* 0x09ef horizlinescan1 ? ??? */
496 /* 0x09f0 horizlinescan3 ? ??? */
497 {0x09f1, 0x2500}, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */
498 /* 0x09f2 horizlinescan7 ? ??? */
499 /* 0x09f3 horizlinescan9 ? ??? */
500 {0x09f4, 0x251c}, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
501 {0x09f5, 0x2524}, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
502 {0x09f6, 0x2534}, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
503 {0x09f7, 0x252c}, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
504 {0x09f8, 0x2502}, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */
505 {0x0aa1, 0x2003}, /* emspace   EM SPACE */
506 {0x0aa2, 0x2002}, /* enspace   EN SPACE */
507 {0x0aa3, 0x2004}, /* em3space   THREE-PER-EM SPACE */
508 {0x0aa4, 0x2005}, /* em4space   FOUR-PER-EM SPACE */
509 {0x0aa5, 0x2007}, /* digitspace   FIGURE SPACE */
510 {0x0aa6, 0x2008}, /* punctspace   PUNCTUATION SPACE */
511 {0x0aa7, 0x2009}, /* thinspace   THIN SPACE */
512 {0x0aa8, 0x200a}, /* hairspace   HAIR SPACE */
513 {0x0aa9, 0x2014}, /* emdash — EM DASH */
514 {0x0aaa, 0x2013}, /* endash – EN DASH */
515 /* 0x0aac signifblank ? ??? */
516 {0x0aae, 0x2026}, /* ellipsis … HORIZONTAL ELLIPSIS */
517 /* 0x0aaf doubbaselinedot ? ??? */
518 {0x0ab0, 0x2153}, /* onethird ⅓ VULGAR FRACTION ONE THIRD */
519 {0x0ab1, 0x2154}, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */
520 {0x0ab2, 0x2155}, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */
521 {0x0ab3, 0x2156}, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */
522 {0x0ab4, 0x2157}, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */
523 {0x0ab5, 0x2158}, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */
524 {0x0ab6, 0x2159}, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */
525 {0x0ab7, 0x215a}, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */
526 {0x0ab8, 0x2105}, /* careof ℅ CARE OF */
527 {0x0abb, 0x2012}, /* figdash ‒ FIGURE DASH */
528 {0x0abc, 0x2329}, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */
529 {0x0abd, 0x002e}, /* decimalpoint . FULL STOP */
530 {0x0abe, 0x232a}, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */
531 /* 0x0abf marker ? ??? */
532 {0x0ac3, 0x215b}, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */
533 {0x0ac4, 0x215c}, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */
534 {0x0ac5, 0x215d}, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */
535 {0x0ac6, 0x215e}, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */
536 {0x0ac9, 0x2122}, /* trademark ™ TRADE MARK SIGN */
537 {0x0aca, 0x2613}, /* signaturemark ☓ SALTIRE */
538 /* 0x0acb trademarkincircle ? ??? */
539 {0x0acc, 0x25c1}, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */
540 {0x0acd, 0x25b7}, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */
541 {0x0ace, 0x25cb}, /* emopencircle ○ WHITE CIRCLE */
542 {0x0acf, 0x25a1}, /* emopenrectangle □ WHITE SQUARE */
543 {0x0ad0, 0x2018}, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */
544 {0x0ad1, 0x2019}, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */
545 {0x0ad2, 0x201c}, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */
546 {0x0ad3, 0x201d}, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */
547 {0x0ad4, 0x211e}, /* prescription ℞ PRESCRIPTION TAKE */
548 {0x0ad6, 0x2032}, /* minutes ′ PRIME */
549 {0x0ad7, 0x2033}, /* seconds ″ DOUBLE PRIME */
550 {0x0ad9, 0x271d}, /* latincross ✝ LATIN CROSS */
551 /* 0x0ada hexagram ? ??? */
552 {0x0adb, 0x25ac}, /* filledrectbullet ▬ BLACK RECTANGLE */
553 {0x0adc, 0x25c0}, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */
554 {0x0add, 0x25b6}, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */
555 {0x0ade, 0x25cf}, /* emfilledcircle ● BLACK CIRCLE */
556 {0x0adf, 0x25a0}, /* emfilledrect ■ BLACK SQUARE */
557 {0x0ae0, 0x25e6}, /* enopencircbullet ◦ WHITE BULLET */
558 {0x0ae1, 0x25ab}, /* enopensquarebullet ▫ WHITE SMALL SQUARE */
559 {0x0ae2, 0x25ad}, /* openrectbullet ▭ WHITE RECTANGLE */
560 {0x0ae3, 0x25b3}, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */
561 {0x0ae4, 0x25bd}, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */
562 {0x0ae5, 0x2606}, /* openstar ☆ WHITE STAR */
563 {0x0ae6, 0x2022}, /* enfilledcircbullet • BULLET */
564 {0x0ae7, 0x25aa}, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */
565 {0x0ae8, 0x25b2}, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */
566 {0x0ae9, 0x25bc}, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */
567 {0x0aea, 0x261c}, /* leftpointer ☜ WHITE LEFT POINTING INDEX */
568 {0x0aeb, 0x261e}, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */
569 {0x0aec, 0x2663}, /* club ♣ BLACK CLUB SUIT */
570 {0x0aed, 0x2666}, /* diamond ♦ BLACK DIAMOND SUIT */
571 {0x0aee, 0x2665}, /* heart ♥ BLACK HEART SUIT */
572 {0x0af0, 0x2720}, /* maltesecross ✠ MALTESE CROSS */
573 {0x0af1, 0x2020}, /* dagger † DAGGER */
574 {0x0af2, 0x2021}, /* doubledagger ‡ DOUBLE DAGGER */
575 {0x0af3, 0x2713}, /* checkmark ✓ CHECK MARK */
576 {0x0af4, 0x2717}, /* ballotcross ✗ BALLOT X */
577 {0x0af5, 0x266f}, /* musicalsharp ♯ MUSIC SHARP SIGN */
578 {0x0af6, 0x266d}, /* musicalflat ♭ MUSIC FLAT SIGN */
579 {0x0af7, 0x2642}, /* malesymbol ♂ MALE SIGN */
580 {0x0af8, 0x2640}, /* femalesymbol ♀ FEMALE SIGN */
581 {0x0af9, 0x260e}, /* telephone ☎ BLACK TELEPHONE */
582 {0x0afa, 0x2315}, /* telephonerecorder ⌕ TELEPHONE RECORDER */
583 {0x0afb, 0x2117}, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */
584 {0x0afc, 0x2038}, /* caret ‸ CARET */
585 {0x0afd, 0x201a}, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */
586 {0x0afe, 0x201e}, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */
587 /* 0x0aff cursor ? ??? */
588 {0x0ba3, 0x003c}, /* leftcaret < LESS-THAN SIGN */
589 {0x0ba6, 0x003e}, /* rightcaret > GREATER-THAN SIGN */
590 {0x0ba8, 0x2228}, /* downcaret ∨ LOGICAL OR */
591 {0x0ba9, 0x2227}, /* upcaret ∧ LOGICAL AND */
592 {0x0bc0, 0x00af}, /* overbar ¯ MACRON */
593 {0x0bc2, 0x22a4}, /* downtack ⊤ DOWN TACK */
594 {0x0bc3, 0x2229}, /* upshoe ∩ INTERSECTION */
595 {0x0bc4, 0x230a}, /* downstile ⌊ LEFT FLOOR */
596 {0x0bc6, 0x005f}, /* underbar _ LOW LINE */
597 {0x0bca, 0x2218}, /* jot ∘ RING OPERATOR */
598 {0x0bcc, 0x2395}, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */
599 {0x0bce, 0x22a5}, /* uptack ⊥ UP TACK */
600 {0x0bcf, 0x25cb}, /* circle ○ WHITE CIRCLE */
601 {0x0bd3, 0x2308}, /* upstile ⌈ LEFT CEILING */
602 {0x0bd6, 0x222a}, /* downshoe ∪ UNION */
603 {0x0bd8, 0x2283}, /* rightshoe ⊃ SUPERSET OF */
604 {0x0bda, 0x2282}, /* leftshoe ⊂ SUBSET OF */
605 {0x0bdc, 0x22a3}, /* lefttack ⊣ LEFT TACK */
606 {0x0bfc, 0x22a2}, /* righttack ⊢ RIGHT TACK */
607 {0x0cdf, 0x2017}, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */
608 {0x0ce0, 0x05d0}, /* hebrew_aleph א HEBREW LETTER ALEF */
609 {0x0ce1, 0x05d1}, /* hebrew_bet ב HEBREW LETTER BET */
610 {0x0ce2, 0x05d2}, /* hebrew_gimel ג HEBREW LETTER GIMEL */
611 {0x0ce3, 0x05d3}, /* hebrew_dalet ד HEBREW LETTER DALET */
612 {0x0ce4, 0x05d4}, /* hebrew_he ה HEBREW LETTER HE */
613 {0x0ce5, 0x05d5}, /* hebrew_waw ו HEBREW LETTER VAV */
614 {0x0ce6, 0x05d6}, /* hebrew_zain ז HEBREW LETTER ZAYIN */
615 {0x0ce7, 0x05d7}, /* hebrew_chet ח HEBREW LETTER HET */
616 {0x0ce8, 0x05d8}, /* hebrew_tet ט HEBREW LETTER TET */
617 {0x0ce9, 0x05d9}, /* hebrew_yod י HEBREW LETTER YOD */
618 {0x0cea, 0x05da}, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */
619 {0x0ceb, 0x05db}, /* hebrew_kaph כ HEBREW LETTER KAF */
620 {0x0cec, 0x05dc}, /* hebrew_lamed ל HEBREW LETTER LAMED */
621 {0x0ced, 0x05dd}, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */
622 {0x0cee, 0x05de}, /* hebrew_mem מ HEBREW LETTER MEM */
623 {0x0cef, 0x05df}, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */
624 {0x0cf0, 0x05e0}, /* hebrew_nun נ HEBREW LETTER NUN */
625 {0x0cf1, 0x05e1}, /* hebrew_samech ס HEBREW LETTER SAMEKH */
626 {0x0cf2, 0x05e2}, /* hebrew_ayin ע HEBREW LETTER AYIN */
627 {0x0cf3, 0x05e3}, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */
628 {0x0cf4, 0x05e4}, /* hebrew_pe פ HEBREW LETTER PE */
629 {0x0cf5, 0x05e5}, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */
630 {0x0cf6, 0x05e6}, /* hebrew_zade צ HEBREW LETTER TSADI */
631 {0x0cf7, 0x05e7}, /* hebrew_qoph ק HEBREW LETTER QOF */
632 {0x0cf8, 0x05e8}, /* hebrew_resh ר HEBREW LETTER RESH */
633 {0x0cf9, 0x05e9}, /* hebrew_shin ש HEBREW LETTER SHIN */
634 {0x0cfa, 0x05ea}, /* hebrew_taw ת HEBREW LETTER TAV */
635 {0x0da1, 0x0e01}, /* Thai_kokai ก THAI CHARACTER KO KAI */
636 {0x0da2, 0x0e02}, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */
637 {0x0da3, 0x0e03}, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */
638 {0x0da4, 0x0e04}, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */
639 {0x0da5, 0x0e05}, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */
640 {0x0da6, 0x0e06}, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */
641 {0x0da7, 0x0e07}, /* Thai_ngongu ง THAI CHARACTER NGO NGU */
642 {0x0da8, 0x0e08}, /* Thai_chochan จ THAI CHARACTER CHO CHAN */
643 {0x0da9, 0x0e09}, /* Thai_choching ฉ THAI CHARACTER CHO CHING */
644 {0x0daa, 0x0e0a}, /* Thai_chochang ช THAI CHARACTER CHO CHANG */
645 {0x0dab, 0x0e0b}, /* Thai_soso ซ THAI CHARACTER SO SO */
646 {0x0dac, 0x0e0c}, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */
647 {0x0dad, 0x0e0d}, /* Thai_yoying ญ THAI CHARACTER YO YING */
648 {0x0dae, 0x0e0e}, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */
649 {0x0daf, 0x0e0f}, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */
650 {0x0db0, 0x0e10}, /* Thai_thothan ฐ THAI CHARACTER THO THAN */
651 {0x0db1, 0x0e11}, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */
652 {0x0db2, 0x0e12}, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */
653 {0x0db3, 0x0e13}, /* Thai_nonen ณ THAI CHARACTER NO NEN */
654 {0x0db4, 0x0e14}, /* Thai_dodek ด THAI CHARACTER DO DEK */
655 {0x0db5, 0x0e15}, /* Thai_totao ต THAI CHARACTER TO TAO */
656 {0x0db6, 0x0e16}, /* Thai_thothung ถ THAI CHARACTER THO THUNG */
657 {0x0db7, 0x0e17}, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */
658 {0x0db8, 0x0e18}, /* Thai_thothong ธ THAI CHARACTER THO THONG */
659 {0x0db9, 0x0e19}, /* Thai_nonu น THAI CHARACTER NO NU */
660 {0x0dba, 0x0e1a}, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */
661 {0x0dbb, 0x0e1b}, /* Thai_popla ป THAI CHARACTER PO PLA */
662 {0x0dbc, 0x0e1c}, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */
663 {0x0dbd, 0x0e1d}, /* Thai_fofa ฝ THAI CHARACTER FO FA */
664 {0x0dbe, 0x0e1e}, /* Thai_phophan พ THAI CHARACTER PHO PHAN */
665 {0x0dbf, 0x0e1f}, /* Thai_fofan ฟ THAI CHARACTER FO FAN */
666 {0x0dc0, 0x0e20}, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */
667 {0x0dc1, 0x0e21}, /* Thai_moma ม THAI CHARACTER MO MA */
668 {0x0dc2, 0x0e22}, /* Thai_yoyak ย THAI CHARACTER YO YAK */
669 {0x0dc3, 0x0e23}, /* Thai_rorua ร THAI CHARACTER RO RUA */
670 {0x0dc4, 0x0e24}, /* Thai_ru ฤ THAI CHARACTER RU */
671 {0x0dc5, 0x0e25}, /* Thai_loling ล THAI CHARACTER LO LING */
672 {0x0dc6, 0x0e26}, /* Thai_lu ฦ THAI CHARACTER LU */
673 {0x0dc7, 0x0e27}, /* Thai_wowaen ว THAI CHARACTER WO WAEN */
674 {0x0dc8, 0x0e28}, /* Thai_sosala ศ THAI CHARACTER SO SALA */
675 {0x0dc9, 0x0e29}, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */
676 {0x0dca, 0x0e2a}, /* Thai_sosua ส THAI CHARACTER SO SUA */
677 {0x0dcb, 0x0e2b}, /* Thai_hohip ห THAI CHARACTER HO HIP */
678 {0x0dcc, 0x0e2c}, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */
679 {0x0dcd, 0x0e2d}, /* Thai_oang อ THAI CHARACTER O ANG */
680 {0x0dce, 0x0e2e}, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */
681 {0x0dcf, 0x0e2f}, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */
682 {0x0dd0, 0x0e30}, /* Thai_saraa ะ THAI CHARACTER SARA A */
683 {0x0dd1, 0x0e31}, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */
684 {0x0dd2, 0x0e32}, /* Thai_saraaa า THAI CHARACTER SARA AA */
685 {0x0dd3, 0x0e33}, /* Thai_saraam ำ THAI CHARACTER SARA AM */
686 {0x0dd4, 0x0e34}, /* Thai_sarai ิ THAI CHARACTER SARA I */
687 {0x0dd5, 0x0e35}, /* Thai_saraii ี THAI CHARACTER SARA II */
688 {0x0dd6, 0x0e36}, /* Thai_saraue ึ THAI CHARACTER SARA UE */
689 {0x0dd7, 0x0e37}, /* Thai_sarauee ื THAI CHARACTER SARA UEE */
690 {0x0dd8, 0x0e38}, /* Thai_sarau ุ THAI CHARACTER SARA U */
691 {0x0dd9, 0x0e39}, /* Thai_sarauu ู THAI CHARACTER SARA UU */
692 {0x0dda, 0x0e3a}, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */
693 {0x0dde, 0x0e3e}, /* Thai_maihanakat_maitho ฾ ??? */
694 {0x0ddf, 0x0e3f}, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */
695 {0x0de0, 0x0e40}, /* Thai_sarae เ THAI CHARACTER SARA E */
696 {0x0de1, 0x0e41}, /* Thai_saraae แ THAI CHARACTER SARA AE */
697 {0x0de2, 0x0e42}, /* Thai_sarao โ THAI CHARACTER SARA O */
698 {0x0de3, 0x0e43}, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */
699 {0x0de4, 0x0e44}, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */
700 {0x0de5, 0x0e45}, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */
701 {0x0de6, 0x0e46}, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */
702 {0x0de7, 0x0e47}, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */
703 {0x0de8, 0x0e48}, /* Thai_maiek ่ THAI CHARACTER MAI EK */
704 {0x0de9, 0x0e49}, /* Thai_maitho ้ THAI CHARACTER MAI THO */
705 {0x0dea, 0x0e4a}, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */
706 {0x0deb, 0x0e4b}, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */
707 {0x0dec, 0x0e4c}, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */
708 {0x0ded, 0x0e4d}, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */
709 {0x0df0, 0x0e50}, /* Thai_leksun ๐ THAI DIGIT ZERO */
710 {0x0df1, 0x0e51}, /* Thai_leknung ๑ THAI DIGIT ONE */
711 {0x0df2, 0x0e52}, /* Thai_leksong ๒ THAI DIGIT TWO */
712 {0x0df3, 0x0e53}, /* Thai_leksam ๓ THAI DIGIT THREE */
713 {0x0df4, 0x0e54}, /* Thai_leksi ๔ THAI DIGIT FOUR */
714 {0x0df5, 0x0e55}, /* Thai_lekha ๕ THAI DIGIT FIVE */
715 {0x0df6, 0x0e56}, /* Thai_lekhok ๖ THAI DIGIT SIX */
716 {0x0df7, 0x0e57}, /* Thai_lekchet ๗ THAI DIGIT SEVEN */
717 {0x0df8, 0x0e58}, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */
718 {0x0df9, 0x0e59}, /* Thai_lekkao ๙ THAI DIGIT NINE */
719 {0x0ea1, 0x3131}, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */
720 {0x0ea2, 0x3132}, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */
721 {0x0ea3, 0x3133}, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */
722 {0x0ea4, 0x3134}, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */
723 {0x0ea5, 0x3135}, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */
724 {0x0ea6, 0x3136}, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */
725 {0x0ea7, 0x3137}, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */
726 {0x0ea8, 0x3138}, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */
727 {0x0ea9, 0x3139}, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */
728 {0x0eaa, 0x313a}, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */
729 {0x0eab, 0x313b}, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */
730 {0x0eac, 0x313c}, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */
731 {0x0ead, 0x313d}, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */
732 {0x0eae, 0x313e}, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */
733 {0x0eaf, 0x313f}, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */
734 {0x0eb0, 0x3140}, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */
735 {0x0eb1, 0x3141}, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */
736 {0x0eb2, 0x3142}, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */
737 {0x0eb3, 0x3143}, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */
738 {0x0eb4, 0x3144}, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */
739 {0x0eb5, 0x3145}, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */
740 {0x0eb6, 0x3146}, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */
741 {0x0eb7, 0x3147}, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */
742 {0x0eb8, 0x3148}, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */
743 {0x0eb9, 0x3149}, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */
744 {0x0eba, 0x314a}, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */
745 {0x0ebb, 0x314b}, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */
746 {0x0ebc, 0x314c}, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */
747 {0x0ebd, 0x314d}, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */
748 {0x0ebe, 0x314e}, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */
749 {0x0ebf, 0x314f}, /* Hangul_A ㅏ HANGUL LETTER A */
750 {0x0ec0, 0x3150}, /* Hangul_AE ㅐ HANGUL LETTER AE */
751 {0x0ec1, 0x3151}, /* Hangul_YA ㅑ HANGUL LETTER YA */
752 {0x0ec2, 0x3152}, /* Hangul_YAE ㅒ HANGUL LETTER YAE */
753 {0x0ec3, 0x3153}, /* Hangul_EO ㅓ HANGUL LETTER EO */
754 {0x0ec4, 0x3154}, /* Hangul_E ㅔ HANGUL LETTER E */
755 {0x0ec5, 0x3155}, /* Hangul_YEO ㅕ HANGUL LETTER YEO */
756 {0x0ec6, 0x3156}, /* Hangul_YE ㅖ HANGUL LETTER YE */
757 {0x0ec7, 0x3157}, /* Hangul_O ㅗ HANGUL LETTER O */
758 {0x0ec8, 0x3158}, /* Hangul_WA ㅘ HANGUL LETTER WA */
759 {0x0ec9, 0x3159}, /* Hangul_WAE ㅙ HANGUL LETTER WAE */
760 {0x0eca, 0x315a}, /* Hangul_OE ㅚ HANGUL LETTER OE */
761 {0x0ecb, 0x315b}, /* Hangul_YO ㅛ HANGUL LETTER YO */
762 {0x0ecc, 0x315c}, /* Hangul_U ㅜ HANGUL LETTER U */
763 {0x0ecd, 0x315d}, /* Hangul_WEO ㅝ HANGUL LETTER WEO */
764 {0x0ece, 0x315e}, /* Hangul_WE ㅞ HANGUL LETTER WE */
765 {0x0ecf, 0x315f}, /* Hangul_WI ㅟ HANGUL LETTER WI */
766 {0x0ed0, 0x3160}, /* Hangul_YU ㅠ HANGUL LETTER YU */
767 {0x0ed1, 0x3161}, /* Hangul_EU ㅡ HANGUL LETTER EU */
768 {0x0ed2, 0x3162}, /* Hangul_YI ㅢ HANGUL LETTER YI */
769 {0x0ed3, 0x3163}, /* Hangul_I ㅣ HANGUL LETTER I */
770 {0x0ed4, 0x11a8}, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */
771 {0x0ed5, 0x11a9}, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */
772 {0x0ed6, 0x11aa}, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */
773 {0x0ed7, 0x11ab}, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */
774 {0x0ed8, 0x11ac}, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */
775 {0x0ed9, 0x11ad}, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */
776 {0x0eda, 0x11ae}, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */
777 {0x0edb, 0x11af}, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */
778 {0x0edc, 0x11b0}, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */
779 {0x0edd, 0x11b1}, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */
780 {0x0ede, 0x11b2}, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */
781 {0x0edf, 0x11b3}, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */
782 {0x0ee0, 0x11b4}, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */
783 {0x0ee1, 0x11b5}, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */
784 {0x0ee2, 0x11b6}, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */
785 {0x0ee3, 0x11b7}, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */
786 {0x0ee4, 0x11b8}, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */
787 {0x0ee5, 0x11b9}, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */
788 {0x0ee6, 0x11ba}, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */
789 {0x0ee7, 0x11bb}, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */
790 {0x0ee8, 0x11bc}, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */
791 {0x0ee9, 0x11bd}, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */
792 {0x0eea, 0x11be}, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */
793 {0x0eeb, 0x11bf}, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */
794 {0x0eec, 0x11c0}, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */
795 {0x0eed, 0x11c1}, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */
796 {0x0eee, 0x11c2}, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */
797 {0x0eef, 0x316d}, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */
798 {0x0ef0, 0x3171}, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */
799 {0x0ef1, 0x3178}, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */
800 {0x0ef2, 0x317f}, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */
801 /* 0x0ef3 Hangul_KkogjiDalrinIeung ? ??? */
802 {0x0ef4, 0x3184}, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */
803 {0x0ef5, 0x3186}, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */
804 {0x0ef6, 0x318d}, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */
805 {0x0ef7, 0x318e}, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */
806 {0x0ef8, 0x11eb}, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */
807 /* 0x0ef9 Hangul_J_KkogjiDalrinIeung ? ??? */
808 {0x0efa, 0x11f9}, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */
809 {0x0eff, 0x20a9}, /* Korean_Won ₩ WON SIGN */
810 {0x13bc, 0x0152}, /* OE ΠLATIN CAPITAL LIGATURE OE */
811 {0x13bd, 0x0153}, /* oe œ LATIN SMALL LIGATURE OE */
812 {0x13be, 0x0178}, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */
813 {0x20ac, 0x20ac}, /* EuroSign € EURO SIGN */
814 };
815
816 long keysym2ucs(xcb_keysym_t keysym) {
817 int min = 0;
818 int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
819 int mid;
820
821 /* first check for Latin-1 characters (1:1 mapping) */
822 if ((keysym >= 0x0020 && keysym <= 0x007e) ||
823 (keysym >= 0x00a0 && keysym <= 0x00ff))
824 return keysym;
825
826 /* also check for directly encoded 24-bit UCS characters */
827 if ((keysym & 0xff000000) == 0x01000000)
828 return keysym & 0x00ffffff;
829
830 /* binary search in table */
831 while (max >= min) {
832 mid = (min + max) / 2;
833 if (keysymtab[mid].keysym < keysym)
834 min = mid + 1;
835 else if (keysymtab[mid].keysym > keysym)
836 max = mid - 1;
837 else {
838 /* found it */
839 return keysymtab[mid].ucs;
840 }
841 }
842
843 /* no matching Unicode value found */
844 return -1;
845 }
0 #include <xcb/xcb.h>
1
2 long keysym2ucs(xcb_keysym_t keysym);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * i3-input/main.c: Utility which lets the user input commands and sends them
7 * to i3.
8 *
9 */
10 #include "libi3.h"
11
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <stdlib.h>
15 #include <stdbool.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <err.h>
20 #include <stdint.h>
21 #include <getopt.h>
22 #include <limits.h>
23
24 #include <xcb/xcb.h>
25 #include <xcb/xcb_aux.h>
26 #include <xcb/xcb_event.h>
27 #include <xcb/xcb_keysyms.h>
28
29 #include <X11/keysym.h>
30
31 #include "keysym2ucs.h"
32
33 #include "i3-input.h"
34
35 #define MAX_WIDTH logical_px(500)
36 #define BORDER logical_px(2)
37 #define PADDING logical_px(2)
38
39 /* IPC format string. %s will be replaced with what the user entered, then
40 * the command will be sent to i3 */
41 static char *format;
42
43 static int sockfd;
44 static xcb_key_symbols_t *symbols;
45 static bool modeswitch_active = false;
46 static xcb_window_t win;
47 static surface_t surface;
48 static xcb_char2b_t glyphs_ucs[512];
49 static char *glyphs_utf8[512];
50 static int input_position;
51 static i3Font font;
52 static i3String *prompt;
53 static int prompt_offset = 0;
54 static int limit;
55 xcb_window_t root;
56 xcb_connection_t *conn;
57 xcb_screen_t *root_screen;
58
59 /*
60 * Having verboselog(), errorlog() and debuglog() is necessary when using libi3.
61 *
62 */
63 void verboselog(char *fmt, ...) {
64 va_list args;
65
66 va_start(args, fmt);
67 vfprintf(stdout, fmt, args);
68 va_end(args);
69 }
70
71 void errorlog(char *fmt, ...) {
72 va_list args;
73
74 va_start(args, fmt);
75 vfprintf(stderr, fmt, args);
76 va_end(args);
77 }
78
79 void debuglog(char *fmt, ...) {
80 }
81
82 /*
83 * Concats the glyphs (either UCS-2 or UTF-8) to a single string, suitable for
84 * rendering it (UCS-2) or sending it to i3 (UTF-8).
85 *
86 */
87 static uint8_t *concat_strings(char **glyphs, int max) {
88 uint8_t *output = scalloc(max + 1, 4);
89 uint8_t *walk = output;
90 for (int c = 0; c < max; c++) {
91 printf("at %c\n", glyphs[c][0]);
92 /* if the first byte is 0, this has to be UCS2 */
93 if (glyphs[c][0] == '\0') {
94 memcpy(walk, glyphs[c], 2);
95 walk += 2;
96 } else {
97 strcpy((char *)walk, glyphs[c]);
98 walk += strlen(glyphs[c]);
99 }
100 }
101 printf("output = %s\n", output);
102 return output;
103 }
104
105 /*
106 * Handles expose events (redraws of the window) and rendering in general. Will
107 * be called from the code with event == NULL or from X with event != NULL.
108 *
109 */
110 static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t *event) {
111 printf("expose!\n");
112
113 color_t border_color = draw_util_hex_to_color("#FF0000");
114 color_t fg_color = draw_util_hex_to_color("#FFFFFF");
115 color_t bg_color = draw_util_hex_to_color("#000000");
116
117 int text_offset = BORDER + PADDING;
118
119 /* draw border */
120 draw_util_rectangle(&surface, border_color, 0, 0, surface.width, surface.height);
121
122 /* draw background */
123 draw_util_rectangle(&surface, bg_color, BORDER, BORDER, surface.width - 2 * BORDER, surface.height - 2 * BORDER);
124
125 /* draw the prompt … */
126 if (prompt != NULL) {
127 draw_util_text(prompt, &surface, fg_color, bg_color, text_offset, text_offset, MAX_WIDTH - text_offset);
128 }
129
130 /* … and the text */
131 if (input_position > 0) {
132 i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
133 draw_util_text(input, &surface, fg_color, bg_color, text_offset + prompt_offset, text_offset, MAX_WIDTH - text_offset);
134 i3string_free(input);
135 }
136
137 xcb_flush(conn);
138
139 return 1;
140 }
141
142 /*
143 * Deactivates the Mode_switch bit upon release of the Mode_switch key.
144 *
145 */
146 static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) {
147 printf("releasing %d, state raw = %d\n", event->detail, event->state);
148
149 xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, event->state);
150 if (sym == XK_Mode_switch) {
151 printf("Mode switch disabled\n");
152 modeswitch_active = false;
153 }
154
155 return 1;
156 }
157
158 static void finish_input(void) {
159 char *command = (char *)concat_strings(glyphs_utf8, input_position);
160
161 /* count the occurrences of %s in the string */
162 int c;
163 int len = strlen(format);
164 int cnt = 0;
165 for (c = 0; c < (len - 1); c++)
166 if (format[c] == '%' && format[c + 1] == 's')
167 cnt++;
168 printf("occurrences = %d\n", cnt);
169
170 /* allocate space for the output */
171 int inputlen = strlen(command);
172 char *full = scalloc(strlen(format) - (2 * cnt) /* format without all %s */
173 + (inputlen * cnt) /* replaced %s */
174 + 1, /* trailing NUL */
175 1);
176 char *dest = full;
177 for (c = 0; c < len; c++) {
178 /* if this is not % or it is % but without a following 's',
179 * just copy the character */
180 if (format[c] != '%' || (c == (len - 1)) || format[c + 1] != 's')
181 *(dest++) = format[c];
182 else {
183 strncat(dest, command, inputlen);
184 dest += inputlen;
185 /* skip the following 's' of '%s' */
186 c++;
187 }
188 }
189
190 /* prefix the command if a prefix was specified on commandline */
191 printf("command = %s\n", full);
192
193 ipc_send_message(sockfd, strlen(full), 0, (uint8_t *)full);
194
195 free(full);
196
197 exit(0);
198 }
199
200 /*
201 * Handles keypresses by converting the keycodes to keysymbols, then the
202 * keysymbols to UCS-2. If the conversion succeeded, the glyph is saved in the
203 * internal buffers and displayed in the input window.
204 *
205 * Also handles backspace (deleting one character) and return (sending the
206 * command to i3).
207 *
208 */
209 static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
210 printf("Keypress %d, state raw = %d\n", event->detail, event->state);
211
212 // TODO: port the input handling code from i3lock once libxkbcommon ≥ 0.5.0
213 // is available in distros.
214
215 /* See the documentation of xcb_key_symbols_get_keysym for this one.
216 * Basically: We get either col 0 or col 1, depending on whether shift is
217 * pressed. */
218 int col = (event->state & XCB_MOD_MASK_SHIFT);
219
220 /* If modeswitch is currently active, we need to look in group 2 or 3,
221 * respectively. */
222 if (modeswitch_active)
223 col += 2;
224
225 xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, col);
226 if (sym == XK_Mode_switch) {
227 printf("Mode switch enabled\n");
228 modeswitch_active = true;
229 return 1;
230 }
231
232 if (sym == XK_Return)
233 finish_input();
234
235 if (sym == XK_BackSpace) {
236 if (input_position == 0)
237 return 1;
238
239 input_position--;
240 free(glyphs_utf8[input_position]);
241
242 handle_expose(NULL, conn, NULL);
243 return 1;
244 }
245 if (sym == XK_Escape) {
246 exit(0);
247 }
248
249 /* TODO: handle all of these? */
250 printf("is_keypad_key = %d\n", xcb_is_keypad_key(sym));
251 printf("is_private_keypad_key = %d\n", xcb_is_private_keypad_key(sym));
252 printf("xcb_is_cursor_key = %d\n", xcb_is_cursor_key(sym));
253 printf("xcb_is_pf_key = %d\n", xcb_is_pf_key(sym));
254 printf("xcb_is_function_key = %d\n", xcb_is_function_key(sym));
255 printf("xcb_is_misc_function_key = %d\n", xcb_is_misc_function_key(sym));
256 printf("xcb_is_modifier_key = %d\n", xcb_is_modifier_key(sym));
257
258 if (xcb_is_modifier_key(sym) || xcb_is_cursor_key(sym))
259 return 1;
260
261 printf("sym = %c (%d)\n", sym, sym);
262
263 /* convert the keysym to UCS */
264 uint16_t ucs = keysym2ucs(sym);
265 if ((int16_t)ucs == -1) {
266 fprintf(stderr, "Keysym could not be converted to UCS, skipping\n");
267 return 1;
268 }
269
270 xcb_char2b_t inp;
271 inp.byte1 = (ucs & 0xff00) >> 2;
272 inp.byte2 = (ucs & 0x00ff) >> 0;
273
274 printf("inp.byte1 = %02x, inp.byte2 = %02x\n", inp.byte1, inp.byte2);
275 /* convert it to UTF-8 */
276 char *out = convert_ucs2_to_utf8(&inp, 1);
277 printf("converted to %s\n", out);
278
279 glyphs_ucs[input_position] = inp;
280 glyphs_utf8[input_position] = out;
281 input_position++;
282
283 if (input_position == limit)
284 finish_input();
285
286 handle_expose(NULL, conn, NULL);
287 return 1;
288 }
289
290 static xcb_rectangle_t get_window_position(void) {
291 xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), MAX_WIDTH, font.height + 2 * BORDER + 2 * PADDING};
292
293 xcb_get_property_reply_t *supporting_wm_reply = NULL;
294 xcb_get_input_focus_reply_t *input_focus = NULL;
295 xcb_get_geometry_reply_t *geometry = NULL;
296 xcb_get_property_reply_t *wm_class = NULL;
297 xcb_translate_coordinates_reply_t *coordinates = NULL;
298
299 xcb_atom_t A__NET_SUPPORTING_WM_CHECK;
300 xcb_intern_atom_cookie_t nswc_cookie = xcb_intern_atom(conn, 0, strlen("_NET_SUPPORTING_WM_CHECK"), "_NET_SUPPORTING_WM_CHECK");
301 xcb_intern_atom_reply_t *nswc_reply = xcb_intern_atom_reply(conn, nswc_cookie, NULL);
302 if (nswc_reply == NULL) {
303 ELOG("Could not intern atom _NET_SUPPORTING_WM_CHECK\n");
304 exit(-1);
305 }
306 A__NET_SUPPORTING_WM_CHECK = nswc_reply->atom;
307 free(nswc_reply);
308
309 supporting_wm_reply = xcb_get_property_reply(
310 conn, xcb_get_property(conn, false, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 0, 32), NULL);
311 xcb_window_t *supporting_wm_win = NULL;
312 if (supporting_wm_reply == NULL || xcb_get_property_value_length(supporting_wm_reply) == 0) {
313 DLOG("Could not determine EWMH support window.\n");
314 } else {
315 supporting_wm_win = xcb_get_property_value(supporting_wm_reply);
316 }
317
318 /* In rare cases, the window holding the input focus might disappear while we are figuring out its
319 * position. To avoid this, we grab the server in the meantime. */
320 xcb_grab_server(conn);
321
322 input_focus = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
323 if (input_focus == NULL || input_focus->focus == XCB_NONE) {
324 DLOG("Failed to receive the current input focus or no window has the input focus right now.\n");
325 goto free_resources;
326 }
327
328 /* We need to ignore the EWMH support window to which the focus can be set if there's no suitable window to focus. */
329 if (supporting_wm_win != NULL && input_focus->focus == *supporting_wm_win) {
330 DLOG("Input focus is on the EWMH support window, ignoring.\n");
331 goto free_resources;
332 }
333
334 geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL);
335 if (geometry == NULL) {
336 DLOG("Failed to received window geometry.\n");
337 goto free_resources;
338 }
339
340 wm_class = xcb_get_property_reply(
341 conn, xcb_get_property(conn, false, input_focus->focus, XCB_ATOM_WM_CLASS, XCB_GET_PROPERTY_TYPE_ANY, 0, 32), NULL);
342
343 /* We need to find out whether the input focus is on an i3 frame window. If it is, we must not translate the coordinates. */
344 if (wm_class == NULL || xcb_get_property_value_length(wm_class) == 0 || strcmp(xcb_get_property_value(wm_class), "i3-frame") != 0) {
345 coordinates = xcb_translate_coordinates_reply(
346 conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL);
347 if (coordinates == NULL) {
348 DLOG("Failed to translate coordinates.\n");
349 goto free_resources;
350 }
351
352 DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y);
353 result.x += coordinates->dst_x;
354 result.y += coordinates->dst_y;
355 } else {
356 DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", geometry->x, geometry->y);
357 result.x += geometry->x;
358 result.y += geometry->y;
359 }
360
361 free_resources:
362 xcb_ungrab_server(conn);
363 xcb_flush(conn);
364
365 FREE(supporting_wm_reply);
366 FREE(input_focus);
367 FREE(geometry);
368 FREE(wm_class);
369 FREE(coordinates);
370 return result;
371 }
372
373 int main(int argc, char *argv[]) {
374 format = sstrdup("%s");
375 char *socket_path = NULL;
376 char *pattern = sstrdup("pango:monospace 8");
377 int o, option_index = 0;
378
379 static struct option long_options[] = {
380 {"socket", required_argument, 0, 's'},
381 {"version", no_argument, 0, 'v'},
382 {"limit", required_argument, 0, 'l'},
383 {"prompt", required_argument, 0, 'P'},
384 {"prefix", required_argument, 0, 'p'},
385 {"format", required_argument, 0, 'F'},
386 {"font", required_argument, 0, 'f'},
387 {"help", no_argument, 0, 'h'},
388 {0, 0, 0, 0}};
389
390 char *options_string = "s:p:P:f:l:F:vh";
391
392 while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
393 switch (o) {
394 case 's':
395 FREE(socket_path);
396 socket_path = sstrdup(optarg);
397 break;
398 case 'v':
399 printf("i3-input " I3_VERSION);
400 return 0;
401 case 'p':
402 /* This option is deprecated, but will still work in i3 v4.1, 4.2 and 4.3 */
403 fprintf(stderr, "i3-input: WARNING: the -p option is DEPRECATED in favor of the -F (format) option\n");
404 FREE(format);
405 sasprintf(&format, "%s%%s", optarg);
406 break;
407 case 'l':
408 limit = atoi(optarg);
409 break;
410 case 'P':
411 i3string_free(prompt);
412 prompt = i3string_from_utf8(optarg);
413 break;
414 case 'f':
415 FREE(pattern);
416 pattern = sstrdup(optarg);
417 break;
418 case 'F':
419 FREE(format);
420 format = sstrdup(optarg);
421 break;
422 case 'h':
423 printf("i3-input " I3_VERSION "\n");
424 printf("i3-input [-s <socket>] [-F <format>] [-l <limit>] [-P <prompt>] [-f <font>] [-v]\n");
425 printf("\n");
426 printf("Example:\n");
427 printf(" i3-input -F 'workspace \"%%s\"' -P 'Switch to workspace: '\n");
428 return 0;
429 }
430 }
431
432 printf("using format \"%s\"\n", format);
433
434 int screen;
435 conn = xcb_connect(NULL, &screen);
436 if (!conn || xcb_connection_has_error(conn))
437 die("Cannot open display\n");
438
439 sockfd = ipc_connect(socket_path);
440
441 root_screen = xcb_aux_get_screen(conn, screen);
442 root = root_screen->root;
443
444 symbols = xcb_key_symbols_alloc(conn);
445
446 init_dpi();
447 font = load_font(pattern, true);
448 set_font(&font);
449
450 if (prompt != NULL)
451 prompt_offset = predict_text_width(prompt);
452
453 const xcb_rectangle_t win_pos = get_window_position();
454
455 /* Open an input window */
456 win = xcb_generate_id(conn);
457 xcb_create_window(
458 conn,
459 XCB_COPY_FROM_PARENT,
460 win, /* the window id */
461 root, /* parent == root */
462 win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
463 0, /* X11 border = 0, we draw our own */
464 XCB_WINDOW_CLASS_INPUT_OUTPUT,
465 XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
466 XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
467 (uint32_t[]){
468 0, /* back pixel: black */
469 1, /* override redirect: don’t manage this window */
470 XCB_EVENT_MASK_EXPOSURE});
471
472 /* Map the window (make it visible) */
473 xcb_map_window(conn, win);
474
475 /* Initialize the drawable surface */
476 draw_util_surface_init(conn, &surface, win, get_visualtype(root_screen), win_pos.width, win_pos.height);
477
478 /* Grab the keyboard to get all input */
479 xcb_flush(conn);
480
481 /* Try (repeatedly, if necessary) to grab the keyboard. We might not
482 * get the keyboard at the first attempt because of the keybinding
483 * still being active when started via a wm’s keybinding. */
484 xcb_grab_keyboard_cookie_t cookie;
485 xcb_grab_keyboard_reply_t *reply = NULL;
486
487 int count = 0;
488 while ((reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) && (count++ < 500)) {
489 cookie = xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
490 reply = xcb_grab_keyboard_reply(conn, cookie, NULL);
491 usleep(1000);
492 }
493
494 if (reply->status != XCB_GRAB_STATUS_SUCCESS) {
495 fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status);
496 exit(-1);
497 }
498
499 xcb_flush(conn);
500
501 xcb_generic_event_t *event;
502 while ((event = xcb_wait_for_event(conn)) != NULL) {
503 if (event->response_type == 0) {
504 fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
505 continue;
506 }
507
508 /* Strip off the highest bit (set if the event is generated) */
509 int type = (event->response_type & 0x7F);
510
511 switch (type) {
512 case XCB_KEY_PRESS:
513 handle_key_press(NULL, conn, (xcb_key_press_event_t *)event);
514 break;
515
516 case XCB_KEY_RELEASE:
517 handle_key_release(NULL, conn, (xcb_key_release_event_t *)event);
518 break;
519
520 case XCB_EXPOSE:
521 if (((xcb_expose_event_t *)event)->count == 0) {
522 handle_expose(NULL, conn, (xcb_expose_event_t *)event);
523 }
524
525 break;
526 }
527
528 free(event);
529 }
530
531 draw_util_surface_free(conn, &surface);
532 return 0;
533 }
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # script to migrate an old config file (i3 < 4.0) to the new format (>= 4.0)
4 # this script only uses modules which come with perl 5.10
5 #
6 # reads an i3 v3 config from stdin and spits out a v4 config on stdout
7 # exit codes:
8 # 0 = the input was a v3 config
9 # 1 = the input was already a v4 config
10 # (the config is printed to stdout nevertheless)
11 #
12 # © 2011 Michael Stapelberg and contributors, see LICENSE
13
14 use strict;
15 use warnings;
16 use Getopt::Long;
17 use v5.10;
18
19 # is this a version 3 config file? disables auto-detection
20 my $v3 = 0;
21 my $result = GetOptions('v3' => \$v3);
22
23 # reads stdin
24 sub slurp {
25 local $/;
26 <>;
27 }
28
29 my @unchanged = qw(
30 font
31 set
32 mode
33 exec
34 assign
35 floating_modifier
36 focus_follows_mouse
37 ipc-socket
38 ipc_socket
39 client.focused
40 client.focused_inactive
41 client.unfocused
42 client.urgent
43 client.background
44 );
45
46 my %workspace_names;
47 my $workspace_bar = 1;
48
49 my $input = slurp();
50 my @lines = split /\n/, $input;
51
52 # remove whitespaces in the beginning of lines
53 @lines = map { s/^[ \t]*//g; $_ } @lines;
54
55 # Try to auto-detect if this is a v3 config file.
56 sub need_to_convert {
57 # If the user passed --v3, we need to convert in any case
58 return 1 if $v3;
59
60 for my $line (@lines) {
61 # only v4 configfiles can use bindcode or workspace_layout
62 return 0 if $line =~ /^bindcode/ ||
63 $line =~ /^workspace_layout/ ||
64 $line =~ /^force_focus_wrapping/;
65
66 # have a look at bindings
67 next unless $line =~ /^bind/;
68
69 my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
70 return 0 if $command =~ /^layout/ ||
71 $command =~ /^floating/ ||
72 $command =~ /^workspace/ ||
73 $command =~ /^focus (left|right|up|down)/ ||
74 $command =~ /^border (normal|1pixel|none)/;
75 }
76
77 return 1;
78 }
79
80 if (!need_to_convert()) {
81 # If this is already a v4 config file, we will spit out the lines
82 # and exit with return code 1
83 print $input;
84 exit 1;
85 }
86
87 # first pass: get workspace names
88 for my $line (@lines) {
89 next if $line =~ /^#/ || $line =~ /^$/;
90
91 my ($statement, $parameters) = ($line =~ /([a-zA-Z._-]+)(.*)/);
92
93 # skip everything but workspace lines
94 next unless defined($statement) and $statement eq 'workspace';
95
96 my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
97
98 # save workspace name (unless the line is actually a workspace assignment)
99 $workspace_names{$number} = $params unless $params =~ /^output/;
100 }
101
102 for my $line (@lines) {
103 # directly use comments and empty lines
104 if ($line =~ /^#/ || $line =~ /^$/) {
105 print "$line\n";
106 next;
107 }
108
109 my ($statement, $parameters) = ($line =~ /([a-zA-Z._-]+)(.*)/);
110
111 # directly use lines which have not changed between 3.x and 4.x
112 if (!defined($statement) || (lc $statement ~~ @unchanged)) {
113 print "$line\n";
114 next;
115 }
116
117 # new_container changed only the statement name to workspace_layout
118 if ($statement eq 'new_container') {
119 print "workspace_layout$parameters\n";
120 next;
121 }
122
123 # workspace_bar is gone, you should use i3bar now
124 if ($statement eq 'workspace_bar') {
125 $workspace_bar = ($parameters =~ /\s+(yes|true|on|enable|active)/);
126 print "# XXX: REMOVED workspace_bar line. There is no internal workspace bar in v4.\n";
127 next;
128 }
129
130 # new_window changed the parameters from bb to none etc.
131 if ($statement eq 'new_window') {
132 if ($parameters =~ /bb/) {
133 print "new_window none\n";
134 } elsif ($parameters =~ /bp/) {
135 print "new_window 1pixel\n";
136 } elsif ($parameters =~ /bn/) {
137 print "new_window normal\n";
138 } else {
139 print "# XXX: Invalid parameter for new_window, not touching line:\n";
140 print "$line\n";
141 }
142 next;
143 }
144
145 # bar colors are obsolete, need to be configured in i3bar
146 if ($statement =~ /^bar\./) {
147 print "# XXX: REMOVED $statement, configure i3bar instead.\n";
148 print "# Old line: $line\n";
149 next;
150 }
151
152 # one form of this is still ok (workspace assignments), the other (named workspaces) isn’t
153 if ($statement eq 'workspace') {
154 my ($number, $params) = ($parameters =~ /\s+([0-9]+)\s+(.+)/);
155 if ($params =~ /^output/) {
156 print "$line\n";
157 next;
158 } else {
159 print "# XXX: workspace name will end up in the corresponding bindings.\n";
160 next;
161 }
162 }
163
164 if ($statement eq 'bind' || $statement eq 'bindsym') {
165 convert_command($line);
166 next;
167 }
168
169 print "# XXX: migration script could not handle line: $line\n";
170 }
171
172 #
173 # Converts a command (after bind/bindsym)
174 #
175 sub convert_command {
176 my ($line) = @_;
177
178 my @unchanged_cmds = qw(
179 exec
180 mark
181 kill
182 restart
183 reload
184 exit
185 );
186
187 my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
188
189 # turn 'bind' to 'bindcode'
190 $statement = 'bindcode' if $statement eq 'bind';
191
192 # check if it’s one of the unchanged commands
193 my ($cmd) = ($command =~ /([a-zA-Z_-]+)/);
194 if ($cmd ~~ @unchanged_cmds) {
195 print "$statement $key $command\n";
196 return;
197 }
198
199 # simple replacements
200 my @replace = (
201 qr/^s/ => 'layout stacking',
202 qr/^d/ => 'layout toggle split',
203 qr/^T/ => 'layout tabbed',
204 qr/^f($|[^go])/ => 'fullscreen',
205 qr/^fg/ => 'fullscreen global',
206 qr/^t/ => 'floating toggle',
207 qr/^h/ => 'focus left',
208 qr/^j($|[^u])/ => 'focus down',
209 qr/^k/ => 'focus up',
210 qr/^l/ => 'focus right',
211 qr/^mh/ => 'move left',
212 qr/^mj/ => 'move down',
213 qr/^mk/ => 'move up',
214 qr/^ml/ => 'move right',
215 qr/^bn/ => 'border normal',
216 qr/^bp/ => 'border 1pixel',
217 qr/^bb/ => 'border none',
218 qr/^bt/ => 'border toggle',
219 qr/^pw/ => 'workspace prev',
220 qr/^nw/ => 'workspace next',
221 );
222
223 my $replaced = 0;
224 for (my $c = 0; $c < @replace; $c += 2) {
225 if ($command =~ $replace[$c]) {
226 $command = $replace[$c+1];
227 $replaced = 1;
228 last;
229 }
230 }
231
232 # goto command is now obsolete due to criteria + focus command
233 if ($command =~ /^goto/) {
234 my ($mark) = ($command =~ /^goto\s+(.*)/);
235 print qq|$statement $key [con_mark="$mark"] focus\n|;
236 return;
237 }
238
239 # the jump command is also obsolete due to criteria + focus
240 if ($command =~ /^jump/) {
241 my ($params) = ($command =~ /^jump\s+(.*)/);
242 if ($params =~ /^"/) {
243 # jump ["]window class[/window title]["]
244 ($params) = ($params =~ /^"([^"]+)"/);
245
246 # check if a window title was specified
247 if ($params =~ m,/,) {
248 my ($class, $title) = ($params =~ m,([^/]+)/(.+),);
249 print qq|$statement $key [class="$class" title="$title"] focus\n|;
250 } else {
251 print qq|$statement $key [class="$params"] focus\n|;
252 }
253 return;
254 } else {
255 # jump <workspace> [ column row ]
256 print "# XXX: jump workspace is obsolete in 4.x: $line\n";
257 return;
258 }
259 }
260
261 if (!$replaced && $command =~ /^focus/) {
262 my ($what) = ($command =~ /^focus\s+(.*)/);
263 $what =~ s/\s//g;
264 if ($what eq 'ft') {
265 $what = 'mode_toggle';
266 } elsif ($what eq 'floating' || $what eq 'tiling') {
267 # those are unchanged
268 } else {
269 print "# XXX: focus <number> is obsolete in 4.x: $line\n";
270 return;
271 }
272 print qq|$statement $key focus $what\n|;
273 return;
274 }
275
276 if ($command =~ /^mode/) {
277 my ($parameters) = ($command =~ /^mode\s+(.*)/);
278 print qq|$statement $key mode "$parameters"\n|;
279 return;
280 }
281
282 # the parameters of the resize command have changed
283 if ($command =~ /^resize/) {
284 # OLD: resize <left|right|top|bottom> [+|-]<pixels>\n")
285 # NEW: resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt]
286 my ($direction, $sign, $px) = ($command =~ /^resize\s+(left|right|top|bottom)\s+([+-])([0-9]+)/);
287 my $cmd = 'resize ';
288 $cmd .= ($sign eq '+' ? 'grow' : 'shrink') . ' ';
289 if ($direction eq 'top') {
290 $direction = 'up';
291 } elsif ($direction eq 'bottom') {
292 $direction = 'down';
293 }
294 $cmd .= "$direction ";
295 $cmd .= "$px px";
296 print qq|$statement $key $cmd\n|;
297 return;
298 }
299
300 # switch workspace
301 if ($command =~ /^[0-9]+/) {
302 my ($number) = ($command =~ /^([0-9]+)/);
303 if (exists $workspace_names{$number}) {
304 print qq|$statement $key workspace $workspace_names{$number}\n|;
305 return;
306 } else {
307 print qq|$statement $key workspace $number\n|;
308 return;
309 }
310 }
311
312 # move to workspace
313 if ($command =~ /^m[0-9]+/) {
314 my ($number) = ($command =~ /^m([0-9]+)/);
315 if (exists $workspace_names{$number}) {
316 print qq|$statement $key move container to workspace $workspace_names{$number}\n|;
317 return;
318 } else {
319 print qq|$statement $key move container to workspace $number\n|;
320 return;
321 }
322 }
323
324 # With Container-commands are now obsolete due to different abstraction
325 if ($command =~ /^wc/) {
326 $command =~ s/^wc//g;
327 my $wc_replaced = 0;
328 for (my $c = 0; $c < @replace; $c += 2) {
329 if ($command =~ $replace[$c]) {
330 $command = $replace[$c+1];
331 $wc_replaced = 1;
332 last;
333 }
334 }
335 if (!$wc_replaced) {
336 print "# XXX: migration script could not handle command: $line\n";
337 } else {
338 # NOTE: This is not 100% accurate, as it only works for one level
339 # of nested containers. As this is a common use case, we use 'focus
340 # parent; $command' nevertheless. For advanced use cases, the user
341 # has to modify their config.
342 print "$statement $key focus parent; $command\n";
343 }
344 return;
345 }
346
347 if ($replaced) {
348 print "$statement $key $command\n";
349 } else {
350 print "# XXX: migration script could not handle command: $line\n";
351 }
352 }
353
354
355 # add an i3bar invocation automatically if no 'workspace_bar no' was found
356 if ($workspace_bar) {
357 print "\n";
358 print "# XXX: Automatically added a bar configuration\n";
359 print "bar {\n";
360 print " status_command i3status\n";
361 print "}\n";
362 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * i3-msg/main.c: Utility which sends messages to a running i3-instance using
7 * IPC via UNIX domain sockets.
8 *
9 * This (in combination with libi3/ipc_send_message.c and
10 * libi3/ipc_recv_message.c) serves as an example for how to send your own
11 * messages to i3.
12 *
13 * Additionally, it’s even useful sometimes :-).
14 *
15 */
16 #include "libi3.h"
17
18 #include <stdio.h>
19 #include <stdbool.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <err.h>
28 #include <stdint.h>
29 #include <getopt.h>
30 #include <limits.h>
31
32 #include <yajl/yajl_parse.h>
33 #include <yajl/yajl_version.h>
34
35 #include <xcb/xcb.h>
36 #include <xcb/xcb_aux.h>
37
38 #include <i3/ipc.h>
39
40 /*
41 * Having verboselog() and errorlog() is necessary when using libi3.
42 *
43 */
44 void verboselog(char *fmt, ...) {
45 va_list args;
46
47 va_start(args, fmt);
48 vfprintf(stdout, fmt, args);
49 va_end(args);
50 }
51
52 void errorlog(char *fmt, ...) {
53 va_list args;
54
55 va_start(args, fmt);
56 vfprintf(stderr, fmt, args);
57 va_end(args);
58 }
59
60 static char *last_key = NULL;
61
62 typedef struct reply_t {
63 bool success;
64 char *error;
65 char *input;
66 char *errorposition;
67 } reply_t;
68
69 static reply_t last_reply;
70
71 static int reply_boolean_cb(void *params, int val) {
72 if (strcmp(last_key, "success") == 0)
73 last_reply.success = val;
74 return 1;
75 }
76
77 static int reply_string_cb(void *params, const unsigned char *val, size_t len) {
78 char *str = scalloc(len + 1, 1);
79 strncpy(str, (const char *)val, len);
80 if (strcmp(last_key, "error") == 0)
81 last_reply.error = str;
82 else if (strcmp(last_key, "input") == 0)
83 last_reply.input = str;
84 else if (strcmp(last_key, "errorposition") == 0)
85 last_reply.errorposition = str;
86 else
87 free(str);
88 return 1;
89 }
90
91 static int reply_start_map_cb(void *params) {
92 return 1;
93 }
94
95 static int reply_end_map_cb(void *params) {
96 if (!last_reply.success) {
97 if (last_reply.input) {
98 fprintf(stderr, "ERROR: Your command: %s\n", last_reply.input);
99 fprintf(stderr, "ERROR: %s\n", last_reply.errorposition);
100 }
101 fprintf(stderr, "ERROR: %s\n", last_reply.error);
102 }
103 return 1;
104 }
105
106 static int reply_map_key_cb(void *params, const unsigned char *keyVal, size_t keyLen) {
107 free(last_key);
108 last_key = scalloc(keyLen + 1, 1);
109 strncpy(last_key, (const char *)keyVal, keyLen);
110 return 1;
111 }
112
113 static yajl_callbacks reply_callbacks = {
114 .yajl_boolean = reply_boolean_cb,
115 .yajl_string = reply_string_cb,
116 .yajl_start_map = reply_start_map_cb,
117 .yajl_map_key = reply_map_key_cb,
118 .yajl_end_map = reply_end_map_cb,
119 };
120
121 /*******************************************************************************
122 * Config reply callbacks
123 *******************************************************************************/
124
125 static char *config_last_key = NULL;
126
127 static int config_string_cb(void *params, const unsigned char *val, size_t len) {
128 char *str = scalloc(len + 1, 1);
129 strncpy(str, (const char *)val, len);
130 if (strcmp(config_last_key, "config") == 0) {
131 fprintf(stdout, "%s", str);
132 }
133 free(str);
134 return 1;
135 }
136
137 static int config_start_map_cb(void *params) {
138 return 1;
139 }
140
141 static int config_end_map_cb(void *params) {
142 return 1;
143 }
144
145 static int config_map_key_cb(void *params, const unsigned char *keyVal, size_t keyLen) {
146 config_last_key = scalloc(keyLen + 1, 1);
147 strncpy(config_last_key, (const char *)keyVal, keyLen);
148 return 1;
149 }
150
151 static yajl_callbacks config_callbacks = {
152 .yajl_string = config_string_cb,
153 .yajl_start_map = config_start_map_cb,
154 .yajl_map_key = config_map_key_cb,
155 .yajl_end_map = config_end_map_cb,
156 };
157
158 int main(int argc, char *argv[]) {
159 #if defined(__OpenBSD__)
160 if (pledge("stdio rpath unix", NULL) == -1)
161 err(EXIT_FAILURE, "pledge");
162 #endif
163 char *socket_path = NULL;
164 int o, option_index = 0;
165 uint32_t message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
166 char *payload = NULL;
167 bool quiet = false;
168 bool monitor = false;
169
170 static struct option long_options[] = {
171 {"socket", required_argument, 0, 's'},
172 {"type", required_argument, 0, 't'},
173 {"version", no_argument, 0, 'v'},
174 {"quiet", no_argument, 0, 'q'},
175 {"monitor", no_argument, 0, 'm'},
176 {"help", no_argument, 0, 'h'},
177 {0, 0, 0, 0}};
178
179 char *options_string = "s:t:vhqm";
180
181 while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
182 if (o == 's') {
183 free(socket_path);
184 socket_path = sstrdup(optarg);
185 } else if (o == 't') {
186 if (strcasecmp(optarg, "command") == 0) {
187 message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
188 } else if (strcasecmp(optarg, "run_command") == 0) {
189 message_type = I3_IPC_MESSAGE_TYPE_RUN_COMMAND;
190 } else if (strcasecmp(optarg, "get_workspaces") == 0) {
191 message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
192 } else if (strcasecmp(optarg, "get_outputs") == 0) {
193 message_type = I3_IPC_MESSAGE_TYPE_GET_OUTPUTS;
194 } else if (strcasecmp(optarg, "get_tree") == 0) {
195 message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
196 } else if (strcasecmp(optarg, "get_marks") == 0) {
197 message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
198 } else if (strcasecmp(optarg, "get_bar_config") == 0) {
199 message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
200 } else if (strcasecmp(optarg, "get_binding_modes") == 0) {
201 message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES;
202 } else if (strcasecmp(optarg, "get_version") == 0) {
203 message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
204 } else if (strcasecmp(optarg, "get_config") == 0) {
205 message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG;
206 } else if (strcasecmp(optarg, "send_tick") == 0) {
207 message_type = I3_IPC_MESSAGE_TYPE_SEND_TICK;
208 } else if (strcasecmp(optarg, "subscribe") == 0) {
209 message_type = I3_IPC_MESSAGE_TYPE_SUBSCRIBE;
210 } else {
211 printf("Unknown message type\n");
212 printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick, subscribe\n");
213 exit(EXIT_FAILURE);
214 }
215 } else if (o == 'q') {
216 quiet = true;
217 } else if (o == 'm') {
218 monitor = true;
219 } else if (o == 'v') {
220 printf("i3-msg " I3_VERSION "\n");
221 return 0;
222 } else if (o == 'h') {
223 printf("i3-msg " I3_VERSION "\n");
224 printf("i3-msg [-s <socket>] [-t <type>] [-m] <message>\n");
225 return 0;
226 } else if (o == '?') {
227 exit(EXIT_FAILURE);
228 }
229 }
230
231 if (monitor && message_type != I3_IPC_MESSAGE_TYPE_SUBSCRIBE) {
232 fprintf(stderr, "The monitor option -m is used with -t SUBSCRIBE exclusively.\n");
233 exit(EXIT_FAILURE);
234 }
235
236 /* Use all arguments, separated by whitespace, as payload.
237 * This way, you don’t have to do i3-msg 'mark foo', you can use
238 * i3-msg mark foo */
239 while (optind < argc) {
240 if (!payload) {
241 payload = sstrdup(argv[optind]);
242 } else {
243 char *both;
244 sasprintf(&both, "%s %s", payload, argv[optind]);
245 free(payload);
246 payload = both;
247 }
248 optind++;
249 }
250
251 if (!payload)
252 payload = sstrdup("");
253
254 int sockfd = ipc_connect(socket_path);
255 if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1)
256 err(EXIT_FAILURE, "IPC: write()");
257 free(payload);
258
259 uint32_t reply_length;
260 uint32_t reply_type;
261 uint8_t *reply;
262 int ret;
263 if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
264 if (ret == -1)
265 err(EXIT_FAILURE, "IPC: read()");
266 exit(1);
267 }
268 if (reply_type != message_type)
269 errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
270 /* For the reply of commands, have a look if that command was successful.
271 * If not, nicely format the error message. */
272 if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
273 yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
274 yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
275 yajl_free(handle);
276
277 switch (state) {
278 case yajl_status_ok:
279 break;
280 case yajl_status_client_canceled:
281 case yajl_status_error:
282 errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
283 }
284
285 if (!quiet) {
286 printf("%.*s\n", reply_length, reply);
287 }
288 } else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) {
289 yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
290 yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
291 yajl_free(handle);
292
293 switch (state) {
294 case yajl_status_ok:
295 break;
296 case yajl_status_client_canceled:
297 case yajl_status_error:
298 errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
299 }
300 } else if (reply_type == I3_IPC_REPLY_TYPE_SUBSCRIBE) {
301 do {
302 free(reply);
303 if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
304 if (ret == -1)
305 err(EXIT_FAILURE, "IPC: read()");
306 exit(1);
307 }
308
309 if (!(reply_type & I3_IPC_EVENT_MASK)) {
310 errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected an event", reply_type);
311 }
312
313 if (!quiet) {
314 fprintf(stdout, "%.*s\n", reply_length, reply);
315 fflush(stdout);
316 }
317 } while (monitor);
318 } else {
319 if (!quiet) {
320 printf("%.*s\n", reply_length, reply);
321 }
322 }
323
324 free(reply);
325
326 close(sockfd);
327
328 return 0;
329 }
0 xmacro(_NET_WM_WINDOW_TYPE)
1 xmacro(_NET_WM_WINDOW_TYPE_DOCK)
2 xmacro(_NET_WM_STRUT_PARTIAL)
3 xmacro(I3_SOCKET_PATH)
4 xmacro(ATOM)
5 xmacro(CARDINAL)
0 #pragma once
1
2 #include <config.h>
3
4 #include <err.h>
5
6 #define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
7 #define FREE(pointer) \
8 do { \
9 free(pointer); \
10 pointer = NULL; \
11 } while (0)
12
13 #define xmacro(atom) xcb_atom_t A_##atom;
14 #include "atoms.xmacro"
15 #undef xmacro
16
17 extern xcb_window_t root;
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * i3-nagbar is a utility which displays a nag message, for example in the case
7 * when the user has an error in their configuration file.
8 *
9 */
10 #include "libi3.h"
11
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 #include <stdlib.h>
17 #include <stdbool.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <err.h>
22 #include <stdint.h>
23 #include <getopt.h>
24 #include <limits.h>
25 #include <fcntl.h>
26 #include <paths.h>
27
28 #include <xcb/xcb.h>
29 #include <xcb/xcb_aux.h>
30 #include <xcb/xcb_event.h>
31 #include <xcb/randr.h>
32 #include <xcb/xcb_cursor.h>
33
34 #define SN_API_NOT_YET_FROZEN 1
35 #include <libsn/sn-launchee.h>
36
37 #include "i3-nagbar.h"
38
39 /** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a
40 * constant for that. */
41 #define XCB_CURSOR_LEFT_PTR 68
42
43 #define MSG_PADDING logical_px(8)
44 #define BTN_PADDING logical_px(3)
45 #define BTN_BORDER logical_px(3)
46 #define BTN_GAP logical_px(20)
47 #define CLOSE_BTN_GAP logical_px(15)
48 #define BAR_BORDER logical_px(2)
49
50 static char *argv0 = NULL;
51
52 typedef struct {
53 i3String *label;
54 char *action;
55 int16_t x;
56 uint16_t width;
57 bool terminal;
58 } button_t;
59
60 static xcb_window_t win;
61 static surface_t bar;
62
63 static i3Font font;
64 static i3String *prompt;
65
66 static button_t btn_close;
67 static button_t *buttons;
68 static int buttoncnt;
69
70 /* Result of get_colorpixel() for the various colors. */
71 static color_t color_background; /* background of the bar */
72 static color_t color_button_background; /* background for buttons */
73 static color_t color_border; /* color of the button border */
74 static color_t color_border_bottom; /* color of the bottom border */
75 static color_t color_text; /* color of the text */
76
77 xcb_window_t root;
78 xcb_connection_t *conn;
79 xcb_screen_t *root_screen;
80
81 /*
82 * Having verboselog(), errorlog() and debuglog() is necessary when using libi3.
83 *
84 */
85 void verboselog(char *fmt, ...) {
86 va_list args;
87
88 va_start(args, fmt);
89 vfprintf(stdout, fmt, args);
90 va_end(args);
91 }
92
93 void errorlog(char *fmt, ...) {
94 va_list args;
95
96 va_start(args, fmt);
97 vfprintf(stderr, fmt, args);
98 va_end(args);
99 }
100
101 void debuglog(char *fmt, ...) {
102 }
103
104 /*
105 * Starts the given application by passing it through a shell. We use double
106 * fork to avoid zombie processes. As the started application’s parent exits
107 * (immediately), the application is reparented to init (process-id 1), which
108 * correctly handles children, so we don’t have to do it :-).
109 *
110 * The shell is determined by looking for the SHELL environment variable. If it
111 * does not exist, /bin/sh is used.
112 *
113 */
114 static void start_application(const char *command) {
115 printf("executing: %s\n", command);
116 if (fork() == 0) {
117 /* Child process */
118 setsid();
119 if (fork() == 0) {
120 /* This is the child */
121 execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL);
122 /* not reached */
123 }
124 exit(0);
125 }
126 wait(0);
127 }
128
129 static button_t *get_button_at(int16_t x, int16_t y) {
130 for (int c = 0; c < buttoncnt; c++)
131 if (x >= (buttons[c].x) && x <= (buttons[c].x + buttons[c].width))
132 return &buttons[c];
133
134 return NULL;
135 }
136
137 static void handle_button_press(xcb_connection_t *conn, xcb_button_press_event_t *event) {
138 printf("button pressed on x = %d, y = %d\n",
139 event->event_x, event->event_y);
140 /* TODO: set a flag for the button, re-render */
141 }
142
143 /*
144 * Called when the user releases the mouse button. Checks whether the
145 * coordinates are over a button and executes the appropriate action.
146 *
147 */
148 static void handle_button_release(xcb_connection_t *conn, xcb_button_release_event_t *event) {
149 printf("button released on x = %d, y = %d\n",
150 event->event_x, event->event_y);
151 /* If the user hits the close button, we exit(0) */
152 if (event->event_x >= btn_close.x && event->event_x < btn_close.x + btn_close.width)
153 exit(0);
154 button_t *button = get_button_at(event->event_x, event->event_y);
155 if (!button)
156 return;
157
158 /* We need to create a custom script containing our actual command
159 * since not every terminal emulator which is contained in
160 * i3-sensible-terminal supports -e with multiple arguments (and not
161 * all of them support -e with one quoted argument either).
162 *
163 * NB: The paths need to be unique, that is, don’t assume users close
164 * their nagbars at any point in time (and they still need to work).
165 * */
166 char *script_path = get_process_filename("nagbar-cmd");
167
168 int fd = open(script_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
169 if (fd == -1) {
170 warn("Could not create temporary script to store the nagbar command");
171 return;
172 }
173 FILE *script = fdopen(fd, "w");
174 if (script == NULL) {
175 warn("Could not fdopen() temporary script to store the nagbar command");
176 return;
177 }
178 fprintf(script, "#!/bin/sh\nrm %s\n%s", script_path, button->action);
179 /* Also closes fd */
180 fclose(script);
181
182 char *link_path;
183 char *exe_path = get_exe_path(argv0);
184 sasprintf(&link_path, "%s.nagbar_cmd", script_path);
185 if (symlink(exe_path, link_path) == -1) {
186 err(EXIT_FAILURE, "Failed to symlink %s to %s", link_path, exe_path);
187 }
188
189 char *terminal_cmd;
190 if (button->terminal) {
191 sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
192 } else {
193 terminal_cmd = sstrdup(link_path);
194 }
195 printf("argv0 = %s\n", argv0);
196 printf("terminal_cmd = %s\n", terminal_cmd);
197
198 start_application(terminal_cmd);
199
200 free(link_path);
201 free(terminal_cmd);
202 free(script_path);
203 free(exe_path);
204
205 /* TODO: unset flag, re-render */
206 }
207
208 /*
209 * Draws a button and returns its width
210 *
211 */
212 static int button_draw(button_t *button, int position) {
213 int text_width = predict_text_width(button->label);
214 button->width = text_width + 2 * BTN_PADDING + 2 * BTN_BORDER;
215 button->x = position - button->width;
216
217 /* draw border */
218 draw_util_rectangle(&bar, color_border,
219 position - button->width,
220 MSG_PADDING - BTN_PADDING - BTN_BORDER,
221 button->width,
222 font.height + 2 * BTN_PADDING + 2 * BTN_BORDER);
223 /* draw background */
224 draw_util_rectangle(&bar, color_button_background,
225 position - button->width + BTN_BORDER,
226 MSG_PADDING - BTN_PADDING,
227 text_width + 2 * BTN_PADDING,
228 font.height + 2 * BTN_PADDING);
229 /* draw label */
230 draw_util_text(button->label, &bar, color_text, color_button_background,
231 position - button->width + BTN_BORDER + BTN_PADDING,
232 MSG_PADDING,
233 200);
234 return button->width;
235 }
236
237 /*
238 * Handles expose events (redraws of the window) and rendering in general. Will
239 * be called from the code with event == NULL or from X with event != NULL.
240 *
241 */
242 static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
243 /* draw background */
244 draw_util_clear_surface(&bar, color_background);
245 /* draw message */
246 draw_util_text(prompt, &bar, color_text, color_background,
247 MSG_PADDING, MSG_PADDING,
248 bar.width - 2 * MSG_PADDING);
249
250 int position = bar.width - (MSG_PADDING - BTN_BORDER - BTN_PADDING);
251
252 /* render close button */
253 position -= button_draw(&btn_close, position);
254 position -= CLOSE_BTN_GAP;
255
256 /* render custom buttons */
257 for (int i = 0; i < buttoncnt; i++) {
258 position -= BTN_GAP;
259 position -= button_draw(&buttons[i], position);
260 }
261
262 /* border line at the bottom */
263 draw_util_rectangle(&bar, color_border_bottom, 0, bar.height - BAR_BORDER, bar.width, BAR_BORDER);
264
265 xcb_flush(conn);
266 return 1;
267 }
268
269 /**
270 * Return the position and size the i3-nagbar window should use.
271 * This will be the primary output or a fallback if it cannot be determined.
272 */
273 static xcb_rectangle_t get_window_position(void) {
274 /* Default values if we cannot determine the primary output or its CRTC info. */
275 xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + 2 * MSG_PADDING + BAR_BORDER};
276
277 xcb_randr_get_screen_resources_current_cookie_t rcookie = xcb_randr_get_screen_resources_current(conn, root);
278 xcb_randr_get_output_primary_cookie_t pcookie = xcb_randr_get_output_primary(conn, root);
279
280 xcb_randr_get_output_primary_reply_t *primary = NULL;
281 xcb_randr_get_screen_resources_current_reply_t *res = NULL;
282
283 if ((primary = xcb_randr_get_output_primary_reply(conn, pcookie, NULL)) == NULL) {
284 DLOG("Could not determine the primary output.\n");
285 goto free_resources;
286 }
287
288 if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
289 goto free_resources;
290 }
291
292 xcb_randr_get_output_info_reply_t *output =
293 xcb_randr_get_output_info_reply(conn,
294 xcb_randr_get_output_info(conn, primary->output, res->config_timestamp),
295 NULL);
296 if (output == NULL || output->crtc == XCB_NONE)
297 goto free_resources;
298
299 xcb_randr_get_crtc_info_reply_t *crtc =
300 xcb_randr_get_crtc_info_reply(conn,
301 xcb_randr_get_crtc_info(conn, output->crtc, res->config_timestamp),
302 NULL);
303 if (crtc == NULL)
304 goto free_resources;
305
306 DLOG("Found primary output on position x = %i / y = %i / w = %i / h = %i.\n",
307 crtc->x, crtc->y, crtc->width, crtc->height);
308 if (crtc->width == 0 || crtc->height == 0) {
309 DLOG("Primary output is not active, ignoring it.\n");
310 goto free_resources;
311 }
312
313 result.x = crtc->x;
314 result.y = crtc->y;
315 goto free_resources;
316
317 free_resources:
318 FREE(res);
319 FREE(primary);
320 return result;
321 }
322
323 int main(int argc, char *argv[]) {
324 /* The following lines are a terribly horrible kludge. Because terminal
325 * emulators have different ways of interpreting the -e command line
326 * argument (some need -e "less /etc/fstab", others need -e less
327 * /etc/fstab), we need to write commands to a script and then just run
328 * that script. However, since on some machines, $XDG_RUNTIME_DIR and
329 * $TMPDIR are mounted with noexec, we cannot directly execute the script
330 * either.
331 *
332 * Initially, we tried to pass the command via the environment variable
333 * _I3_NAGBAR_CMD. But turns out that some terminal emulators such as
334 * xfce4-terminal run all windows from a single master process and only
335 * pass on the command (not the environment) to that master process.
336 *
337 * Therefore, we symlink i3-nagbar (which MUST reside on an executable
338 * filesystem) with a special name and run that symlink. When i3-nagbar
339 * recognizes it’s started as a binary ending in .nagbar_cmd, it strips off
340 * the .nagbar_cmd suffix and runs /bin/sh on argv[0]. That way, we can run
341 * a shell script on a noexec filesystem.
342 *
343 * From a security point of view, i3-nagbar is just an alias to /bin/sh in
344 * certain circumstances. This should not open any new security issues, I
345 * hope. */
346 char *cmd = NULL;
347 const size_t argv0_len = strlen(argv[0]);
348 if (argv0_len > strlen(".nagbar_cmd") &&
349 strcmp(argv[0] + argv0_len - strlen(".nagbar_cmd"), ".nagbar_cmd") == 0) {
350 unlink(argv[0]);
351 cmd = sstrdup(argv[0]);
352 *(cmd + argv0_len - strlen(".nagbar_cmd")) = '\0';
353 execl("/bin/sh", "/bin/sh", cmd, NULL);
354 err(EXIT_FAILURE, "execv(/bin/sh, /bin/sh, %s)", cmd);
355 }
356
357 argv0 = argv[0];
358
359 char *pattern = sstrdup("pango:monospace 8");
360 int o, option_index = 0;
361 enum { TYPE_ERROR = 0,
362 TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
363
364 static struct option long_options[] = {
365 {"version", no_argument, 0, 'v'},
366 {"font", required_argument, 0, 'f'},
367 {"button", required_argument, 0, 'b'},
368 {"button-no-terminal", required_argument, 0, 'B'},
369 {"help", no_argument, 0, 'h'},
370 {"message", required_argument, 0, 'm'},
371 {"type", required_argument, 0, 't'},
372 {0, 0, 0, 0}};
373
374 char *options_string = "b:B:f:m:t:vh";
375
376 prompt = i3string_from_utf8("Please do not run this program.");
377
378 while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
379 switch (o) {
380 case 'v':
381 printf("i3-nagbar " I3_VERSION "\n");
382 return 0;
383 case 'f':
384 FREE(pattern);
385 pattern = sstrdup(optarg);
386 break;
387 case 'm':
388 i3string_free(prompt);
389 prompt = i3string_from_utf8(optarg);
390 break;
391 case 't':
392 bar_type = (strcasecmp(optarg, "warning") == 0 ? TYPE_WARNING : TYPE_ERROR);
393 break;
394 case 'h':
395 printf("i3-nagbar " I3_VERSION "\n");
396 printf("i3-nagbar [-m <message>] [-b <button> <action>] [-B <button> <action>] [-t warning|error] [-f <font>] [-v]\n");
397 return 0;
398 case 'b':
399 case 'B':
400 buttons = srealloc(buttons, sizeof(button_t) * (buttoncnt + 1));
401 buttons[buttoncnt].label = i3string_from_utf8(optarg);
402 buttons[buttoncnt].action = argv[optind];
403 buttons[buttoncnt].terminal = (o == 'b');
404 printf("button with label *%s* and action *%s*\n",
405 i3string_as_utf8(buttons[buttoncnt].label),
406 buttons[buttoncnt].action);
407 buttoncnt++;
408 printf("now %d buttons\n", buttoncnt);
409 if (optind < argc)
410 optind++;
411 break;
412 }
413 }
414
415 btn_close.label = i3string_from_utf8("X");
416
417 int screens;
418 if ((conn = xcb_connect(NULL, &screens)) == NULL ||
419 xcb_connection_has_error(conn))
420 die("Cannot open display\n");
421
422 /* Place requests for the atoms we need as soon as possible */
423 #define xmacro(atom) \
424 xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
425 #include "atoms.xmacro"
426 #undef xmacro
427
428 /* Init startup notification. */
429 SnDisplay *sndisplay = sn_xcb_display_new(conn, NULL, NULL);
430 SnLauncheeContext *sncontext = sn_launchee_context_new_from_environment(sndisplay, screens);
431 sn_display_unref(sndisplay);
432
433 root_screen = xcb_aux_get_screen(conn, screens);
434 root = root_screen->root;
435
436 if (bar_type == TYPE_ERROR) {
437 /* Red theme for error messages */
438 color_button_background = draw_util_hex_to_color("#680a0a");
439 color_background = draw_util_hex_to_color("#900000");
440 color_text = draw_util_hex_to_color("#ffffff");
441 color_border = draw_util_hex_to_color("#d92424");
442 color_border_bottom = draw_util_hex_to_color("#470909");
443 } else {
444 /* Yellowish theme for warnings */
445 color_button_background = draw_util_hex_to_color("#ffc100");
446 color_background = draw_util_hex_to_color("#ffa8000");
447 color_text = draw_util_hex_to_color("#000000");
448 color_border = draw_util_hex_to_color("#ab7100");
449 color_border_bottom = draw_util_hex_to_color("#ab7100");
450 }
451
452 init_dpi();
453 font = load_font(pattern, true);
454 set_font(&font);
455
456 #if defined(__OpenBSD__)
457 if (pledge("stdio rpath wpath cpath getpw proc exec", NULL) == -1)
458 err(EXIT_FAILURE, "pledge");
459 #endif
460
461 xcb_rectangle_t win_pos = get_window_position();
462
463 xcb_cursor_t cursor;
464 xcb_cursor_context_t *cursor_ctx;
465 if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) == 0) {
466 cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr");
467 xcb_cursor_context_free(cursor_ctx);
468 } else {
469 cursor = xcb_generate_id(conn);
470 i3Font cursor_font = load_font("cursor", false);
471 xcb_create_glyph_cursor(
472 conn,
473 cursor,
474 cursor_font.specific.xcb.id,
475 cursor_font.specific.xcb.id,
476 XCB_CURSOR_LEFT_PTR,
477 XCB_CURSOR_LEFT_PTR + 1,
478 0, 0, 0,
479 65535, 65535, 65535);
480 }
481
482 /* Open an input window */
483 win = xcb_generate_id(conn);
484
485 xcb_create_window(
486 conn,
487 XCB_COPY_FROM_PARENT,
488 win, /* the window id */
489 root, /* parent == root */
490 win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
491 0, /* x11 border = 0, we draw our own */
492 XCB_WINDOW_CLASS_INPUT_OUTPUT,
493 XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
494 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_CURSOR,
495 (uint32_t[]){
496 0, /* back pixel: black */
497 XCB_EVENT_MASK_EXPOSURE |
498 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
499 XCB_EVENT_MASK_BUTTON_PRESS |
500 XCB_EVENT_MASK_BUTTON_RELEASE,
501 cursor});
502 if (sncontext) {
503 sn_launchee_context_setup_window(sncontext, win);
504 }
505
506 /* Map the window (make it visible) */
507 xcb_map_window(conn, win);
508
509 /* Setup NetWM atoms */
510 #define xmacro(name) \
511 do { \
512 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
513 if (!reply) \
514 die("Could not get atom " #name "\n"); \
515 \
516 A_##name = reply->atom; \
517 free(reply); \
518 } while (0);
519 #include "atoms.xmacro"
520 #undef xmacro
521
522 /* Set dock mode */
523 xcb_change_property(conn,
524 XCB_PROP_MODE_REPLACE,
525 win,
526 A__NET_WM_WINDOW_TYPE,
527 A_ATOM,
528 32,
529 1,
530 (unsigned char *)&A__NET_WM_WINDOW_TYPE_DOCK);
531
532 /* Reserve some space at the top of the screen */
533 struct {
534 uint32_t left;
535 uint32_t right;
536 uint32_t top;
537 uint32_t bottom;
538 uint32_t left_start_y;
539 uint32_t left_end_y;
540 uint32_t right_start_y;
541 uint32_t right_end_y;
542 uint32_t top_start_x;
543 uint32_t top_end_x;
544 uint32_t bottom_start_x;
545 uint32_t bottom_end_x;
546 } __attribute__((__packed__)) strut_partial;
547 memset(&strut_partial, 0, sizeof(strut_partial));
548
549 strut_partial.top = font.height + logical_px(6);
550 strut_partial.top_start_x = 0;
551 strut_partial.top_end_x = 800;
552
553 xcb_change_property(conn,
554 XCB_PROP_MODE_REPLACE,
555 win,
556 A__NET_WM_STRUT_PARTIAL,
557 A_CARDINAL,
558 32,
559 12,
560 &strut_partial);
561
562 /* Initialize the drawable bar */
563 draw_util_surface_init(conn, &bar, win, get_visualtype(root_screen), win_pos.width, win_pos.height);
564
565 /* Startup complete. */
566 if (sncontext) {
567 sn_launchee_context_complete(sncontext);
568 sn_launchee_context_unref(sncontext);
569 }
570
571 /* Grab the keyboard to get all input */
572 xcb_flush(conn);
573
574 xcb_generic_event_t *event;
575 while ((event = xcb_wait_for_event(conn)) != NULL) {
576 if (event->response_type == 0) {
577 fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
578 continue;
579 }
580
581 /* Strip off the highest bit (set if the event is generated) */
582 int type = (event->response_type & 0x7F);
583
584 switch (type) {
585 case XCB_EXPOSE:
586 if (((xcb_expose_event_t *)event)->count == 0) {
587 handle_expose(conn, (xcb_expose_event_t *)event);
588 }
589
590 break;
591
592 case XCB_BUTTON_PRESS:
593 handle_button_press(conn, (xcb_button_press_event_t *)event);
594 break;
595
596 case XCB_BUTTON_RELEASE:
597 handle_button_release(conn, (xcb_button_release_event_t *)event);
598 break;
599
600 case XCB_CONFIGURE_NOTIFY: {
601 xcb_configure_notify_event_t *configure_notify = (xcb_configure_notify_event_t *)event;
602 if (configure_notify->width > 0 && configure_notify->height > 0) {
603 draw_util_surface_set_size(&bar, configure_notify->width, configure_notify->height);
604 }
605 break;
606 }
607 }
608
609 free(event);
610 }
611
612 FREE(pattern);
613 draw_util_surface_free(conn, &bar);
614
615 return 0;
616 }
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # © 2013 Michael Stapelberg
4 #
5 # Requires perl ≥ v5.10, AnyEvent::I3 and JSON::XS
6
7 use strict;
8 use warnings qw(FATAL utf8);
9 use Data::Dumper;
10 use IPC::Open2;
11 use POSIX qw(locale_h);
12 use File::Find;
13 use File::Basename qw(basename);
14 use File::Temp qw(tempfile);
15 use List::Util qw(first);
16 use Getopt::Long;
17 use Pod::Usage;
18 use AnyEvent::I3;
19 use JSON::XS;
20 use List::Util qw(first);
21 use Encode qw(decode);
22 use v5.10;
23 use utf8;
24 use open ':encoding(UTF-8)';
25
26 binmode STDOUT, ':utf8';
27 binmode STDERR, ':utf8';
28
29 my $workspace;
30 my $output;
31 my $result = GetOptions(
32 'workspace=s' => \$workspace,
33 'output=s' => \$output,
34 'version' => sub {
35 say "i3-save-tree 0.1 © 2013 Michael Stapelberg";
36 exit 0;
37 },
38 'help' => sub {
39 pod2usage(-exitval => 0);
40 });
41
42 die "Could not parse command line options" unless $result;
43
44 if (defined($workspace) && defined($output)) {
45 die "Only one of --workspace or --output can be specified";
46 }
47
48 $workspace = decode('utf-8', $workspace);
49 $output = decode('utf-8', $output);
50
51 my $i3 = i3();
52 if (!$i3->connect->recv) {
53 die "Could not connect to i3";
54 }
55
56 sub get_current_workspace {
57 my $current = first { $_->{focused} } @{$i3->get_workspaces->recv};
58 return $current->{name};
59 }
60
61 if (!defined($workspace) && !defined($output)) {
62 $workspace = get_current_workspace();
63 }
64
65 sub filter_containers {
66 my ($tree, $pred) = @_;
67
68 $_ = $tree;
69 return $tree if $pred->();
70
71 for my $child (@{$tree->{nodes}}, @{$tree->{floating_nodes}}) {
72 my $result = filter_containers($child, $pred);
73 return $result if defined($result);
74 }
75
76 return undef;
77 }
78
79 sub leaf_node {
80 my ($tree) = @_;
81
82 return $tree->{type} eq 'con' &&
83 @{$tree->{nodes}} == 0 &&
84 @{$tree->{floating_nodes}} == 0;
85 }
86
87 my %allowed_keys = map { ($_, 1) } qw(
88 type
89 fullscreen_mode
90 layout
91 border
92 current_border_width
93 floating
94 percent
95 nodes
96 floating_nodes
97 name
98 geometry
99 window_properties
100 marks
101 rect
102 );
103
104 sub strip_containers {
105 my ($tree) = @_;
106
107 # layout is not relevant for a leaf container
108 delete $tree->{layout} if leaf_node($tree);
109
110 # fullscreen_mode conveys no state at all, it can either be 0 or 1 and the
111 # default is _always_ 0, so skip noop entries.
112 delete $tree->{fullscreen_mode} if $tree->{fullscreen_mode} == 0;
113
114 # names for non-leafs are auto-generated and useful only for i3 debugging
115 delete $tree->{name} unless leaf_node($tree);
116
117 delete $tree->{geometry} if zero_rect($tree->{geometry});
118
119 # Retain the rect for floating containers to keep their positions.
120 delete $tree->{rect} unless $tree->{type} eq 'floating_con';
121
122 delete $tree->{current_border_width} if $tree->{current_border_width} == -1;
123
124 for my $key (keys %$tree) {
125 delete $tree->{$key} unless exists($allowed_keys{$key});
126 }
127
128 for my $key (qw(nodes floating_nodes)) {
129 $tree->{$key} = [ map { strip_containers($_) } @{$tree->{$key}} ];
130 }
131
132 return $tree;
133 }
134
135 my $json_xs = JSON::XS->new->pretty(1)->allow_nonref->space_before(0)->canonical(1);
136
137 sub zero_rect {
138 my ($rect) = @_;
139 return $rect->{x} == 0 &&
140 $rect->{y} == 0 &&
141 $rect->{width} == 0 &&
142 $rect->{height} == 0;
143 }
144
145 # Dumps the containers in JSON, but with comments to explain the user what she
146 # needs to fix.
147 sub dump_containers {
148 my ($tree, $ws, $last) = @_;
149
150 $ws //= "";
151
152 say $ws . '{';
153
154 $ws .= (' ' x 4);
155
156 if (!leaf_node($tree)) {
157 my $desc = $tree->{layout} . ' split container';
158 if ($tree->{type} ne 'con') {
159 $desc = $tree->{type};
160 }
161 say "$ws// $desc with " . @{$tree->{nodes}} . " children";
162 }
163
164 # Turn “window_properties” into “swallows” expressions, but only for leaf
165 # nodes. It only makes sense for leaf nodes to swallow anything.
166 if (leaf_node($tree)) {
167 my $swallows = {};
168 for my $property (keys %{$tree->{window_properties}}) {
169 $swallows->{$property} = '^' . quotemeta($tree->{window_properties}->{$property}) . '$'
170 if $property ne 'transient_for';
171 }
172 $tree->{swallows} = [ $swallows ];
173 }
174 delete $tree->{window_properties};
175
176 my @keys = sort keys %$tree;
177 for (0 .. (@keys-1)) {
178 my $key = $keys[$_];
179 # Those are handled recursively, not printed.
180 next if $key eq 'nodes' || $key eq 'floating_nodes';
181
182 # JSON::XS’s encode appends a newline
183 chomp(my $val = $json_xs->encode($tree->{$key}));
184
185 # Fix indentation. Keep in mind we are producing output to be
186 # read/modified by a human.
187 $val =~ s/^/$ws/mg;
188 $val =~ s/^\s+//;
189
190 # Comment out all swallows criteria, they are just suggestions.
191 if ($key eq 'swallows') {
192 $val =~ s,^(\s*)\s{3}",\1// ",gm;
193 }
194
195 # Append a comma unless this is the last value.
196 # Ugly, but necessary so that we can print all values before recursing.
197 my $comma = ($_ == (@keys-1) &&
198 @{$tree->{nodes}} == 0 &&
199 @{$tree->{floating_nodes}} == 0 ? '' : ',');
200 say qq#$ws"$key": $val$comma#;
201 }
202
203 for my $key (qw(nodes floating_nodes)) {
204 my $num = scalar @{$tree->{$key}};
205 next if !$num;
206
207 say qq#$ws"$key": [#;
208 for (0 .. ($num-1)) {
209 dump_containers(
210 $tree->{$key}->[$_],
211 $ws . (' ' x 4),
212 ($_ == ($num-1)));
213 }
214 say qq#$ws]#;
215 }
216
217 $ws =~ s/\s{4}$//;
218
219 say $ws . ($last ? '}' : '},');
220 }
221
222 my $tree = $i3->get_tree->recv;
223
224 my $dump;
225 if (defined($workspace)) {
226 $dump = filter_containers($tree, sub {
227 $_->{type} eq 'workspace' && ($_->{name} eq $workspace || ($workspace =~ /^\d+$/ && $_->{num} eq $workspace))
228 });
229 } else {
230 $dump = filter_containers($tree, sub {
231 $_->{type} eq 'output' && $_->{name} eq $output
232 });
233 # Get the output’s content container (living beneath dockarea containers).
234 $dump = first { $_->{type} eq 'con' } @{$dump->{nodes}};
235 }
236
237 $dump = strip_containers($dump);
238
239 say "// vim:ts=4:sw=4:et";
240 for my $key (qw(nodes floating_nodes)) {
241 for (0 .. (@{$dump->{$key}} - 1)) {
242 dump_containers($dump->{$key}->[$_], undef, 1);
243 # Newlines separate containers so that one can use { and } in vim to
244 # jump out of the current container.
245 say '';
246 }
247 }
248
249 =encoding utf-8
250
251 =head1 NAME
252
253 i3-save-tree - save (parts of) the layout tree for restoring
254
255 =head1 SYNOPSIS
256
257 i3-save-tree [--workspace=name|number] [--output=name]
258
259 =head1 DESCRIPTION
260
261 Dumps a workspace (or an entire output) to stdout. The data is supposed to be
262 edited a bit by a human, then later fed to i3 via the append_layout command.
263
264 The append_layout command will create placeholder windows, arranged in the
265 layout the input file specifies. Each container should have a swallows
266 specification. When a window is mapped (made visible on the screen) that
267 matches the specification, i3 will put it into that place and kill the
268 placeholder.
269
270 If neither argument is specified, the currently focused workspace will be used.
271
272 =head1 OPTIONS
273
274 =over
275
276 =item B<--workspace=name|number>
277
278 Specifies the workspace that should be dumped, e.g. 1. This can either be a
279 name or the number of a workspace.
280
281 =item B<--output=name>
282
283 Specifies the output that should be dumped, e.g. LVDS-1.
284
285 =back
286
287 =head1 VERSION
288
289 Version 0.1
290
291 =head1 AUTHOR
292
293 Michael Stapelberg, C<< <michael at i3wm.org> >>
294
295 =head1 LICENSE AND COPYRIGHT
296
297 Copyright 2013 Michael Stapelberg.
298
299 This program is free software; you can redistribute it and/or modify it
300 under the terms of the BSD license.
301
302 =cut
0 #!/bin/sh
1 #
2 # This code is released in public domain by Han Boetes <han@mijncomputer.nl>
3 #
4 # This script tries to exec an editor by trying some known editors if $EDITOR is
5 # not set.
6 #
7 # Distributions/packagers can enhance this script with a distribution-specific
8 # mechanism to find the preferred editor
9
10 # Hopefully one of these is installed (no flamewars about preference please!):
11 for editor in "$VISUAL" "$EDITOR" nano nvim vim vi emacs pico qe mg jed gedit mcedit gvim; do
12 if command -v "$editor" > /dev/null 2>&1; then
13 exec "$editor" "$@"
14 fi
15 done
0 #!/bin/sh
1 #
2 # This code is released in public domain by Han Boetes <han@mijncomputer.nl>
3
4 # This script tries to exec a pager by trying some known pagers if $PAGER is
5 # not set.
6 #
7 # Distributions/packagers can enhance this script with a
8 # distribution-specific mechanism to find the preferred pager.
9
10 # Hopefully one of these is installed (no flamewars about preference please!):
11 # We don't use 'more' because it will exit if the file is too short.
12 # Worst case scenario we'll open the file in your editor.
13 for pager in "$PAGER" less most w3m pg i3-sensible-editor; do
14 if command -v "$pager" > /dev/null 2>&1; then
15 exec "$pager" "$@"
16 fi
17 done
0 #!/bin/sh
1 #
2 # This code is released in public domain by Han Boetes <han@mijncomputer.nl>
3 #
4 # This script tries to exec a terminal emulator by trying some known terminal
5 # emulators.
6 #
7 # We welcome patches that add distribution-specific mechanisms to find the
8 # preferred terminal emulator. On Debian, there is the x-terminal-emulator
9 # symlink for example.
10 for terminal in "$TERMINAL" x-terminal-emulator urxvt rxvt termit terminator Eterm aterm uxterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper; do
11 if command -v "$terminal" > /dev/null 2>&1; then
12 exec "$terminal" "$@"
13 fi
14 done
15
16 i3-nagbar -m 'i3-sensible-terminal could not find a terminal emulator. Please install one.'
0 Copyright © 2010 Axel Wagner
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12
13 * Neither the name of Axel Wagner nor the
14 names of contributors may be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY Axel Wagner ''AS IS'' AND ANY
18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL Axel Wagner BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * child.c: Getting input for the statusline
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <stdbool.h>
14
15 #define STDIN_CHUNK_SIZE 1024
16
17 typedef struct {
18 pid_t pid;
19
20 /**
21 * The version number is an uint32_t to avoid machines with different sizes of
22 * 'int' to allow different values here. It’s highly unlikely we ever exceed
23 * even an int8_t, but still…
24 */
25 uint32_t version;
26
27 bool stopped;
28 /**
29 * The signal requested by the client to inform it of the hidden state of i3bar
30 */
31 int stop_signal;
32 /**
33 * The signal requested by the client to inform it of the unhidden state of i3bar
34 */
35 int cont_signal;
36
37 /**
38 * Enable click events
39 */
40 bool click_events;
41 bool click_events_init;
42 } i3bar_child;
43
44 /*
45 * Start a child process with the specified command and reroute stdin.
46 * We actually start a $SHELL to execute the command so we don't have to care
47 * about arguments and such
48 *
49 */
50 void start_child(char *command);
51
52 /*
53 * kill()s the child process (if any). Called when exit()ing.
54 *
55 */
56 void kill_child_at_exit(void);
57
58 /*
59 * kill()s the child process (if any) and closes and
60 * free()s the stdin- and SIGCHLD-watchers
61 *
62 */
63 void kill_child(void);
64
65 /*
66 * Sends a SIGSTOP to the child process (if existent)
67 *
68 */
69 void stop_child(void);
70
71 /*
72 * Sends a SIGCONT to the child process (if existent)
73 *
74 */
75 void cont_child(void);
76
77 /*
78 * Whether or not the child want click events
79 *
80 */
81 bool child_want_click_events(void);
82
83 /*
84 * Generates a click event, if enabled.
85 *
86 */
87 void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height, int mods);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 */
7 #pragma once
8
9 #include <config.h>
10
11 #include <stdbool.h>
12 #include <xcb/xcb.h>
13 #include <xcb/xproto.h>
14 #include "libi3.h"
15 #include "queue.h"
16
17 typedef struct rect_t rect;
18
19 struct ev_loop *main_loop;
20
21 struct rect_t {
22 int x;
23 int y;
24 int w;
25 int h;
26 };
27
28 typedef enum {
29 /* First value to make it the default. */
30 ALIGN_LEFT,
31 ALIGN_CENTER,
32 ALIGN_RIGHT
33 } blockalign_t;
34
35 /* This data structure describes the way a status block should be rendered. These
36 * variables are updated each time the statusline is re-rendered. */
37 struct status_block_render_desc {
38 uint32_t width;
39 uint32_t x_offset;
40 uint32_t x_append;
41 };
42
43 /* This data structure represents one JSON dictionary, multiple of these make
44 * up one status line. */
45 struct status_block {
46 i3String *full_text;
47 i3String *short_text;
48
49 char *color;
50 char *background;
51 char *border;
52
53 /* min_width can be specified either as a numeric value (in pixels) or as a
54 * string. For strings, we set min_width to the measured text width of
55 * min_width_str. */
56 uint32_t min_width;
57 char *min_width_str;
58
59 blockalign_t align;
60
61 bool urgent;
62 bool no_separator;
63 bool pango_markup;
64
65 /* The amount of pixels necessary to render a separater after the block. */
66 uint32_t sep_block_width;
67
68 /* Continuously-updated information on how to render this status block. */
69 struct status_block_render_desc full_render;
70 struct status_block_render_desc short_render;
71
72 /* Optional */
73 char *name;
74 char *instance;
75
76 TAILQ_ENTRY(status_block)
77 blocks;
78 };
79
80 TAILQ_HEAD(statusline_head, status_block)
81 statusline_head;
82
83 #include "child.h"
84 #include "ipc.h"
85 #include "outputs.h"
86 #include "util.h"
87 #include "workspaces.h"
88 #include "mode.h"
89 #include "trayclients.h"
90 #include "xcb.h"
91 #include "configuration.h"
92 #include "libi3.h"
93 #include "parse_json_header.h"
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * config.c: Parses the configuration (received from i3).
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include "common.h"
14
15 typedef enum {
16 POS_NONE = 0,
17 POS_TOP,
18 POS_BOT
19 } position_t;
20
21 /* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
22 typedef enum { M_DOCK = 0,
23 M_HIDE = 1,
24 M_INVISIBLE = 2 } bar_display_mode_t;
25
26 typedef struct binding_t {
27 int input_code;
28 char *command;
29 bool release;
30
31 TAILQ_ENTRY(binding_t)
32 bindings;
33 } binding_t;
34
35 typedef struct tray_output_t {
36 char *output;
37
38 TAILQ_ENTRY(tray_output_t)
39 tray_outputs;
40 } tray_output_t;
41
42 typedef struct config_t {
43 uint32_t modifier;
44
45 TAILQ_HEAD(bindings_head, binding_t)
46 bindings;
47
48 position_t position;
49 bool verbose;
50 struct xcb_color_strings_t colors;
51 bool disable_binding_mode_indicator;
52 bool disable_ws;
53 bool strip_ws_numbers;
54 bool strip_ws_name;
55 char *bar_id;
56 char *command;
57 char *fontname;
58 i3String *separator_symbol;
59
60 TAILQ_HEAD(tray_outputs_head, tray_output_t)
61 tray_outputs;
62
63 int tray_padding;
64 int num_outputs;
65 char **outputs;
66
67 bar_display_mode_t hide_on_modifier;
68
69 /* The current hidden_state of the bar, which indicates whether it is hidden or shown */
70 enum { S_HIDE = 0,
71 S_SHOW = 1 } hidden_state;
72 } config_t;
73
74 config_t config;
75
76 /**
77 * Start parsing the received bar configuration JSON string
78 *
79 */
80 void parse_config_json(char *json);
81
82 /**
83 * free()s the color strings as soon as they are not needed anymore.
84 *
85 */
86 void free_colors(struct xcb_color_strings_t *colors);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * ipc.c: Communicating with i3
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <stdint.h>
14
15 /*
16 * Initiate a connection to i3.
17 * socket_path must be a valid path to the ipc_socket of i3
18 *
19 */
20 int init_connection(const char *socket_path);
21
22 /*
23 * Destroy the connection to i3.
24 *
25 */
26 void destroy_connection(void);
27
28 /*
29 * Sends a message to i3.
30 * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
31 *
32 */
33 int i3_send_msg(uint32_t type, const char *payload);
34
35 /*
36 * Subscribe to all the i3-events, we need
37 *
38 */
39 void subscribe_events(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * mode.c: Handle "mode" event and show current binding mode in the bar
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <xcb/xproto.h>
14
15 #include "common.h"
16
17 /* Name of current binding mode and its render width */
18 struct mode {
19 i3String *name;
20 int width;
21 };
22
23 typedef struct mode mode;
24
25 /*
26 * Start parsing the received JSON string
27 *
28 */
29 void parse_mode_json(char *json);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * outputs.c: Maintaining the outputs list
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <xcb/xcb.h>
14 #include <cairo/cairo-xcb.h>
15
16 #include "common.h"
17
18 typedef struct i3_output i3_output;
19
20 SLIST_HEAD(outputs_head, i3_output);
21 struct outputs_head* outputs;
22
23 /*
24 * Start parsing the received JSON string
25 *
26 */
27 void parse_outputs_json(char* json);
28
29 /*
30 * Initiate the outputs list
31 *
32 */
33 void init_outputs(void);
34
35 /*
36 * free() all outputs data structures.
37 *
38 */
39 void free_outputs(void);
40
41 /*
42 * Returns the output with the given name
43 *
44 */
45 i3_output* get_output_by_name(char* name);
46
47 /*
48 * Returns true if the output has the currently focused workspace
49 *
50 */
51 bool output_has_focus(i3_output* output);
52
53 struct i3_output {
54 char* name; /* Name of the output */
55 bool active; /* If the output is active */
56 bool primary; /* If it is the primary output */
57 bool visible; /* If the bar is visible on this output */
58 int ws; /* The number of the currently visible ws */
59 rect rect; /* The rect (relative to the root window) */
60
61 /* Off-screen buffer for preliminary rendering of the bar. */
62 surface_t buffer;
63 /* Off-screen buffer for pre-rendering the statusline, separated to make clipping easier. */
64 surface_t statusline_buffer;
65 /* How much of statusline_buffer's horizontal space was used on last statusline render. */
66 int statusline_width;
67 /* Whether statusline block short texts where used on last statusline render. */
68 bool statusline_short_text;
69 /* The actual window on which we draw. */
70 surface_t bar;
71
72 struct ws_head* workspaces; /* The workspaces on this output */
73 struct tc_head* trayclients; /* The tray clients on this output */
74
75 SLIST_ENTRY(i3_output)
76 slist; /* Pointer for the SLIST-Macro */
77 };
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * parse_json_header.c: Parse the JSON protocol header to determine
7 * protocol version and features.
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 #include <stdint.h>
15
16 /**
17 * Parse the JSON protocol header to determine protocol version and features.
18 * In case the buffer does not contain a valid header (invalid JSON, or no
19 * version field found), the 'correct' field of the returned header is set to
20 * false. The amount of bytes consumed by parsing the header is returned in
21 * *consumed (if non-NULL).
22 *
23 */
24 void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 */
7 #pragma once
8
9 #include "common.h"
10
11 typedef struct trayclient trayclient;
12
13 TAILQ_HEAD(tc_head, trayclient);
14
15 struct trayclient {
16 xcb_window_t win; /* The window ID of the tray client */
17 bool mapped; /* Whether this window is mapped */
18 int xe_version; /* The XEMBED version supported by the client */
19
20 TAILQ_ENTRY(trayclient)
21 tailq; /* Pointer for the TAILQ-Macro */
22 };
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #pragma once
8
9 #include <config.h>
10
11 #include "queue.h"
12
13 /* Get the maximum/minimum of x and y */
14 #undef MAX
15 #define MAX(x, y) ((x) > (y) ? (x) : (y))
16 #undef MIN
17 #define MIN(x, y) ((x) < (y) ? (x) : (y))
18
19 #define STARTS_WITH(string, len, needle) (((len) >= strlen((needle))) && strncasecmp((string), (needle), strlen((needle))) == 0)
20
21 /* Securely free p */
22 #define FREE(p) \
23 do { \
24 free(p); \
25 p = NULL; \
26 } while (0)
27
28 /* Securely free single-linked list */
29 #define FREE_SLIST(l, type) \
30 do { \
31 type *walk = SLIST_FIRST(l); \
32 while (!SLIST_EMPTY(l)) { \
33 SLIST_REMOVE_HEAD(l, slist); \
34 FREE(walk); \
35 walk = SLIST_FIRST(l); \
36 } \
37 } while (0)
38
39 /* Securely free tail queue */
40 #define FREE_TAILQ(l, type) \
41 do { \
42 type *walk = TAILQ_FIRST(l); \
43 while (!TAILQ_EMPTY(l)) { \
44 TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
45 FREE(walk); \
46 walk = TAILQ_FIRST(l); \
47 } \
48 } while (0)
49
50 #if defined(DLOG)
51 #undef DLOG
52 #endif
53 /* Use cool logging macros */
54 #define DLOG(fmt, ...) \
55 do { \
56 if (config.verbose) { \
57 printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
58 } \
59 } while (0)
60
61 /* We will include libi3.h which define its own version of ELOG.
62 * We want *our* version, so we undef the libi3 one. */
63 #if defined(ELOG)
64 #undef ELOG
65 #endif
66 #define ELOG(fmt, ...) \
67 do { \
68 fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
69 } while (0)
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * workspaces.c: Maintaining the workspace lists
7 *
8 */
9 #pragma once
10
11 #include "common.h"
12
13 #include <xcb/xproto.h>
14
15 typedef struct i3_ws i3_ws;
16
17 TAILQ_HEAD(ws_head, i3_ws);
18
19 /*
20 * Start parsing the received JSON string
21 *
22 */
23 void parse_workspaces_json(char *json);
24
25 /*
26 * free() all workspace data structures
27 *
28 */
29 void free_workspaces(void);
30
31 struct i3_ws {
32 int num; /* The internal number of the ws */
33 char *canonical_name; /* The true name of the ws according to the ipc */
34 i3String *name; /* The name of the ws that is displayed on the bar */
35 int name_width; /* The rendered width of the name */
36 bool visible; /* If the ws is currently visible on an output */
37 bool focused; /* If the ws is currently focused */
38 bool urgent; /* If the urgent hint of the ws is set */
39 rect rect; /* The rect of the ws (not used (yet)) */
40 struct i3_output *output; /* The current output of the ws */
41
42 TAILQ_ENTRY(i3_ws)
43 tailq; /* Pointer for the TAILQ-Macro */
44 };
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * xcb.c: Communicating with X
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <stdint.h>
14 //#include "outputs.h"
15
16 #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
17 #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
18 #define SYSTEM_TRAY_REQUEST_DOCK 0
19 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
20 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
21 #define XEMBED_MAPPED (1 << 0)
22 #define XEMBED_EMBEDDED_NOTIFY 0
23
24 /* We define xcb_request_failed as a macro to include the relevant line number */
25 #define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
26 int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line);
27
28 struct xcb_color_strings_t {
29 char *bar_fg;
30 char *bar_bg;
31 char *sep_fg;
32 char *focus_bar_fg;
33 char *focus_bar_bg;
34 char *focus_sep_fg;
35 char *active_ws_fg;
36 char *active_ws_bg;
37 char *active_ws_border;
38 char *inactive_ws_fg;
39 char *inactive_ws_bg;
40 char *inactive_ws_border;
41 char *focus_ws_bg;
42 char *focus_ws_fg;
43 char *focus_ws_border;
44 char *urgent_ws_bg;
45 char *urgent_ws_fg;
46 char *urgent_ws_border;
47 char *binding_mode_bg;
48 char *binding_mode_fg;
49 char *binding_mode_border;
50 };
51
52 typedef struct xcb_colors_t xcb_colors_t;
53
54 /* Cached width of the custom separator if one was set */
55 int separator_symbol_width;
56
57 /*
58 * Early initialization of the connection to X11: Everything which does not
59 * depend on 'config'.
60 *
61 */
62 char *init_xcb_early(void);
63
64 /**
65 * Initialization which depends on 'config' being usable. Called after the
66 * configuration has arrived.
67 *
68 */
69 void init_xcb_late(char *fontname);
70
71 /*
72 * Initialize the colors
73 *
74 */
75 void init_colors(const struct xcb_color_strings_t *colors);
76
77 /*
78 * Cleanup the xcb stuff.
79 * Called once, before the program terminates.
80 *
81 */
82 void clean_xcb(void);
83
84 /*
85 * Get the earlier requested atoms and save them in the prepared data structure
86 *
87 */
88 void get_atoms(void);
89
90 /*
91 * Reparents all tray clients of the specified output to the root window. This
92 * is either used when shutting down, when an output appears (xrandr --output
93 * VGA1 --off) or when the primary output changes.
94 *
95 * Applications using the tray will start the protocol from the beginning again
96 * afterwards.
97 *
98 */
99 void kick_tray_clients(i3_output *output);
100
101 /*
102 * We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
103 * to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
104 * they assume a light background.
105 * See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
106 *
107 */
108 void init_tray_colors(void);
109
110 /*
111 * Destroy the bar of the specified output
112 *
113 */
114 void destroy_window(i3_output *output);
115
116 /*
117 * Reallocate the statusline buffer
118 *
119 */
120 void realloc_sl_buffer(void);
121
122 /*
123 * Reconfigure all bars and create new for newly activated outputs
124 *
125 */
126 void reconfig_windows(bool redraw_bars);
127
128 /*
129 * Render the bars, with buttons and statusline
130 *
131 */
132 void draw_bars(bool force_unhide);
133
134 /*
135 * Redraw the bars, i.e. simply copy the buffer to the barwindow
136 *
137 */
138 void redraw_bars(void);
139
140 /*
141 * Set the current binding mode
142 *
143 */
144 void set_current_mode(struct mode *mode);
0 ATOM_DO(_NET_WM_WINDOW_TYPE)
1 ATOM_DO(_NET_WM_WINDOW_TYPE_DOCK)
2 ATOM_DO(_NET_WM_STRUT_PARTIAL)
3 ATOM_DO(I3_SOCKET_PATH)
4 ATOM_DO(MANAGER)
5 ATOM_DO(_NET_SYSTEM_TRAY_ORIENTATION)
6 ATOM_DO(_NET_SYSTEM_TRAY_VISUAL)
7 ATOM_DO(_NET_SYSTEM_TRAY_OPCODE)
8 ATOM_DO(_NET_SYSTEM_TRAY_COLORS)
9 ATOM_DO(_XEMBED_INFO)
10 ATOM_DO(_XEMBED)
11 ATOM_DO(I3_SYNC)
12 #undef ATOM_DO
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * child.c: Getting input for the statusline
7 *
8 */
9 #include "common.h"
10 #include "yajl_utils.h"
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <signal.h>
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <err.h>
23 #include <ev.h>
24 #include <yajl/yajl_common.h>
25 #include <yajl/yajl_parse.h>
26 #include <yajl/yajl_version.h>
27 #include <yajl/yajl_gen.h>
28 #include <paths.h>
29
30 #include <xcb/xcb_keysyms.h>
31
32 /* Global variables for child_*() */
33 i3bar_child child;
34
35 /* stdin- and SIGCHLD-watchers */
36 ev_io *stdin_io;
37 ev_child *child_sig;
38
39 /* JSON parser for stdin */
40 yajl_handle parser;
41
42 /* JSON generator for stdout */
43 yajl_gen gen;
44
45 typedef struct parser_ctx {
46 /* True if one of the parsed blocks was urgent */
47 bool has_urgent;
48
49 /* A copy of the last JSON map key. */
50 char *last_map_key;
51
52 /* The current block. Will be filled, then copied and put into the list of
53 * blocks. */
54 struct status_block block;
55 } parser_ctx;
56
57 parser_ctx parser_context;
58
59 struct statusline_head statusline_head = TAILQ_HEAD_INITIALIZER(statusline_head);
60 /* Used temporarily while reading a statusline */
61 struct statusline_head statusline_buffer = TAILQ_HEAD_INITIALIZER(statusline_buffer);
62
63 int child_stdin;
64
65 /*
66 * Remove all blocks from the given statusline.
67 * If free_resources is set, the fields of each status block will be free'd.
68 */
69 static void clear_statusline(struct statusline_head *head, bool free_resources) {
70 struct status_block *first;
71 while (!TAILQ_EMPTY(head)) {
72 first = TAILQ_FIRST(head);
73 if (free_resources) {
74 I3STRING_FREE(first->full_text);
75 I3STRING_FREE(first->short_text);
76 FREE(first->color);
77 FREE(first->name);
78 FREE(first->instance);
79 FREE(first->min_width_str);
80 FREE(first->background);
81 FREE(first->border);
82 }
83
84 TAILQ_REMOVE(head, first, blocks);
85 free(first);
86 }
87 }
88
89 static void copy_statusline(struct statusline_head *from, struct statusline_head *to) {
90 struct status_block *current;
91 TAILQ_FOREACH(current, from, blocks) {
92 struct status_block *new_block = smalloc(sizeof(struct status_block));
93 memcpy(new_block, current, sizeof(struct status_block));
94 TAILQ_INSERT_TAIL(to, new_block, blocks);
95 }
96 }
97
98 /*
99 * Replaces the statusline in memory with an error message. Pass a format
100 * string and format parameters as you would in `printf'. The next time
101 * `draw_bars' is called, the error message text will be drawn on the bar in
102 * the space allocated for the statusline.
103 */
104 __attribute__((format(printf, 1, 2))) static void set_statusline_error(const char *format, ...) {
105 clear_statusline(&statusline_head, true);
106
107 char *message;
108 va_list args;
109 va_start(args, format);
110 if (vasprintf(&message, format, args) == -1) {
111 goto finish;
112 }
113
114 struct status_block *err_block = scalloc(1, sizeof(struct status_block));
115 err_block->full_text = i3string_from_utf8("Error: ");
116 err_block->name = sstrdup("error");
117 err_block->color = sstrdup("#ff0000");
118 err_block->no_separator = true;
119
120 struct status_block *message_block = scalloc(1, sizeof(struct status_block));
121 message_block->full_text = i3string_from_utf8(message);
122 message_block->name = sstrdup("error_message");
123 message_block->color = sstrdup("#ff0000");
124 message_block->no_separator = true;
125
126 TAILQ_INSERT_HEAD(&statusline_head, err_block, blocks);
127 TAILQ_INSERT_TAIL(&statusline_head, message_block, blocks);
128
129 finish:
130 FREE(message);
131 va_end(args);
132 }
133
134 /*
135 * Stop and free() the stdin- and SIGCHLD-watchers
136 *
137 */
138 static void cleanup(void) {
139 if (stdin_io != NULL) {
140 ev_io_stop(main_loop, stdin_io);
141 FREE(stdin_io);
142 }
143
144 if (child_sig != NULL) {
145 ev_child_stop(main_loop, child_sig);
146 FREE(child_sig);
147 }
148
149 memset(&child, 0, sizeof(i3bar_child));
150 }
151
152 /*
153 * The start of a new array is the start of a new status line, so we clear all
154 * previous entries from the buffer.
155 */
156 static int stdin_start_array(void *context) {
157 // the blocks are still used by statusline_head, so we won't free the
158 // resources here.
159 clear_statusline(&statusline_buffer, false);
160 return 1;
161 }
162
163 /*
164 * The start of a map is the start of a single block of the status line.
165 *
166 */
167 static int stdin_start_map(void *context) {
168 parser_ctx *ctx = context;
169 memset(&(ctx->block), '\0', sizeof(struct status_block));
170
171 /* Default width of the separator block. */
172 if (config.separator_symbol == NULL)
173 ctx->block.sep_block_width = logical_px(9);
174 else
175 ctx->block.sep_block_width = logical_px(8) + separator_symbol_width;
176
177 return 1;
178 }
179
180 static int stdin_map_key(void *context, const unsigned char *key, size_t len) {
181 parser_ctx *ctx = context;
182 FREE(ctx->last_map_key);
183 sasprintf(&(ctx->last_map_key), "%.*s", len, key);
184 return 1;
185 }
186
187 static int stdin_boolean(void *context, int val) {
188 parser_ctx *ctx = context;
189 if (strcasecmp(ctx->last_map_key, "urgent") == 0) {
190 ctx->block.urgent = val;
191 return 1;
192 }
193 if (strcasecmp(ctx->last_map_key, "separator") == 0) {
194 ctx->block.no_separator = !val;
195 return 1;
196 }
197
198 return 1;
199 }
200
201 static int stdin_string(void *context, const unsigned char *val, size_t len) {
202 parser_ctx *ctx = context;
203 if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
204 ctx->block.full_text = i3string_from_markup_with_length((const char *)val, len);
205 return 1;
206 }
207 if (strcasecmp(ctx->last_map_key, "short_text") == 0) {
208 ctx->block.short_text = i3string_from_markup_with_length((const char *)val, len);
209 return 1;
210 }
211 if (strcasecmp(ctx->last_map_key, "color") == 0) {
212 sasprintf(&(ctx->block.color), "%.*s", len, val);
213 return 1;
214 }
215 if (strcasecmp(ctx->last_map_key, "background") == 0) {
216 sasprintf(&(ctx->block.background), "%.*s", len, val);
217 return 1;
218 }
219 if (strcasecmp(ctx->last_map_key, "border") == 0) {
220 sasprintf(&(ctx->block.border), "%.*s", len, val);
221 return 1;
222 }
223 if (strcasecmp(ctx->last_map_key, "markup") == 0) {
224 ctx->block.pango_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango")));
225 return 1;
226 }
227 if (strcasecmp(ctx->last_map_key, "align") == 0) {
228 if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
229 ctx->block.align = ALIGN_CENTER;
230 } else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) {
231 ctx->block.align = ALIGN_RIGHT;
232 } else {
233 ctx->block.align = ALIGN_LEFT;
234 }
235 return 1;
236 }
237 if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
238 sasprintf(&(ctx->block.min_width_str), "%.*s", len, val);
239 return 1;
240 }
241 if (strcasecmp(ctx->last_map_key, "name") == 0) {
242 sasprintf(&(ctx->block.name), "%.*s", len, val);
243 return 1;
244 }
245 if (strcasecmp(ctx->last_map_key, "instance") == 0) {
246 sasprintf(&(ctx->block.instance), "%.*s", len, val);
247 return 1;
248 }
249
250 return 1;
251 }
252
253 static int stdin_integer(void *context, long long val) {
254 parser_ctx *ctx = context;
255 if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
256 ctx->block.min_width = (uint32_t)val;
257 return 1;
258 }
259 if (strcasecmp(ctx->last_map_key, "separator_block_width") == 0) {
260 ctx->block.sep_block_width = (uint32_t)val;
261 return 1;
262 }
263
264 return 1;
265 }
266
267 /*
268 * When a map is finished, we have an entire status block.
269 * Move it from the parser's context to the statusline buffer.
270 */
271 static int stdin_end_map(void *context) {
272 parser_ctx *ctx = context;
273 struct status_block *new_block = smalloc(sizeof(struct status_block));
274 memcpy(new_block, &(ctx->block), sizeof(struct status_block));
275 /* Ensure we have a full_text set, so that when it is missing (or null),
276 * i3bar doesn’t crash and the user gets an annoying message. */
277 if (!new_block->full_text)
278 new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
279 if (new_block->urgent)
280 ctx->has_urgent = true;
281
282 if (new_block->min_width_str) {
283 i3String *text = i3string_from_utf8(new_block->min_width_str);
284 i3string_set_markup(text, new_block->pango_markup);
285 new_block->min_width = (uint32_t)predict_text_width(text);
286 i3string_free(text);
287 }
288
289 i3string_set_markup(new_block->full_text, new_block->pango_markup);
290
291 if (new_block->short_text != NULL)
292 i3string_set_markup(new_block->short_text, new_block->pango_markup);
293
294 TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
295 return 1;
296 }
297
298 /*
299 * When an array is finished, we have an entire statusline.
300 * Copy it from the buffer to the actual statusline.
301 */
302 static int stdin_end_array(void *context) {
303 DLOG("copying statusline_buffer to statusline_head\n");
304 clear_statusline(&statusline_head, true);
305 copy_statusline(&statusline_buffer, &statusline_head);
306
307 DLOG("dumping statusline:\n");
308 struct status_block *current;
309 TAILQ_FOREACH(current, &statusline_head, blocks) {
310 DLOG("full_text = %s\n", i3string_as_utf8(current->full_text));
311 DLOG("short_text = %s\n", (current->short_text == NULL ? NULL : i3string_as_utf8(current->short_text)));
312 DLOG("color = %s\n", current->color);
313 }
314 DLOG("end of dump\n");
315 return 1;
316 }
317
318 /*
319 * Helper function to read stdin
320 *
321 * Returns NULL on EOF.
322 *
323 */
324 static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
325 int fd = watcher->fd;
326 int n = 0;
327 int rec = 0;
328 int buffer_len = STDIN_CHUNK_SIZE;
329 unsigned char *buffer = smalloc(buffer_len + 1);
330 buffer[0] = '\0';
331 while (1) {
332 n = read(fd, buffer + rec, buffer_len - rec);
333 if (n == -1) {
334 if (errno == EAGAIN) {
335 /* finish up */
336 break;
337 }
338 ELOG("read() failed!: %s\n", strerror(errno));
339 FREE(buffer);
340 exit(EXIT_FAILURE);
341 }
342 if (n == 0) {
343 ELOG("stdin: received EOF\n");
344 FREE(buffer);
345 *ret_buffer_len = -1;
346 return NULL;
347 }
348 rec += n;
349
350 if (rec == buffer_len) {
351 buffer_len += STDIN_CHUNK_SIZE;
352 buffer = srealloc(buffer, buffer_len);
353 }
354 }
355 if (*buffer == '\0') {
356 FREE(buffer);
357 rec = -1;
358 }
359 *ret_buffer_len = rec;
360 return buffer;
361 }
362
363 static void read_flat_input(char *buffer, int length) {
364 struct status_block *first = TAILQ_FIRST(&statusline_head);
365 /* Clear the old buffer if any. */
366 I3STRING_FREE(first->full_text);
367 /* Remove the trailing newline and terminate the string at the same
368 * time. */
369 if (buffer[length - 1] == '\n' || buffer[length - 1] == '\r') {
370 buffer[length - 1] = '\0';
371 } else {
372 buffer[length] = '\0';
373 }
374
375 first->full_text = i3string_from_utf8(buffer);
376 }
377
378 static bool read_json_input(unsigned char *input, int length) {
379 yajl_status status = yajl_parse(parser, input, length);
380 bool has_urgent = false;
381 if (status != yajl_status_ok) {
382 char *message = (char *)yajl_get_error(parser, 0, input, length);
383
384 /* strip the newline yajl adds to the error message */
385 if (message[strlen(message) - 1] == '\n')
386 message[strlen(message) - 1] = '\0';
387
388 fprintf(stderr, "[i3bar] Could not parse JSON input (code = %d, message = %s): %.*s\n",
389 status, message, length, input);
390
391 set_statusline_error("Could not parse JSON (%s)", message);
392 yajl_free_error(parser, (unsigned char *)message);
393 draw_bars(false);
394 } else if (parser_context.has_urgent) {
395 has_urgent = true;
396 }
397 return has_urgent;
398 }
399
400 /*
401 * Callbalk for stdin. We read a line from stdin and store the result
402 * in statusline
403 *
404 */
405 static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
406 int rec;
407 unsigned char *buffer = get_buffer(watcher, &rec);
408 if (buffer == NULL)
409 return;
410 bool has_urgent = false;
411 if (child.version > 0) {
412 has_urgent = read_json_input(buffer, rec);
413 } else {
414 read_flat_input((char *)buffer, rec);
415 }
416 free(buffer);
417 draw_bars(has_urgent);
418 }
419
420 /*
421 * Callbalk for stdin first line. We read the first line to detect
422 * whether this is JSON or plain text
423 *
424 */
425 static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
426 int rec;
427 unsigned char *buffer = get_buffer(watcher, &rec);
428 if (buffer == NULL)
429 return;
430 DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
431 /* Detect whether this is JSON or plain text. */
432 unsigned int consumed = 0;
433 /* At the moment, we don’t care for the version. This might change
434 * in the future, but for now, we just discard it. */
435 parse_json_header(&child, buffer, rec, &consumed);
436 if (child.version > 0) {
437 /* If hide-on-modifier is set, we start of by sending the
438 * child a SIGSTOP, because the bars aren't mapped at start */
439 if (config.hide_on_modifier) {
440 stop_child();
441 }
442 draw_bars(read_json_input(buffer + consumed, rec - consumed));
443 } else {
444 /* In case of plaintext, we just add a single block and change its
445 * full_text pointer later. */
446 struct status_block *new_block = scalloc(1, sizeof(struct status_block));
447 TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
448 read_flat_input((char *)buffer, rec);
449 }
450 free(buffer);
451 ev_io_stop(main_loop, stdin_io);
452 ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
453 ev_io_start(main_loop, stdin_io);
454 }
455
456 /*
457 * We received a SIGCHLD, meaning, that the child process terminated.
458 * We simply free the respective data structures and don't care for input
459 * anymore
460 *
461 */
462 static void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
463 int exit_status = WEXITSTATUS(watcher->rstatus);
464
465 ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
466 child.pid,
467 exit_status);
468
469 /* this error is most likely caused by a user giving a nonexecutable or
470 * nonexistent file, so we will handle those cases separately. */
471 if (exit_status == 126)
472 set_statusline_error("status_command is not executable (exit %d)", exit_status);
473 else if (exit_status == 127)
474 set_statusline_error("status_command not found or is missing a library dependency (exit %d)", exit_status);
475 else
476 set_statusline_error("status_command process exited unexpectedly (exit %d)", exit_status);
477
478 cleanup();
479 draw_bars(false);
480 }
481
482 static void child_write_output(void) {
483 if (child.click_events) {
484 const unsigned char *output;
485 size_t size;
486 ssize_t n;
487
488 yajl_gen_get_buf(gen, &output, &size);
489
490 n = writeall(child_stdin, output, size);
491 if (n != -1)
492 n = writeall(child_stdin, "\n", 1);
493
494 yajl_gen_clear(gen);
495
496 if (n == -1) {
497 child.click_events = false;
498 kill_child();
499 set_statusline_error("child_write_output failed");
500 draw_bars(false);
501 }
502 }
503 }
504
505 /*
506 * Start a child process with the specified command and reroute stdin.
507 * We actually start a $SHELL to execute the command so we don't have to care
508 * about arguments and such.
509 *
510 * If `command' is NULL, such as in the case when no `status_command' is given
511 * in the bar config, no child will be started.
512 *
513 */
514 void start_child(char *command) {
515 if (command == NULL)
516 return;
517
518 /* Allocate a yajl parser which will be used to parse stdin. */
519 static yajl_callbacks callbacks = {
520 .yajl_boolean = stdin_boolean,
521 .yajl_integer = stdin_integer,
522 .yajl_string = stdin_string,
523 .yajl_start_map = stdin_start_map,
524 .yajl_map_key = stdin_map_key,
525 .yajl_end_map = stdin_end_map,
526 .yajl_start_array = stdin_start_array,
527 .yajl_end_array = stdin_end_array,
528 };
529 parser = yajl_alloc(&callbacks, NULL, &parser_context);
530
531 gen = yajl_gen_alloc(NULL);
532
533 int pipe_in[2]; /* pipe we read from */
534 int pipe_out[2]; /* pipe we write to */
535
536 if (pipe(pipe_in) == -1)
537 err(EXIT_FAILURE, "pipe(pipe_in)");
538 if (pipe(pipe_out) == -1)
539 err(EXIT_FAILURE, "pipe(pipe_out)");
540
541 child.pid = fork();
542 switch (child.pid) {
543 case -1:
544 ELOG("Couldn't fork(): %s\n", strerror(errno));
545 exit(EXIT_FAILURE);
546 case 0:
547 /* Child-process. Reroute streams and start shell */
548
549 close(pipe_in[0]);
550 close(pipe_out[1]);
551
552 dup2(pipe_in[1], STDOUT_FILENO);
553 dup2(pipe_out[0], STDIN_FILENO);
554
555 setpgid(child.pid, 0);
556 execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char *)NULL);
557 return;
558 default:
559 /* Parent-process. Reroute streams */
560
561 close(pipe_in[1]);
562 close(pipe_out[0]);
563
564 dup2(pipe_in[0], STDIN_FILENO);
565 child_stdin = pipe_out[1];
566
567 break;
568 }
569
570 /* We set O_NONBLOCK because blocking is evil in event-driven software */
571 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
572
573 stdin_io = smalloc(sizeof(ev_io));
574 ev_io_init(stdin_io, &stdin_io_first_line_cb, STDIN_FILENO, EV_READ);
575 ev_io_start(main_loop, stdin_io);
576
577 /* We must cleanup, if the child unexpectedly terminates */
578 child_sig = smalloc(sizeof(ev_child));
579 ev_child_init(child_sig, &child_sig_cb, child.pid, 0);
580 ev_child_start(main_loop, child_sig);
581
582 atexit(kill_child_at_exit);
583 }
584
585 static void child_click_events_initialize(void) {
586 if (!child.click_events_init) {
587 yajl_gen_array_open(gen);
588 child_write_output();
589 child.click_events_init = true;
590 }
591 }
592
593 /*
594 * Generates a click event, if enabled.
595 *
596 */
597 void send_block_clicked(int button, const char *name, const char *instance, int x, int y, int x_rel, int y_rel, int width, int height, int mods) {
598 if (!child.click_events) {
599 return;
600 }
601
602 child_click_events_initialize();
603
604 yajl_gen_map_open(gen);
605
606 if (name) {
607 ystr("name");
608 ystr(name);
609 }
610
611 if (instance) {
612 ystr("instance");
613 ystr(instance);
614 }
615
616 ystr("button");
617 yajl_gen_integer(gen, button);
618
619 ystr("modifiers");
620 yajl_gen_array_open(gen);
621 if (mods & XCB_MOD_MASK_SHIFT)
622 ystr("Shift");
623 if (mods & XCB_MOD_MASK_CONTROL)
624 ystr("Control");
625 if (mods & XCB_MOD_MASK_1)
626 ystr("Mod1");
627 if (mods & XCB_MOD_MASK_2)
628 ystr("Mod2");
629 if (mods & XCB_MOD_MASK_3)
630 ystr("Mod3");
631 if (mods & XCB_MOD_MASK_4)
632 ystr("Mod4");
633 if (mods & XCB_MOD_MASK_5)
634 ystr("Mod5");
635 yajl_gen_array_close(gen);
636
637 ystr("x");
638 yajl_gen_integer(gen, x);
639
640 ystr("y");
641 yajl_gen_integer(gen, y);
642
643 ystr("relative_x");
644 yajl_gen_integer(gen, x_rel);
645
646 ystr("relative_y");
647 yajl_gen_integer(gen, y_rel);
648
649 ystr("width");
650 yajl_gen_integer(gen, width);
651
652 ystr("height");
653 yajl_gen_integer(gen, height);
654
655 yajl_gen_map_close(gen);
656 child_write_output();
657 }
658
659 /*
660 * kill()s the child process (if any). Called when exit()ing.
661 *
662 */
663 void kill_child_at_exit(void) {
664 if (child.pid > 0) {
665 if (child.cont_signal > 0 && child.stopped)
666 killpg(child.pid, child.cont_signal);
667 killpg(child.pid, SIGTERM);
668 }
669 }
670
671 /*
672 * kill()s the child process (if existent) and closes and
673 * free()s the stdin- and SIGCHLD-watchers
674 *
675 */
676 void kill_child(void) {
677 if (child.pid > 0) {
678 if (child.cont_signal > 0 && child.stopped)
679 killpg(child.pid, child.cont_signal);
680 killpg(child.pid, SIGTERM);
681 int status;
682 waitpid(child.pid, &status, 0);
683 cleanup();
684 }
685 }
686
687 /*
688 * Sends a SIGSTOP to the child process (if existent)
689 *
690 */
691 void stop_child(void) {
692 if (child.stop_signal > 0 && !child.stopped) {
693 child.stopped = true;
694 killpg(child.pid, child.stop_signal);
695 }
696 }
697
698 /*
699 * Sends a SIGCONT to the child process (if existent)
700 *
701 */
702 void cont_child(void) {
703 if (child.cont_signal > 0 && child.stopped) {
704 child.stopped = false;
705 killpg(child.pid, child.cont_signal);
706 }
707 }
708
709 /*
710 * Whether or not the child want click events
711 *
712 */
713 bool child_want_click_events(void) {
714 return child.click_events;
715 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * config.c: Parses the configuration (received from i3).
7 *
8 */
9 #include "common.h"
10
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <i3/ipc.h>
16 #include <yajl/yajl_parse.h>
17 #include <yajl/yajl_version.h>
18
19 #include <X11/Xlib.h>
20
21 static char *cur_key;
22 static bool parsing_bindings;
23 static bool parsing_tray_outputs;
24
25 /*
26 * Parse a key.
27 *
28 * Essentially we just save it in cur_key.
29 *
30 */
31 static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
32 FREE(cur_key);
33 sasprintf(&(cur_key), "%.*s", keyLen, keyVal);
34
35 if (strcmp(cur_key, "bindings") == 0) {
36 parsing_bindings = true;
37 }
38
39 if (strcmp(cur_key, "tray_outputs") == 0) {
40 parsing_tray_outputs = true;
41 }
42
43 return 1;
44 }
45
46 static int config_end_array_cb(void *params_) {
47 parsing_bindings = false;
48 parsing_tray_outputs = false;
49 return 1;
50 }
51
52 /*
53 * Parse a null value (current_workspace)
54 *
55 */
56 static int config_null_cb(void *params_) {
57 if (!strcmp(cur_key, "id")) {
58 /* If 'id' is NULL, the bar config was not found. Error out. */
59 ELOG("No such bar config. Use 'i3-msg -t get_bar_config' to get the available configs.\n");
60 ELOG("Are you starting i3bar by hand? You should not:\n");
61 ELOG("Configure a 'bar' block in your i3 config and i3 will launch i3bar automatically.\n");
62 exit(EXIT_FAILURE);
63 }
64
65 return 1;
66 }
67
68 /*
69 * Parse a string
70 *
71 */
72 static int config_string_cb(void *params_, const unsigned char *val, size_t _len) {
73 int len = (int)_len;
74 /* The id and socket_path are ignored, we already know them. */
75 if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path"))
76 return 1;
77
78 if (parsing_bindings) {
79 if (strcmp(cur_key, "command") == 0) {
80 binding_t *binding = TAILQ_LAST(&(config.bindings), bindings_head);
81 if (binding == NULL) {
82 ELOG("There is no binding to put the current command onto. This is a bug in i3.\n");
83 return 0;
84 }
85
86 if (binding->command != NULL) {
87 ELOG("The binding for input_code = %d already has a command. This is a bug in i3.\n", binding->input_code);
88 return 0;
89 }
90
91 sasprintf(&(binding->command), "%.*s", len, val);
92 return 1;
93 }
94
95 ELOG("Unknown key \"%s\" while parsing bar bindings.\n", cur_key);
96 return 0;
97 }
98
99 if (parsing_tray_outputs) {
100 DLOG("Adding tray_output = %.*s to the list.\n", len, val);
101 tray_output_t *tray_output = scalloc(1, sizeof(tray_output_t));
102 sasprintf(&(tray_output->output), "%.*s", len, val);
103 TAILQ_INSERT_TAIL(&(config.tray_outputs), tray_output, tray_outputs);
104 return 1;
105 }
106
107 if (!strcmp(cur_key, "mode")) {
108 DLOG("mode = %.*s, len = %d\n", len, val, len);
109 config.hide_on_modifier = (len == strlen("dock") && !strncmp((const char *)val, "dock", strlen("dock")) ? M_DOCK
110 : (len == strlen("hide") && !strncmp((const char *)val, "hide", strlen("hide")) ? M_HIDE
111 : M_INVISIBLE));
112 return 1;
113 }
114
115 if (!strcmp(cur_key, "hidden_state")) {
116 DLOG("hidden_state = %.*s, len = %d\n", len, val, len);
117 config.hidden_state = (len == strlen("hide") && !strncmp((const char *)val, "hide", strlen("hide")) ? S_HIDE : S_SHOW);
118 return 1;
119 }
120
121 /* Kept for backwards compatibility. */
122 if (!strcmp(cur_key, "modifier")) {
123 DLOG("modifier = %.*s\n", len, val);
124 if (len == strlen("none") && !strncmp((const char *)val, "none", strlen("none"))) {
125 config.modifier = XCB_NONE;
126 return 1;
127 }
128
129 if (len == strlen("shift") && !strncmp((const char *)val, "shift", strlen("shift"))) {
130 config.modifier = ShiftMask;
131 return 1;
132 }
133 if (len == strlen("ctrl") && !strncmp((const char *)val, "ctrl", strlen("ctrl"))) {
134 config.modifier = ControlMask;
135 return 1;
136 }
137 if (len == strlen("Mod") + 1 && !strncmp((const char *)val, "Mod", strlen("Mod"))) {
138 switch (val[3]) {
139 case '1':
140 config.modifier = Mod1Mask;
141 return 1;
142 case '2':
143 config.modifier = Mod2Mask;
144 return 1;
145 case '3':
146 config.modifier = Mod3Mask;
147 return 1;
148 case '5':
149 config.modifier = Mod5Mask;
150 return 1;
151 }
152 }
153
154 config.modifier = Mod4Mask;
155 return 1;
156 }
157
158 /* This key was sent in <= 4.10.2. We keep it around to avoid breakage for
159 * users updating from that version and restarting i3bar before i3. */
160 if (!strcmp(cur_key, "wheel_up_cmd")) {
161 DLOG("wheel_up_cmd = %.*s\n", len, val);
162 binding_t *binding = scalloc(1, sizeof(binding_t));
163 binding->input_code = 4;
164 sasprintf(&(binding->command), "%.*s", len, val);
165 TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
166 return 1;
167 }
168
169 /* This key was sent in <= 4.10.2. We keep it around to avoid breakage for
170 * users updating from that version and restarting i3bar before i3. */
171 if (!strcmp(cur_key, "wheel_down_cmd")) {
172 DLOG("wheel_down_cmd = %.*s\n", len, val);
173 binding_t *binding = scalloc(1, sizeof(binding_t));
174 binding->input_code = 5;
175 sasprintf(&(binding->command), "%.*s", len, val);
176 TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
177 return 1;
178 }
179
180 if (!strcmp(cur_key, "position")) {
181 DLOG("position = %.*s\n", len, val);
182 config.position = (len == strlen("top") && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
183 return 1;
184 }
185
186 if (!strcmp(cur_key, "status_command")) {
187 DLOG("command = %.*s\n", len, val);
188 FREE(config.command);
189 sasprintf(&config.command, "%.*s", len, val);
190 return 1;
191 }
192
193 if (!strcmp(cur_key, "font")) {
194 DLOG("font = %.*s\n", len, val);
195 FREE(config.fontname);
196 sasprintf(&config.fontname, "%.*s", len, val);
197 return 1;
198 }
199
200 if (!strcmp(cur_key, "separator_symbol")) {
201 DLOG("separator = %.*s\n", len, val);
202 I3STRING_FREE(config.separator_symbol);
203 config.separator_symbol = i3string_from_utf8_with_length((const char *)val, len);
204 return 1;
205 }
206
207 if (!strcmp(cur_key, "outputs")) {
208 DLOG("+output %.*s\n", len, val);
209 int new_num_outputs = config.num_outputs + 1;
210 config.outputs = srealloc(config.outputs, sizeof(char *) * new_num_outputs);
211 sasprintf(&config.outputs[config.num_outputs], "%.*s", len, val);
212 config.num_outputs = new_num_outputs;
213 return 1;
214 }
215
216 /* We keep the old single tray_output working for users who only restart i3bar
217 * after updating. */
218 if (!strcmp(cur_key, "tray_output")) {
219 DLOG("Found deprecated key tray_output %.*s.\n", len, val);
220 tray_output_t *tray_output = scalloc(1, sizeof(tray_output_t));
221 sasprintf(&(tray_output->output), "%.*s", len, val);
222 TAILQ_INSERT_TAIL(&(config.tray_outputs), tray_output, tray_outputs);
223 return 1;
224 }
225
226 #define COLOR(json_name, struct_name) \
227 do { \
228 if (!strcmp(cur_key, #json_name)) { \
229 DLOG(#json_name " = " #struct_name " = %.*s\n", len, val); \
230 sasprintf(&(config.colors.struct_name), "%.*s", len, val); \
231 return 1; \
232 } \
233 } while (0)
234
235 COLOR(statusline, bar_fg);
236 COLOR(background, bar_bg);
237 COLOR(separator, sep_fg);
238 COLOR(focused_statusline, focus_bar_fg);
239 COLOR(focused_background, focus_bar_bg);
240 COLOR(focused_separator, focus_sep_fg);
241 COLOR(focused_workspace_border, focus_ws_border);
242 COLOR(focused_workspace_bg, focus_ws_bg);
243 COLOR(focused_workspace_text, focus_ws_fg);
244 COLOR(active_workspace_border, active_ws_border);
245 COLOR(active_workspace_bg, active_ws_bg);
246 COLOR(active_workspace_text, active_ws_fg);
247 COLOR(inactive_workspace_border, inactive_ws_border);
248 COLOR(inactive_workspace_bg, inactive_ws_bg);
249 COLOR(inactive_workspace_text, inactive_ws_fg);
250 COLOR(urgent_workspace_border, urgent_ws_border);
251 COLOR(urgent_workspace_bg, urgent_ws_bg);
252 COLOR(urgent_workspace_text, urgent_ws_fg);
253 COLOR(binding_mode_border, binding_mode_border);
254 COLOR(binding_mode_bg, binding_mode_bg);
255 COLOR(binding_mode_text, binding_mode_fg);
256
257 printf("got unexpected string %.*s for cur_key = %s\n", len, val, cur_key);
258
259 return 0;
260 }
261
262 /*
263 * Parse a boolean value
264 *
265 */
266 static int config_boolean_cb(void *params_, int val) {
267 if (parsing_bindings) {
268 if (strcmp(cur_key, "release") == 0) {
269 binding_t *binding = TAILQ_LAST(&(config.bindings), bindings_head);
270 if (binding == NULL) {
271 ELOG("There is no binding to put the current command onto. This is a bug in i3.\n");
272 return 0;
273 }
274
275 binding->release = val;
276 return 1;
277 }
278
279 ELOG("Unknown key \"%s\" while parsing bar bindings.\n", cur_key);
280 }
281
282 if (!strcmp(cur_key, "binding_mode_indicator")) {
283 DLOG("binding_mode_indicator = %d\n", val);
284 config.disable_binding_mode_indicator = !val;
285 return 1;
286 }
287
288 if (!strcmp(cur_key, "workspace_buttons")) {
289 DLOG("workspace_buttons = %d\n", val);
290 config.disable_ws = !val;
291 return 1;
292 }
293
294 if (!strcmp(cur_key, "strip_workspace_numbers")) {
295 DLOG("strip_workspace_numbers = %d\n", val);
296 config.strip_ws_numbers = val;
297 return 1;
298 }
299
300 if (!strcmp(cur_key, "strip_workspace_name")) {
301 DLOG("strip_workspace_name = %d\n", val);
302 config.strip_ws_name = val;
303 return 1;
304 }
305
306 if (!strcmp(cur_key, "verbose")) {
307 if (!config.verbose) {
308 DLOG("verbose = %d\n", val);
309 config.verbose = val;
310 }
311 return 1;
312 }
313
314 return 0;
315 }
316
317 /*
318 * Parse an integer value
319 *
320 */
321 static int config_integer_cb(void *params_, long long val) {
322 if (parsing_bindings) {
323 if (strcmp(cur_key, "input_code") == 0) {
324 binding_t *binding = scalloc(1, sizeof(binding_t));
325 binding->input_code = val;
326 TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
327
328 return 1;
329 }
330
331 ELOG("Unknown key \"%s\" while parsing bar bindings.\n", cur_key);
332 return 0;
333 }
334
335 if (!strcmp(cur_key, "tray_padding")) {
336 DLOG("tray_padding = %lld\n", val);
337 config.tray_padding = val;
338 return 1;
339 }
340
341 if (!strcmp(cur_key, "modifier")) {
342 DLOG("modifier = %lld\n", val);
343 config.modifier = (uint32_t)val;
344 return 1;
345 }
346
347 return 0;
348 }
349
350 /* A datastructure to pass all these callbacks to yajl */
351 static yajl_callbacks outputs_callbacks = {
352 .yajl_null = config_null_cb,
353 .yajl_boolean = config_boolean_cb,
354 .yajl_integer = config_integer_cb,
355 .yajl_string = config_string_cb,
356 .yajl_end_array = config_end_array_cb,
357 .yajl_map_key = config_map_key_cb,
358 };
359
360 /*
361 * Start parsing the received bar configuration JSON string
362 *
363 */
364 void parse_config_json(char *json) {
365 yajl_handle handle;
366 yajl_status state;
367 handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
368
369 TAILQ_INIT(&(config.bindings));
370 TAILQ_INIT(&(config.tray_outputs));
371
372 state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
373
374 /* FIXME: Proper error handling for JSON parsing */
375 switch (state) {
376 case yajl_status_ok:
377 break;
378 case yajl_status_client_canceled:
379 case yajl_status_error:
380 ELOG("Could not parse config reply!\n");
381 exit(EXIT_FAILURE);
382 break;
383 }
384
385 yajl_free(handle);
386 }
387
388 /*
389 * free()s the color strings as soon as they are not needed anymore.
390 *
391 */
392 void free_colors(struct xcb_color_strings_t *colors) {
393 #define FREE_COLOR(x) \
394 do { \
395 FREE(colors->x); \
396 } while (0)
397 FREE_COLOR(bar_fg);
398 FREE_COLOR(bar_bg);
399 FREE_COLOR(sep_fg);
400 FREE_COLOR(focus_bar_fg);
401 FREE_COLOR(focus_bar_bg);
402 FREE_COLOR(focus_sep_fg);
403 FREE_COLOR(active_ws_fg);
404 FREE_COLOR(active_ws_bg);
405 FREE_COLOR(active_ws_border);
406 FREE_COLOR(inactive_ws_fg);
407 FREE_COLOR(inactive_ws_bg);
408 FREE_COLOR(inactive_ws_border);
409 FREE_COLOR(urgent_ws_fg);
410 FREE_COLOR(urgent_ws_bg);
411 FREE_COLOR(urgent_ws_border);
412 FREE_COLOR(focus_ws_fg);
413 FREE_COLOR(focus_ws_bg);
414 FREE_COLOR(focus_ws_border);
415 FREE_COLOR(binding_mode_fg);
416 FREE_COLOR(binding_mode_bg);
417 FREE_COLOR(binding_mode_border);
418 #undef FREE_COLOR
419 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * ipc.c: Communicating with i3
7 *
8 */
9 #include "common.h"
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdint.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <i3/ipc.h>
20 #include <ev.h>
21 #ifdef I3_ASAN_ENABLED
22 #include <sanitizer/lsan_interface.h>
23 #endif
24
25 ev_io *i3_connection;
26
27 const char *sock_path;
28
29 typedef void (*handler_t)(char *);
30
31 /*
32 * Called, when we get a reply to a command from i3.
33 * Since i3 does not give us much feedback on commands, we do not much
34 *
35 */
36 static void got_command_reply(char *reply) {
37 /* TODO: Error handling for command replies */
38 }
39
40 /*
41 * Called, when we get a reply with workspaces data
42 *
43 */
44 static void got_workspace_reply(char *reply) {
45 DLOG("Got workspace data!\n");
46 parse_workspaces_json(reply);
47 draw_bars(false);
48 }
49
50 /*
51 * Called, when we get a reply for a subscription.
52 * Since i3 does not give us much feedback on commands, we do not much
53 *
54 */
55 static void got_subscribe_reply(char *reply) {
56 DLOG("Got subscribe reply: %s\n", reply);
57 /* TODO: Error handling for subscribe commands */
58 }
59
60 /*
61 * Called, when we get a reply with outputs data
62 *
63 */
64 static void got_output_reply(char *reply) {
65 DLOG("Clearing old output configuration...\n");
66 free_outputs();
67
68 DLOG("Parsing outputs JSON...\n");
69 parse_outputs_json(reply);
70 DLOG("Reconfiguring windows...\n");
71 reconfig_windows(false);
72
73 i3_output *o_walk;
74 SLIST_FOREACH(o_walk, outputs, slist) {
75 kick_tray_clients(o_walk);
76 }
77
78 if (!config.disable_ws) {
79 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
80 }
81
82 draw_bars(false);
83 }
84
85 /*
86 * Called when we get the configuration for our bar instance
87 *
88 */
89 static void got_bar_config(char *reply) {
90 DLOG("Received bar config \"%s\"\n", reply);
91 /* We initiate the main function by requesting infos about the outputs and
92 * workspaces. Everything else (creating the bars, showing the right workspace-
93 * buttons and more) is taken care of by the event-drivenness of the code */
94 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
95
96 free_colors(&(config.colors));
97 parse_config_json(reply);
98
99 /* Now we can actually use 'config', so let's subscribe to the appropriate
100 * events and request the workspaces if necessary. */
101 subscribe_events();
102 if (!config.disable_ws)
103 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
104
105 /* Initialize the rest of XCB */
106 init_xcb_late(config.fontname);
107
108 /* Resolve color strings to colorpixels and save them, then free the strings. */
109 init_colors(&(config.colors));
110
111 start_child(config.command);
112 }
113
114 /* Data structure to easily call the reply handlers later */
115 handler_t reply_handlers[] = {
116 &got_command_reply, /* I3_IPC_REPLY_TYPE_COMMAND */
117 &got_workspace_reply, /* I3_IPC_REPLY_TYPE_WORKSPACES */
118 &got_subscribe_reply, /* I3_IPC_REPLY_TYPE_SUBSCRIBE */
119 &got_output_reply, /* I3_IPC_REPLY_TYPE_OUTPUTS */
120 NULL, /* I3_IPC_REPLY_TYPE_TREE */
121 NULL, /* I3_IPC_REPLY_TYPE_MARKS */
122 &got_bar_config, /* I3_IPC_REPLY_TYPE_BAR_CONFIG */
123 NULL, /* I3_IPC_REPLY_TYPE_VERSION */
124 NULL, /* I3_IPC_REPLY_TYPE_BINDING_MODES */
125 NULL, /* I3_IPC_REPLY_TYPE_CONFIG */
126 NULL, /* I3_IPC_REPLY_TYPE_TICK */
127 NULL, /* I3_IPC_REPLY_TYPE_SYNC */
128 };
129
130 /*
131 * Called, when a workspace event arrives (i.e. the user changed the workspace)
132 *
133 */
134 static void got_workspace_event(char *event) {
135 DLOG("Got workspace event!\n");
136 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
137 }
138
139 /*
140 * Called, when an output event arrives (i.e. the screen configuration changed)
141 *
142 */
143 static void got_output_event(char *event) {
144 DLOG("Got output event!\n");
145 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
146 if (!config.disable_ws) {
147 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
148 }
149 }
150
151 /*
152 * Called, when a mode event arrives (i3 changed binding mode).
153 *
154 */
155 static void got_mode_event(char *event) {
156 DLOG("Got mode event!\n");
157 parse_mode_json(event);
158 draw_bars(false);
159 }
160
161 /*
162 * Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode)
163 *
164 */
165 static void got_bar_config_update(char *event) {
166 /* check whether this affect this bar instance by checking the bar_id */
167 char *expected_id;
168 sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id);
169 char *found_id = strstr(event, expected_id);
170 FREE(expected_id);
171 if (found_id == NULL)
172 return;
173
174 /* reconfigure the bar based on the current outputs */
175 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
176
177 free_colors(&(config.colors));
178
179 /* update the configuration with the received settings */
180 DLOG("Received bar config update \"%s\"\n", event);
181 char *old_command = config.command ? sstrdup(config.command) : NULL;
182 bar_display_mode_t old_mode = config.hide_on_modifier;
183 parse_config_json(event);
184 if (old_mode != config.hide_on_modifier) {
185 reconfig_windows(true);
186 }
187
188 /* update fonts and colors */
189 init_xcb_late(config.fontname);
190 init_colors(&(config.colors));
191
192 /* restart status command process */
193 if (old_command && strcmp(old_command, config.command) != 0) {
194 kill_child();
195 start_child(config.command);
196 }
197 free(old_command);
198
199 draw_bars(false);
200 }
201
202 /* Data structure to easily call the event handlers later */
203 handler_t event_handlers[] = {
204 &got_workspace_event,
205 &got_output_event,
206 &got_mode_event,
207 NULL,
208 &got_bar_config_update,
209 };
210
211 /*
212 * Called, when we get a message from i3
213 *
214 */
215 static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
216 DLOG("Got data!\n");
217 int fd = watcher->fd;
218
219 /* First we only read the header, because we know its length */
220 uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) * 2;
221 char *header = smalloc(header_len);
222
223 /* We first parse the fixed-length IPC header, to know, how much data
224 * we have to expect */
225 uint32_t rec = 0;
226 while (rec < header_len) {
227 int n = read(fd, header + rec, header_len - rec);
228 if (n == -1) {
229 ELOG("read() failed: %s\n", strerror(errno));
230 exit(EXIT_FAILURE);
231 }
232 if (n == 0) {
233 /* EOF received. Since i3 will restart i3bar instances as appropriate,
234 * we exit here. */
235 DLOG("EOF received, exiting...\n");
236 #ifdef I3_ASAN_ENABLED
237 __lsan_do_leak_check();
238 #endif
239 clean_xcb();
240 exit(EXIT_SUCCESS);
241 }
242 rec += n;
243 }
244
245 if (strncmp(header, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC))) {
246 ELOG("Wrong magic code: %.*s\n Expected: %s\n",
247 (int)strlen(I3_IPC_MAGIC),
248 header,
249 I3_IPC_MAGIC);
250 exit(EXIT_FAILURE);
251 }
252
253 char *walk = header + strlen(I3_IPC_MAGIC);
254 uint32_t size;
255 memcpy(&size, (uint32_t *)walk, sizeof(uint32_t));
256 walk += sizeof(uint32_t);
257 uint32_t type;
258 memcpy(&type, (uint32_t *)walk, sizeof(uint32_t));
259
260 /* Now that we know, what to expect, we can start read()ing the rest
261 * of the message */
262 char *buffer = smalloc(size + 1);
263 rec = 0;
264
265 while (rec < size) {
266 int n = read(fd, buffer + rec, size - rec);
267 if (n == -1) {
268 ELOG("read() failed: %s\n", strerror(errno));
269 exit(EXIT_FAILURE);
270 }
271 if (n == 0) {
272 ELOG("Nothing to read!\n");
273 exit(EXIT_FAILURE);
274 }
275 rec += n;
276 }
277 buffer[size] = '\0';
278
279 /* And call the callback (indexed by the type) */
280 if (type & (1UL << 31)) {
281 type ^= 1UL << 31;
282 event_handlers[type](buffer);
283 } else {
284 if (reply_handlers[type])
285 reply_handlers[type](buffer);
286 }
287
288 FREE(header);
289 FREE(buffer);
290 }
291
292 /*
293 * Sends a message to i3.
294 * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
295 *
296 */
297 int i3_send_msg(uint32_t type, const char *payload) {
298 uint32_t len = 0;
299 if (payload != NULL) {
300 len = strlen(payload);
301 }
302
303 /* We are a wellbehaved client and send a proper header first */
304 uint32_t to_write = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) * 2 + len;
305 /* TODO: I'm not entirely sure if this buffer really has to contain more
306 * than the pure header (why not just write() the payload from *payload?),
307 * but we leave it for now */
308 char *buffer = smalloc(to_write);
309 char *walk = buffer;
310
311 memcpy(buffer, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC));
312 walk += strlen(I3_IPC_MAGIC);
313 memcpy(walk, &len, sizeof(uint32_t));
314 walk += sizeof(uint32_t);
315 memcpy(walk, &type, sizeof(uint32_t));
316 walk += sizeof(uint32_t);
317
318 if (payload != NULL)
319 strncpy(walk, payload, len);
320
321 swrite(i3_connection->fd, buffer, to_write);
322
323 FREE(buffer);
324
325 return 1;
326 }
327
328 /*
329 * Initiate a connection to i3.
330 * socket_path must be a valid path to the ipc_socket of i3
331 *
332 */
333 int init_connection(const char *socket_path) {
334 sock_path = socket_path;
335 int sockfd = ipc_connect(socket_path);
336 i3_connection = smalloc(sizeof(ev_io));
337 ev_io_init(i3_connection, &got_data, sockfd, EV_READ);
338 ev_io_start(main_loop, i3_connection);
339 return 1;
340 }
341
342 /*
343 * Destroy the connection to i3.
344 */
345 void destroy_connection(void) {
346 close(i3_connection->fd);
347 ev_io_stop(main_loop, i3_connection);
348 }
349
350 /*
351 * Subscribe to all the i3-events, we need
352 *
353 */
354 void subscribe_events(void) {
355 if (config.disable_ws) {
356 i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\", \"barconfig_update\" ]");
357 } else {
358 i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\", \"barconfig_update\" ]");
359 }
360 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 */
7 #include "common.h"
8
9 #include <stdio.h>
10 #include <i3/ipc.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <ev.h>
16 #include <getopt.h>
17 #include <glob.h>
18
19 /*
20 * Having verboselog(), errorlog() and debuglog() is necessary when using libi3.
21 *
22 */
23 void verboselog(char *fmt, ...) {
24 va_list args;
25
26 va_start(args, fmt);
27 vfprintf(stdout, fmt, args);
28 va_end(args);
29 }
30
31 void errorlog(char *fmt, ...) {
32 va_list args;
33
34 va_start(args, fmt);
35 vfprintf(stderr, fmt, args);
36 va_end(args);
37 }
38
39 void debuglog(char *fmt, ...) {
40 }
41
42 /*
43 * Glob path, i.e. expand ~
44 *
45 */
46 static char *expand_path(char *path) {
47 static glob_t globbuf;
48 if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
49 ELOG("glob() failed\n");
50 exit(EXIT_FAILURE);
51 }
52 char *result = sstrdup(globbuf.gl_pathc > 0 ? globbuf.gl_pathv[0] : path);
53 globfree(&globbuf);
54 return result;
55 }
56
57 static void print_usage(char *elf_name) {
58 printf("Usage: %s -b bar_id [-s sock_path] [-h] [-v]\n", elf_name);
59 printf("\n");
60 printf("-b, --bar_id <bar_id>\tBar ID for which to get the configuration\n");
61 printf("-s, --socket <sock_path>\tConnect to i3 via <sock_path>\n");
62 printf("-h, --help Display this help message and exit\n");
63 printf("-v, --version Display version number and exit\n");
64 printf("-V, --verbose Enable verbose mode\n");
65 printf("\n");
66 printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
67 " as soon as there is a 'bar' configuration block in your\n"
68 " config file. You should never need to start it manually.\n");
69 printf("\n");
70 }
71
72 /*
73 * We watch various signals, that are there to make our application stop.
74 * If we get one of those, we ev_unloop() and invoke the cleanup routines
75 * in main() with that
76 *
77 */
78 static void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
79 switch (watcher->signum) {
80 case SIGTERM:
81 DLOG("Got a SIGTERM, stopping\n");
82 break;
83 case SIGINT:
84 DLOG("Got a SIGINT, stopping\n");
85 break;
86 case SIGHUP:
87 DLOG("Got a SIGHUP, stopping\n");
88 }
89 ev_unloop(main_loop, EVUNLOOP_ALL);
90 }
91
92 int main(int argc, char **argv) {
93 int opt;
94 int option_index = 0;
95 char *socket_path = getenv("I3SOCK");
96 if (socket_path != NULL) {
97 socket_path = sstrdup(socket_path);
98 }
99 char *i3_default_sock_path = "/tmp/i3-ipc.sock";
100
101 /* Initialize the standard config to use 0 as default */
102 memset(&config, '\0', sizeof(config_t));
103
104 static struct option long_opt[] = {
105 {"socket", required_argument, 0, 's'},
106 {"bar_id", required_argument, 0, 'b'},
107 {"help", no_argument, 0, 'h'},
108 {"version", no_argument, 0, 'v'},
109 {"verbose", no_argument, 0, 'V'},
110 {NULL, 0, 0, 0}};
111
112 while ((opt = getopt_long(argc, argv, "b:s:hvV", long_opt, &option_index)) != -1) {
113 switch (opt) {
114 case 's':
115 socket_path = expand_path(optarg);
116 break;
117 case 'v':
118 printf("i3bar version " I3_VERSION " © 2010 Axel Wagner and contributors\n");
119 exit(EXIT_SUCCESS);
120 break;
121 case 'b':
122 config.bar_id = sstrdup(optarg);
123 break;
124 case 'V':
125 config.verbose = true;
126 break;
127 default:
128 print_usage(argv[0]);
129 exit(EXIT_SUCCESS);
130 break;
131 }
132 }
133
134 if (!config.bar_id) {
135 /* TODO: maybe we want -f which will automatically ask i3 for the first
136 * configured bar (and error out if there are too many)? */
137 ELOG("No bar_id passed. Please let i3 start i3bar or specify --bar_id\n");
138 exit(EXIT_FAILURE);
139 }
140
141 main_loop = ev_default_loop(0);
142
143 char *atom_sock_path = init_xcb_early();
144
145 if (socket_path == NULL) {
146 socket_path = atom_sock_path;
147 } else {
148 free(atom_sock_path);
149 }
150
151 if (socket_path == NULL) {
152 ELOG("No socket path specified, default to %s\n", i3_default_sock_path);
153 socket_path = expand_path(i3_default_sock_path);
154 }
155
156 init_dpi();
157
158 init_outputs();
159 if (init_connection(socket_path)) {
160 /* Request the bar configuration. When it arrives, we fill the config array. */
161 i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
162 }
163 free(socket_path);
164
165 /* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main loop.
166 * We only need those watchers on the stack, so putting them on the stack saves us
167 * some calls to free() */
168 ev_signal *sig_term = smalloc(sizeof(ev_signal));
169 ev_signal *sig_int = smalloc(sizeof(ev_signal));
170 ev_signal *sig_hup = smalloc(sizeof(ev_signal));
171
172 ev_signal_init(sig_term, &sig_cb, SIGTERM);
173 ev_signal_init(sig_int, &sig_cb, SIGINT);
174 ev_signal_init(sig_hup, &sig_cb, SIGHUP);
175
176 ev_signal_start(main_loop, sig_term);
177 ev_signal_start(main_loop, sig_int);
178 ev_signal_start(main_loop, sig_hup);
179
180 /* From here on everything should run smooth for itself, just start listening for
181 * events. We stop simply stop the event loop, when we are finished */
182 ev_loop(main_loop, 0);
183
184 kill_child();
185
186 clean_xcb();
187 ev_default_destroy();
188
189 return 0;
190 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * mode.c: Handle mode event and show current binding mode in the bar
7 *
8 */
9 #include "common.h"
10
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <yajl/yajl_parse.h>
16 #include <yajl/yajl_version.h>
17
18 /* A datatype to pass through the callbacks to save the state */
19 struct mode_json_params {
20 char *json;
21 char *cur_key;
22 char *name;
23 bool pango_markup;
24 mode *mode;
25 };
26
27 /*
28 * Parse a string (change)
29 *
30 */
31 static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
32 struct mode_json_params *params = (struct mode_json_params *)params_;
33
34 if (!strcmp(params->cur_key, "change")) {
35 sasprintf(&(params->name), "%.*s", len, val);
36 FREE(params->cur_key);
37 return 1;
38 }
39
40 FREE(params->cur_key);
41 return 0;
42 }
43
44 /*
45 * Parse a boolean.
46 *
47 */
48 static int mode_boolean_cb(void *params_, int val) {
49 struct mode_json_params *params = (struct mode_json_params *)params_;
50
51 if (strcmp(params->cur_key, "pango_markup") == 0) {
52 DLOG("Setting pango_markup to %d.\n", val);
53 params->pango_markup = val;
54
55 FREE(params->cur_key);
56 return 1;
57 }
58
59 FREE(params->cur_key);
60 return 0;
61 }
62
63 /*
64 * Parse a key.
65 *
66 * Essentially we just save it in the parsing state
67 *
68 */
69 static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
70 struct mode_json_params *params = (struct mode_json_params *)params_;
71 FREE(params->cur_key);
72 sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
73 return 1;
74 }
75
76 static int mode_end_map_cb(void *params_) {
77 struct mode_json_params *params = (struct mode_json_params *)params_;
78
79 /* Save the name */
80 params->mode->name = i3string_from_utf8(params->name);
81 i3string_set_markup(params->mode->name, params->pango_markup);
82 /* Save its rendered width */
83 params->mode->width = predict_text_width(params->mode->name);
84
85 DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name));
86 FREE(params->cur_key);
87
88 return 1;
89 }
90
91 /* A datastructure to pass all these callbacks to yajl */
92 static yajl_callbacks mode_callbacks = {
93 .yajl_string = mode_string_cb,
94 .yajl_boolean = mode_boolean_cb,
95 .yajl_map_key = mode_map_key_cb,
96 .yajl_end_map = mode_end_map_cb,
97 };
98
99 /*
100 * Start parsing the received JSON string
101 *
102 */
103 void parse_mode_json(char *json) {
104 /* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
105 * JSON in chunks */
106 struct mode_json_params params;
107
108 mode binding;
109
110 params.cur_key = NULL;
111 params.json = json;
112 params.mode = &binding;
113
114 yajl_handle handle;
115 yajl_status state;
116
117 handle = yajl_alloc(&mode_callbacks, NULL, (void *)&params);
118
119 state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
120
121 /* FIXME: Proper error handling for JSON parsing */
122 switch (state) {
123 case yajl_status_ok:
124 break;
125 case yajl_status_client_canceled:
126 case yajl_status_error:
127 ELOG("Could not parse mode event!\n");
128 exit(EXIT_FAILURE);
129 break;
130 }
131
132 /* We don't want to indicate default binding mode */
133 if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0)
134 I3STRING_FREE(params.mode->name);
135
136 /* Set the new binding mode */
137 set_current_mode(&binding);
138
139 yajl_free(handle);
140
141 FREE(params.cur_key);
142 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * outputs.c: Maintaining the outputs list
7 *
8 */
9 #include "common.h"
10
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <i3/ipc.h>
16 #include <yajl/yajl_parse.h>
17 #include <yajl/yajl_version.h>
18
19 /* A datatype to pass through the callbacks to save the state */
20 struct outputs_json_params {
21 struct outputs_head *outputs;
22 i3_output *outputs_walk;
23 char *cur_key;
24 char *json;
25 bool in_rect;
26 };
27
28 /*
29 * Parse a null value (current_workspace)
30 *
31 */
32 static int outputs_null_cb(void *params_) {
33 struct outputs_json_params *params = (struct outputs_json_params *)params_;
34
35 FREE(params->cur_key);
36
37 return 1;
38 }
39
40 /*
41 * Parse a boolean value (active)
42 *
43 */
44 static int outputs_boolean_cb(void *params_, int val) {
45 struct outputs_json_params *params = (struct outputs_json_params *)params_;
46
47 if (!strcmp(params->cur_key, "active")) {
48 params->outputs_walk->active = val;
49 FREE(params->cur_key);
50 return 1;
51 }
52
53 if (!strcmp(params->cur_key, "primary")) {
54 params->outputs_walk->primary = val;
55 FREE(params->cur_key);
56 return 1;
57 }
58
59 return 0;
60 }
61
62 /*
63 * Parse an integer (current_workspace or the rect)
64 *
65 */
66 static int outputs_integer_cb(void *params_, long long val) {
67 struct outputs_json_params *params = (struct outputs_json_params *)params_;
68
69 if (!strcmp(params->cur_key, "current_workspace")) {
70 params->outputs_walk->ws = (int)val;
71 FREE(params->cur_key);
72 return 1;
73 }
74
75 if (!strcmp(params->cur_key, "x")) {
76 params->outputs_walk->rect.x = (int)val;
77 FREE(params->cur_key);
78 return 1;
79 }
80
81 if (!strcmp(params->cur_key, "y")) {
82 params->outputs_walk->rect.y = (int)val;
83 FREE(params->cur_key);
84 return 1;
85 }
86
87 if (!strcmp(params->cur_key, "width")) {
88 params->outputs_walk->rect.w = (int)val;
89 FREE(params->cur_key);
90 return 1;
91 }
92
93 if (!strcmp(params->cur_key, "height")) {
94 params->outputs_walk->rect.h = (int)val;
95 FREE(params->cur_key);
96 return 1;
97 }
98
99 return 0;
100 }
101
102 /*
103 * Parse a string (name)
104 *
105 */
106 static int outputs_string_cb(void *params_, const unsigned char *val, size_t len) {
107 struct outputs_json_params *params = (struct outputs_json_params *)params_;
108
109 if (!strcmp(params->cur_key, "current_workspace")) {
110 char *copy = NULL;
111 sasprintf(&copy, "%.*s", len, val);
112
113 char *end;
114 errno = 0;
115 long parsed_num = strtol(copy, &end, 10);
116 if (errno == 0 &&
117 (end && *end == '\0'))
118 params->outputs_walk->ws = parsed_num;
119
120 FREE(copy);
121 FREE(params->cur_key);
122 return 1;
123 }
124
125 if (strcmp(params->cur_key, "name")) {
126 return 0;
127 }
128
129 sasprintf(&(params->outputs_walk->name), "%.*s", len, val);
130
131 FREE(params->cur_key);
132 return 1;
133 }
134
135 /*
136 * We hit the start of a JSON map (rect or a new output)
137 *
138 */
139 static int outputs_start_map_cb(void *params_) {
140 struct outputs_json_params *params = (struct outputs_json_params *)params_;
141 i3_output *new_output = NULL;
142
143 if (params->cur_key == NULL) {
144 new_output = smalloc(sizeof(i3_output));
145 new_output->name = NULL;
146 new_output->active = false;
147 new_output->primary = false;
148 new_output->visible = false;
149 new_output->ws = 0,
150 new_output->statusline_width = 0;
151 new_output->statusline_short_text = false;
152 memset(&new_output->rect, 0, sizeof(rect));
153 memset(&new_output->bar, 0, sizeof(surface_t));
154 memset(&new_output->buffer, 0, sizeof(surface_t));
155 memset(&new_output->statusline_buffer, 0, sizeof(surface_t));
156
157 new_output->workspaces = smalloc(sizeof(struct ws_head));
158 TAILQ_INIT(new_output->workspaces);
159
160 new_output->trayclients = smalloc(sizeof(struct tc_head));
161 TAILQ_INIT(new_output->trayclients);
162
163 params->outputs_walk = new_output;
164
165 return 1;
166 }
167
168 if (!strcmp(params->cur_key, "rect")) {
169 params->in_rect = true;
170 }
171
172 return 1;
173 }
174
175 static void clear_output(i3_output *output) {
176 FREE(output->name);
177 FREE(output->workspaces);
178 FREE(output->trayclients);
179 }
180
181 /*
182 * We hit the end of a map (rect or a new output)
183 *
184 */
185 static int outputs_end_map_cb(void *params_) {
186 struct outputs_json_params *params = (struct outputs_json_params *)params_;
187 if (params->in_rect) {
188 params->in_rect = false;
189 /* Ignore the end of a rect */
190 return 1;
191 }
192
193 /* See if we actually handle that output */
194 if (config.num_outputs > 0) {
195 bool handle_output = false;
196 for (int c = 0; c < config.num_outputs; c++) {
197 if (strcasecmp(params->outputs_walk->name, config.outputs[c]) == 0 ||
198 (strcasecmp(config.outputs[c], "primary") == 0 &&
199 params->outputs_walk->primary)) {
200 handle_output = true;
201 break;
202 }
203 }
204 if (!handle_output) {
205 DLOG("Ignoring output \"%s\", not configured to handle it.\n",
206 params->outputs_walk->name);
207 clear_output(params->outputs_walk);
208 FREE(params->outputs_walk);
209 FREE(params->cur_key);
210 return 1;
211 }
212 }
213
214 i3_output *target = get_output_by_name(params->outputs_walk->name);
215
216 if (target == NULL) {
217 SLIST_INSERT_HEAD(outputs, params->outputs_walk, slist);
218 } else {
219 target->active = params->outputs_walk->active;
220 target->primary = params->outputs_walk->primary;
221 target->ws = params->outputs_walk->ws;
222 target->rect = params->outputs_walk->rect;
223
224 clear_output(params->outputs_walk);
225 FREE(params->outputs_walk);
226 }
227 return 1;
228 }
229
230 /*
231 * Parse a key.
232 *
233 * Essentially we just save it in the parsing state
234 *
235 */
236 static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
237 struct outputs_json_params *params = (struct outputs_json_params *)params_;
238 FREE(params->cur_key);
239 sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
240 return 1;
241 }
242
243 /* A datastructure to pass all these callbacks to yajl */
244 static yajl_callbacks outputs_callbacks = {
245 .yajl_null = outputs_null_cb,
246 .yajl_boolean = outputs_boolean_cb,
247 .yajl_integer = outputs_integer_cb,
248 .yajl_string = outputs_string_cb,
249 .yajl_start_map = outputs_start_map_cb,
250 .yajl_map_key = outputs_map_key_cb,
251 .yajl_end_map = outputs_end_map_cb,
252 };
253
254 /*
255 * Initiate the outputs list
256 *
257 */
258 void init_outputs(void) {
259 outputs = smalloc(sizeof(struct outputs_head));
260 SLIST_INIT(outputs);
261 }
262
263 /*
264 * Start parsing the received JSON string
265 *
266 */
267 void parse_outputs_json(char *json) {
268 struct outputs_json_params params;
269 params.outputs_walk = NULL;
270 params.cur_key = NULL;
271 params.json = json;
272 params.in_rect = false;
273
274 yajl_handle handle;
275 yajl_status state;
276 handle = yajl_alloc(&outputs_callbacks, NULL, (void *)&params);
277
278 state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
279
280 /* FIXME: Proper errorhandling for JSON-parsing */
281 switch (state) {
282 case yajl_status_ok:
283 break;
284 case yajl_status_client_canceled:
285 case yajl_status_error:
286 ELOG("Could not parse outputs reply!\n");
287 exit(EXIT_FAILURE);
288 break;
289 }
290
291 yajl_free(handle);
292 }
293
294 /*
295 * free() all outputs data structures.
296 *
297 */
298 void free_outputs(void) {
299 free_workspaces();
300
301 i3_output *outputs_walk;
302 if (outputs == NULL) {
303 return;
304 }
305 SLIST_FOREACH(outputs_walk, outputs, slist) {
306 destroy_window(outputs_walk);
307 if (outputs_walk->trayclients != NULL && !TAILQ_EMPTY(outputs_walk->trayclients)) {
308 FREE_TAILQ(outputs_walk->trayclients, trayclient);
309 }
310 clear_output(outputs_walk);
311 }
312 FREE_SLIST(outputs, i3_output);
313 }
314
315 /*
316 * Returns the output with the given name
317 *
318 */
319 i3_output *get_output_by_name(char *name) {
320 i3_output *walk;
321 if (name == NULL) {
322 return NULL;
323 }
324 SLIST_FOREACH(walk, outputs, slist) {
325 if (!strcmp(walk->name, name)) {
326 break;
327 }
328 }
329
330 return walk;
331 }
332
333 /*
334 * Returns true if the output has the currently focused workspace
335 *
336 */
337 bool output_has_focus(i3_output *output) {
338 i3_ws *ws_walk;
339 TAILQ_FOREACH(ws_walk, output->workspaces, tailq) {
340 if (ws_walk->focused) {
341 return true;
342 }
343 }
344 return false;
345 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * parse_json_header.c: Parse the JSON protocol header to determine
7 * protocol version and features.
8 *
9 */
10 #include "common.h"
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <signal.h>
17 #include <stdio.h>
18 #include <fcntl.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <err.h>
22 #include <ev.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <yajl/yajl_common.h>
26 #include <yajl/yajl_parse.h>
27 #include <yajl/yajl_version.h>
28
29 static enum {
30 KEY_VERSION,
31 KEY_STOP_SIGNAL,
32 KEY_CONT_SIGNAL,
33 KEY_CLICK_EVENTS,
34 NO_KEY
35 } current_key;
36
37 static int header_integer(void *ctx, long long val) {
38 i3bar_child *child = ctx;
39
40 switch (current_key) {
41 case KEY_VERSION:
42 child->version = val;
43 break;
44 case KEY_STOP_SIGNAL:
45 child->stop_signal = val;
46 break;
47 case KEY_CONT_SIGNAL:
48 child->cont_signal = val;
49 break;
50 default:
51 break;
52 }
53
54 return 1;
55 }
56
57 static int header_boolean(void *ctx, int val) {
58 i3bar_child *child = ctx;
59
60 switch (current_key) {
61 case KEY_CLICK_EVENTS:
62 child->click_events = val;
63 break;
64 default:
65 break;
66 }
67
68 return 1;
69 }
70
71 #define CHECK_KEY(name) (stringlen == strlen(name) && \
72 STARTS_WITH((const char *)stringval, stringlen, name))
73
74 static int header_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
75 if (CHECK_KEY("version")) {
76 current_key = KEY_VERSION;
77 } else if (CHECK_KEY("stop_signal")) {
78 current_key = KEY_STOP_SIGNAL;
79 } else if (CHECK_KEY("cont_signal")) {
80 current_key = KEY_CONT_SIGNAL;
81 } else if (CHECK_KEY("click_events")) {
82 current_key = KEY_CLICK_EVENTS;
83 }
84 return 1;
85 }
86
87 static void child_init(i3bar_child *child) {
88 child->version = 0;
89 child->stop_signal = SIGSTOP;
90 child->cont_signal = SIGCONT;
91 }
92
93 /*
94 * Parse the JSON protocol header to determine protocol version and features.
95 * In case the buffer does not contain a valid header (invalid JSON, or no
96 * version field found), the 'correct' field of the returned header is set to
97 * false. The amount of bytes consumed by parsing the header is returned in
98 * *consumed (if non-NULL).
99 *
100 */
101 void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed) {
102 static yajl_callbacks version_callbacks = {
103 .yajl_boolean = header_boolean,
104 .yajl_integer = header_integer,
105 .yajl_map_key = &header_map_key,
106 };
107
108 child_init(child);
109
110 current_key = NO_KEY;
111
112 yajl_handle handle = yajl_alloc(&version_callbacks, NULL, child);
113 /* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
114 * yajl 2, we need to be explicit. */
115 yajl_config(handle, yajl_allow_trailing_garbage, 1);
116
117 yajl_status state = yajl_parse(handle, buffer, length);
118 if (state != yajl_status_ok) {
119 child_init(child);
120 if (consumed != NULL)
121 *consumed = 0;
122 } else {
123 if (consumed != NULL)
124 *consumed = yajl_get_bytes_consumed(handle);
125 }
126
127 yajl_free(handle);
128 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * workspaces.c: Maintaining the workspace lists
7 *
8 */
9 #include "common.h"
10
11 #include <string.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <yajl/yajl_parse.h>
16 #include <yajl/yajl_version.h>
17
18 /* A datatype to pass through the callbacks to save the state */
19 struct workspaces_json_params {
20 struct ws_head *workspaces;
21 i3_ws *workspaces_walk;
22 char *cur_key;
23 char *json;
24 };
25
26 /*
27 * Parse a boolean value (visible, focused, urgent)
28 *
29 */
30 static int workspaces_boolean_cb(void *params_, int val) {
31 struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
32
33 if (!strcmp(params->cur_key, "visible")) {
34 params->workspaces_walk->visible = val;
35 FREE(params->cur_key);
36 return 1;
37 }
38
39 if (!strcmp(params->cur_key, "focused")) {
40 params->workspaces_walk->focused = val;
41 FREE(params->cur_key);
42 return 1;
43 }
44
45 if (!strcmp(params->cur_key, "urgent")) {
46 params->workspaces_walk->urgent = val;
47 FREE(params->cur_key);
48 return 1;
49 }
50
51 FREE(params->cur_key);
52
53 return 0;
54 }
55
56 /*
57 * Parse an integer (num or the rect)
58 *
59 */
60 static int workspaces_integer_cb(void *params_, long long val) {
61 struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
62
63 if (!strcmp(params->cur_key, "num")) {
64 params->workspaces_walk->num = (int)val;
65 FREE(params->cur_key);
66 return 1;
67 }
68
69 if (!strcmp(params->cur_key, "x")) {
70 params->workspaces_walk->rect.x = (int)val;
71 FREE(params->cur_key);
72 return 1;
73 }
74
75 if (!strcmp(params->cur_key, "y")) {
76 params->workspaces_walk->rect.y = (int)val;
77 FREE(params->cur_key);
78 return 1;
79 }
80
81 if (!strcmp(params->cur_key, "width")) {
82 params->workspaces_walk->rect.w = (int)val;
83 FREE(params->cur_key);
84 return 1;
85 }
86
87 if (!strcmp(params->cur_key, "height")) {
88 params->workspaces_walk->rect.h = (int)val;
89 FREE(params->cur_key);
90 return 1;
91 }
92
93 FREE(params->cur_key);
94 return 0;
95 }
96
97 /*
98 * Parse a string (name, output)
99 *
100 */
101 static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) {
102 struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
103
104 if (!strcmp(params->cur_key, "name")) {
105 const char *ws_name = (const char *)val;
106 params->workspaces_walk->canonical_name = sstrndup(ws_name, len);
107
108 if ((config.strip_ws_numbers || config.strip_ws_name) && params->workspaces_walk->num >= 0) {
109 /* Special case: strip off the workspace number/name */
110 static char ws_num[10];
111
112 snprintf(ws_num, sizeof(ws_num), "%d", params->workspaces_walk->num);
113
114 /* Calculate the length of the number str in the name */
115 size_t offset = strspn(ws_name, ws_num);
116
117 /* Also strip off the conventional ws name delimiter */
118 if (offset && ws_name[offset] == ':')
119 offset += 1;
120
121 if (config.strip_ws_numbers) {
122 /* Offset may be equal to length, in which case display the number */
123 params->workspaces_walk->name = (offset < len
124 ? i3string_from_markup_with_length(ws_name + offset, len - offset)
125 : i3string_from_markup(ws_num));
126 } else {
127 params->workspaces_walk->name = i3string_from_markup(ws_num);
128 }
129 } else {
130 /* Default case: just save the name */
131 params->workspaces_walk->name = i3string_from_markup_with_length(ws_name, len);
132 }
133
134 /* Save its rendered width */
135 params->workspaces_walk->name_width =
136 predict_text_width(params->workspaces_walk->name);
137
138 DLOG("Got workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
139 params->workspaces_walk->canonical_name,
140 i3string_as_utf8(params->workspaces_walk->name),
141 params->workspaces_walk->name_width,
142 i3string_get_num_glyphs(params->workspaces_walk->name));
143 FREE(params->cur_key);
144
145 return 1;
146 }
147
148 if (!strcmp(params->cur_key, "output")) {
149 /* We add the ws to the TAILQ of the output, it belongs to */
150 char *output_name = NULL;
151 sasprintf(&output_name, "%.*s", len, val);
152
153 i3_output *target = get_output_by_name(output_name);
154 if (target != NULL) {
155 params->workspaces_walk->output = target;
156
157 TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces,
158 params->workspaces_walk,
159 tailq);
160 }
161
162 FREE(output_name);
163 return 1;
164 }
165
166 return 0;
167 }
168
169 /*
170 * We hit the start of a JSON map (rect or a new output)
171 *
172 */
173 static int workspaces_start_map_cb(void *params_) {
174 struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
175
176 i3_ws *new_workspace = NULL;
177
178 if (params->cur_key == NULL) {
179 new_workspace = smalloc(sizeof(i3_ws));
180 new_workspace->num = -1;
181 new_workspace->name = NULL;
182 new_workspace->visible = 0;
183 new_workspace->focused = 0;
184 new_workspace->urgent = 0;
185 memset(&new_workspace->rect, 0, sizeof(rect));
186 new_workspace->output = NULL;
187
188 params->workspaces_walk = new_workspace;
189 return 1;
190 }
191
192 return 1;
193 }
194
195 /*
196 * Parse a key.
197 *
198 * Essentially we just save it in the parsing state
199 *
200 */
201 static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
202 struct workspaces_json_params *params = (struct workspaces_json_params *)params_;
203 FREE(params->cur_key);
204 sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
205 return 1;
206 }
207
208 /* A datastructure to pass all these callbacks to yajl */
209 static yajl_callbacks workspaces_callbacks = {
210 .yajl_boolean = workspaces_boolean_cb,
211 .yajl_integer = workspaces_integer_cb,
212 .yajl_string = workspaces_string_cb,
213 .yajl_start_map = workspaces_start_map_cb,
214 .yajl_map_key = workspaces_map_key_cb,
215 };
216
217 /*
218 * Start parsing the received JSON string
219 *
220 */
221 void parse_workspaces_json(char *json) {
222 /* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
223 * JSON in chunks */
224 struct workspaces_json_params params;
225
226 free_workspaces();
227
228 params.workspaces_walk = NULL;
229 params.cur_key = NULL;
230 params.json = json;
231
232 yajl_handle handle;
233 yajl_status state;
234 handle = yajl_alloc(&workspaces_callbacks, NULL, (void *)&params);
235
236 state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
237
238 /* FIXME: Proper error handling for JSON parsing */
239 switch (state) {
240 case yajl_status_ok:
241 break;
242 case yajl_status_client_canceled:
243 case yajl_status_error:
244 ELOG("Could not parse workspaces reply!\n");
245 exit(EXIT_FAILURE);
246 break;
247 }
248
249 yajl_free(handle);
250
251 FREE(params.cur_key);
252 }
253
254 /*
255 * free() all workspace data structures. Does not free() the heads of the tailqueues.
256 *
257 */
258 void free_workspaces(void) {
259 i3_output *outputs_walk;
260 if (outputs == NULL) {
261 return;
262 }
263 i3_ws *ws_walk;
264
265 SLIST_FOREACH(outputs_walk, outputs, slist) {
266 if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
267 TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
268 I3STRING_FREE(ws_walk->name);
269 FREE(ws_walk->canonical_name);
270 }
271 FREE_TAILQ(outputs_walk->workspaces, i3_ws);
272 }
273 }
274 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3bar - an xcb-based status- and ws-bar for i3
4 * © 2010 Axel Wagner and contributors (see also: LICENSE)
5 *
6 * xcb.c: Communicating with X
7 *
8 */
9 #include "common.h"
10
11 #include <xcb/xcb.h>
12 #include <xcb/xkb.h>
13 #include <xcb/xproto.h>
14 #include <xcb/xcb_aux.h>
15 #include <xcb/xcb_cursor.h>
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <i3/ipc.h>
23 #include <ev.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <err.h>
27
28 #include <X11/Xlib.h>
29 #include <X11/XKBlib.h>
30 #include <X11/extensions/XKB.h>
31
32 #ifdef I3_ASAN_ENABLED
33 #include <sanitizer/lsan_interface.h>
34 #endif
35
36 #include "libi3.h"
37
38 /** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a
39 * constant for that. */
40 #define XCB_CURSOR_LEFT_PTR 68
41
42 /* We save the atoms in an easy to access array, indexed by an enum */
43 enum {
44 #define ATOM_DO(name) name,
45 #include "xcb_atoms.def"
46 NUM_ATOMS
47 };
48
49 xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
50 xcb_atom_t atoms[NUM_ATOMS];
51
52 /* Variables, that are the same for all functions at all times */
53 xcb_connection_t *xcb_connection;
54 int screen;
55 xcb_screen_t *root_screen;
56 xcb_window_t xcb_root;
57 static xcb_cursor_t cursor;
58
59 /* selection window for tray support */
60 static xcb_window_t selwin = XCB_NONE;
61 static xcb_intern_atom_reply_t *tray_reply = NULL;
62
63 /* This is needed for integration with libi3 */
64 xcb_connection_t *conn;
65
66 /* The font we'll use */
67 static i3Font font;
68
69 /* Icon size (based on font size) */
70 int icon_size;
71
72 xcb_visualtype_t *visual_type;
73 uint8_t depth;
74 xcb_colormap_t colormap;
75
76 /* Overall height of the bar (based on font size) */
77 int bar_height;
78
79 /* These are only relevant for XKB, which we only need for grabbing modifiers */
80 int xkb_base;
81 bool mod_pressed = 0;
82
83 /* Event watchers, to interact with the user */
84 ev_prepare *xcb_prep;
85 ev_io *xcb_io;
86 ev_io *xkb_io;
87
88 /* The name of current binding mode */
89 static mode binding;
90
91 /* Indicates whether a new binding mode was recently activated */
92 bool activated_mode = false;
93
94 /* The output in which the tray should be displayed. */
95 static i3_output *output_for_tray;
96
97 /* The parsed colors */
98 struct xcb_colors_t {
99 color_t bar_fg;
100 color_t bar_bg;
101 color_t sep_fg;
102 color_t focus_bar_fg;
103 color_t focus_bar_bg;
104 color_t focus_sep_fg;
105 color_t active_ws_fg;
106 color_t active_ws_bg;
107 color_t active_ws_border;
108 color_t inactive_ws_fg;
109 color_t inactive_ws_bg;
110 color_t inactive_ws_border;
111 color_t urgent_ws_bg;
112 color_t urgent_ws_fg;
113 color_t urgent_ws_border;
114 color_t focus_ws_bg;
115 color_t focus_ws_fg;
116 color_t focus_ws_border;
117 color_t binding_mode_bg;
118 color_t binding_mode_fg;
119 color_t binding_mode_border;
120 };
121 struct xcb_colors_t colors;
122
123 /* Horizontal offset between a workspace label and button borders */
124 static const int ws_hoff_px = 4;
125
126 /* Vertical offset between a workspace label and button borders */
127 static const int ws_voff_px = 3;
128
129 /* Offset between two workspace buttons */
130 static const int ws_spacing_px = 1;
131
132 /* Offset between the statusline and 1) workspace buttons on the left
133 * 2) the tray or screen edge on the right */
134 static const int sb_hoff_px = 4;
135
136 /* Additional offset between the tray and the statusline, if the tray is not empty */
137 static const int tray_loff_px = 2;
138
139 /* Vertical offset between the bar and a separator */
140 static const int sep_voff_px = 4;
141
142 int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
143 xcb_generic_error_t *err;
144 if ((err = xcb_request_check(xcb_connection, cookie)) != NULL) {
145 fprintf(stderr, "[%s:%d] ERROR: %s. X Error Code: %d\n", __FILE__, line, err_msg, err->error_code);
146 return err->error_code;
147 }
148 return 0;
149 }
150
151 static uint32_t get_sep_offset(struct status_block *block) {
152 if (!block->no_separator && block->sep_block_width > 0)
153 return block->sep_block_width / 2 + block->sep_block_width % 2;
154 return 0;
155 }
156
157 static int get_tray_width(struct tc_head *trayclients) {
158 trayclient *trayclient;
159 int tray_width = 0;
160 TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
161 if (!trayclient->mapped)
162 continue;
163 tray_width += icon_size + logical_px(config.tray_padding);
164 }
165 if (tray_width > 0)
166 tray_width += logical_px(tray_loff_px);
167 return tray_width;
168 }
169
170 /*
171 * Draws a separator for the given block if necessary.
172 *
173 */
174 static void draw_separator(i3_output *output, uint32_t x, struct status_block *block, bool use_focus_colors) {
175 color_t sep_fg = (use_focus_colors ? colors.focus_sep_fg : colors.sep_fg);
176 color_t bar_bg = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
177
178 uint32_t sep_offset = get_sep_offset(block);
179 if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0)
180 return;
181
182 uint32_t center_x = x - sep_offset;
183 if (config.separator_symbol == NULL) {
184 /* Draw a classic one pixel, vertical separator. */
185 draw_util_rectangle(&output->statusline_buffer, sep_fg,
186 center_x,
187 logical_px(sep_voff_px),
188 logical_px(1),
189 bar_height - 2 * logical_px(sep_voff_px));
190 } else {
191 /* Draw a custom separator. */
192 uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2);
193 draw_util_text(config.separator_symbol, &output->statusline_buffer, sep_fg, bar_bg,
194 separator_x, logical_px(ws_voff_px), x - separator_x);
195 }
196 }
197
198 static uint32_t predict_statusline_length(bool use_short_text) {
199 uint32_t width = 0;
200 struct status_block *block;
201
202 TAILQ_FOREACH(block, &statusline_head, blocks) {
203 i3String *text = block->full_text;
204 struct status_block_render_desc *render = &block->full_render;
205 if (use_short_text && block->short_text != NULL) {
206 text = block->short_text;
207 render = &block->short_render;
208 }
209
210 if (i3string_get_num_bytes(text) == 0)
211 continue;
212
213 render->width = predict_text_width(text);
214 if (block->border)
215 render->width += logical_px(2);
216
217 /* Compute offset and append for text aligment in min_width. */
218 if (block->min_width <= render->width) {
219 render->x_offset = 0;
220 render->x_append = 0;
221 } else {
222 uint32_t padding_width = block->min_width - render->width;
223 switch (block->align) {
224 case ALIGN_LEFT:
225 render->x_append = padding_width;
226 break;
227 case ALIGN_RIGHT:
228 render->x_offset = padding_width;
229 break;
230 case ALIGN_CENTER:
231 render->x_offset = padding_width / 2;
232 render->x_append = padding_width / 2 + padding_width % 2;
233 break;
234 }
235 }
236
237 width += render->width + render->x_offset + render->x_append;
238
239 /* If this is not the last block, add some pixels for a separator. */
240 if (TAILQ_NEXT(block, blocks) != NULL)
241 width += block->sep_block_width;
242 }
243
244 return width;
245 }
246
247 /*
248 * Redraws the statusline to the output's statusline_buffer
249 */
250 static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
251 struct status_block *block;
252
253 color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
254 draw_util_clear_surface(&output->statusline_buffer, bar_color);
255
256 /* Use unsigned integer wraparound to clip off the left side.
257 * For example, if clip_left is 75, then x will start at the very large
258 * number INT_MAX-75, which is way outside the surface dimensions. Drawing
259 * to that x position is a no-op which XCB and Cairo safely ignore. Once x moves
260 * up by 75 and goes past INT_MAX, it will wrap around again to 0, and we start
261 * actually rendering content to the surface. */
262 uint32_t x = 0 - clip_left;
263
264 /* Draw the text of each block */
265 TAILQ_FOREACH(block, &statusline_head, blocks) {
266 i3String *text = block->full_text;
267 struct status_block_render_desc *render = &block->full_render;
268 if (use_short_text && block->short_text != NULL) {
269 text = block->short_text;
270 render = &block->short_render;
271 }
272
273 if (i3string_get_num_bytes(text) == 0)
274 continue;
275
276 color_t fg_color;
277 if (block->urgent) {
278 fg_color = colors.urgent_ws_fg;
279 } else if (block->color) {
280 fg_color = draw_util_hex_to_color(block->color);
281 } else if (use_focus_colors) {
282 fg_color = colors.focus_bar_fg;
283 } else {
284 fg_color = colors.bar_fg;
285 }
286
287 color_t bg_color = bar_color;
288
289 int border_width = (block->border) ? logical_px(1) : 0;
290 int full_render_width = render->width + render->x_offset + render->x_append;
291 if (block->border || block->background || block->urgent) {
292 /* Let's determine the colors first. */
293 color_t border_color = bar_color;
294 if (block->urgent) {
295 border_color = colors.urgent_ws_border;
296 bg_color = colors.urgent_ws_bg;
297 } else {
298 if (block->border)
299 border_color = draw_util_hex_to_color(block->border);
300 if (block->background)
301 bg_color = draw_util_hex_to_color(block->background);
302 }
303
304 /* Draw the border. */
305 draw_util_rectangle(&output->statusline_buffer, border_color,
306 x, logical_px(1),
307 full_render_width,
308 bar_height - logical_px(2));
309
310 /* Draw the background. */
311 draw_util_rectangle(&output->statusline_buffer, bg_color,
312 x + border_width,
313 logical_px(1) + border_width,
314 full_render_width - 2 * border_width,
315 bar_height - 2 * border_width - logical_px(2));
316 }
317
318 draw_util_text(text, &output->statusline_buffer, fg_color, bg_color,
319 x + render->x_offset + border_width, logical_px(ws_voff_px),
320 render->width - 2 * border_width);
321 x += full_render_width;
322
323 /* If this is not the last block, draw a separator. */
324 if (TAILQ_NEXT(block, blocks) != NULL) {
325 x += block->sep_block_width;
326 draw_separator(output, x, block, use_focus_colors);
327 }
328 }
329 }
330
331 /*
332 * Hides all bars (unmaps them)
333 *
334 */
335 static void hide_bars(void) {
336 if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW && config.hide_on_modifier == M_HIDE)) {
337 return;
338 }
339
340 i3_output *walk;
341 SLIST_FOREACH(walk, outputs, slist) {
342 if (!walk->active) {
343 continue;
344 }
345 xcb_unmap_window(xcb_connection, walk->bar.id);
346 }
347 stop_child();
348 }
349
350 /*
351 * Unhides all bars (maps them)
352 *
353 */
354 static void unhide_bars(void) {
355 if (config.hide_on_modifier != M_HIDE) {
356 return;
357 }
358
359 i3_output *walk;
360 xcb_void_cookie_t cookie;
361 uint32_t mask;
362 uint32_t values[5];
363
364 cont_child();
365
366 SLIST_FOREACH(walk, outputs, slist) {
367 if (walk->bar.id == XCB_NONE) {
368 continue;
369 }
370 mask = XCB_CONFIG_WINDOW_X |
371 XCB_CONFIG_WINDOW_Y |
372 XCB_CONFIG_WINDOW_WIDTH |
373 XCB_CONFIG_WINDOW_HEIGHT |
374 XCB_CONFIG_WINDOW_STACK_MODE;
375 values[0] = walk->rect.x;
376 if (config.position == POS_TOP)
377 values[1] = walk->rect.y;
378 else
379 values[1] = walk->rect.y + walk->rect.h - bar_height;
380 values[2] = walk->rect.w;
381 values[3] = bar_height;
382 values[4] = XCB_STACK_MODE_ABOVE;
383 DLOG("Reconfiguring window for output %s to %d,%d\n", walk->name, values[0], values[1]);
384 cookie = xcb_configure_window_checked(xcb_connection,
385 walk->bar.id,
386 mask,
387 values);
388
389 if (xcb_request_failed(cookie, "Could not reconfigure window")) {
390 exit(EXIT_FAILURE);
391 }
392 xcb_map_window(xcb_connection, walk->bar.id);
393 }
394 }
395
396 /*
397 * Parse the colors into a format that we can use
398 *
399 */
400 void init_colors(const struct xcb_color_strings_t *new_colors) {
401 #define PARSE_COLOR(name, def) \
402 do { \
403 colors.name = draw_util_hex_to_color(new_colors->name ? new_colors->name : def); \
404 } while (0)
405 PARSE_COLOR(bar_fg, "#FFFFFF");
406 PARSE_COLOR(bar_bg, "#000000");
407 PARSE_COLOR(sep_fg, "#666666");
408 PARSE_COLOR(active_ws_fg, "#FFFFFF");
409 PARSE_COLOR(active_ws_bg, "#333333");
410 PARSE_COLOR(active_ws_border, "#333333");
411 PARSE_COLOR(inactive_ws_fg, "#888888");
412 PARSE_COLOR(inactive_ws_bg, "#222222");
413 PARSE_COLOR(inactive_ws_border, "#333333");
414 PARSE_COLOR(urgent_ws_fg, "#FFFFFF");
415 PARSE_COLOR(urgent_ws_bg, "#900000");
416 PARSE_COLOR(urgent_ws_border, "#2f343a");
417 PARSE_COLOR(focus_ws_fg, "#FFFFFF");
418 PARSE_COLOR(focus_ws_bg, "#285577");
419 PARSE_COLOR(focus_ws_border, "#4c7899");
420 #undef PARSE_COLOR
421
422 #define PARSE_COLOR_FALLBACK(name, fallback) \
423 do { \
424 colors.name = new_colors->name ? draw_util_hex_to_color(new_colors->name) : colors.fallback; \
425 } while (0)
426
427 /* For the binding mode indicator colors, we don't hardcode a default.
428 * Instead, we fall back to urgent_ws_* colors. */
429 PARSE_COLOR_FALLBACK(binding_mode_fg, urgent_ws_fg);
430 PARSE_COLOR_FALLBACK(binding_mode_bg, urgent_ws_bg);
431 PARSE_COLOR_FALLBACK(binding_mode_border, urgent_ws_border);
432
433 /* Similarly, for unspecified focused bar colors, we fall back to the
434 * regular bar colors. */
435 PARSE_COLOR_FALLBACK(focus_bar_fg, bar_fg);
436 PARSE_COLOR_FALLBACK(focus_bar_bg, bar_bg);
437 PARSE_COLOR_FALLBACK(focus_sep_fg, sep_fg);
438 #undef PARSE_COLOR_FALLBACK
439
440 init_tray_colors();
441 xcb_flush(xcb_connection);
442 }
443
444 static bool execute_custom_command(xcb_keycode_t input_code, bool event_is_release) {
445 binding_t *binding;
446 TAILQ_FOREACH(binding, &(config.bindings), bindings) {
447 if ((binding->input_code != input_code) || (binding->release != event_is_release))
448 continue;
449
450 i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, binding->command);
451 return true;
452 }
453 return false;
454 }
455
456 /*
457 * Handle a button press event (i.e. a mouse click on one of our bars).
458 * We determine, whether the click occurred on a workspace button or if the scroll-
459 * wheel was used and change the workspace appropriately
460 *
461 */
462 static void handle_button(xcb_button_press_event_t *event) {
463 /* Determine, which bar was clicked */
464 i3_output *walk;
465 xcb_window_t bar = event->event;
466 SLIST_FOREACH(walk, outputs, slist) {
467 if (walk->bar.id == bar) {
468 break;
469 }
470 }
471
472 if (walk == NULL) {
473 DLOG("Unknown bar clicked!\n");
474 return;
475 }
476
477 DLOG("Got button %d\n", event->detail);
478
479 /* During button release events, only check for custom commands. */
480 const bool event_is_release = (event->response_type & ~0x80) == XCB_BUTTON_RELEASE;
481 if (event_is_release) {
482 execute_custom_command(event->detail, event_is_release);
483 return;
484 }
485
486 int32_t x = event->event_x >= 0 ? event->event_x : 0;
487 int workspace_width = 0;
488 i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk;
489
490 TAILQ_FOREACH(ws_walk, walk->workspaces, tailq) {
491 int w = 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
492 if (x >= workspace_width && x <= workspace_width + w)
493 clicked_ws = ws_walk;
494 if (ws_walk->visible)
495 cur_ws = ws_walk;
496 workspace_width += w;
497 if (TAILQ_NEXT(ws_walk, tailq) != NULL)
498 workspace_width += logical_px(ws_spacing_px);
499 }
500
501 if (x > workspace_width && child_want_click_events()) {
502 /* If the child asked for click events,
503 * check if a status block has been clicked. */
504 int tray_width = get_tray_width(walk->trayclients);
505 int last_block_x = 0;
506 int offset = walk->rect.w - walk->statusline_width - tray_width - logical_px((tray_width > 0) * sb_hoff_px);
507 int32_t statusline_x = x - offset;
508
509 if (statusline_x >= 0 && statusline_x < walk->statusline_width) {
510 struct status_block *block;
511
512 TAILQ_FOREACH(block, &statusline_head, blocks) {
513 i3String *text = block->full_text;
514 struct status_block_render_desc *render = &block->full_render;
515 if (walk->statusline_short_text && block->short_text != NULL) {
516 text = block->short_text;
517 render = &block->short_render;
518 }
519
520 if (i3string_get_num_bytes(text) == 0)
521 continue;
522
523 const int relative_x = statusline_x - last_block_x;
524 if (relative_x >= 0 && (uint32_t)relative_x <= render->width) {
525 send_block_clicked(event->detail, block->name, block->instance,
526 event->root_x, event->root_y, relative_x, event->event_y, render->width, bar_height,
527 event->state);
528 return;
529 }
530
531 last_block_x += render->width + render->x_append + render->x_offset + block->sep_block_width;
532 }
533 }
534 }
535
536 /* If a custom command was specified for this mouse button, it overrides
537 * the default behavior. */
538 if (execute_custom_command(event->detail, event_is_release)) {
539 return;
540 }
541
542 if (cur_ws == NULL) {
543 DLOG("No workspace active?\n");
544 return;
545 }
546 switch (event->detail) {
547 case XCB_BUTTON_SCROLL_UP:
548 case XCB_BUTTON_SCROLL_LEFT:
549 /* Mouse wheel up. We select the previous ws, if any.
550 * If there is no more workspace, don’t even send the workspace
551 * command, otherwise (with workspace auto_back_and_forth) we’d end
552 * up on the wrong workspace. */
553 if (cur_ws == TAILQ_FIRST(walk->workspaces))
554 return;
555
556 cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
557 break;
558 case XCB_BUTTON_SCROLL_DOWN:
559 case XCB_BUTTON_SCROLL_RIGHT:
560 /* Mouse wheel down. We select the next ws, if any.
561 * If there is no more workspace, don’t even send the workspace
562 * command, otherwise (with workspace auto_back_and_forth) we’d end
563 * up on the wrong workspace. */
564 if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
565 return;
566
567 cur_ws = TAILQ_NEXT(cur_ws, tailq);
568 break;
569 case 1:
570 cur_ws = clicked_ws;
571
572 /* if no workspace was clicked, focus our currently visible
573 * workspace if it is not already focused */
574 if (cur_ws == NULL) {
575 TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
576 if (cur_ws->visible && !cur_ws->focused)
577 break;
578 }
579 }
580
581 /* if there is nothing to focus, we are done */
582 if (cur_ws == NULL)
583 return;
584
585 break;
586 default:
587 return;
588 }
589
590 /* To properly handle workspace names with double quotes in them, we need
591 * to escape the double quotes. Unfortunately, that’s rather ugly in C: We
592 * first count the number of double quotes, then we allocate a large enough
593 * buffer, then we copy character by character. */
594 int num_quotes = 0;
595 size_t namelen = 0;
596 const char *utf8_name = cur_ws->canonical_name;
597 for (const char *walk = utf8_name; *walk != '\0'; walk++) {
598 if (*walk == '"' || *walk == '\\')
599 num_quotes++;
600 /* While we’re looping through the name anyway, we can save one
601 * strlen(). */
602 namelen++;
603 }
604
605 const size_t len = namelen + strlen("workspace \"\"") + 1;
606 char *buffer = scalloc(len + num_quotes, 1);
607 memcpy(buffer, "workspace \"", strlen("workspace \""));
608 size_t inpos, outpos;
609 for (inpos = 0, outpos = strlen("workspace \"");
610 inpos < namelen;
611 inpos++, outpos++) {
612 if (utf8_name[inpos] == '"' || utf8_name[inpos] == '\\') {
613 buffer[outpos] = '\\';
614 outpos++;
615 }
616 buffer[outpos] = utf8_name[inpos];
617 }
618 buffer[outpos] = '"';
619 i3_send_msg(I3_IPC_MESSAGE_TYPE_RUN_COMMAND, buffer);
620 free(buffer);
621 }
622
623 /*
624 * Handle visibility notifications: when none of the bars are visible, e.g.
625 * if windows are in fullscreen on each output, suspend the child process.
626 *
627 */
628 static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
629 bool visible = (event->state != XCB_VISIBILITY_FULLY_OBSCURED);
630 int num_visible = 0;
631 i3_output *output;
632
633 SLIST_FOREACH(output, outputs, slist) {
634 if (!output->active) {
635 continue;
636 }
637 if (output->bar.id == event->window) {
638 if (output->visible == visible) {
639 return;
640 }
641 output->visible = visible;
642 }
643 num_visible += output->visible;
644 }
645
646 if (num_visible == 0) {
647 stop_child();
648 } else if (num_visible == visible) {
649 /* Wake the child only when transitioning from 0 to 1 visible bar.
650 * We cannot transition from 0 to 2 or more visible bars at once since
651 * visibility events are delivered to each window separately */
652 cont_child();
653 }
654 }
655
656 /*
657 * Adjusts the size of the tray window and alignment of the tray clients by
658 * configuring their respective x coordinates. To be called when mapping or
659 * unmapping a tray client window.
660 *
661 */
662 static void configure_trayclients(void) {
663 trayclient *trayclient;
664 i3_output *output;
665 SLIST_FOREACH(output, outputs, slist) {
666 if (!output->active)
667 continue;
668
669 int clients = 0;
670 TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
671 if (!trayclient->mapped)
672 continue;
673 clients++;
674
675 DLOG("Configuring tray window %08x to x=%d\n",
676 trayclient->win, output->rect.w - (clients * (icon_size + logical_px(config.tray_padding))));
677 uint32_t x = output->rect.w - (clients * (icon_size + logical_px(config.tray_padding)));
678 xcb_configure_window(xcb_connection,
679 trayclient->win,
680 XCB_CONFIG_WINDOW_X,
681 &x);
682 }
683 }
684 }
685
686 /*
687 * Handles ClientMessages (messages sent from another client directly to us).
688 *
689 * At the moment, only the tray window will receive client messages. All
690 * supported client messages currently are _NET_SYSTEM_TRAY_OPCODE.
691 *
692 */
693 static void handle_client_message(xcb_client_message_event_t *event) {
694 if (event->type == atoms[I3_SYNC]) {
695 xcb_window_t window = event->data.data32[0];
696 uint32_t rnd = event->data.data32[1];
697 /* Forward the request to i3 via the IPC interface so that all pending
698 * IPC messages are guaranteed to be handled. */
699 char *payload = NULL;
700 sasprintf(&payload, "{\"rnd\":%d, \"window\":%d}", rnd, window);
701 i3_send_msg(I3_IPC_MESSAGE_TYPE_SYNC, payload);
702 free(payload);
703 } else if (event->type == atoms[_NET_SYSTEM_TRAY_OPCODE] &&
704 event->format == 32) {
705 DLOG("_NET_SYSTEM_TRAY_OPCODE received\n");
706 /* event->data.data32[0] is the timestamp */
707 uint32_t op = event->data.data32[1];
708 uint32_t mask;
709 uint32_t values[2];
710 if (op == SYSTEM_TRAY_REQUEST_DOCK) {
711 xcb_window_t client = event->data.data32[2];
712
713 mask = XCB_CW_EVENT_MASK;
714
715 /* Needed to get the most recent value of XEMBED_MAPPED. */
716 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
717 /* Needed for UnmapNotify events. */
718 values[0] |= XCB_EVENT_MASK_STRUCTURE_NOTIFY;
719 /* Needed because some tray applications (e.g., VLC) use
720 * override_redirect which causes no ConfigureRequest to be sent. */
721 values[0] |= XCB_EVENT_MASK_RESIZE_REDIRECT;
722
723 xcb_change_window_attributes(xcb_connection, client, mask, values);
724
725 /* Request the _XEMBED_INFO property. The XEMBED specification
726 * (which is referred by the tray specification) says this *has* to
727 * be set, but VLC does not set it… */
728 bool map_it = true;
729 int xe_version = 1;
730 xcb_get_property_cookie_t xembedc;
731 xcb_generic_error_t *error;
732 xembedc = xcb_get_property(xcb_connection,
733 0,
734 client,
735 atoms[_XEMBED_INFO],
736 XCB_GET_PROPERTY_TYPE_ANY,
737 0,
738 2 * 32);
739
740 xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection,
741 xembedc,
742 &error);
743 if (error != NULL) {
744 ELOG("Error getting _XEMBED_INFO property: error_code %d\n",
745 error->error_code);
746 free(error);
747 return;
748 }
749 if (xembedr != NULL && xembedr->length != 0) {
750 DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length);
751 uint32_t *xembed = xcb_get_property_value(xembedr);
752 DLOG("xembed version = %d\n", xembed[0]);
753 DLOG("xembed flags = %d\n", xembed[1]);
754 map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
755 xe_version = xembed[0];
756 if (xe_version > 1)
757 xe_version = 1;
758 free(xembedr);
759 } else {
760 ELOG("Window %08x violates the XEMBED protocol, _XEMBED_INFO not set\n", client);
761 }
762
763 DLOG("X window %08x requested docking\n", client);
764
765 if (output_for_tray == NULL) {
766 ELOG("No output found for tray\n");
767 return;
768 }
769
770 xcb_void_cookie_t rcookie = xcb_reparent_window(xcb_connection,
771 client,
772 output_for_tray->bar.id,
773 output_for_tray->rect.w - icon_size - logical_px(config.tray_padding),
774 logical_px(config.tray_padding));
775 if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?"))
776 return;
777
778 /* We reconfigure the window to use a reasonable size. The systray
779 * specification explicitly says:
780 * Tray icons may be assigned any size by the system tray, and
781 * should do their best to cope with any size effectively
782 */
783 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
784 values[0] = icon_size;
785 values[1] = icon_size;
786 xcb_configure_window(xcb_connection,
787 client,
788 mask,
789 values);
790
791 /* send the XEMBED_EMBEDDED_NOTIFY message */
792 void *event = scalloc(32, 1);
793 xcb_client_message_event_t *ev = event;
794 ev->response_type = XCB_CLIENT_MESSAGE;
795 ev->window = client;
796 ev->type = atoms[_XEMBED];
797 ev->format = 32;
798 ev->data.data32[0] = XCB_CURRENT_TIME;
799 ev->data.data32[1] = XEMBED_EMBEDDED_NOTIFY;
800 ev->data.data32[2] = output_for_tray->bar.id;
801 ev->data.data32[3] = xe_version;
802 xcb_send_event(xcb_connection,
803 0,
804 client,
805 XCB_EVENT_MASK_NO_EVENT,
806 (char *)ev);
807 free(event);
808
809 /* Put the client inside the save set. Upon termination (whether
810 * killed or normal exit does not matter) of i3bar, these clients
811 * will be correctly reparented to their most closest living
812 * ancestor. Without this, tray icons might die when i3bar
813 * exits/crashes. */
814 xcb_change_save_set(xcb_connection, XCB_SET_MODE_INSERT, client);
815
816 trayclient *tc = smalloc(sizeof(trayclient));
817 tc->win = client;
818 tc->xe_version = xe_version;
819 tc->mapped = false;
820 TAILQ_INSERT_TAIL(output_for_tray->trayclients, tc, tailq);
821
822 if (map_it) {
823 DLOG("Mapping dock client\n");
824 xcb_map_window(xcb_connection, client);
825 } else {
826 DLOG("Not mapping dock client yet\n");
827 }
828 /* Trigger an update to copy the statusline text to the appropriate
829 * position */
830 configure_trayclients();
831 draw_bars(false);
832 }
833 }
834 }
835
836 /*
837 * Handles DestroyNotify events by removing the tray client from the data
838 * structure. According to the XEmbed protocol, this is one way for a tray
839 * client to finish the protocol. After this event is received, there is no
840 * further interaction with the tray client.
841 *
842 * See: https://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html
843 *
844 */
845 static void handle_destroy_notify(xcb_destroy_notify_event_t *event) {
846 DLOG("DestroyNotify for window = %08x, event = %08x\n", event->window, event->event);
847
848 i3_output *walk;
849 SLIST_FOREACH(walk, outputs, slist) {
850 if (!walk->active)
851 continue;
852 DLOG("checking output %s\n", walk->name);
853 trayclient *trayclient;
854 TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
855 if (trayclient->win != event->window) {
856 continue;
857 }
858
859 DLOG("Removing tray client with window ID %08x\n", event->window);
860 TAILQ_REMOVE(walk->trayclients, trayclient, tailq);
861 FREE(trayclient);
862
863 /* Trigger an update, we now have more space for the statusline */
864 configure_trayclients();
865 draw_bars(false);
866 return;
867 }
868 }
869 }
870
871 /*
872 * Handles MapNotify events. These events happen when a tray client shows its
873 * window. We respond by realigning the tray clients.
874 *
875 */
876 static void handle_map_notify(xcb_map_notify_event_t *event) {
877 DLOG("MapNotify for window = %08x, event = %08x\n", event->window, event->event);
878
879 i3_output *walk;
880 SLIST_FOREACH(walk, outputs, slist) {
881 if (!walk->active)
882 continue;
883 DLOG("checking output %s\n", walk->name);
884 trayclient *trayclient;
885 TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
886 if (trayclient->win != event->window)
887 continue;
888
889 DLOG("Tray client mapped (window ID %08x). Adjusting tray.\n", event->window);
890 trayclient->mapped = true;
891
892 /* Trigger an update, we now have more space for the statusline */
893 configure_trayclients();
894 draw_bars(false);
895 return;
896 }
897 }
898 }
899 /*
900 * Handles UnmapNotify events. These events happen when a tray client hides its
901 * window. We respond by realigning the tray clients.
902 *
903 */
904 static void handle_unmap_notify(xcb_unmap_notify_event_t *event) {
905 DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event);
906
907 i3_output *walk;
908 SLIST_FOREACH(walk, outputs, slist) {
909 if (!walk->active)
910 continue;
911 DLOG("checking output %s\n", walk->name);
912 trayclient *trayclient;
913 TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
914 if (trayclient->win != event->window)
915 continue;
916
917 DLOG("Tray client unmapped (window ID %08x). Adjusting tray.\n", event->window);
918 trayclient->mapped = false;
919
920 /* Trigger an update, we now have more space for the statusline */
921 configure_trayclients();
922 draw_bars(false);
923 return;
924 }
925 }
926 }
927
928 /*
929 * Handle PropertyNotify messages. Currently only the _XEMBED_INFO property is
930 * handled, which tells us whether a dock client should be mapped or unmapped.
931 *
932 */
933 static void handle_property_notify(xcb_property_notify_event_t *event) {
934 DLOG("PropertyNotify\n");
935 if (event->atom == atoms[_XEMBED_INFO] &&
936 event->state == XCB_PROPERTY_NEW_VALUE) {
937 DLOG("xembed_info updated\n");
938 trayclient *trayclient = NULL, *walk;
939 i3_output *o_walk;
940 SLIST_FOREACH(o_walk, outputs, slist) {
941 if (!o_walk->active)
942 continue;
943
944 TAILQ_FOREACH(walk, o_walk->trayclients, tailq) {
945 if (walk->win != event->window)
946 continue;
947 trayclient = walk;
948 break;
949 }
950
951 if (trayclient)
952 break;
953 }
954 if (!trayclient) {
955 ELOG("PropertyNotify received for unknown window %08x\n",
956 event->window);
957 return;
958 }
959 xcb_get_property_cookie_t xembedc;
960 xembedc = xcb_get_property_unchecked(xcb_connection,
961 0,
962 trayclient->win,
963 atoms[_XEMBED_INFO],
964 XCB_GET_PROPERTY_TYPE_ANY,
965 0,
966 2 * 32);
967
968 xcb_get_property_reply_t *xembedr = xcb_get_property_reply(xcb_connection,
969 xembedc,
970 NULL);
971 if (xembedr == NULL || xembedr->length == 0) {
972 DLOG("xembed_info unset\n");
973 return;
974 }
975
976 DLOG("xembed format = %d, len = %d\n", xembedr->format, xembedr->length);
977 uint32_t *xembed = xcb_get_property_value(xembedr);
978 DLOG("xembed version = %d\n", xembed[0]);
979 DLOG("xembed flags = %d\n", xembed[1]);
980 bool map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
981 DLOG("map state now %d\n", map_it);
982 if (trayclient->mapped && !map_it) {
983 /* need to unmap the window */
984 xcb_unmap_window(xcb_connection, trayclient->win);
985 } else if (!trayclient->mapped && map_it) {
986 /* need to map the window */
987 xcb_map_window(xcb_connection, trayclient->win);
988 }
989 free(xembedr);
990 }
991 }
992
993 /*
994 * If a tray client attempts to change its size we deny the request and respond
995 * by telling it its actual size.
996 *
997 */
998 static void handle_configuration_change(xcb_window_t window) {
999 trayclient *trayclient;
1000 i3_output *output;
1001 SLIST_FOREACH(output, outputs, slist) {
1002 if (!output->active)
1003 continue;
1004
1005 int clients = 0;
1006 TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
1007 if (!trayclient->mapped)
1008 continue;
1009 clients++;
1010
1011 if (trayclient->win != window)
1012 continue;
1013
1014 xcb_rectangle_t rect;
1015 rect.x = output->rect.w - (clients * (icon_size + logical_px(config.tray_padding)));
1016 rect.y = logical_px(config.tray_padding);
1017 rect.width = icon_size;
1018 rect.height = icon_size;
1019
1020 DLOG("This is a tray window. x = %d\n", rect.x);
1021 fake_configure_notify(xcb_connection, rect, window, 0);
1022 return;
1023 }
1024 }
1025
1026 DLOG("WARNING: Could not find corresponding tray window.\n");
1027 }
1028
1029 static void handle_configure_request(xcb_configure_request_event_t *event) {
1030 DLOG("ConfigureRequest for window = %08x\n", event->window);
1031 handle_configuration_change(event->window);
1032 }
1033
1034 static void handle_resize_request(xcb_resize_request_event_t *event) {
1035 DLOG("ResizeRequest for window = %08x\n", event->window);
1036 handle_configuration_change(event->window);
1037 }
1038
1039 /*
1040 * This function is called immediately before the main loop locks. We check for
1041 * events from X11, handle them, then flush our outgoing queue.
1042 *
1043 */
1044 static void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
1045 xcb_generic_event_t *event;
1046
1047 if (xcb_connection_has_error(xcb_connection)) {
1048 ELOG("X11 connection was closed unexpectedly - maybe your X server terminated / crashed?\n");
1049 #ifdef I3_ASAN_ENABLED
1050 __lsan_do_leak_check();
1051 #endif
1052 exit(1);
1053 }
1054
1055 while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
1056 if (event->response_type == 0) {
1057 xcb_generic_error_t *error = (xcb_generic_error_t *)event;
1058 DLOG("Received X11 error, sequence 0x%x, error_code = %d\n", error->sequence, error->error_code);
1059 free(event);
1060 continue;
1061 }
1062
1063 int type = (event->response_type & ~0x80);
1064
1065 if (type == xkb_base && xkb_base > -1) {
1066 DLOG("received an xkb event\n");
1067
1068 xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
1069 const uint32_t mod = (config.modifier & 0xFFFF);
1070 const bool new_mod_pressed = (mod != 0 && (state->mods & mod) == mod);
1071 if (new_mod_pressed != mod_pressed) {
1072 mod_pressed = new_mod_pressed;
1073 if (state->xkbType == XCB_XKB_STATE_NOTIFY && config.modifier != XCB_NONE) {
1074 if (mod_pressed) {
1075 activated_mode = false;
1076 unhide_bars();
1077 } else if (!activated_mode) {
1078 hide_bars();
1079 }
1080 }
1081 }
1082
1083 free(event);
1084 continue;
1085 }
1086
1087 switch (type) {
1088 case XCB_VISIBILITY_NOTIFY:
1089 /* Visibility change: a bar is [un]obscured by other window */
1090 handle_visibility_notify((xcb_visibility_notify_event_t *)event);
1091 break;
1092 case XCB_EXPOSE:
1093 if (((xcb_expose_event_t *)event)->count == 0) {
1094 /* Expose-events happen, when the window needs to be redrawn */
1095 redraw_bars();
1096 }
1097
1098 break;
1099 case XCB_BUTTON_RELEASE:
1100 case XCB_BUTTON_PRESS:
1101 /* Button press events are mouse buttons clicked on one of our bars */
1102 handle_button((xcb_button_press_event_t *)event);
1103 break;
1104 case XCB_CLIENT_MESSAGE:
1105 /* Client messages are used for client-to-client communication, for
1106 * example system tray widgets talk to us directly via client messages. */
1107 handle_client_message((xcb_client_message_event_t *)event);
1108 break;
1109 case XCB_DESTROY_NOTIFY:
1110 /* DestroyNotify signifies the end of the XEmbed protocol */
1111 handle_destroy_notify((xcb_destroy_notify_event_t *)event);
1112 break;
1113 case XCB_UNMAP_NOTIFY:
1114 /* UnmapNotify is received when a tray client hides its window. */
1115 handle_unmap_notify((xcb_unmap_notify_event_t *)event);
1116 break;
1117 case XCB_MAP_NOTIFY:
1118 handle_map_notify((xcb_map_notify_event_t *)event);
1119 break;
1120 case XCB_PROPERTY_NOTIFY:
1121 /* PropertyNotify */
1122 handle_property_notify((xcb_property_notify_event_t *)event);
1123 break;
1124 case XCB_CONFIGURE_REQUEST:
1125 /* ConfigureRequest, sent by a tray child */
1126 handle_configure_request((xcb_configure_request_event_t *)event);
1127 break;
1128 case XCB_RESIZE_REQUEST:
1129 /* ResizeRequest sent by a tray child using override_redirect. */
1130 handle_resize_request((xcb_resize_request_event_t *)event);
1131 break;
1132 }
1133 free(event);
1134 }
1135
1136 xcb_flush(xcb_connection);
1137 }
1138
1139 /*
1140 * Dummy callback. We only need this, so that the prepare and check watchers
1141 * are triggered
1142 *
1143 */
1144 static void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
1145 }
1146
1147 /*
1148 * Early initialization of the connection to X11: Everything which does not
1149 * depend on 'config'.
1150 *
1151 */
1152 char *init_xcb_early(void) {
1153 /* FIXME: xcb_connect leaks memory */
1154 xcb_connection = xcb_connect(NULL, &screen);
1155 if (xcb_connection_has_error(xcb_connection)) {
1156 ELOG("Cannot open display\n");
1157 exit(EXIT_FAILURE);
1158 }
1159 conn = xcb_connection;
1160 DLOG("Connected to xcb\n");
1161
1162 /* We have to request the atoms we need */
1163 #define ATOM_DO(name) atom_cookies[name] = xcb_intern_atom(xcb_connection, 0, strlen(#name), #name);
1164 #include "xcb_atoms.def"
1165
1166 root_screen = xcb_aux_get_screen(xcb_connection, screen);
1167 xcb_root = root_screen->root;
1168
1169 depth = root_screen->root_depth;
1170 colormap = root_screen->default_colormap;
1171 visual_type = get_visualtype(root_screen);
1172
1173 xcb_cursor_context_t *cursor_ctx;
1174 if (xcb_cursor_context_new(conn, root_screen, &cursor_ctx) == 0) {
1175 cursor = xcb_cursor_load_cursor(cursor_ctx, "left_ptr");
1176 xcb_cursor_context_free(cursor_ctx);
1177 } else {
1178 cursor = xcb_generate_id(xcb_connection);
1179 i3Font cursor_font = load_font("cursor", false);
1180 xcb_create_glyph_cursor(
1181 xcb_connection,
1182 cursor,
1183 cursor_font.specific.xcb.id,
1184 cursor_font.specific.xcb.id,
1185 XCB_CURSOR_LEFT_PTR,
1186 XCB_CURSOR_LEFT_PTR + 1,
1187 0, 0, 0,
1188 65535, 65535, 65535);
1189 }
1190
1191 /* The various watchers to communicate with xcb */
1192 xcb_io = smalloc(sizeof(ev_io));
1193 xcb_prep = smalloc(sizeof(ev_prepare));
1194
1195 ev_io_init(xcb_io, &xcb_io_cb, xcb_get_file_descriptor(xcb_connection), EV_READ);
1196 ev_prepare_init(xcb_prep, &xcb_prep_cb);
1197
1198 ev_io_start(main_loop, xcb_io);
1199 ev_prepare_start(main_loop, xcb_prep);
1200
1201 /* Now we get the atoms and save them in a nice data structure */
1202 get_atoms();
1203
1204 char *path = root_atom_contents("I3_SOCKET_PATH", xcb_connection, screen);
1205
1206 return path;
1207 }
1208
1209 /*
1210 * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key events
1211 * involving that modifier, we sadly have to use xkb which is not yet fully supported
1212 * in xcb.
1213 *
1214 */
1215 static void register_xkb_keyevents(void) {
1216 const xcb_query_extension_reply_t *extreply;
1217 extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
1218 if (!extreply->present) {
1219 ELOG("xkb is not present on this server\n");
1220 exit(EXIT_FAILURE);
1221 }
1222 DLOG("initializing xcb-xkb\n");
1223 xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
1224 xcb_xkb_select_events(conn,
1225 XCB_XKB_ID_USE_CORE_KBD,
1226 XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
1227 0,
1228 XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
1229 0xff,
1230 0xff,
1231 NULL);
1232 xkb_base = extreply->first_event;
1233 }
1234
1235 /*
1236 * Deregister from xkb keyevents.
1237 *
1238 */
1239 static void deregister_xkb_keyevents(void) {
1240 xcb_xkb_select_events(conn,
1241 XCB_XKB_ID_USE_CORE_KBD,
1242 0,
1243 0,
1244 0,
1245 0xff,
1246 0xff,
1247 NULL);
1248 }
1249
1250 /*
1251 * Initialization which depends on 'config' being usable. Called after the
1252 * configuration has arrived.
1253 *
1254 */
1255 void init_xcb_late(char *fontname) {
1256 if (fontname == NULL)
1257 fontname = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
1258
1259 /* Load the font */
1260 font = load_font(fontname, true);
1261 set_font(&font);
1262 DLOG("Calculated font height: %d\n", font.height);
1263 bar_height = font.height + 2 * logical_px(ws_voff_px);
1264 icon_size = bar_height - 2 * logical_px(config.tray_padding);
1265
1266 if (config.separator_symbol)
1267 separator_symbol_width = predict_text_width(config.separator_symbol);
1268
1269 xcb_flush(xcb_connection);
1270
1271 if (config.hide_on_modifier == M_HIDE)
1272 register_xkb_keyevents();
1273 }
1274
1275 /*
1276 * Inform clients waiting for a new _NET_SYSTEM_TRAY that we took the
1277 * selection.
1278 *
1279 */
1280 static void send_tray_clientmessage(void) {
1281 uint8_t buffer[32] = {0};
1282 xcb_client_message_event_t *ev = (xcb_client_message_event_t *)buffer;
1283
1284 ev->response_type = XCB_CLIENT_MESSAGE;
1285 ev->window = xcb_root;
1286 ev->type = atoms[MANAGER];
1287 ev->format = 32;
1288 ev->data.data32[0] = XCB_CURRENT_TIME;
1289 ev->data.data32[1] = tray_reply->atom;
1290 ev->data.data32[2] = selwin;
1291
1292 xcb_send_event(xcb_connection,
1293 0,
1294 xcb_root,
1295 0xFFFFFF,
1296 (char *)buffer);
1297 }
1298
1299 /*
1300 * Initializes tray support by requesting the appropriate _NET_SYSTEM_TRAY atom
1301 * for the X11 display we are running on, then acquiring the selection for this
1302 * atom. Afterwards, tray clients will send ClientMessages to our window.
1303 *
1304 */
1305 static void init_tray(void) {
1306 DLOG("Initializing system tray functionality\n");
1307 /* request the tray manager atom for the X11 display we are running on */
1308 char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
1309 snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
1310 xcb_intern_atom_cookie_t tray_cookie;
1311 if (tray_reply == NULL)
1312 tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
1313
1314 /* tray support: we need a window to own the selection */
1315 selwin = xcb_generate_id(xcb_connection);
1316 uint32_t selmask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_COLORMAP;
1317 uint32_t selval[] = {root_screen->black_pixel, root_screen->black_pixel, 1, colormap};
1318 xcb_create_window(xcb_connection,
1319 depth,
1320 selwin,
1321 xcb_root,
1322 -1, -1,
1323 1, 1,
1324 0,
1325 XCB_WINDOW_CLASS_INPUT_OUTPUT,
1326 visual_type->visual_id,
1327 selmask,
1328 selval);
1329
1330 uint32_t orientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
1331 /* set the atoms */
1332 xcb_change_property(xcb_connection,
1333 XCB_PROP_MODE_REPLACE,
1334 selwin,
1335 atoms[_NET_SYSTEM_TRAY_ORIENTATION],
1336 XCB_ATOM_CARDINAL,
1337 32,
1338 1,
1339 &orientation);
1340 xcb_change_property(xcb_connection,
1341 XCB_PROP_MODE_REPLACE,
1342 selwin,
1343 atoms[_NET_SYSTEM_TRAY_VISUAL],
1344 XCB_ATOM_VISUALID,
1345 32,
1346 1,
1347 &visual_type->visual_id);
1348
1349 init_tray_colors();
1350
1351 if (tray_reply == NULL) {
1352 if (!(tray_reply = xcb_intern_atom_reply(xcb_connection, tray_cookie, NULL))) {
1353 ELOG("Could not get atom %s\n", atomname);
1354 exit(EXIT_FAILURE);
1355 }
1356 }
1357
1358 xcb_set_selection_owner(xcb_connection,
1359 selwin,
1360 tray_reply->atom,
1361 XCB_CURRENT_TIME);
1362
1363 /* Verify that we have the selection */
1364 xcb_get_selection_owner_cookie_t selcookie;
1365 xcb_get_selection_owner_reply_t *selreply;
1366
1367 selcookie = xcb_get_selection_owner(xcb_connection, tray_reply->atom);
1368 if (!(selreply = xcb_get_selection_owner_reply(xcb_connection, selcookie, NULL))) {
1369 ELOG("Could not get selection owner for %s\n", atomname);
1370 exit(EXIT_FAILURE);
1371 }
1372
1373 if (selreply->owner != selwin) {
1374 ELOG("Could not set the %s selection. "
1375 "Maybe another tray is already running?\n",
1376 atomname);
1377 /* NOTE that this error is not fatal. We just can’t provide tray
1378 * functionality */
1379 free(selreply);
1380 return;
1381 }
1382
1383 free(selreply);
1384
1385 send_tray_clientmessage();
1386 }
1387
1388 /*
1389 * We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
1390 * to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
1391 * they assume a light background.
1392 * See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
1393 *
1394 */
1395 void init_tray_colors(void) {
1396 /* Convert colors.bar_fg (#rrggbb) to 16-bit RGB */
1397 const char *bar_fg = (config.colors.bar_fg ? config.colors.bar_fg : "#FFFFFF");
1398
1399 DLOG("Setting bar_fg = %s as _NET_SYSTEM_TRAY_COLORS\n", bar_fg);
1400
1401 char strgroups[3][3] = {{bar_fg[1], bar_fg[2], '\0'},
1402 {bar_fg[3], bar_fg[4], '\0'},
1403 {bar_fg[5], bar_fg[6], '\0'}};
1404 const uint8_t r = strtol(strgroups[0], NULL, 16);
1405 const uint8_t g = strtol(strgroups[1], NULL, 16);
1406 const uint8_t b = strtol(strgroups[2], NULL, 16);
1407
1408 const uint16_t r16 = ((uint16_t)r << 8) | r;
1409 const uint16_t g16 = ((uint16_t)g << 8) | g;
1410 const uint16_t b16 = ((uint16_t)b << 8) | b;
1411
1412 const uint32_t tray_colors[12] = {
1413 r16, g16, b16, /* foreground color */
1414 r16, g16, b16, /* error color */
1415 r16, g16, b16, /* warning color */
1416 r16, g16, b16, /* success color */
1417 };
1418
1419 xcb_change_property(xcb_connection,
1420 XCB_PROP_MODE_REPLACE,
1421 selwin,
1422 atoms[_NET_SYSTEM_TRAY_COLORS],
1423 XCB_ATOM_CARDINAL,
1424 32,
1425 12,
1426 tray_colors);
1427 }
1428
1429 /*
1430 * Cleanup the xcb stuff.
1431 * Called once, before the program terminates.
1432 *
1433 */
1434 void clean_xcb(void) {
1435 free_outputs();
1436
1437 free_font();
1438
1439 xcb_free_cursor(xcb_connection, cursor);
1440 xcb_flush(xcb_connection);
1441 xcb_aux_sync(xcb_connection);
1442 xcb_disconnect(xcb_connection);
1443
1444 ev_prepare_stop(main_loop, xcb_prep);
1445 ev_io_stop(main_loop, xcb_io);
1446
1447 FREE(xcb_prep);
1448 FREE(xcb_io);
1449 }
1450
1451 /*
1452 * Get the earlier requested atoms and save them in the prepared data structure
1453 *
1454 */
1455 void get_atoms(void) {
1456 xcb_intern_atom_reply_t *reply;
1457 #define ATOM_DO(name) \
1458 reply = xcb_intern_atom_reply(xcb_connection, atom_cookies[name], NULL); \
1459 if (reply == NULL) { \
1460 ELOG("Could not get atom %s\n", #name); \
1461 exit(EXIT_FAILURE); \
1462 } \
1463 atoms[name] = reply->atom; \
1464 free(reply);
1465
1466 #include "xcb_atoms.def"
1467 DLOG("Got atoms\n");
1468 }
1469
1470 /*
1471 * Reparents all tray clients of the specified output to the root window. This
1472 * is either used when shutting down, when an output appears (xrandr --output
1473 * VGA1 --off) or when the primary output changes.
1474 *
1475 * Applications using the tray will start the protocol from the beginning again
1476 * afterwards.
1477 *
1478 */
1479 void kick_tray_clients(i3_output *output) {
1480 if (TAILQ_EMPTY(output->trayclients))
1481 return;
1482
1483 trayclient *trayclient;
1484 while (!TAILQ_EMPTY(output->trayclients)) {
1485 trayclient = TAILQ_FIRST(output->trayclients);
1486 /* Unmap, then reparent (to root) the tray client windows */
1487 xcb_unmap_window(xcb_connection, trayclient->win);
1488 xcb_reparent_window(xcb_connection,
1489 trayclient->win,
1490 xcb_root,
1491 0,
1492 0);
1493
1494 /* We remove the trayclient right here. We might receive an UnmapNotify
1495 * event afterwards, but better safe than sorry. */
1496 TAILQ_REMOVE(output->trayclients, trayclient, tailq);
1497 FREE(trayclient);
1498 }
1499
1500 /* Fake a DestroyNotify so that Qt re-adds tray icons.
1501 * We cannot actually destroy the window because then Qt will not restore
1502 * its event mask on the new window. */
1503 uint8_t buffer[32] = {0};
1504 xcb_destroy_notify_event_t *event = (xcb_destroy_notify_event_t *)buffer;
1505
1506 event->response_type = XCB_DESTROY_NOTIFY;
1507 event->event = selwin;
1508 event->window = selwin;
1509
1510 xcb_send_event(conn, false, selwin, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)event);
1511
1512 send_tray_clientmessage();
1513 }
1514
1515 /*
1516 * Destroy the bar of the specified output
1517 *
1518 */
1519 void destroy_window(i3_output *output) {
1520 if (output == NULL) {
1521 return;
1522 }
1523 if (output->bar.id == XCB_NONE) {
1524 return;
1525 }
1526
1527 kick_tray_clients(output);
1528 xcb_destroy_window(xcb_connection, output->bar.id);
1529 output->bar.id = XCB_NONE;
1530 }
1531
1532 /* Strut partial tells i3 where to reserve space for i3bar. This is determined
1533 * by the `position` bar config directive. */
1534 static xcb_void_cookie_t config_strut_partial(i3_output *output) {
1535 /* A local struct to save the strut_partial property */
1536 struct {
1537 uint32_t left;
1538 uint32_t right;
1539 uint32_t top;
1540 uint32_t bottom;
1541 uint32_t left_start_y;
1542 uint32_t left_end_y;
1543 uint32_t right_start_y;
1544 uint32_t right_end_y;
1545 uint32_t top_start_x;
1546 uint32_t top_end_x;
1547 uint32_t bottom_start_x;
1548 uint32_t bottom_end_x;
1549 } __attribute__((__packed__)) strut_partial;
1550 memset(&strut_partial, 0, sizeof(strut_partial));
1551
1552 switch (config.position) {
1553 case POS_NONE:
1554 break;
1555 case POS_TOP:
1556 strut_partial.top = bar_height;
1557 strut_partial.top_start_x = output->rect.x;
1558 strut_partial.top_end_x = output->rect.x + output->rect.w;
1559 break;
1560 case POS_BOT:
1561 strut_partial.bottom = bar_height;
1562 strut_partial.bottom_start_x = output->rect.x;
1563 strut_partial.bottom_end_x = output->rect.x + output->rect.w;
1564 break;
1565 }
1566 return xcb_change_property(xcb_connection,
1567 XCB_PROP_MODE_REPLACE,
1568 output->bar.id,
1569 atoms[_NET_WM_STRUT_PARTIAL],
1570 XCB_ATOM_CARDINAL,
1571 32,
1572 12,
1573 &strut_partial);
1574 }
1575
1576 /*
1577 * Returns the output which should hold the tray, if one exists.
1578 *
1579 * An output is returned in these scenarios:
1580 * 1. A specific output was listed in tray_outputs which is also in the list
1581 * of outputs managed by this bar.
1582 * 2. No tray_output directive was specified. In this case, we use the first
1583 * available output.
1584 * 3. 'tray_output primary' was specified. In this case we use the primary
1585 * output.
1586 *
1587 * Three scenarios in which we specifically don't want to use a tray:
1588 * 1. 'tray_output none' was specified.
1589 * 2. A specific output was listed as a tray_output, but is not one of the
1590 * outputs managed by this bar. For example, consider tray_outputs == [VGA-1],
1591 * but outputs == [HDMI-1].
1592 * 3. 'tray_output primary' was specified and no output in the list is
1593 * primary.
1594 */
1595 static i3_output *get_tray_output(void) {
1596 i3_output *output = NULL;
1597 if (TAILQ_EMPTY(&(config.tray_outputs))) {
1598 /* No tray_output specified, use first active output. */
1599 SLIST_FOREACH(output, outputs, slist) {
1600 if (output->active) {
1601 return output;
1602 }
1603 }
1604 return NULL;
1605 } else if (strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0) {
1606 /* Check for "tray_output none" */
1607 return NULL;
1608 }
1609
1610 /* If one or more tray_output assignments were specified, we ensure that at
1611 * least one of them is actually an output managed by this instance. */
1612 tray_output_t *tray_output;
1613 TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
1614 SLIST_FOREACH(output, outputs, slist) {
1615 if (output->active &&
1616 (strcasecmp(output->name, tray_output->output) == 0 ||
1617 (strcasecmp(tray_output->output, "primary") == 0 && output->primary))) {
1618 return output;
1619 }
1620 }
1621 }
1622
1623 return NULL;
1624 }
1625
1626 /*
1627 * Reconfigure all bars and create new bars for recently activated outputs
1628 *
1629 */
1630 void reconfig_windows(bool redraw_bars) {
1631 uint32_t mask;
1632 uint32_t values[6];
1633
1634 i3_output *walk;
1635 SLIST_FOREACH(walk, outputs, slist) {
1636 if (!walk->active) {
1637 /* If an output is not active, we destroy its bar */
1638 /* FIXME: Maybe we rather want to unmap? */
1639 DLOG("Destroying window for output %s\n", walk->name);
1640 destroy_window(walk);
1641 continue;
1642 }
1643 if (walk->bar.id == XCB_NONE) {
1644 DLOG("Creating window for output %s\n", walk->name);
1645
1646 xcb_window_t bar_id = xcb_generate_id(xcb_connection);
1647 xcb_pixmap_t buffer_id = xcb_generate_id(xcb_connection);
1648 xcb_pixmap_t statusline_buffer_id = xcb_generate_id(xcb_connection);
1649 mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP | XCB_CW_CURSOR;
1650
1651 values[0] = colors.bar_bg.colorpixel;
1652 values[1] = root_screen->black_pixel;
1653 /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar windows */
1654 values[2] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
1655 /* We enable the following EventMask fields:
1656 * EXPOSURE, to get expose events (we have to re-draw then)
1657 * SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray
1658 * child windows use ConfigureWindow
1659 * BUTTON_PRESS, to handle clicks on the workspace buttons
1660 * */
1661 values[3] = XCB_EVENT_MASK_EXPOSURE |
1662 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
1663 XCB_EVENT_MASK_BUTTON_PRESS |
1664 XCB_EVENT_MASK_BUTTON_RELEASE;
1665 if (config.hide_on_modifier == M_DOCK) {
1666 /* If the bar is normally visible, catch visibility change events to suspend
1667 * the status process when the bar is obscured by full-screened windows. */
1668 values[3] |= XCB_EVENT_MASK_VISIBILITY_CHANGE;
1669 walk->visible = true;
1670 }
1671 values[4] = colormap;
1672 values[5] = cursor;
1673
1674 xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection,
1675 depth,
1676 bar_id,
1677 xcb_root,
1678 walk->rect.x, walk->rect.y + walk->rect.h - bar_height,
1679 walk->rect.w, bar_height,
1680 0,
1681 XCB_WINDOW_CLASS_INPUT_OUTPUT,
1682 visual_type->visual_id,
1683 mask,
1684 values);
1685
1686 /* The double-buffer we use to render stuff off-screen */
1687 xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
1688 depth,
1689 buffer_id,
1690 bar_id,
1691 walk->rect.w,
1692 bar_height);
1693
1694 /* The double-buffer we use to render the statusline before copying to buffer */
1695 xcb_void_cookie_t slpm_cookie = xcb_create_pixmap_checked(xcb_connection,
1696 depth,
1697 statusline_buffer_id,
1698 bar_id,
1699 walk->rect.w,
1700 bar_height);
1701
1702 /* Set the WM_CLASS and WM_NAME (we don't need UTF-8) atoms */
1703 xcb_void_cookie_t class_cookie;
1704 class_cookie = xcb_change_property(xcb_connection,
1705 XCB_PROP_MODE_REPLACE,
1706 bar_id,
1707 XCB_ATOM_WM_CLASS,
1708 XCB_ATOM_STRING,
1709 8,
1710 (strlen("i3bar") + 1) * 2,
1711 "i3bar\0i3bar\0");
1712
1713 char *name;
1714 sasprintf(&name, "i3bar for output %s", walk->name);
1715 xcb_void_cookie_t name_cookie;
1716 name_cookie = xcb_change_property(xcb_connection,
1717 XCB_PROP_MODE_REPLACE,
1718 bar_id,
1719 XCB_ATOM_WM_NAME,
1720 XCB_ATOM_STRING,
1721 8,
1722 strlen(name),
1723 name);
1724 free(name);
1725
1726 /* We want dock windows (for now). When override_redirect is set, i3 is ignoring
1727 * this one */
1728 xcb_void_cookie_t dock_cookie = xcb_change_property(xcb_connection,
1729 XCB_PROP_MODE_REPLACE,
1730 bar_id,
1731 atoms[_NET_WM_WINDOW_TYPE],
1732 XCB_ATOM_ATOM,
1733 32,
1734 1,
1735 (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
1736
1737 draw_util_surface_init(xcb_connection, &walk->bar, bar_id, NULL, walk->rect.w, bar_height);
1738 draw_util_surface_init(xcb_connection, &walk->buffer, buffer_id, NULL, walk->rect.w, bar_height);
1739 draw_util_surface_init(xcb_connection, &walk->statusline_buffer, statusline_buffer_id, NULL, walk->rect.w, bar_height);
1740
1741 xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
1742
1743 /* We finally map the bar (display it on screen), unless the modifier-switch is on */
1744 xcb_void_cookie_t map_cookie;
1745 if (config.hide_on_modifier == M_DOCK) {
1746 map_cookie = xcb_map_window_checked(xcb_connection, bar_id);
1747 }
1748
1749 if (xcb_request_failed(win_cookie, "Could not create window") ||
1750 xcb_request_failed(pm_cookie, "Could not create pixmap") ||
1751 xcb_request_failed(slpm_cookie, "Could not create statusline pixmap") ||
1752 xcb_request_failed(dock_cookie, "Could not set dock mode") ||
1753 xcb_request_failed(class_cookie, "Could not set WM_CLASS") ||
1754 xcb_request_failed(name_cookie, "Could not set WM_NAME") ||
1755 xcb_request_failed(strut_cookie, "Could not set strut") ||
1756 ((config.hide_on_modifier == M_DOCK) && xcb_request_failed(map_cookie, "Could not map window"))) {
1757 exit(EXIT_FAILURE);
1758 }
1759
1760 } else {
1761 /* We already have a bar, so we just reconfigure it */
1762 mask = XCB_CONFIG_WINDOW_X |
1763 XCB_CONFIG_WINDOW_Y |
1764 XCB_CONFIG_WINDOW_WIDTH |
1765 XCB_CONFIG_WINDOW_HEIGHT |
1766 XCB_CONFIG_WINDOW_STACK_MODE;
1767 values[0] = walk->rect.x;
1768 if (config.position == POS_TOP)
1769 values[1] = walk->rect.y;
1770 else
1771 values[1] = walk->rect.y + walk->rect.h - bar_height;
1772 values[2] = walk->rect.w;
1773 values[3] = bar_height;
1774 values[4] = XCB_STACK_MODE_ABOVE;
1775
1776 DLOG("Reconfiguring strut partial property for output %s\n", walk->name);
1777 xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
1778
1779 DLOG("Destroying buffer for output %s\n", walk->name);
1780 xcb_free_pixmap(xcb_connection, walk->buffer.id);
1781
1782 DLOG("Destroying statusline buffer for output %s\n", walk->name);
1783 xcb_free_pixmap(xcb_connection, walk->statusline_buffer.id);
1784
1785 DLOG("Reconfiguring window for output %s to %d,%d\n", walk->name, values[0], values[1]);
1786 xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
1787 walk->bar.id,
1788 mask,
1789 values);
1790
1791 mask = XCB_CW_OVERRIDE_REDIRECT;
1792 values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
1793 DLOG("Changing window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
1794 xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection,
1795 walk->bar.id,
1796 mask,
1797 values);
1798
1799 DLOG("Recreating buffer for output %s\n", walk->name);
1800 xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection,
1801 depth,
1802 walk->buffer.id,
1803 walk->bar.id,
1804 walk->rect.w,
1805 bar_height);
1806
1807 DLOG("Recreating statusline buffer for output %s\n", walk->name);
1808 xcb_void_cookie_t slpm_cookie = xcb_create_pixmap_checked(xcb_connection,
1809 depth,
1810 walk->statusline_buffer.id,
1811 walk->bar.id,
1812 walk->rect.w,
1813 bar_height);
1814
1815 draw_util_surface_free(xcb_connection, &(walk->bar));
1816 draw_util_surface_free(xcb_connection, &(walk->buffer));
1817 draw_util_surface_free(xcb_connection, &(walk->statusline_buffer));
1818 draw_util_surface_init(xcb_connection, &(walk->bar), walk->bar.id, NULL, walk->rect.w, bar_height);
1819 draw_util_surface_init(xcb_connection, &(walk->buffer), walk->buffer.id, NULL, walk->rect.w, bar_height);
1820 draw_util_surface_init(xcb_connection, &(walk->statusline_buffer), walk->statusline_buffer.id, NULL, walk->rect.w, bar_height);
1821
1822 xcb_void_cookie_t map_cookie, umap_cookie;
1823 if (redraw_bars) {
1824 /* Unmap the window, and draw it again when in dock mode */
1825 umap_cookie = xcb_unmap_window_checked(xcb_connection, walk->bar.id);
1826 if (config.hide_on_modifier == M_DOCK) {
1827 cont_child();
1828 map_cookie = xcb_map_window_checked(xcb_connection, walk->bar.id);
1829 } else {
1830 stop_child();
1831 }
1832
1833 if (config.hide_on_modifier == M_HIDE) {
1834 /* Switching to hide mode, register for keyevents */
1835 register_xkb_keyevents();
1836 } else {
1837 /* Switching to dock/invisible mode, deregister from keyevents */
1838 deregister_xkb_keyevents();
1839 }
1840 }
1841
1842 if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
1843 xcb_request_failed(chg_cookie, "Could not change window") ||
1844 xcb_request_failed(pm_cookie, "Could not create pixmap") ||
1845 xcb_request_failed(slpm_cookie, "Could not create statusline pixmap") ||
1846 xcb_request_failed(strut_cookie, "Could not set strut") ||
1847 (redraw_bars && (xcb_request_failed(umap_cookie, "Could not unmap window") ||
1848 (config.hide_on_modifier == M_DOCK && xcb_request_failed(map_cookie, "Could not map window"))))) {
1849 exit(EXIT_FAILURE);
1850 }
1851 }
1852 }
1853
1854 /* Finally, check if we want to initialize the tray or destroy the selection
1855 * window. The result of get_tray_output() is cached. */
1856 output_for_tray = get_tray_output();
1857 if (output_for_tray) {
1858 if (selwin == XCB_NONE) {
1859 init_tray();
1860 }
1861 } else if (selwin != XCB_NONE) {
1862 DLOG("Destroying tray selection window\n");
1863 xcb_destroy_window(xcb_connection, selwin);
1864 selwin = XCB_NONE;
1865 }
1866 }
1867
1868 /*
1869 * Render the bars, with buttons and statusline
1870 *
1871 */
1872 void draw_bars(bool unhide) {
1873 DLOG("Drawing bars...\n");
1874
1875 uint32_t full_statusline_width = predict_statusline_length(false);
1876 uint32_t short_statusline_width = predict_statusline_length(true);
1877
1878 i3_output *outputs_walk;
1879 SLIST_FOREACH(outputs_walk, outputs, slist) {
1880 int workspace_width = 0;
1881
1882 if (!outputs_walk->active) {
1883 DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
1884 continue;
1885 }
1886 if (outputs_walk->bar.id == XCB_NONE) {
1887 /* Oh shit, an active output without an own bar. Create it now! */
1888 reconfig_windows(false);
1889 }
1890
1891 bool use_focus_colors = output_has_focus(outputs_walk);
1892
1893 /* First things first: clear the backbuffer */
1894 draw_util_clear_surface(&(outputs_walk->buffer), (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg));
1895
1896 if (!config.disable_ws) {
1897 i3_ws *ws_walk;
1898 TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
1899 DLOG("Drawing button for WS %s at x = %d, len = %d\n",
1900 i3string_as_utf8(ws_walk->name), workspace_width, ws_walk->name_width);
1901 color_t fg_color = colors.inactive_ws_fg;
1902 color_t bg_color = colors.inactive_ws_bg;
1903 color_t border_color = colors.inactive_ws_border;
1904 if (ws_walk->visible) {
1905 if (!ws_walk->focused) {
1906 fg_color = colors.active_ws_fg;
1907 bg_color = colors.active_ws_bg;
1908 border_color = colors.active_ws_border;
1909 } else {
1910 fg_color = colors.focus_ws_fg;
1911 bg_color = colors.focus_ws_bg;
1912 border_color = colors.focus_ws_border;
1913 }
1914 }
1915 if (ws_walk->urgent) {
1916 DLOG("WS %s is urgent!\n", i3string_as_utf8(ws_walk->name));
1917 fg_color = colors.urgent_ws_fg;
1918 bg_color = colors.urgent_ws_bg;
1919 border_color = colors.urgent_ws_border;
1920 unhide = true;
1921 }
1922
1923 /* Draw the border of the button. */
1924 draw_util_rectangle(&(outputs_walk->buffer), border_color,
1925 workspace_width,
1926 logical_px(1),
1927 ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
1928 font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
1929
1930 /* Draw the inside of the button. */
1931 draw_util_rectangle(&(outputs_walk->buffer), bg_color,
1932 workspace_width + logical_px(1),
1933 2 * logical_px(1),
1934 ws_walk->name_width + 2 * logical_px(ws_hoff_px),
1935 font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1));
1936
1937 draw_util_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color,
1938 workspace_width + logical_px(ws_hoff_px) + logical_px(1),
1939 logical_px(ws_voff_px),
1940 ws_walk->name_width);
1941
1942 workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
1943 if (TAILQ_NEXT(ws_walk, tailq) != NULL)
1944 workspace_width += logical_px(ws_spacing_px);
1945 }
1946 }
1947
1948 if (binding.name && !config.disable_binding_mode_indicator) {
1949 workspace_width += logical_px(ws_spacing_px);
1950
1951 color_t fg_color = colors.binding_mode_fg;
1952 color_t bg_color = colors.binding_mode_bg;
1953
1954 draw_util_rectangle(&(outputs_walk->buffer), colors.binding_mode_border,
1955 workspace_width,
1956 logical_px(1),
1957 binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
1958 font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
1959
1960 draw_util_rectangle(&(outputs_walk->buffer), bg_color,
1961 workspace_width + logical_px(1),
1962 2 * logical_px(1),
1963 binding.width + 2 * logical_px(ws_hoff_px),
1964 font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1));
1965
1966 draw_util_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color,
1967 workspace_width + logical_px(ws_hoff_px) + logical_px(1),
1968 logical_px(ws_voff_px),
1969 binding.width);
1970
1971 unhide = true;
1972 workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width;
1973 }
1974
1975 if (!TAILQ_EMPTY(&statusline_head)) {
1976 DLOG("Printing statusline!\n");
1977
1978 int tray_width = get_tray_width(outputs_walk->trayclients);
1979 uint32_t hoff = logical_px(((workspace_width > 0) + (tray_width > 0)) * sb_hoff_px);
1980 uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - hoff;
1981 uint32_t clip_left = 0;
1982 uint32_t statusline_width = full_statusline_width;
1983 bool use_short_text = false;
1984
1985 if (statusline_width > max_statusline_width) {
1986 statusline_width = short_statusline_width;
1987 use_short_text = true;
1988 if (statusline_width > max_statusline_width) {
1989 clip_left = statusline_width - max_statusline_width;
1990 }
1991 }
1992
1993 int16_t visible_statusline_width = MIN(statusline_width, max_statusline_width);
1994 int x_dest = outputs_walk->rect.w - tray_width - logical_px((tray_width > 0) * sb_hoff_px) - visible_statusline_width;
1995
1996 draw_statusline(outputs_walk, clip_left, use_focus_colors, use_short_text);
1997 draw_util_copy_surface(&outputs_walk->statusline_buffer, &outputs_walk->buffer, 0, 0,
1998 x_dest, 0, visible_statusline_width, (int16_t)bar_height);
1999
2000 outputs_walk->statusline_width = statusline_width;
2001 outputs_walk->statusline_short_text = use_short_text;
2002 }
2003 }
2004
2005 /* Assure the bar is hidden/unhidden according to the specified hidden_state and mode */
2006 if (mod_pressed ||
2007 config.hidden_state == S_SHOW ||
2008 unhide) {
2009 unhide_bars();
2010 } else if (config.hide_on_modifier == M_HIDE) {
2011 hide_bars();
2012 }
2013
2014 redraw_bars();
2015 }
2016
2017 /*
2018 * Redraw the bars, i.e. simply copy the buffer to the barwindow
2019 *
2020 */
2021 void redraw_bars(void) {
2022 i3_output *outputs_walk;
2023 SLIST_FOREACH(outputs_walk, outputs, slist) {
2024 if (!outputs_walk->active) {
2025 continue;
2026 }
2027
2028 draw_util_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0,
2029 0, 0, outputs_walk->rect.w, outputs_walk->rect.h);
2030 xcb_flush(xcb_connection);
2031 }
2032 }
2033
2034 /*
2035 * Set the current binding mode
2036 *
2037 */
2038 void set_current_mode(struct mode *current) {
2039 I3STRING_FREE(binding.name);
2040 binding = *current;
2041 activated_mode = binding.name != NULL;
2042 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * This header file includes all relevant files of i3 and the most often used
7 * system header files. This reduces boilerplate (the amount of code duplicated
8 * at the beginning of each source file) and is not significantly slower at
9 * compile-time.
10 *
11 */
12 #pragma once
13
14 #include <config.h>
15
16 #include <assert.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <locale.h>
22 #include <getopt.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <glob.h>
27 #include <errno.h>
28 #include <err.h>
29 #include <stdint.h>
30 #include <inttypes.h>
31 #include <math.h>
32 #include <limits.h>
33
34 #include <xcb/xcb.h>
35 #include <xcb/xcb_aux.h>
36 #include <xcb/xcb_keysyms.h>
37 #include <xcb/xcb_icccm.h>
38
39 #include <yajl/yajl_gen.h>
40 #include <yajl/yajl_version.h>
41
42 #include "data.h"
43 #include "util.h"
44 #include "ipc.h"
45 #include "tree.h"
46 #include "log.h"
47 #include "xcb.h"
48 #include "manage.h"
49 #include "workspace.h"
50 #include "i3.h"
51 #include "x.h"
52 #include "click.h"
53 #include "key_press.h"
54 #include "floating.h"
55 #include "configuration.h"
56 #include "handlers.h"
57 #include "randr.h"
58 #include "xinerama.h"
59 #include "con.h"
60 #include "load_layout.h"
61 #include "render.h"
62 #include "window.h"
63 #include "match.h"
64 #include "cmdparse.h"
65 #include "xcursor.h"
66 #include "resize.h"
67 #include "sighandler.h"
68 #include "move.h"
69 #include "output.h"
70 #include "ewmh.h"
71 #include "assignments.h"
72 #include "regex.h"
73 #include "libi3.h"
74 #include "startup.h"
75 #include "scratchpad.h"
76 #include "commands.h"
77 #include "commands_parser.h"
78 #include "bindings.h"
79 #include "config_directives.h"
80 #include "config_parser.h"
81 #include "fake_outputs.h"
82 #include "display_version.h"
83 #include "restore_layout.h"
84 #include "sync.h"
85 #include "main.h"
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * assignments.c: Assignments for specific windows (for_window).
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Checks the list of assignments for the given window and runs all matching
15 * ones (unless they have already been run for this specific window).
16 *
17 */
18 void run_assignments(i3Window *window);
19
20 /**
21 * Returns the first matching assignment for the given window.
22 *
23 */
24 Assignment *assignment_for(i3Window *window, int type);
0 #include "atoms_NET_SUPPORTED.xmacro"
1 #include "atoms_rest.xmacro"
0 xmacro(_NET_SUPPORTED)
1 xmacro(_NET_SUPPORTING_WM_CHECK)
2 xmacro(_NET_WM_NAME)
3 xmacro(_NET_WM_VISIBLE_NAME)
4 xmacro(_NET_WM_MOVERESIZE)
5 xmacro(_NET_WM_STATE_STICKY)
6 xmacro(_NET_WM_STATE_FULLSCREEN)
7 xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
8 xmacro(_NET_WM_STATE_MODAL)
9 xmacro(_NET_WM_STATE_HIDDEN)
10 xmacro(_NET_WM_STATE_FOCUSED)
11 xmacro(_NET_WM_STATE)
12 xmacro(_NET_WM_WINDOW_TYPE)
13 xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
14 xmacro(_NET_WM_WINDOW_TYPE_DOCK)
15 xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
16 xmacro(_NET_WM_WINDOW_TYPE_UTILITY)
17 xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
18 xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
19 xmacro(_NET_WM_WINDOW_TYPE_MENU)
20 xmacro(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
21 xmacro(_NET_WM_WINDOW_TYPE_POPUP_MENU)
22 xmacro(_NET_WM_WINDOW_TYPE_TOOLTIP)
23 xmacro(_NET_WM_WINDOW_TYPE_NOTIFICATION)
24 xmacro(_NET_WM_DESKTOP)
25 xmacro(_NET_WM_STRUT_PARTIAL)
26 xmacro(_NET_CLIENT_LIST)
27 xmacro(_NET_CLIENT_LIST_STACKING)
28 xmacro(_NET_CURRENT_DESKTOP)
29 xmacro(_NET_NUMBER_OF_DESKTOPS)
30 xmacro(_NET_DESKTOP_NAMES)
31 xmacro(_NET_DESKTOP_VIEWPORT)
32 xmacro(_NET_ACTIVE_WINDOW)
33 xmacro(_NET_CLOSE_WINDOW)
34 xmacro(_NET_MOVERESIZE_WINDOW)
0 xmacro(_NET_WM_USER_TIME)
1 xmacro(_NET_STARTUP_ID)
2 xmacro(_NET_WORKAREA)
3 xmacro(WM_PROTOCOLS)
4 xmacro(WM_DELETE_WINDOW)
5 xmacro(UTF8_STRING)
6 xmacro(WM_STATE)
7 xmacro(WM_CLIENT_LEADER)
8 xmacro(WM_TAKE_FOCUS)
9 xmacro(WM_WINDOW_ROLE)
10 xmacro(I3_SOCKET_PATH)
11 xmacro(I3_CONFIG_PATH)
12 xmacro(I3_SYNC)
13 xmacro(I3_SHMLOG_PATH)
14 xmacro(I3_PID)
15 xmacro(I3_FLOATING_WINDOW)
16 xmacro(_NET_REQUEST_FRAME_EXTENTS)
17 xmacro(_NET_FRAME_EXTENTS)
18 xmacro(_MOTIF_WM_HINTS)
19 xmacro(WM_CHANGE_STATE)
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * bindings.h: Functions for configuring, finding, and running bindings.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 extern pid_t command_error_nagbar_pid;
14
15 /**
16 * The name of the default mode.
17 *
18 */
19 extern const char *DEFAULT_BINDING_MODE;
20
21 /**
22 * Adds a binding from config parameters given as strings and returns a
23 * pointer to the binding structure. Returns NULL if the input code could not
24 * be parsed.
25 *
26 */
27 Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
28 const char *release, const char *border, const char *whole_window,
29 const char *exclude_titlebar, const char *command, const char *mode,
30 bool pango_markup);
31
32 /**
33 * Grab the bound keys (tell X to send us keypress events for those keycodes)
34 *
35 */
36 void grab_all_keys(xcb_connection_t *conn);
37
38 /**
39 * Release the button grabs on all managed windows and regrab them,
40 * reevaluating which buttons need to be grabbed.
41 *
42 */
43 void regrab_all_buttons(xcb_connection_t *conn);
44
45 /**
46 * Returns a pointer to the Binding that matches the given xcb event or NULL if
47 * no such binding exists.
48 *
49 */
50 Binding *get_binding_from_xcb_event(xcb_generic_event_t *event);
51
52 /**
53 * Translates keysymbols to keycodes for all bindings which use keysyms.
54 *
55 */
56 void translate_keysyms(void);
57
58 /**
59 * Switches the key bindings to the given mode, if the mode exists
60 *
61 */
62 void switch_mode(const char *new_mode);
63
64 /**
65 * Reorders bindings by event_state_mask descendingly so that get_binding()
66 * correctly matches more specific bindings before more generic bindings. Take
67 * the following binding configuration as an example:
68 *
69 * bindsym n nop lower-case n pressed
70 * bindsym Shift+n nop upper-case n pressed
71 *
72 * Without reordering, the first binding’s event_state_mask of 0x0 would match
73 * the actual event_stat_mask of 0x1 and hence trigger instead of the second
74 * keybinding.
75 *
76 */
77 void reorder_bindings(void);
78
79 /**
80 * Checks for duplicate key bindings (the same keycode or keysym is configured
81 * more than once). If a duplicate binding is found, a message is printed to
82 * stderr and the has_errors variable is set to true, which will start
83 * i3-nagbar.
84 *
85 */
86 void check_for_duplicate_bindings(struct context *context);
87
88 /**
89 * Frees the binding. If bind is null, it simply returns.
90 */
91 void binding_free(Binding *bind);
92
93 /**
94 * Runs the given binding and handles parse errors. If con is passed, it will
95 * execute the command binding with that container selected by criteria.
96 * Returns a CommandResult for running the binding's command. Caller should
97 * render tree if needs_tree_render is true. Free with command_result_free().
98 *
99 */
100 CommandResult *run_binding(Binding *bind, Con *con);
101
102 /**
103 * Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
104 *
105 */
106 bool load_keymap(void);
107
108 /**
109 * Returns a list of buttons that should be grabbed on a window.
110 * This list will always contain 1–3, all higher buttons will only be returned
111 * if there is a whole-window binding for it on some window in the current
112 * config.
113 * The list is terminated by a 0.
114 */
115 int *bindings_get_buttons_to_grab(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * click.c: Button press (mouse click) events.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * The button press X callback. This function determines whether the floating
15 * modifier is pressed and where the user clicked (decoration, border, inside
16 * the window).
17 *
18 * Then, route_click is called on the appropriate con.
19 *
20 */
21 int handle_button_press(xcb_button_press_event_t *event);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * cmdparse.y: the parser for commands you send to i3 (or bind on keys)
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 char *parse_cmd(const char *new);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * commands.c: all command functions (see commands_parser.c)
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include "commands_parser.h"
14
15 /** The beginning of the prototype for every cmd_ function. */
16 #define I3_CMD Match *current_match, struct CommandResultIR *cmd_output
17
18 /**
19 * Initializes the specified 'Match' data structure and the initial state of
20 * commands.c for matching target windows of a command.
21 *
22 */
23 void cmd_criteria_init(I3_CMD);
24
25 /**
26 * A match specification just finished (the closing square bracket was found),
27 * so we filter the list of owindows.
28 *
29 */
30 void cmd_criteria_match_windows(I3_CMD);
31
32 /**
33 * Interprets a ctype=cvalue pair and adds it to the current match
34 * specification.
35 *
36 */
37 void cmd_criteria_add(I3_CMD, const char *ctype, const char *cvalue);
38
39 /**
40 * Implementation of 'move [window|container] [to] workspace
41 * next|prev|next_on_output|prev_on_output'.
42 *
43 */
44 void cmd_move_con_to_workspace(I3_CMD, const char *which);
45
46 /**
47 * Implementation of 'move [window|container] [to] workspace back_and_forth'.
48 *
49 */
50 void cmd_move_con_to_workspace_back_and_forth(I3_CMD);
51
52 /**
53 * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace <name>'.
54 *
55 */
56 void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth);
57
58 /**
59 * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace number <number>'.
60 *
61 */
62 void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth);
63
64 /**
65 * Implementation of 'resize set <width> [px | ppt] <height> [px | ppt]'.
66 *
67 */
68 void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, const char *mode_height);
69
70 /**
71 * Implementation of 'resize grow|shrink <direction> [<px> px] [or <ppt> ppt]'.
72 *
73 */
74 void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px, long resize_ppt);
75
76 /**
77 * Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
78 *
79 */
80 void cmd_border(I3_CMD, const char *border_style_str, long border_width);
81
82 /**
83 * Implementation of 'nop <comment>'.
84 *
85 */
86 void cmd_nop(I3_CMD, const char *comment);
87
88 /**
89 * Implementation of 'append_layout <path>'.
90 *
91 */
92 void cmd_append_layout(I3_CMD, const char *path);
93
94 /**
95 * Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
96 *
97 */
98 void cmd_workspace(I3_CMD, const char *which);
99
100 /**
101 * Implementation of 'workspace [--no-auto-back-and-forth] number <number>'
102 *
103 */
104 void cmd_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth);
105
106 /**
107 * Implementation of 'workspace back_and_forth'.
108 *
109 */
110 void cmd_workspace_back_and_forth(I3_CMD);
111
112 /**
113 * Implementation of 'workspace [--no-auto-back-and-forth] <name>'
114 *
115 */
116 void cmd_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth);
117
118 /**
119 * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
120 *
121 */
122 void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle);
123
124 /**
125 * Implementation of 'unmark [mark]'
126 *
127 */
128 void cmd_unmark(I3_CMD, const char *mark);
129
130 /**
131 * Implementation of 'mode <string>'.
132 *
133 */
134 void cmd_mode(I3_CMD, const char *mode);
135
136 /**
137 * Implementation of 'move [window|container] [to] output <str>'.
138 *
139 */
140 void cmd_move_con_to_output(I3_CMD, const char *name);
141
142 /**
143 * Implementation of 'move [window|container] [to] mark <str>'.
144 *
145 */
146 void cmd_move_con_to_mark(I3_CMD, const char *mark);
147
148 /**
149 * Implementation of 'floating enable|disable|toggle'
150 *
151 */
152 void cmd_floating(I3_CMD, const char *floating_mode);
153
154 /**
155 * Implementation of 'move workspace to [output] <str>'.
156 *
157 */
158 void cmd_move_workspace_to_output(I3_CMD, const char *name);
159
160 /**
161 * Implementation of 'split v|h|t|vertical|horizontal|toggle'.
162 *
163 */
164 void cmd_split(I3_CMD, const char *direction);
165
166 /**
167 * Implementation of 'kill [window|client]'.
168 *
169 */
170 void cmd_kill(I3_CMD, const char *kill_mode_str);
171
172 /**
173 * Implementation of 'exec [--no-startup-id] <command>'.
174 *
175 */
176 void cmd_exec(I3_CMD, const char *nosn, const char *command);
177
178 /**
179 * Implementation of 'focus left|right|up|down'.
180 *
181 */
182 void cmd_focus_direction(I3_CMD, const char *direction);
183
184 /**
185 * Implementation of 'focus tiling|floating|mode_toggle'.
186 *
187 */
188 void cmd_focus_window_mode(I3_CMD, const char *window_mode);
189
190 /**
191 * Implementation of 'focus parent|child'.
192 *
193 */
194 void cmd_focus_level(I3_CMD, const char *level);
195
196 /**
197 * Implementation of 'focus'.
198 *
199 */
200 void cmd_focus(I3_CMD);
201
202 /**
203 * Implementation of 'fullscreen [enable|disable|toggle] [global]'.
204 *
205 */
206 void cmd_fullscreen(I3_CMD, const char *action, const char *fullscreen_mode);
207
208 /**
209 * Implementation of 'sticky enable|disable|toggle'.
210 *
211 */
212 void cmd_sticky(I3_CMD, const char *action);
213
214 /**
215 * Implementation of 'move <direction> [<pixels> [px]]'.
216 *
217 */
218 void cmd_move_direction(I3_CMD, const char *direction_str, long move_px);
219
220 /**
221 * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
222 *
223 */
224 void cmd_layout(I3_CMD, const char *layout_str);
225
226 /**
227 * Implementation of 'layout toggle [all|split]'.
228 *
229 */
230 void cmd_layout_toggle(I3_CMD, const char *toggle_mode);
231
232 /**
233 * Implementation of 'exit'.
234 *
235 */
236 void cmd_exit(I3_CMD);
237
238 /**
239 * Implementation of 'reload'.
240 *
241 */
242 void cmd_reload(I3_CMD);
243
244 /**
245 * Implementation of 'restart'.
246 *
247 */
248 void cmd_restart(I3_CMD);
249
250 /**
251 * Implementation of 'open'.
252 *
253 */
254 void cmd_open(I3_CMD);
255
256 /**
257 * Implementation of 'focus output <output>'.
258 *
259 */
260 void cmd_focus_output(I3_CMD, const char *name);
261
262 /**
263 * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
264 *
265 */
266 void cmd_move_window_to_position(I3_CMD, long x, long y);
267
268 /**
269 * Implementation of 'move [window|container] [to] [absolute] position center
270 *
271 */
272 void cmd_move_window_to_center(I3_CMD, const char *method);
273
274 /**
275 * Implementation of 'move [window|container] [to] position mouse'
276 *
277 */
278 void cmd_move_window_to_mouse(I3_CMD);
279
280 /**
281 * Implementation of 'move scratchpad'.
282 *
283 */
284 void cmd_move_scratchpad(I3_CMD);
285
286 /**
287 * Implementation of 'scratchpad show'.
288 *
289 */
290 void cmd_scratchpad_show(I3_CMD);
291
292 /**
293 * Implementation of 'swap [container] [with] id|con_id|mark <arg>'.
294 *
295 */
296 void cmd_swap(I3_CMD, const char *mode, const char *arg);
297
298 /**
299 * Implementation of 'title_format <format>'
300 *
301 */
302 void cmd_title_format(I3_CMD, const char *format);
303
304 /**
305 * Implementation of 'rename workspace <name> to <name>'
306 *
307 */
308 void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name);
309
310 /**
311 * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
312 *
313 */
314 void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id);
315
316 /**
317 * Implementation of 'shmlog <size>|toggle|on|off'
318 *
319 */
320 void cmd_shmlog(I3_CMD, const char *argument);
321
322 /**
323 * Implementation of 'debuglog toggle|on|off'
324 *
325 */
326 void cmd_debuglog(I3_CMD, const char *argument);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * commands.c: all command functions (see commands_parser.c)
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <yajl/yajl_gen.h>
14
15 /**
16 * Holds an intermediate represenation of the result of a call to any command.
17 * When calling parse_command("floating enable, border none"), the parser will
18 * internally use this struct when calling cmd_floating and cmd_border.
19 */
20 struct CommandResultIR {
21 /* The JSON generator to append a reply to (may be NULL). */
22 yajl_gen json_gen;
23
24 /* The next state to transition to. Passed to the function so that we can
25 * determine the next state as a result of a function call, like
26 * cfg_criteria_pop_state() does. */
27 int next_state;
28
29 /* Whether the command requires calling tree_render. */
30 bool needs_tree_render;
31 };
32
33 typedef struct CommandResult CommandResult;
34
35 /**
36 * A struct that contains useful information about the result of a command as a
37 * whole (e.g. a compound command like "floating enable, border none").
38 * needs_tree_render is true if needs_tree_render of any individual command was
39 * true.
40 */
41 struct CommandResult {
42 bool parse_error;
43 /* the error_message is currently only set for parse errors */
44 char *error_message;
45 bool needs_tree_render;
46 };
47
48 /**
49 * Parses a string (or word, if as_word is true). Extracted out of
50 * parse_command so that it can be used in src/workspace.c for interpreting
51 * workspace commands.
52 *
53 */
54 char *parse_string(const char **walk, bool as_word);
55
56 /**
57 * Parses and executes the given command. If a caller-allocated yajl_gen is
58 * passed, a json reply will be generated in the format specified by the ipc
59 * protocol. Pass NULL if no json reply is required.
60 *
61 * Free the returned CommandResult with command_result_free().
62 */
63 CommandResult *parse_command(const char *input, yajl_gen gen);
64
65 /**
66 * Frees a CommandResult
67 */
68 void command_result_free(CommandResult *result);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * con.c: Functions which deal with containers directly (creating containers,
7 * searching containers, getting specific properties from containers,
8 * …).
9 *
10 */
11 #pragma once
12
13 #include <config.h>
14
15 /**
16 * Create a new container (and attach it to the given parent, if not NULL).
17 * This function only initializes the data structures.
18 *
19 */
20 Con *con_new_skeleton(Con *parent, i3Window *window);
21
22 /**
23 * A wrapper for con_new_skeleton, to retain the old con_new behaviour
24 *
25 */
26 Con *con_new(Con *parent, i3Window *window);
27
28 /**
29 * Frees the specified container.
30 *
31 */
32 void con_free(Con *con);
33
34 /**
35 * Sets input focus to the given container. Will be updated in X11 in the next
36 * run of x_push_changes().
37 *
38 */
39 void con_focus(Con *con);
40
41 /**
42 * Sets input focus to the given container and raises it to the top.
43 *
44 */
45 void con_activate(Con *con);
46
47 /**
48 * Closes the given container.
49 *
50 */
51 void con_close(Con *con, kill_window_t kill_window);
52
53 /**
54 * Returns true when this node is a leaf node (has no children)
55 *
56 */
57 bool con_is_leaf(Con *con);
58
59 /**
60 * Returns true when this con is a leaf node with a managed X11 window (e.g.,
61 * excluding dock containers)
62 */
63 bool con_has_managed_window(Con *con);
64
65 /**
66 * Returns true if a container should be considered split.
67 *
68 */
69 bool con_is_split(Con *con);
70
71 /**
72 * This will only return true for containers which have some parent with
73 * a tabbed / stacked parent of which they are not the currently focused child.
74 *
75 */
76 bool con_is_hidden(Con *con);
77
78 /**
79 * Returns whether the container or any of its children is sticky.
80 *
81 */
82 bool con_is_sticky(Con *con);
83
84 /**
85 * Returns true if this node has regular or floating children.
86 *
87 */
88 bool con_has_children(Con *con);
89
90 /**
91 * Returns true if this node accepts a window (if the node swallows windows,
92 * it might already have swallowed enough and cannot hold any more).
93 *
94 */
95 bool con_accepts_window(Con *con);
96
97 /**
98 * Gets the output container (first container with CT_OUTPUT in hierarchy) this
99 * node is on.
100 *
101 */
102 Con *con_get_output(Con *con);
103
104 /**
105 * Gets the workspace container this node is on.
106 *
107 */
108 Con *con_get_workspace(Con *con);
109
110 /**
111 * Searches parents of the given 'con' until it reaches one with the specified
112 * 'orientation'. Aborts when it comes across a floating_con.
113 *
114 */
115 Con *con_parent_with_orientation(Con *con, orientation_t orientation);
116
117 /**
118 * Returns the first fullscreen node below this node.
119 *
120 */
121 Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode);
122
123 /**
124 * Returns the fullscreen node that covers the given workspace if it exists.
125 * This is either a CF_GLOBAL fullscreen container anywhere or a CF_OUTPUT
126 * fullscreen container in the workspace.
127 *
128 */
129 Con *con_get_fullscreen_covering_ws(Con *ws);
130
131 /**
132 * Returns true if the container is internal, such as __i3_scratch
133 *
134 */
135 bool con_is_internal(Con *con);
136
137 /**
138 * Returns true if the node is floating.
139 *
140 */
141 bool con_is_floating(Con *con);
142
143 /**
144 * Returns true if the container is a docked container.
145 *
146 */
147 bool con_is_docked(Con *con);
148
149 /**
150 * Checks if the given container is either floating or inside some floating
151 * container. It returns the FLOATING_CON container.
152 *
153 */
154 Con *con_inside_floating(Con *con);
155
156 /**
157 * Checks if the given container is inside a focused container.
158 *
159 */
160 bool con_inside_focused(Con *con);
161
162 /**
163 * Checks if the container has the given parent as an actual parent.
164 *
165 */
166 bool con_has_parent(Con *con, Con *parent);
167
168 /**
169 * Returns the container with the given client window ID or NULL if no such
170 * container exists.
171 *
172 */
173 Con *con_by_window_id(xcb_window_t window);
174
175 /**
176 * Returns the container with the given container ID or NULL if no such
177 * container exists.
178 *
179 */
180 Con *con_by_con_id(long target);
181
182 /**
183 * Returns true if the given container (still) exists.
184 * This can be used, e.g., to make sure a container hasn't been closed in the meantime.
185 *
186 */
187 bool con_exists(Con *con);
188
189 /**
190 * Returns the container with the given frame ID or NULL if no such container
191 * exists.
192 *
193 */
194 Con *con_by_frame_id(xcb_window_t frame);
195
196 /**
197 * Returns the container with the given mark or NULL if no such container
198 * exists.
199 *
200 */
201 Con *con_by_mark(const char *mark);
202
203 /**
204 * Returns true if and only if the given containers holds the mark.
205 *
206 */
207 bool con_has_mark(Con *con, const char *mark);
208
209 /**
210 * Toggles the mark on a container.
211 * If the container already has this mark, the mark is removed.
212 * Otherwise, the mark is assigned to the container.
213 *
214 */
215 void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode);
216
217 /**
218 * Assigns a mark to the container.
219 *
220 */
221 void con_mark(Con *con, const char *mark, mark_mode_t mode);
222
223 /**
224 * Removes marks from containers.
225 * If con is NULL, all containers are considered.
226 * If name is NULL, this removes all existing marks.
227 * Otherwise, it will only remove the given mark (if it is present).
228 *
229 */
230 void con_unmark(Con *con, const char *name);
231
232 /**
233 * Returns the first container below 'con' which wants to swallow this window
234 * TODO: priority
235 *
236 */
237 Con *con_for_window(Con *con, i3Window *window, Match **store_match);
238
239 /**
240 * Iterate over the container's focus stack and return an array with the
241 * containers inside it, ordered from higher focus order to lowest.
242 *
243 */
244 Con **get_focus_order(Con *con);
245
246 /**
247 * Clear the container's focus stack and re-add it using the provided container
248 * array. The function doesn't check if the provided array contains the same
249 * containers with the previous focus stack but will not add floating containers
250 * in the new focus stack if container is not a workspace.
251 *
252 */
253 void set_focus_order(Con *con, Con **focus_order);
254
255 /**
256 * Returns the number of children of this container.
257 *
258 */
259 int con_num_children(Con *con);
260
261 /**
262 * Returns the number of visible non-floating children of this container.
263 * For example, if the container contains a hsplit which has two children,
264 * this will return 2 instead of 1.
265 */
266 int con_num_visible_children(Con *con);
267
268 /**
269 * Count the number of windows (i.e., leaf containers).
270 *
271 */
272 int con_num_windows(Con *con);
273
274 /**
275 * Attaches the given container to the given parent. This happens when moving
276 * a container or when inserting a new container at a specific place in the
277 * tree.
278 *
279 * ignore_focus is to just insert the Con at the end (useful when creating a
280 * new split container *around* some containers, that is, detaching and
281 * attaching them in order without wanting to mess with the focus in between).
282 *
283 */
284 void con_attach(Con *con, Con *parent, bool ignore_focus);
285
286 /**
287 * Detaches the given container from its current parent
288 *
289 */
290 void con_detach(Con *con);
291
292 /**
293 * Updates the percent attribute of the children of the given container. This
294 * function needs to be called when a window is added or removed from a
295 * container.
296 *
297 */
298 void con_fix_percent(Con *con);
299
300 /**
301 * Toggles fullscreen mode for the given container. Fullscreen mode will not be
302 * entered when there already is a fullscreen container on this workspace.
303 *
304 */
305 void con_toggle_fullscreen(Con *con, int fullscreen_mode);
306
307 /**
308 * Enables fullscreen mode for the given container, if necessary.
309 *
310 */
311 void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode);
312
313 /**
314 * Disables fullscreen mode for the given container, if necessary.
315 *
316 */
317 void con_disable_fullscreen(Con *con);
318
319 /**
320 * Moves the given container to the currently focused container on the given
321 * workspace.
322 *
323 * The fix_coordinates flag will translate the current coordinates (offset from
324 * the monitor position basically) to appropriate coordinates on the
325 * destination workspace.
326 * Not enabling this behaviour comes in handy when this function gets called by
327 * floating_maybe_reassign_ws, which will only "move" a floating window when it
328 * *already* changed its coordinates to a different output.
329 *
330 * The dont_warp flag disables pointer warping and will be set when this
331 * function is called while dragging a floating window.
332 *
333 * If ignore_focus is set, the container will be moved without modifying focus
334 * at all.
335 *
336 * TODO: is there a better place for this function?
337 *
338 */
339 void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates,
340 bool dont_warp, bool ignore_focus);
341
342 /**
343 * Moves the given container to the currently focused container on the
344 * visible workspace on the given output.
345 *
346 */
347 void con_move_to_output(Con *con, Output *output, bool fix_coordinates);
348
349 /**
350 * Moves the given container to the currently focused container on the
351 * visible workspace on the output specified by the given name.
352 * The current output for the container is used to resolve relative names
353 * such as left, right, up, down.
354 *
355 */
356 bool con_move_to_output_name(Con *con, const char *name, bool fix_coordinates);
357
358 /**
359 * Moves the given container to the given mark.
360 *
361 */
362 bool con_move_to_mark(Con *con, const char *mark);
363
364 /**
365 * Returns the orientation of the given container (for stacked containers,
366 * vertical orientation is used regardless of the actual orientation of the
367 * container).
368 *
369 */
370 orientation_t con_orientation(Con *con);
371
372 /**
373 * Returns the container which will be focused next when the given container
374 * is not available anymore. Called in tree_close_internal and con_move_to_workspace
375 * to properly restore focus.
376 *
377 */
378 Con *con_next_focused(Con *con);
379
380 /**
381 * Get the next/previous container in the specified orientation. This may
382 * travel up until it finds a container with suitable orientation.
383 *
384 */
385 Con *con_get_next(Con *con, char way, orientation_t orientation);
386
387 /**
388 * Returns the focused con inside this client, descending the tree as far as
389 * possible. This comes in handy when attaching a con to a workspace at the
390 * currently focused position, for example.
391 *
392 */
393 Con *con_descend_focused(Con *con);
394
395 /**
396 * Returns the focused con inside this client, descending the tree as far as
397 * possible. This comes in handy when attaching a con to a workspace at the
398 * currently focused position, for example.
399 *
400 * Works like con_descend_focused but considers only tiling cons.
401 *
402 */
403 Con *con_descend_tiling_focused(Con *con);
404
405 /**
406 * Returns the leftmost, rightmost, etc. container in sub-tree. For example, if
407 * direction is D_LEFT, then we return the rightmost container and if direction
408 * is D_RIGHT, we return the leftmost container. This is because if we are
409 * moving D_LEFT, and thus want the rightmost container.
410 */
411 Con *con_descend_direction(Con *con, direction_t direction);
412
413 /**
414 * Returns a "relative" Rect which contains the amount of pixels that need to
415 * be added to the original Rect to get the final position (obviously the
416 * amount of pixels for normal, 1pixel and borderless are different).
417 *
418 */
419 Rect con_border_style_rect(Con *con);
420
421 /**
422 * Returns adjacent borders of the window. We need this if hide_edge_borders is
423 * enabled.
424 */
425 adjacent_t con_adjacent_borders(Con *con);
426
427 /**
428 * Use this function to get a container’s border style. This is important
429 * because when inside a stack, the border style is always BS_NORMAL.
430 * For tabbed mode, the same applies, with one exception: when the container is
431 * borderless and the only element in the tabbed container, the border is not
432 * rendered.
433 *
434 * For children of a CT_DOCKAREA, the border style is always none.
435 *
436 */
437 int con_border_style(Con *con);
438
439 /**
440 * Sets the given border style on con, correctly keeping the position/size of a
441 * floating window.
442 *
443 */
444 void con_set_border_style(Con *con, int border_style, int border_width);
445
446 /**
447 * This function changes the layout of a given container. Use it to handle
448 * special cases like changing a whole workspace to stacked/tabbed (creates a
449 * new split container before).
450 *
451 */
452 void con_set_layout(Con *con, layout_t layout);
453
454 /**
455 * This function toggles the layout of a given container. toggle_mode can be
456 * either 'default' (toggle only between stacked/tabbed/last_split_layout),
457 * 'split' (toggle only between splitv/splith) or 'all' (toggle between all
458 * layouts).
459 *
460 */
461 void con_toggle_layout(Con *con, const char *toggle_mode);
462
463 /**
464 * Determines the minimum size of the given con by looking at its children (for
465 * split/stacked/tabbed cons). Will be called when resizing floating cons
466 *
467 */
468 Rect con_minimum_size(Con *con);
469
470 /**
471 * Returns true if changing the focus to con would be allowed considering
472 * the fullscreen focus constraints. Specifically, if a fullscreen container or
473 * any of its descendants is focused, this function returns true if and only if
474 * focusing con would mean that focus would still be visible on screen, i.e.,
475 * the newly focused container would not be obscured by a fullscreen container.
476 *
477 * In the simplest case, if a fullscreen container or any of its descendants is
478 * fullscreen, this functions returns true if con is the fullscreen container
479 * itself or any of its descendants, as this means focus wouldn't escape the
480 * boundaries of the fullscreen container.
481 *
482 * In case the fullscreen container is of type CF_OUTPUT, this function returns
483 * true if con is on a different workspace, as focus wouldn't be obscured by
484 * the fullscreen container that is constrained to a different workspace.
485 *
486 * Note that this same logic can be applied to moving containers. If a
487 * container can be focused under the fullscreen focus constraints, it can also
488 * become a parent or sibling to the currently focused container.
489 *
490 */
491 bool con_fullscreen_permits_focusing(Con *con);
492
493 /**
494 * Checks if the given container has an urgent child.
495 *
496 */
497 bool con_has_urgent_child(Con *con);
498
499 /**
500 * Make all parent containers urgent if con is urgent or clear the urgent flag
501 * of all parent containers if there are no more urgent children left.
502 *
503 */
504 void con_update_parents_urgency(Con *con);
505
506 /**
507 * Set urgency flag to the container, all the parent containers and the workspace.
508 *
509 */
510 void con_set_urgency(Con *con, bool urgent);
511
512 /**
513 * Create a string representing the subtree under con.
514 *
515 */
516 char *con_get_tree_representation(Con *con);
517
518 /**
519 * force parent split containers to be redrawn
520 *
521 */
522 void con_force_split_parents_redraw(Con *con);
523
524 /**
525 * Returns the window title considering the current title format.
526 *
527 */
528 i3String *con_parse_title_format(Con *con);
529
530 /**
531 * Swaps the two containers.
532 *
533 */
534 bool con_swap(Con *first, Con *second);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * config_directives.h: all config storing functions (see config_parser.c)
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include "config_parser.h"
14
15 /**
16 * A utility function to convert a string containing the group and modifiers to
17 * the corresponding bit mask.
18 */
19 i3_event_state_mask_t event_state_from_str(const char *str);
20
21 /** The beginning of the prototype for every cfg_ function. */
22 #define I3_CFG Match *current_match, struct ConfigResultIR *result
23
24 /* Defines a configuration function, that is, anything that can be called by
25 * using 'call cfg_foo()' in parser-specs/.*.spec. Useful so that we don’t need
26 * to repeat the definition all the time. */
27 #define CFGFUN(name, ...) \
28 void cfg_##name(I3_CFG, ##__VA_ARGS__)
29
30 /* The following functions are called by the config parser, see
31 * parser-specs/config.spec. They get the parsed parameters and store them in
32 * our data structures, e.g. cfg_font gets a font name and stores it in
33 * config.font.
34 *
35 * Since they are so similar, individual comments were omitted. */
36
37 CFGFUN(criteria_init, int _state);
38 CFGFUN(criteria_add, const char *ctype, const char *cvalue);
39 CFGFUN(criteria_pop_state);
40
41 CFGFUN(font, const char *font);
42 CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command);
43 CFGFUN(for_window, const char *command);
44 CFGFUN(floating_minimum_size, const long width, const long height);
45 CFGFUN(floating_maximum_size, const long width, const long height);
46 CFGFUN(default_orientation, const char *orientation);
47 CFGFUN(workspace_layout, const char *layout);
48 CFGFUN(workspace_back_and_forth, const char *value);
49 CFGFUN(focus_follows_mouse, const char *value);
50 CFGFUN(mouse_warping, const char *value);
51 CFGFUN(focus_wrapping, const char *value);
52 CFGFUN(force_focus_wrapping, const char *value);
53 CFGFUN(force_xinerama, const char *value);
54 CFGFUN(disable_randr15, const char *value);
55 CFGFUN(fake_outputs, const char *outputs);
56 CFGFUN(force_display_urgency_hint, const long duration_ms);
57 CFGFUN(focus_on_window_activation, const char *mode);
58 CFGFUN(title_align, const char *alignment);
59 CFGFUN(show_marks, const char *value);
60 CFGFUN(hide_edge_borders, const char *borders);
61 CFGFUN(assign_output, const char *output);
62 CFGFUN(assign, const char *workspace, bool is_number);
63 CFGFUN(no_focus);
64 CFGFUN(ipc_socket, const char *path);
65 CFGFUN(ipc_kill_timeout, const long timeout_ms);
66 CFGFUN(restart_state, const char *path);
67 CFGFUN(popup_during_fullscreen, const char *value);
68 CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border);
69 CFGFUN(color_single, const char *colorclass, const char *color);
70 CFGFUN(floating_modifier, const char *modifiers);
71 CFGFUN(default_border, const char *windowtype, const char *border, const long width);
72 CFGFUN(workspace, const char *workspace, const char *output);
73 CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command);
74
75 CFGFUN(enter_mode, const char *pango_markup, const char *mode);
76 CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command);
77
78 CFGFUN(bar_font, const char *font);
79 CFGFUN(bar_separator_symbol, const char *separator);
80 CFGFUN(bar_mode, const char *mode);
81 CFGFUN(bar_hidden_state, const char *hidden_state);
82 CFGFUN(bar_id, const char *bar_id);
83 CFGFUN(bar_output, const char *output);
84 CFGFUN(bar_verbose, const char *verbose);
85 CFGFUN(bar_modifier, const char *modifiers);
86 CFGFUN(bar_wheel_up_cmd, const char *command);
87 CFGFUN(bar_wheel_down_cmd, const char *command);
88 CFGFUN(bar_bindsym, const char *button, const char *release, const char *command);
89 CFGFUN(bar_position, const char *position);
90 CFGFUN(bar_i3bar_command, const char *i3bar_command);
91 CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text);
92 CFGFUN(bar_socket_path, const char *socket_path);
93 CFGFUN(bar_tray_output, const char *output);
94 CFGFUN(bar_tray_padding, const long spacing_px);
95 CFGFUN(bar_color_single, const char *colorclass, const char *color);
96 CFGFUN(bar_status_command, const char *command);
97 CFGFUN(bar_binding_mode_indicator, const char *value);
98 CFGFUN(bar_workspace_buttons, const char *value);
99 CFGFUN(bar_strip_workspace_numbers, const char *value);
100 CFGFUN(bar_strip_workspace_name, const char *value);
101 CFGFUN(bar_start);
102 CFGFUN(bar_finish);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * config_parser.h: config parser-related definitions
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <yajl/yajl_gen.h>
14
15 SLIST_HEAD(variables_head, Variable);
16 extern pid_t config_error_nagbar_pid;
17
18 /**
19 * An intermediate reprsentation of the result of a parse_config call.
20 * Currently unused, but the JSON output will be useful in the future when we
21 * implement a config parsing IPC command.
22 *
23 */
24 struct ConfigResultIR {
25 /* The JSON generator to append a reply to. */
26 yajl_gen json_gen;
27
28 /* The next state to transition to. Passed to the function so that we can
29 * determine the next state as a result of a function call, like
30 * cfg_criteria_pop_state() does. */
31 int next_state;
32 };
33
34 struct ConfigResultIR *parse_config(const char *input, struct context *context);
35
36 /**
37 * launch nagbar to indicate errors in the configuration file.
38 */
39 void start_config_error_nagbar(const char *configpath, bool has_errors);
40
41 /**
42 * Parses the given file by first replacing the variables, then calling
43 * parse_config and launching i3-nagbar if use_nagbar is true.
44 *
45 * The return value is a boolean indicating whether there were errors during
46 * parsing.
47 *
48 */
49 bool parse_file(const char *f, bool use_nagbar);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * include/configuration.h: Contains all structs/variables for the configurable
7 * part of i3 as well as functions handling the configuration file (calling
8 * the parser (src/config_parse.c) with the correct path, switching key
9 * bindings mode).
10 *
11 */
12 #pragma once
13
14 #include "libi3.h"
15
16 #include <stdbool.h>
17 #include "queue.h"
18 #include "i3.h"
19
20 typedef struct Config Config;
21 typedef struct Barconfig Barconfig;
22 extern char *current_configpath;
23 extern char *current_config;
24 extern Config config;
25 extern SLIST_HEAD(modes_head, Mode) modes;
26 extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
27
28 /**
29 * Used during the config file lexing/parsing to keep the state of the lexer
30 * in order to provide useful error messages in yyerror().
31 *
32 */
33 struct context {
34 bool has_errors;
35 bool has_warnings;
36
37 int line_number;
38 char *line_copy;
39 const char *filename;
40
41 char *compact_error;
42
43 /* These are the same as in YYLTYPE */
44 int first_column;
45 int last_column;
46 };
47
48 /**
49 * Part of the struct Config. It makes sense to group colors for background,
50 * border and text as every element in i3 has them (window decorations, bar).
51 *
52 */
53 struct Colortriple {
54 color_t border;
55 color_t background;
56 color_t text;
57 color_t indicator;
58 color_t child_border;
59 };
60
61 /**
62 * Holds a user-assigned variable for parsing the configuration file. The key
63 * is replaced by value in every following line of the file.
64 *
65 */
66 struct Variable {
67 char *key;
68 char *value;
69 char *next_match;
70
71 SLIST_ENTRY(Variable)
72 variables;
73 };
74
75 /**
76 * The configuration file can contain multiple sets of bindings. Apart from the
77 * default set (name == "default"), you can specify other sets and change the
78 * currently active set of bindings by using the "mode <name>" command.
79 *
80 */
81 struct Mode {
82 char *name;
83 bool pango_markup;
84 struct bindings_head *bindings;
85
86 SLIST_ENTRY(Mode)
87 modes;
88 };
89
90 /**
91 * Holds part of the configuration (the part which is not already in dedicated
92 * structures in include/data.h).
93 *
94 */
95 struct Config {
96 const char *terminal;
97 i3Font font;
98
99 char *ipc_socket_path;
100 char *restart_state_path;
101
102 layout_t default_layout;
103 int container_stack_limit;
104 int container_stack_limit_value;
105 int default_border_width;
106 int default_floating_border_width;
107
108 /** Default orientation for new containers */
109 int default_orientation;
110
111 /** By default, focus follows mouse. If the user explicitly wants to
112 * turn this off (and instead rely only on the keyboard for changing
113 * focus), we allow them to do this with this relatively special option.
114 * It is not planned to add any different focus models. */
115 bool disable_focus_follows_mouse;
116
117 /** By default, when switching focus to a window on a different output
118 * (e.g. focusing a window on workspace 3 on output VGA-1, coming from
119 * workspace 2 on LVDS-1), the mouse cursor is warped to the center of
120 * that window.
121 *
122 * With the mouse_warping option, you can control when the mouse cursor
123 * should be warped. "none" disables warping entirely, whereas "output"
124 * is the default behavior described above. */
125 warping_t mouse_warping;
126
127 /** Remove borders if they are adjacent to the screen edge.
128 * This is useful if you are reaching scrollbar on the edge of the
129 * screen or do not want to waste a single pixel of displayspace.
130 * By default, this is disabled. */
131 hide_edge_borders_mode_t hide_edge_borders;
132
133 /** By default, a workspace bar is drawn at the bottom of the screen.
134 * If you want to have a more fancy bar, it is recommended to replace
135 * the whole bar by dzen2, for example using the i3-wsbar script which
136 * comes with i3. Thus, you can turn it off entirely. */
137 bool disable_workspace_bar;
138
139 /** When focus wrapping is enabled (the default), attempting to
140 * move focus past the edge of the screen (in other words, in a
141 * direction in which there are no more containers to focus) will
142 * cause the focus to wrap to the opposite edge of the current
143 * container. When it is disabled, nothing happens; the current
144 * focus is preserved.
145 *
146 * Additionally, focus wrapping may be forced. Think of the
147 * following layout: Horizontal workspace with a tabbed con on the
148 * left of the screen and a terminal on the right of the
149 * screen. You are in the second container in the tabbed container
150 * and focus to the right. By default, i3 will set focus to the
151 * terminal on the right. If you are in the first container in the
152 * tabbed container however, focusing to the left will
153 * wrap. Setting focus_wrapping to FOCUS_WRAPPING_FORCE forces i3
154 * to always wrap, which will result in you having to use "focus
155 * parent" more often. */
156 focus_wrapping_t focus_wrapping;
157
158 /** By default, use the RandR API for multi-monitor setups.
159 * Unfortunately, the nVidia binary graphics driver doesn't support
160 * this API. Instead, it only support the less powerful Xinerama API,
161 * which can be enabled by this option.
162 *
163 * Note: this option takes only effect on the initial startup (eg.
164 * reconfiguration is not possible). On startup, the list of screens
165 * is fetched once and never updated. */
166 bool force_xinerama;
167
168 /** Don’t use RandR 1.5 for querying outputs. */
169 bool disable_randr15;
170
171 /** Overwrites output detection (for testing), see src/fake_outputs.c */
172 char *fake_outputs;
173
174 /** Automatic workspace back and forth switching. If this is set, a
175 * switch to the currently active workspace will switch to the
176 * previously focused one instead, making it possible to fast toggle
177 * between two workspaces. */
178 bool workspace_auto_back_and_forth;
179
180 /** By default, urgency is cleared immediately when switching to another
181 * workspace leads to focusing the con with the urgency hint. When having
182 * multiple windows on that workspace, the user needs to guess which
183 * application raised the event. To prevent this, the reset of the urgency
184 * flag can be delayed using an urgency timer. */
185 float workspace_urgency_timer;
186
187 /** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
188 enum {
189 /* Focus if the target workspace is visible, set urgency hint otherwise. */
190 FOWA_SMART,
191 /* Always set the urgency hint. */
192 FOWA_URGENT,
193 /* Always focus the window. */
194 FOWA_FOCUS,
195 /* Ignore the request (no focus, no urgency hint). */
196 FOWA_NONE
197 } focus_on_window_activation;
198
199 /** Specifies whether or not marks should be displayed in the window
200 * decoration. Marks starting with a "_" will be ignored either way. */
201 bool show_marks;
202
203 /** Title alignment options. */
204 enum {
205 ALIGN_LEFT,
206 ALIGN_CENTER,
207 ALIGN_RIGHT
208 } title_align;
209
210 /** The default border style for new windows. */
211 border_style_t default_border;
212
213 /** The default border style for new floating windows. */
214 border_style_t default_floating_border;
215
216 /** The modifier which needs to be pressed in combination with your mouse
217 * buttons to do things with floating windows (move, resize) */
218 uint32_t floating_modifier;
219
220 /** Maximum and minimum dimensions of a floating window */
221 int32_t floating_maximum_width;
222 int32_t floating_maximum_height;
223 int32_t floating_minimum_width;
224 int32_t floating_minimum_height;
225
226 /* Color codes are stored here */
227 struct config_client {
228 color_t background;
229 struct Colortriple focused;
230 struct Colortriple focused_inactive;
231 struct Colortriple unfocused;
232 struct Colortriple urgent;
233 struct Colortriple placeholder;
234 } client;
235 struct config_bar {
236 struct Colortriple focused;
237 struct Colortriple unfocused;
238 struct Colortriple urgent;
239 } bar;
240
241 /** What should happen when a new popup is opened during fullscreen mode */
242 enum {
243 /* display (and focus) the popup when it belongs to the fullscreen
244 * window only. */
245 PDF_SMART = 0,
246
247 /* leave fullscreen mode unconditionally */
248 PDF_LEAVE_FULLSCREEN = 1,
249
250 /* just ignore the popup, that is, don’t map it */
251 PDF_IGNORE = 2,
252 } popup_during_fullscreen;
253
254 /* The number of currently parsed barconfigs */
255 int number_barconfigs;
256 };
257
258 /**
259 * Holds the status bar configuration (i3bar). One of these structures is
260 * created for each 'bar' block in the config.
261 *
262 */
263 struct Barconfig {
264 /** Automatically generated ID for this bar config. Used by the bar process
265 * to request a specific configuration. */
266 char *id;
267
268 /** Number of outputs in the outputs array */
269 int num_outputs;
270 /** Outputs on which this bar should show up on. We use an array for
271 * simplicity (since we store just strings). */
272 char **outputs;
273
274 /* List of outputs on which the tray is allowed to be shown, in order.
275 * The special value "none" disables it (per default, it will be shown) and
276 * the special value "primary" enabled it on the primary output. */
277 TAILQ_HEAD(tray_outputs_head, tray_output_t)
278 tray_outputs;
279
280 /* Padding around the tray icons. */
281 int tray_padding;
282
283 /** Path to the i3 IPC socket. This option is discouraged since programs
284 * can find out the path by looking for the I3_SOCKET_PATH property on the
285 * root window! */
286 char *socket_path;
287
288 /** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
289 enum { M_DOCK = 0,
290 M_HIDE = 1,
291 M_INVISIBLE = 2 } mode;
292
293 /* The current hidden_state of the bar, which indicates whether it is hidden or shown */
294 enum { S_HIDE = 0,
295 S_SHOW = 1 } hidden_state;
296
297 /** Bar modifier (to show bar when in hide mode). */
298 uint32_t modifier;
299
300 TAILQ_HEAD(bar_bindings_head, Barbinding)
301 bar_bindings;
302
303 /** Bar position (bottom by default). */
304 enum { P_BOTTOM = 0,
305 P_TOP = 1 } position;
306
307 /** Command that should be run to execute i3bar, give a full path if i3bar is not
308 * in your $PATH.
309 * By default just 'i3bar' is executed. */
310 char *i3bar_command;
311
312 /** Command that should be run to get a statusline, for example 'i3status'.
313 * Will be passed to the shell. */
314 char *status_command;
315
316 /** Font specification for all text rendered on the bar. */
317 char *font;
318
319 /** A custom separator to use instead of a vertical line. */
320 char *separator_symbol;
321
322 /** Hide workspace buttons? Configuration option is 'workspace_buttons no'
323 * but we invert the bool to get the correct default when initializing with
324 * zero. */
325 bool hide_workspace_buttons;
326
327 /** Strip workspace numbers? Configuration option is
328 * 'strip_workspace_numbers yes'. */
329 bool strip_workspace_numbers;
330
331 /** Strip workspace name? Configuration option is
332 * 'strip_workspace_name yes'. */
333 bool strip_workspace_name;
334
335 /** Hide mode button? Configuration option is 'binding_mode_indicator no'
336 * but we invert the bool for the same reason as hide_workspace_buttons.*/
337 bool hide_binding_mode_indicator;
338
339 /** Enable verbose mode? Useful for debugging purposes. */
340 bool verbose;
341
342 struct bar_colors {
343 char *background;
344 char *statusline;
345 char *separator;
346
347 char *focused_background;
348 char *focused_statusline;
349 char *focused_separator;
350
351 char *focused_workspace_border;
352 char *focused_workspace_bg;
353 char *focused_workspace_text;
354
355 char *active_workspace_border;
356 char *active_workspace_bg;
357 char *active_workspace_text;
358
359 char *inactive_workspace_border;
360 char *inactive_workspace_bg;
361 char *inactive_workspace_text;
362
363 char *urgent_workspace_border;
364 char *urgent_workspace_bg;
365 char *urgent_workspace_text;
366
367 char *binding_mode_border;
368 char *binding_mode_bg;
369 char *binding_mode_text;
370 } colors;
371
372 TAILQ_ENTRY(Barconfig)
373 configs;
374 };
375
376 /**
377 * Defines a mouse command to be executed instead of the default behavior when
378 * clicking on the non-statusline part of i3bar.
379 *
380 */
381 struct Barbinding {
382 /** The button to be used (e.g., 1 for "button1"). */
383 int input_code;
384
385 /** The command which is to be executed for this button. */
386 char *command;
387
388 /** If true, the command will be executed after the button is released. */
389 bool release;
390
391 TAILQ_ENTRY(Barbinding)
392 bindings;
393 };
394
395 struct tray_output_t {
396 char *output;
397
398 TAILQ_ENTRY(tray_output_t)
399 tray_outputs;
400 };
401
402 /**
403 * Finds the configuration file to use (either the one specified by
404 * override_configpath), the user’s one or the system default) and calls
405 * parse_file().
406 *
407 * If you specify override_configpath, only this path is used to look for a
408 * configuration file.
409 *
410 * If use_nagbar is false, don't try to start i3-nagbar but log the errors to
411 * stdout/stderr instead.
412 *
413 */
414 bool parse_configuration(const char *override_configpath, bool use_nagbar);
415
416 /**
417 * Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
418 *
419 * If you specify override_configpath, only this path is used to look for a
420 * configuration file.
421 *
422 */
423 void load_configuration(xcb_connection_t *conn, const char *override_configfile, bool reload);
424
425 /**
426 * Ungrabs all keys, to be called before re-grabbing the keys because of a
427 * mapping_notify event or a configuration file reload
428 *
429 */
430 void ungrab_all_keys(xcb_connection_t *conn);
431
432 /**
433 * Sends the current bar configuration as an event to all barconfig_update listeners.
434 *
435 */
436 void update_barconfig(void);
437
438 /**
439 * Kills the configerror i3-nagbar process, if any.
440 *
441 * Called when reloading/restarting.
442 *
443 * If wait_for_it is set (restarting), this function will waitpid(), otherwise,
444 * ev is assumed to handle it (reloading).
445 *
446 */
447 void kill_configerror_nagbar(bool wait_for_it);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * include/data.h: This file defines all data structures used by i3
7 *
8 */
9 #pragma once
10
11 #include "libi3.h"
12
13 #define SN_API_NOT_YET_FROZEN 1
14 #include <libsn/sn-launcher.h>
15
16 #include <xcb/randr.h>
17 #include <stdbool.h>
18 #include <pcre.h>
19 #include <sys/time.h>
20
21 #include "queue.h"
22
23 /*
24 * To get the big concept: There are helper structures like struct
25 * Workspace_Assignment. Every struct which is also defined as type (see
26 * forward definitions) is considered to be a major structure, thus important.
27 *
28 * The following things are all stored in a 'Con', from very high level (the
29 * biggest Cons) to very small (a single window):
30 *
31 * 1) X11 root window (as big as all your outputs combined)
32 * 2) output (like LVDS1)
33 * 3) content container, dockarea containers
34 * 4) workspaces
35 * 5) split containers
36 * ... (you can arbitrarily nest split containers)
37 * 6) X11 window containers
38 *
39 */
40
41 /* Forward definitions */
42 typedef struct Binding Binding;
43 typedef struct Rect Rect;
44 typedef struct xoutput Output;
45 typedef struct Con Con;
46 typedef struct Match Match;
47 typedef struct Assignment Assignment;
48 typedef struct Window i3Window;
49 typedef struct mark_t mark_t;
50
51 /******************************************************************************
52 * Helper types
53 *****************************************************************************/
54 typedef enum { D_LEFT,
55 D_RIGHT,
56 D_UP,
57 D_DOWN } direction_t;
58 typedef enum { NO_ORIENTATION = 0,
59 HORIZ,
60 VERT } orientation_t;
61 typedef enum { BS_NORMAL = 0,
62 BS_NONE = 1,
63 BS_PIXEL = 2 } border_style_t;
64
65 /** parameter to specify whether tree_close_internal() and x_window_kill() should kill
66 * only this specific window or the whole X11 client */
67 typedef enum { DONT_KILL_WINDOW = 0,
68 KILL_WINDOW = 1,
69 KILL_CLIENT = 2 } kill_window_t;
70
71 /** describes if the window is adjacent to the output (physical screen) edges. */
72 typedef enum { ADJ_NONE = 0,
73 ADJ_LEFT_SCREEN_EDGE = (1 << 0),
74 ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
75 ADJ_UPPER_SCREEN_EDGE = (1 << 2),
76 ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
77
78 typedef enum { HEBM_NONE = ADJ_NONE,
79 HEBM_VERTICAL = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE,
80 HEBM_HORIZONTAL = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE,
81 HEBM_BOTH = HEBM_VERTICAL | HEBM_HORIZONTAL,
82 HEBM_SMART = (1 << 5) } hide_edge_borders_mode_t;
83
84 typedef enum { MM_REPLACE,
85 MM_ADD } mark_mode_t;
86
87 /**
88 * Container layouts. See Con::layout.
89 */
90 typedef enum {
91 L_DEFAULT = 0,
92 L_STACKED = 1,
93 L_TABBED = 2,
94 L_DOCKAREA = 3,
95 L_OUTPUT = 4,
96 L_SPLITV = 5,
97 L_SPLITH = 6
98 } layout_t;
99
100 /**
101 * Binding input types. See Binding::input_type.
102 */
103 typedef enum {
104 B_KEYBOARD = 0,
105 B_MOUSE = 1
106 } input_type_t;
107
108 /**
109 * Bitmask for matching XCB_XKB_GROUP_1 to XCB_XKB_GROUP_4.
110 */
111 typedef enum {
112 I3_XKB_GROUP_MASK_ANY = 0,
113 I3_XKB_GROUP_MASK_1 = (1 << 0),
114 I3_XKB_GROUP_MASK_2 = (1 << 1),
115 I3_XKB_GROUP_MASK_3 = (1 << 2),
116 I3_XKB_GROUP_MASK_4 = (1 << 3)
117 } i3_xkb_group_mask_t;
118
119 /**
120 * The lower 16 bits contain a xcb_key_but_mask_t, the higher 16 bits contain
121 * an i3_xkb_group_mask_t. This type is necessary for the fallback logic to
122 * work when handling XKB groups (see ticket #1775) and makes the code which
123 * locates keybindings upon KeyPress/KeyRelease events simpler.
124 */
125 typedef uint32_t i3_event_state_mask_t;
126
127 /**
128 * Mouse pointer warping modes.
129 */
130 typedef enum {
131 POINTER_WARPING_OUTPUT = 0,
132 POINTER_WARPING_NONE = 1
133 } warping_t;
134
135 /**
136 * Focus wrapping modes.
137 */
138 typedef enum {
139 FOCUS_WRAPPING_OFF = 0,
140 FOCUS_WRAPPING_ON = 1,
141 FOCUS_WRAPPING_FORCE = 2
142 } focus_wrapping_t;
143
144 /**
145 * Stores a rectangle, for example the size of a window, the child window etc.
146 * It needs to be packed so that the compiler will not add any padding bytes.
147 * (it is used in src/ewmh.c for example)
148 *
149 * Note that x and y can contain signed values in some cases (for example when
150 * used for the coordinates of a window, which can be set outside of the
151 * visible area, but not when specifying the position of a workspace for the
152 * _NET_WM_WORKAREA hint). Not declaring x/y as int32_t saves us a lot of
153 * typecasts.
154 *
155 */
156 struct Rect {
157 uint32_t x;
158 uint32_t y;
159 uint32_t width;
160 uint32_t height;
161 } __attribute__((packed));
162
163 /**
164 * Stores the reserved pixels on each screen edge read from a
165 * _NET_WM_STRUT_PARTIAL.
166 *
167 */
168 struct reservedpx {
169 uint32_t left;
170 uint32_t right;
171 uint32_t top;
172 uint32_t bottom;
173 };
174
175 /**
176 * Stores a width/height pair, used as part of deco_render_params to check
177 * whether the rects width/height have changed.
178 *
179 */
180 struct width_height {
181 uint32_t w;
182 uint32_t h;
183 };
184
185 /**
186 * Stores the parameters for rendering a window decoration. This structure is
187 * cached in every Con and no re-rendering will be done if the parameters have
188 * not changed (only the pixmaps will be copied).
189 *
190 */
191 struct deco_render_params {
192 struct Colortriple *color;
193 int border_style;
194 struct width_height con_rect;
195 struct width_height con_window_rect;
196 Rect con_deco_rect;
197 color_t background;
198 layout_t parent_layout;
199 bool con_is_leaf;
200 };
201
202 /**
203 * Stores which workspace (by name or number) goes to which output.
204 *
205 */
206 struct Workspace_Assignment {
207 char *name;
208 char *output;
209
210 TAILQ_ENTRY(Workspace_Assignment)
211 ws_assignments;
212 };
213
214 struct Ignore_Event {
215 int sequence;
216 int response_type;
217 time_t added;
218
219 SLIST_ENTRY(Ignore_Event)
220 ignore_events;
221 };
222
223 /**
224 * Stores internal information about a startup sequence, like the workspace it
225 * was initiated on.
226 *
227 */
228 struct Startup_Sequence {
229 /** startup ID for this sequence, generated by libstartup-notification */
230 char *id;
231 /** workspace on which this startup was initiated */
232 char *workspace;
233 /** libstartup-notification context for this launch */
234 SnLauncherContext *context;
235 /** time at which this sequence should be deleted (after it was marked as
236 * completed) */
237 time_t delete_at;
238
239 TAILQ_ENTRY(Startup_Sequence)
240 sequences;
241 };
242
243 /**
244 * Regular expression wrapper. It contains the pattern itself as a string (like
245 * ^foo[0-9]$) as well as a pointer to the compiled PCRE expression and the
246 * pcre_extra data returned by pcre_study().
247 *
248 * This makes it easier to have a useful logfile, including the matching or
249 * non-matching pattern.
250 *
251 */
252 struct regex {
253 char *pattern;
254 pcre *regex;
255 pcre_extra *extra;
256 };
257
258 /**
259 * Stores a resolved keycode (from a keysym), including the modifier mask. Will
260 * be passed to xcb_grab_key().
261 *
262 */
263 struct Binding_Keycode {
264 xcb_keycode_t keycode;
265 i3_event_state_mask_t modifiers;
266
267 TAILQ_ENTRY(Binding_Keycode)
268 keycodes;
269 };
270
271 /******************************************************************************
272 * Major types
273 *****************************************************************************/
274
275 /**
276 * Holds a keybinding, consisting of a keycode combined with modifiers and the
277 * command which is executed as soon as the key is pressed (see
278 * src/config_parser.c)
279 *
280 */
281 struct Binding {
282 /* The type of input this binding is for. (Mouse bindings are not yet
283 * implemented. All bindings are currently assumed to be keyboard bindings.) */
284 input_type_t input_type;
285
286 /** If true, the binding should be executed upon a KeyRelease event, not a
287 * KeyPress (the default). */
288 enum {
289 /* This binding will only be executed upon KeyPress events */
290 B_UPON_KEYPRESS = 0,
291 /* This binding will be executed either upon a KeyRelease event, or… */
292 B_UPON_KEYRELEASE = 1,
293 /* …upon a KeyRelease event, even if the modifiers don’t match. This
294 * state is triggered from get_binding() when the corresponding
295 * KeyPress (!) happens, so that users can release the modifier keys
296 * before releasing the actual key. */
297 B_UPON_KEYRELEASE_IGNORE_MODS = 2,
298 } release;
299
300 /** If this is true for a mouse binding, the binding should be executed
301 * when the button is pressed over the window border. */
302 bool border;
303
304 /** If this is true for a mouse binding, the binding should be executed
305 * when the button is pressed over any part of the window, not just the
306 * title bar (default). */
307 bool whole_window;
308
309 /** If this is true for a mouse binding, the binding should only be
310 * executed if the button press was not on the titlebar. */
311 bool exclude_titlebar;
312
313 /** Keycode to bind */
314 uint32_t keycode;
315
316 /** Bitmask which is applied against event->state for KeyPress and
317 * KeyRelease events to determine whether this binding applies to the
318 * current state. */
319 i3_event_state_mask_t event_state_mask;
320
321 /** Symbol the user specified in configfile, if any. This needs to be
322 * stored with the binding to be able to re-convert it into a keycode
323 * if the keyboard mapping changes (using Xmodmap for example) */
324 char *symbol;
325
326 /** Only in use if symbol != NULL. Contains keycodes which generate the
327 * specified symbol. Useful for unbinding and checking which binding was
328 * used when a key press event comes in. */
329 TAILQ_HEAD(keycodes_head, Binding_Keycode)
330 keycodes_head;
331
332 /** Command, like in command mode */
333 char *command;
334
335 TAILQ_ENTRY(Binding)
336 bindings;
337 };
338
339 /**
340 * Holds a command specified by either an:
341 * - exec-line
342 * - exec_always-line
343 * in the config (see src/config.c)
344 *
345 */
346 struct Autostart {
347 /** Command, like in command mode */
348 char *command;
349 /** no_startup_id flag for start_application(). Determines whether a
350 * startup notification context/ID should be created. */
351 bool no_startup_id;
352
353 TAILQ_ENTRY(Autostart)
354 autostarts;
355
356 TAILQ_ENTRY(Autostart)
357 autostarts_always;
358 };
359
360 struct output_name {
361 char *name;
362
363 SLIST_ENTRY(output_name)
364 names;
365 };
366
367 /**
368 * An Output is a physical output on your graphics driver. Outputs which
369 * are currently in use have (output->active == true). Each output has a
370 * position and a mode. An output usually corresponds to one connected
371 * screen (except if you are running multiple screens in clone mode).
372 *
373 */
374 struct xoutput {
375 /** Output id, so that we can requery the output directly later */
376 xcb_randr_output_t id;
377
378 /** Whether the output is currently active (has a CRTC attached with a
379 * valid mode) */
380 bool active;
381
382 /** Internal flags, necessary for querying RandR screens (happens in
383 * two stages) */
384 bool changed;
385 bool to_be_disabled;
386 bool primary;
387
388 /** List of names for the output.
389 * An output always has at least one name; the first name is
390 * considered the primary one. */
391 SLIST_HEAD(names_head, output_name)
392 names_head;
393
394 /** Pointer to the Con which represents this output */
395 Con *con;
396
397 /** x, y, width, height */
398 Rect rect;
399
400 TAILQ_ENTRY(xoutput)
401 outputs;
402 };
403
404 /**
405 * A 'Window' is a type which contains an xcb_window_t and all the related
406 * information (hints like _NET_WM_NAME for that window).
407 *
408 */
409 struct Window {
410 xcb_window_t id;
411
412 /** Holds the xcb_window_t (just an ID) for the leader window (logical
413 * parent for toolwindows and similar floating windows) */
414 xcb_window_t leader;
415 xcb_window_t transient_for;
416
417 /** Pointers to the Assignments which were already ran for this Window
418 * (assignments run only once) */
419 uint32_t nr_assignments;
420 Assignment **ran_assignments;
421
422 char *class_class;
423 char *class_instance;
424
425 /** The name of the window. */
426 i3String *name;
427
428 /** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
429 * sets "buddy list"). Useful to match specific windows in assignments or
430 * for_window. */
431 char *role;
432
433 /** Flag to force re-rendering the decoration upon changes */
434 bool name_x_changed;
435
436 /** Whether the application used _NET_WM_NAME */
437 bool uses_net_wm_name;
438
439 /** Whether the application needs to receive WM_TAKE_FOCUS */
440 bool needs_take_focus;
441
442 /** Whether this window accepts focus. We store this inverted so that the
443 * default will be 'accepts focus'. */
444 bool doesnt_accept_focus;
445
446 /** The _NET_WM_WINDOW_TYPE for this window. */
447 xcb_atom_t window_type;
448
449 /** The _NET_WM_DESKTOP for this window. */
450 uint32_t wm_desktop;
451
452 /** Whether the window says it is a dock window */
453 enum { W_NODOCK = 0,
454 W_DOCK_TOP = 1,
455 W_DOCK_BOTTOM = 2 } dock;
456
457 /** When this window was marked urgent. 0 means not urgent */
458 struct timeval urgent;
459
460 /** Pixels the window reserves. left/right/top/bottom */
461 struct reservedpx reserved;
462
463 /** Depth of the window */
464 uint16_t depth;
465
466 /* the wanted size of the window, used in combination with size
467 * increments (see below). */
468 int base_width;
469 int base_height;
470
471 /* minimum increment size specified for the window (in pixels) */
472 int width_increment;
473 int height_increment;
474
475 /* Minimum size specified for the window. */
476 int min_width;
477 int min_height;
478
479 /* Maximum size specified for the window. */
480 int max_width;
481 int max_height;
482
483 /* aspect ratio from WM_NORMAL_HINTS (MPlayer uses this for example) */
484 double aspect_ratio;
485 };
486
487 /**
488 * A "match" is a data structure which acts like a mask or expression to match
489 * certain windows or not. For example, when using commands, you can specify a
490 * command like this: [title="*Firefox*"] kill. The title member of the match
491 * data structure will then be filled and i3 will check each window using
492 * match_matches_window() to find the windows affected by this command.
493 *
494 */
495 struct Match {
496 /* Set if a criterion was specified incorrectly. */
497 char *error;
498
499 struct regex *title;
500 struct regex *application;
501 struct regex *class;
502 struct regex *instance;
503 struct regex *mark;
504 struct regex *window_role;
505 struct regex *workspace;
506 xcb_atom_t window_type;
507 enum {
508 U_DONTCHECK = -1,
509 U_LATEST = 0,
510 U_OLDEST = 1
511 } urgent;
512 enum {
513 M_DONTCHECK = -1,
514 M_NODOCK = 0,
515 M_DOCK_ANY = 1,
516 M_DOCK_TOP = 2,
517 M_DOCK_BOTTOM = 3
518 } dock;
519 xcb_window_t id;
520 enum { WM_ANY = 0,
521 WM_TILING,
522 WM_FLOATING } window_mode;
523 Con *con_id;
524
525 /* Where the window looking for a match should be inserted:
526 *
527 * M_HERE = the matched container will be replaced by the window
528 * (layout saving)
529 * M_ASSIGN_WS = the matched container will be inserted in the target_ws.
530 * M_BELOW = the window will be inserted as a child of the matched container
531 * (dockareas)
532 *
533 */
534 enum { M_HERE = 0,
535 M_ASSIGN_WS,
536 M_BELOW } insert_where;
537
538 TAILQ_ENTRY(Match)
539 matches;
540
541 /* Whether this match was generated when restarting i3 inplace.
542 * Leads to not setting focus when managing a new window, because the old
543 * focus stack should be restored. */
544 bool restart_mode;
545 };
546
547 /**
548 * An Assignment makes specific windows go to a specific workspace/output or
549 * run a command for that window. With this mechanism, the user can -- for
550 * example -- assign their browser to workspace "www". Checking if a window is
551 * assigned works by comparing the Match data structure with the window (see
552 * match_matches_window()).
553 *
554 */
555 struct Assignment {
556 /** type of this assignment:
557 *
558 * A_COMMAND = run the specified command for the matching window
559 * A_TO_WORKSPACE = assign the matching window to the specified workspace
560 * A_NO_FOCUS = don't focus matched window when it is managed
561 *
562 * While the type is a bitmask, only one value can be set at a time. It is
563 * a bitmask to allow filtering for multiple types, for example in the
564 * assignment_for() function.
565 *
566 */
567 enum {
568 A_ANY = 0,
569 A_COMMAND = (1 << 0),
570 A_TO_WORKSPACE = (1 << 1),
571 A_NO_FOCUS = (1 << 2),
572 A_TO_WORKSPACE_NUMBER = (1 << 3),
573 A_TO_OUTPUT = (1 << 4)
574 } type;
575
576 /** the criteria to check if a window matches */
577 Match match;
578
579 /** destination workspace/command/output, depending on the type */
580 union {
581 char *command;
582 char *workspace;
583 char *output;
584 } dest;
585
586 TAILQ_ENTRY(Assignment)
587 assignments;
588 };
589
590 /** Fullscreen modes. Used by Con.fullscreen_mode. */
591 typedef enum { CF_NONE = 0,
592 CF_OUTPUT = 1,
593 CF_GLOBAL = 2 } fullscreen_mode_t;
594
595 struct mark_t {
596 char *name;
597
598 TAILQ_ENTRY(mark_t)
599 marks;
600 };
601
602 /**
603 * A 'Con' represents everything from the X11 root window down to a single X11 window.
604 *
605 */
606 struct Con {
607 bool mapped;
608
609 /* Should this container be marked urgent? This gets set when the window
610 * inside this container (if any) sets the urgency hint, for example. */
611 bool urgent;
612
613 /** This counter contains the number of UnmapNotify events for this
614 * container (or, more precisely, for its ->frame) which should be ignored.
615 * UnmapNotify events need to be ignored when they are caused by i3 itself,
616 * for example when reparenting or when unmapping the window on a workspace
617 * change. */
618 uint8_t ignore_unmap;
619
620 /* The surface used for the frame window. */
621 surface_t frame;
622 surface_t frame_buffer;
623 bool pixmap_recreated;
624
625 enum {
626 CT_ROOT = 0,
627 CT_OUTPUT = 1,
628 CT_CON = 2,
629 CT_FLOATING_CON = 3,
630 CT_WORKSPACE = 4,
631 CT_DOCKAREA = 5
632 } type;
633
634 /** the workspace number, if this Con is of type CT_WORKSPACE and the
635 * workspace is not a named workspace (for named workspaces, num == -1) */
636 int num;
637
638 struct Con *parent;
639
640 /* The position and size for this con. These coordinates are absolute. Note
641 * that the rect of a container does not include the decoration. */
642 struct Rect rect;
643 /* The position and size of the actual client window. These coordinates are
644 * relative to the container's rect. */
645 struct Rect window_rect;
646 /* The position and size of the container's decoration. These coordinates
647 * are relative to the container's parent's rect. */
648 struct Rect deco_rect;
649 /** the geometry this window requested when getting mapped */
650 struct Rect geometry;
651
652 char *name;
653
654 /** The format with which the window's name should be displayed. */
655 char *title_format;
656
657 /* a sticky-group is an identifier which bundles several containers to a
658 * group. The contents are shared between all of them, that is they are
659 * displayed on whichever of the containers is currently visible */
660 char *sticky_group;
661
662 /* user-definable marks to jump to this container later */
663 TAILQ_HEAD(marks_head, mark_t)
664 marks_head;
665 /* cached to decide whether a redraw is needed */
666 bool mark_changed;
667
668 double percent;
669
670 /* the x11 border pixel attribute */
671 int border_width;
672 int current_border_width;
673
674 struct Window *window;
675
676 /* timer used for disabling urgency */
677 struct ev_timer *urgency_timer;
678
679 /** Cache for the decoration rendering */
680 struct deco_render_params *deco_render_params;
681
682 /* Only workspace-containers can have floating clients */
683 TAILQ_HEAD(floating_head, Con)
684 floating_head;
685
686 TAILQ_HEAD(nodes_head, Con)
687 nodes_head;
688
689 TAILQ_HEAD(focus_head, Con)
690 focus_head;
691
692 TAILQ_HEAD(swallow_head, Match)
693 swallow_head;
694
695 fullscreen_mode_t fullscreen_mode;
696
697 /* Whether this window should stick to the glass. This corresponds to
698 * the _NET_WM_STATE_STICKY atom and will only be respected if the
699 * window is floating. */
700 bool sticky;
701
702 /* layout is the layout of this container: one of split[v|h], stacked or
703 * tabbed. Special containers in the tree (above workspaces) have special
704 * layouts like dockarea or output.
705 *
706 * last_split_layout is one of splitv or splith to support the old "layout
707 * default" command which by now should be "layout splitv" or "layout
708 * splith" explicitly.
709 *
710 * workspace_layout is only for type == CT_WORKSPACE cons. When you change
711 * the layout of a workspace without any children, i3 cannot just set the
712 * layout (because workspaces need to be splitv/splith to allow focus
713 * parent and opening new containers). Instead, it stores the requested
714 * layout in workspace_layout and creates a new split container with that
715 * layout whenever a new container is attached to the workspace. */
716 layout_t layout, last_split_layout, workspace_layout;
717 border_style_t border_style;
718 /** floating? (= not in tiling layout) This cannot be simply a bool
719 * because we want to keep track of whether the status was set by the
720 * application (by setting _NET_WM_WINDOW_TYPE appropriately) or by the
721 * user. The user’s choice overwrites automatic mode, of course. The
722 * order of the values is important because we check with >=
723 * FLOATING_AUTO_ON if a client is floating. */
724 enum {
725 FLOATING_AUTO_OFF = 0,
726 FLOATING_USER_OFF = 1,
727 FLOATING_AUTO_ON = 2,
728 FLOATING_USER_ON = 3
729 } floating;
730
731 TAILQ_ENTRY(Con)
732 nodes;
733
734 TAILQ_ENTRY(Con)
735 focused;
736
737 TAILQ_ENTRY(Con)
738 all_cons;
739
740 TAILQ_ENTRY(Con)
741 floating_windows;
742
743 /** callbacks */
744 void (*on_remove_child)(Con *);
745
746 enum {
747 /* Not a scratchpad window. */
748 SCRATCHPAD_NONE = 0,
749
750 /* Just moved to scratchpad, not resized by the user yet.
751 * Window will be auto-centered and sized appropriately. */
752 SCRATCHPAD_FRESH = 1,
753
754 /* The user changed position/size of the scratchpad window. */
755 SCRATCHPAD_CHANGED = 2
756 } scratchpad_state;
757
758 /* The ID of this container before restarting. Necessary to correctly
759 * interpret back-references in the JSON (such as the focus stack). */
760 int old_id;
761
762 /* Depth of the container window */
763 uint16_t depth;
764
765 /* The colormap for this con if a custom one is used. */
766 xcb_colormap_t colormap;
767 };
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * display_version.c: displays the running i3 version, runs as part of
7 * i3 --moreversion.
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Connects to i3 to find out the currently running version. Useful since it
15 * might be different from the version compiled into this binary (maybe the
16 * user didn’t correctly install i3 or forgot te restart it).
17 *
18 * The output looks like this:
19 * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
20 *
21 * The i3 binary you just called: /home/michael/i3/i3
22 * The i3 binary you are running: /home/michael/i3/i3
23 *
24 */
25 void display_running_version(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * ewmh.c: Get/set certain EWMH properties easily.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Updates _NET_CURRENT_DESKTOP with the current desktop number.
15 *
16 * EWMH: The index of the current desktop. This is always an integer between 0
17 * and _NET_NUMBER_OF_DESKTOPS - 1.
18 *
19 */
20 void ewmh_update_current_desktop(void);
21
22 /**
23 * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
24 * noninternal workspaces.
25 */
26 void ewmh_update_number_of_desktops(void);
27
28 /**
29 * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
30 * list of NULL-terminated strings in UTF-8 encoding"
31 */
32 void ewmh_update_desktop_names(void);
33
34 /**
35 * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
36 * define the top left corner of each desktop's viewport.
37 */
38 void ewmh_update_desktop_viewport(void);
39
40 /**
41 * Updates _NET_WM_DESKTOP for all windows.
42 * A request will only be made if the cached value differs from the calculated value.
43 *
44 */
45 void ewmh_update_wm_desktop(void);
46
47 /**
48 * Updates _NET_ACTIVE_WINDOW with the currently focused window.
49 *
50 * EWMH: The window ID of the currently active window or None if no window has
51 * the focus.
52 *
53 */
54 void ewmh_update_active_window(xcb_window_t window);
55
56 /**
57 * Updates _NET_WM_VISIBLE_NAME.
58 *
59 */
60 void ewmh_update_visible_name(xcb_window_t window, const char *name);
61
62 /**
63 * Updates the _NET_CLIENT_LIST hint. Used for window listers.
64 */
65 void ewmh_update_client_list(xcb_window_t *list, int num_windows);
66
67 /**
68 * Updates the _NET_CLIENT_LIST_STACKING hint. Necessary to move tabs in
69 * Chromium correctly.
70 *
71 * EWMH: These arrays contain all X Windows managed by the Window Manager.
72 * _NET_CLIENT_LIST has initial mapping order, starting with the oldest window.
73 * _NET_CLIENT_LIST_STACKING has bottom-to-top stacking order. These properties
74 * SHOULD be set and updated by the Window Manager.
75 *
76 */
77 void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows);
78
79 /**
80 * Set or remove _NET_WM_STATE_STICKY on the window.
81 *
82 */
83 void ewmh_update_sticky(xcb_window_t window, bool sticky);
84
85 /**
86 * Set or remove _NEW_WM_STATE_FOCUSED on the window.
87 *
88 */
89 void ewmh_update_focused(xcb_window_t window, bool is_focused);
90
91 /**
92 * Set up the EWMH hints on the root window.
93 *
94 */
95 void ewmh_setup_hints(void);
96
97 /**
98 * i3 currently does not support _NET_WORKAREA, because it does not correspond
99 * to i3’s concept of workspaces. See also:
100 * https://bugs.i3wm.org/539
101 * https://bugs.i3wm.org/301
102 * https://bugs.i3wm.org/1038
103 *
104 * We need to actively delete this property because some display managers (e.g.
105 * LightDM) set it.
106 *
107 * EWMH: Contains a geometry for each desktop. These geometries specify an area
108 * that is completely contained within the viewport. Work area SHOULD be used by
109 * desktop applications to place desktop icons appropriately.
110 *
111 */
112 void ewmh_update_workarea(void);
113
114 /**
115 * Returns the workspace container as enumerated by the EWMH desktop model.
116 * Returns NULL if no workspace could be found for the index.
117 *
118 * This is the reverse of ewmh_get_workspace_index.
119 *
120 */
121 Con *ewmh_get_workspace_by_index(uint32_t idx);
122
123 /**
124 * Returns the EWMH desktop index for the workspace the given container is on.
125 * Returns NET_WM_DESKTOP_NONE if the desktop index cannot be determined.
126 *
127 * This is the reverse of ewmh_get_workspace_by_index.
128 *
129 */
130 uint32_t ewmh_get_workspace_index(Con *con);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * Faking outputs is useful in pathological situations (like network X servers
7 * which don’t support multi-monitor in a useful way) and for our testsuite.
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 /**
15 * Creates outputs according to the given specification.
16 * The specification must be in the format wxh+x+y, for example 1024x768+0+0,
17 * with multiple outputs separated by commas:
18 * 1900x1200+0+0,1280x1024+1900+0
19 *
20 */
21 void fake_outputs_init(const char *output_spec);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * floating.c: Floating windows.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include "tree.h"
14
15 /** Callback for dragging */
16 typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *);
17
18 /** Macro to create a callback function for dragging */
19 #define DRAGGING_CB(name) \
20 static void name(Con *con, Rect *old_rect, uint32_t new_x, \
21 uint32_t new_y, const void *extra)
22
23 /** On which border was the dragging initiated? */
24 typedef enum { BORDER_LEFT = (1 << 0),
25 BORDER_RIGHT = (1 << 1),
26 BORDER_TOP = (1 << 2),
27 BORDER_BOTTOM = (1 << 3) } border_t;
28
29 /**
30 * Enables floating mode for the given container by detaching it from its
31 * parent, creating a new container around it and storing this container in the
32 * floating_windows list of the workspace.
33 *
34 */
35 void floating_enable(Con *con, bool automatic);
36
37 /**
38 * Disables floating mode for the given container by re-attaching the container
39 * to its old parent.
40 *
41 */
42 void floating_disable(Con *con, bool automatic);
43
44 /**
45 * Calls floating_enable() for tiling containers and floating_disable() for
46 * floating containers.
47 *
48 * If the automatic flag is set to true, this was an automatic update by a
49 * change of the window class from the application which can be overwritten by
50 * the user.
51 *
52 */
53 void toggle_floating_mode(Con *con, bool automatic);
54
55 /**
56 * Raises the given container in the list of floating containers
57 *
58 */
59 void floating_raise_con(Con *con);
60
61 /**
62 * Checks if con’s coordinates are within its workspace and re-assigns it to
63 * the actual workspace if not.
64 *
65 */
66 bool floating_maybe_reassign_ws(Con *con);
67
68 /**
69 * Centers a floating con above the specified rect.
70 *
71 */
72 void floating_center(Con *con, Rect rect);
73
74 /**
75 * Moves the given floating con to the current pointer position.
76 *
77 */
78 void floating_move_to_pointer(Con *con);
79
80 /**
81 * Called when the user clicked on the titlebar of a floating window.
82 * Calls the drag_pointer function with the drag_window callback
83 *
84 */
85 void floating_drag_window(Con *con, const xcb_button_press_event_t *event);
86
87 /**
88 * Called when the user clicked on a floating window while holding the
89 * floating_modifier and the right mouse button.
90 * Calls the drag_pointer function with the resize_window callback
91 *
92 */
93 void floating_resize_window(Con *con, const bool proportional, const xcb_button_press_event_t *event);
94
95 /**
96 * Called when a floating window is created or resized.
97 * This function resizes the window if its size is higher or lower than the
98 * configured maximum/minimum size, respectively.
99 *
100 */
101 void floating_check_size(Con *floating_con);
102
103 /**
104 * This is the return value of a drag operation like drag_pointer.
105 *
106 * DRAGGING will indicate the drag action is still in progress and can be
107 * continued or resolved.
108 *
109 * DRAG_SUCCESS will indicate the intention of the drag action should be
110 * carried out.
111 *
112 * DRAG_REVERT will indicate an attempt should be made to restore the state of
113 * the involved windows to their condition before the drag.
114 *
115 * DRAG_ABORT will indicate that the intention of the drag action cannot be
116 * carried out (e.g. because the window has been unmapped).
117 *
118 */
119 typedef enum {
120 DRAGGING = 0,
121 DRAG_SUCCESS,
122 DRAG_REVERT,
123 DRAG_ABORT
124 } drag_result_t;
125
126 /**
127 * This function grabs your pointer and keyboard and lets you drag stuff around
128 * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will
129 * be received and the given callback will be called with the parameters
130 * specified (client, border on which the click originally was), the original
131 * rect of the client, the event and the new coordinates (x, y).
132 *
133 */
134 drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event,
135 xcb_window_t confine_to, border_t border, int cursor,
136 callback_t callback, const void *extra);
137
138 /**
139 * Repositions the CT_FLOATING_CON to have the coordinates specified by
140 * newrect, but only if the coordinates are not out-of-bounds. Also reassigns
141 * the floating con to a different workspace if this move was across different
142 * outputs.
143 *
144 */
145 bool floating_reposition(Con *con, Rect newrect);
146
147 /**
148 * Sets size of the CT_FLOATING_CON to specified dimensions. Might limit the
149 * actual size with regard to size constraints taken from user settings.
150 * Additionally, the dimensions may be upscaled until they're divisible by the
151 * window's size hints.
152 *
153 */
154 void floating_resize(Con *floating_con, int x, int y);
155
156 /**
157 * Fixes the coordinates of the floating window whenever the window gets
158 * reassigned to a different output (or when the output’s rect changes).
159 *
160 */
161 void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * handlers.c: Small handlers for various events (keypresses, focus changes,
7 * …).
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 #include <xcb/randr.h>
15
16 extern int randr_base;
17 extern int xkb_base;
18
19 /**
20 * Adds the given sequence to the list of events which are ignored.
21 * If this ignore should only affect a specific response_type, pass
22 * response_type, otherwise, pass -1.
23 *
24 * Every ignored sequence number gets garbage collected after 5 seconds.
25 *
26 */
27 void add_ignore_event(const int sequence, const int response_type);
28
29 /**
30 * Checks if the given sequence is ignored and returns true if so.
31 *
32 */
33 bool event_is_ignored(const int sequence, const int response_type);
34
35 /**
36 * Takes an xcb_generic_event_t and calls the appropriate handler, based on the
37 * event type.
38 *
39 */
40 void handle_event(int type, xcb_generic_event_t *event);
41
42 /**
43 * Sets the appropriate atoms for the property handlers after the atoms were
44 * received from X11
45 *
46 */
47 void property_handlers_init(void);
48
49 #if 0
50 /**
51 * Configuration notifies are only handled because we need to set up ignore
52 * for the following enter notify events
53 *
54 */
55 int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event);
56 #endif
57
58 #if 0
59 /**
60 * Handles _NET_WM_WINDOW_TYPE changes
61 *
62 */
63 int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state,
64 xcb_window_t window, xcb_atom_t atom,
65 xcb_get_property_reply_t *property);
66 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * This public header defines the different constants and message types to use
7 * for the IPC interface to i3 (see docs/ipc for more information).
8 *
9 */
10 #pragma once
11
12 #include <stdint.h>
13
14 typedef struct i3_ipc_header {
15 /* 6 = strlen(I3_IPC_MAGIC) */
16 char magic[6];
17 uint32_t size;
18 uint32_t type;
19 } __attribute__((packed)) i3_ipc_header_t;
20
21 /*
22 * Messages from clients to i3
23 *
24 */
25
26 /** Never change this, only on major IPC breakage (don’t do that) */
27 #define I3_IPC_MAGIC "i3-ipc"
28
29 /** Deprecated: use I3_IPC_MESSAGE_TYPE_RUN_COMMAND */
30 #define I3_IPC_MESSAGE_TYPE_COMMAND 0
31
32 /** The payload of the message will be interpreted as a command */
33 #define I3_IPC_MESSAGE_TYPE_RUN_COMMAND 0
34
35 /** Requests the current workspaces from i3 */
36 #define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1
37
38 /** Subscribe to the specified events */
39 #define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2
40
41 /** Requests the current outputs from i3 */
42 #define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3
43
44 /** Requests the tree layout from i3 */
45 #define I3_IPC_MESSAGE_TYPE_GET_TREE 4
46
47 /** Request the current defined marks from i3 */
48 #define I3_IPC_MESSAGE_TYPE_GET_MARKS 5
49
50 /** Request the configuration for a specific 'bar' */
51 #define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
52
53 /** Request the i3 version */
54 #define I3_IPC_MESSAGE_TYPE_GET_VERSION 7
55
56 /** Request a list of configured binding modes. */
57 #define I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES 8
58
59 /** Request the raw last loaded i3 config. */
60 #define I3_IPC_MESSAGE_TYPE_GET_CONFIG 9
61
62 /** Send a tick event to all subscribers. */
63 #define I3_IPC_MESSAGE_TYPE_SEND_TICK 10
64
65 /** Trigger an i3 sync protocol message via IPC. */
66 #define I3_IPC_MESSAGE_TYPE_SYNC 11
67
68 /*
69 * Messages from i3 to clients
70 *
71 */
72 #define I3_IPC_REPLY_TYPE_COMMAND 0
73 #define I3_IPC_REPLY_TYPE_WORKSPACES 1
74 #define I3_IPC_REPLY_TYPE_SUBSCRIBE 2
75 #define I3_IPC_REPLY_TYPE_OUTPUTS 3
76 #define I3_IPC_REPLY_TYPE_TREE 4
77 #define I3_IPC_REPLY_TYPE_MARKS 5
78 #define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
79 #define I3_IPC_REPLY_TYPE_VERSION 7
80 #define I3_IPC_REPLY_TYPE_BINDING_MODES 8
81 #define I3_IPC_REPLY_TYPE_CONFIG 9
82 #define I3_IPC_REPLY_TYPE_TICK 10
83 #define I3_IPC_REPLY_TYPE_SYNC 11
84
85 /*
86 * Events from i3 to clients. Events have the first bit set high.
87 *
88 */
89 #define I3_IPC_EVENT_MASK (1UL << 31)
90
91 /* The workspace event will be triggered upon changes in the workspace list */
92 #define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0)
93
94 /* The output event will be triggered upon changes in the output list */
95 #define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1)
96
97 /* The output event will be triggered upon mode changes */
98 #define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2)
99
100 /* The window event will be triggered upon window changes */
101 #define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3)
102
103 /** Bar config update will be triggered to update the bar config */
104 #define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
105
106 /** The binding event will be triggered when bindings run */
107 #define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
108
109 /** The shutdown event will be triggered when the ipc shuts down */
110 #define I3_IPC_EVENT_SHUTDOWN (I3_IPC_EVENT_MASK | 6)
111
112 /** The tick event will be sent upon a tick IPC message */
113 #define I3_IPC_EVENT_TICK (I3_IPC_EVENT_MASK | 7)
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * i3.h: global variables that are used all over i3.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <sys/time.h>
14 #include <sys/resource.h>
15
16 #include <xcb/xcb_keysyms.h>
17 #include <xcb/xkb.h>
18
19 #include <X11/XKBlib.h>
20
21 #define SN_API_NOT_YET_FROZEN 1
22 #include <libsn/sn-launcher.h>
23
24 #include "queue.h"
25 #include "data.h"
26 #include "xcb.h"
27
28 /** Git commit identifier, from version.c */
29 extern const char *i3_version;
30
31 /** The original value of RLIMIT_CORE when i3 was started. We need to restore
32 * this before starting any other process, since we set RLIMIT_CORE to
33 * RLIM_INFINITY for i3 debugging versions. */
34 extern struct rlimit original_rlimit_core;
35 /** Whether this version of i3 is a debug build or a release build. */
36 extern bool debug_build;
37 /** The number of file descriptors passed via socket activation. */
38 extern int listen_fds;
39 extern int conn_screen;
40 /**
41 * The EWMH support window that is used to indicate that an EWMH-compliant
42 * window manager is present. This window is created when i3 starts and
43 * kept alive until i3 exits.
44 * We also use this window as the focused window if no other window is
45 * available to be focused on the active workspace in order to prevent
46 * keyboard focus issues (see #1378).
47 */
48 extern xcb_window_t ewmh_window;
49 /** The last timestamp we got from X11 (timestamps are included in some events
50 * and are used for some things, like determining a unique ID in startup
51 * notification). */
52 extern xcb_timestamp_t last_timestamp;
53 extern SnDisplay *sndisplay;
54 extern xcb_key_symbols_t *keysyms;
55 extern char **start_argv;
56 extern Display *xlibdpy, *xkbdpy;
57 extern int xkb_current_group;
58 extern TAILQ_HEAD(bindings_head, Binding) * bindings;
59 extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
60 extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
61 extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;
62 extern TAILQ_HEAD(assignments_head, Assignment) assignments;
63 extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
64
65 /* Color depth, visual id and colormap to use when creating windows and
66 * pixmaps. Will use 32 bit depth and an appropriate visual, if available,
67 * otherwise the root window’s default (usually 24 bit TrueColor). */
68 extern uint8_t root_depth;
69 extern xcb_visualid_t visual_id;
70 extern xcb_colormap_t colormap;
71
72 extern bool xcursor_supported, xkb_supported;
73 extern xcb_window_t root;
74 extern struct ev_loop *main_loop;
75 extern bool only_check_config;
76 extern bool force_xinerama;
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <ev.h>
14 #include <stdbool.h>
15 #include <yajl/yajl_gen.h>
16 #include <yajl/yajl_parse.h>
17
18 #include "data.h"
19 #include "tree.h"
20 #include "configuration.h"
21
22 #include "i3/ipc.h"
23
24 extern char *current_socketpath;
25
26 typedef struct ipc_client {
27 int fd;
28
29 /* The events which this client wants to receive */
30 int num_events;
31 char **events;
32
33 /* For clients which subscribe to the tick event: whether the first tick
34 * event has been sent by i3. */
35 bool first_tick_sent;
36
37 struct ev_io *read_callback;
38 struct ev_io *write_callback;
39 struct ev_timer *timeout;
40 uint8_t *buffer;
41 size_t buffer_size;
42
43 TAILQ_ENTRY(ipc_client)
44 clients;
45 } ipc_client;
46
47 /*
48 * Callback type for the different message types.
49 *
50 * message is the raw packet, as received from the UNIX domain socket. size
51 * is the remaining size of bytes for this packet.
52 *
53 * message_size is the size of the message as the sender specified it.
54 * message_type is the type of the message as the sender specified it.
55 *
56 */
57 typedef void (*handler_t)(ipc_client *, uint8_t *, int, uint32_t, uint32_t);
58
59 /* Macro to declare a callback */
60 #define IPC_HANDLER(name) \
61 static void handle_##name(ipc_client *client, uint8_t *message, \
62 int size, uint32_t message_size, \
63 uint32_t message_type)
64
65 /**
66 * Handler for activity on the listening socket, meaning that a new client
67 * has just connected and we should accept() him. Sets up the event handler
68 * for activity on the new connection and inserts the file descriptor into
69 * the list of clients.
70 *
71 */
72 void ipc_new_client(EV_P_ struct ev_io *w, int revents);
73
74 /**
75 * Creates the UNIX domain socket at the given path, sets it to non-blocking
76 * mode, bind()s and listen()s on it.
77 *
78 */
79 int ipc_create_socket(const char *filename);
80
81 /**
82 * Sends the specified event to all IPC clients which are currently connected
83 * and subscribed to this kind of event.
84 *
85 */
86 void ipc_send_event(const char *event, uint32_t message_type, const char *payload);
87
88 /**
89 * Calls to ipc_shutdown() should provide a reason for the shutdown.
90 */
91 typedef enum {
92 SHUTDOWN_REASON_RESTART,
93 SHUTDOWN_REASON_EXIT
94 } shutdown_reason_t;
95
96 /**
97 * Calls shutdown() on each socket and closes it.
98 *
99 */
100 void ipc_shutdown(shutdown_reason_t reason);
101
102 void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
103
104 /**
105 * Generates a json workspace event. Returns a dynamically allocated yajl
106 * generator. Free with yajl_gen_free().
107 */
108 yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old);
109
110 /**
111 * For the workspace events we send, along with the usual "change" field, also
112 * the workspace container in "current". For focus events, we send the
113 * previously focused workspace in "old".
114 */
115 void ipc_send_workspace_event(const char *change, Con *current, Con *old);
116
117 /**
118 * For the window events we send, along the usual "change" field,
119 * also the window container, in "container".
120 */
121 void ipc_send_window_event(const char *property, Con *con);
122
123 /**
124 * For the barconfig update events, we send the serialized barconfig.
125 */
126 void ipc_send_barconfig_update_event(Barconfig *barconfig);
127
128 /**
129 * For the binding events, we send the serialized binding struct.
130 */
131 void ipc_send_binding_event(const char *event_type, Binding *bind);
132
133 /**
134 * Set the maximum duration that we allow for a connection with an unwriteable
135 * socket.
136 */
137 void ipc_set_kill_timeout(ev_tstamp new);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * key_press.c: key press handler
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * There was a key press. We compare this key code with our bindings table and pass
15 * the bound action to parse_command().
16 *
17 */
18 void handle_key_press(xcb_key_press_event_t *event);
19
20 /**
21 * Kills the commanderror i3-nagbar process, if any.
22 *
23 * Called when reloading/restarting, since the user probably fixed their wrong
24 * keybindings.
25 *
26 * If wait_for_it is set (restarting), this function will waitpid(), otherwise,
27 * ev is assumed to handle it (reloading).
28 *
29 */
30 void kill_commanderror_nagbar(bool wait_for_it);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * libi3: contains functions which are used by i3 *and* accompanying tools such
7 * as i3-msg, i3-config-wizard, …
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 #include <stdbool.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <xcb/xcb.h>
18 #include <xcb/xproto.h>
19 #include <xcb/xcb_keysyms.h>
20
21 #include <pango/pango.h>
22 #include <cairo/cairo-xcb.h>
23
24 #define DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
25
26 /** Mouse buttons */
27 #define XCB_BUTTON_CLICK_LEFT XCB_BUTTON_INDEX_1
28 #define XCB_BUTTON_CLICK_MIDDLE XCB_BUTTON_INDEX_2
29 #define XCB_BUTTON_CLICK_RIGHT XCB_BUTTON_INDEX_3
30 #define XCB_BUTTON_SCROLL_UP XCB_BUTTON_INDEX_4
31 #define XCB_BUTTON_SCROLL_DOWN XCB_BUTTON_INDEX_5
32 /* xcb doesn't define constants for these. */
33 #define XCB_BUTTON_SCROLL_LEFT 6
34 #define XCB_BUTTON_SCROLL_RIGHT 7
35
36 /**
37 * XCB connection and root screen
38 *
39 */
40 extern xcb_connection_t *conn;
41 extern xcb_screen_t *root_screen;
42
43 /**
44 * Opaque data structure for storing strings.
45 *
46 */
47 typedef struct _i3String i3String;
48
49 typedef struct Font i3Font;
50
51 /**
52 * Data structure for cached font information:
53 * - font id in X11 (load it once)
54 * - font height (multiple calls needed to get it)
55 *
56 */
57 struct Font {
58 /** The type of font */
59 enum {
60 FONT_TYPE_NONE = 0,
61 FONT_TYPE_XCB,
62 FONT_TYPE_PANGO
63 } type;
64
65 /** The height of the font, built from font_ascent + font_descent */
66 int height;
67
68 /** The pattern/name used to load the font. */
69 char *pattern;
70
71 union {
72 struct {
73 /** The xcb-id for the font */
74 xcb_font_t id;
75
76 /** Font information gathered from the server */
77 xcb_query_font_reply_t *info;
78
79 /** Font table for this font (may be NULL) */
80 xcb_charinfo_t *table;
81 } xcb;
82
83 /** The pango font description */
84 PangoFontDescription *pango_desc;
85 } specific;
86 };
87
88 /* Since this file also gets included by utilities which don’t use the i3 log
89 * infrastructure, we define a fallback. */
90 #if !defined(LOG)
91 void verboselog(char *fmt, ...)
92 __attribute__((format(printf, 1, 2)));
93 #define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__)
94 #endif
95 #if !defined(ELOG)
96 void errorlog(char *fmt, ...)
97 __attribute__((format(printf, 1, 2)));
98 #define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
99 #endif
100 #if !defined(DLOG)
101 void debuglog(char *fmt, ...)
102 __attribute__((format(printf, 1, 2)));
103 #define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, STRIPPED__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
104 #endif
105
106 /**
107 * Try to get the contents of the given atom (for example I3_SOCKET_PATH) from
108 * the X11 root window and return NULL if it doesn’t work.
109 *
110 * If the provided XCB connection is NULL, a new connection will be
111 * established.
112 *
113 * The memory for the contents is dynamically allocated and has to be
114 * free()d by the caller.
115 *
116 */
117 char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn, int screen);
118
119 /**
120 * Safe-wrapper around malloc which exits if malloc returns NULL (meaning that
121 * there is no more memory available)
122 *
123 */
124 void *smalloc(size_t size);
125
126 /**
127 * Safe-wrapper around calloc which exits if malloc returns NULL (meaning that
128 * there is no more memory available)
129 *
130 */
131 void *scalloc(size_t num, size_t size);
132
133 /**
134 * Safe-wrapper around realloc which exits if realloc returns NULL (meaning
135 * that there is no more memory available).
136 *
137 */
138 void *srealloc(void *ptr, size_t size);
139
140 /**
141 * Safe-wrapper around strdup which exits if malloc returns NULL (meaning that
142 * there is no more memory available)
143 *
144 */
145 char *sstrdup(const char *str);
146
147 /**
148 * Safe-wrapper around strndup which exits if strndup returns NULL (meaning that
149 * there is no more memory available)
150 *
151 */
152 char *sstrndup(const char *str, size_t size);
153
154 /**
155 * Safe-wrapper around asprintf which exits if it returns -1 (meaning that
156 * there is no more memory available)
157 *
158 */
159 int sasprintf(char **strp, const char *fmt, ...);
160
161 /**
162 * Wrapper around correct write which returns -1 (meaning that
163 * write failed) or count (meaning that all bytes were written)
164 *
165 */
166 ssize_t writeall(int fd, const void *buf, size_t count);
167
168 /**
169 * Like writeall, but instead of retrying upon EAGAIN (returned when a write
170 * would block), the function stops and returns the total number of bytes
171 * written so far.
172 *
173 */
174 ssize_t writeall_nonblock(int fd, const void *buf, size_t count);
175
176 /**
177 * Safe-wrapper around writeall which exits if it returns -1 (meaning that
178 * write failed)
179 *
180 */
181 ssize_t swrite(int fd, const void *buf, size_t count);
182
183 /**
184 * Build an i3String from an UTF-8 encoded string.
185 * Returns the newly-allocated i3String.
186 *
187 */
188 i3String *i3string_from_utf8(const char *from_utf8);
189
190 /**
191 * Build an i3String from an UTF-8 encoded string in Pango markup.
192 *
193 */
194 i3String *i3string_from_markup(const char *from_markup);
195
196 /**
197 * Build an i3String from an UTF-8 encoded string with fixed length.
198 * To be used when no proper NULL-termination is available.
199 * Returns the newly-allocated i3String.
200 *
201 */
202 i3String *i3string_from_utf8_with_length(const char *from_utf8, ssize_t num_bytes);
203
204 /**
205 * Build an i3String from an UTF-8 encoded string in Pango markup with fixed
206 * length.
207 *
208 */
209 i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes);
210
211 /**
212 * Build an i3String from an UCS-2 encoded string.
213 * Returns the newly-allocated i3String.
214 *
215 */
216 i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs);
217
218 /**
219 * Copies the given i3string.
220 * Note that this will not free the source string.
221 */
222 i3String *i3string_copy(i3String *str);
223
224 /**
225 * Free an i3String.
226 *
227 */
228 void i3string_free(i3String *str);
229
230 /**
231 * Securely i3string_free by setting the pointer to NULL
232 * to prevent accidentally using freed memory.
233 *
234 */
235 #define I3STRING_FREE(str) \
236 do { \
237 if (str != NULL) { \
238 i3string_free(str); \
239 str = NULL; \
240 } \
241 } while (0)
242
243 /**
244 * Returns the UTF-8 encoded version of the i3String.
245 *
246 */
247 const char *i3string_as_utf8(i3String *str);
248
249 /**
250 * Returns the UCS-2 encoded version of the i3String.
251 *
252 */
253 const xcb_char2b_t *i3string_as_ucs2(i3String *str);
254
255 /**
256 * Returns the number of bytes (UTF-8 encoded) in an i3String.
257 *
258 */
259 size_t i3string_get_num_bytes(i3String *str);
260
261 /**
262 * Whether the given i3String is in Pango markup.
263 */
264 bool i3string_is_markup(i3String *str);
265
266 /**
267 * Set whether the i3String should use Pango markup.
268 */
269 void i3string_set_markup(i3String *str, bool pango_markup);
270
271 /**
272 * Escape pango markup characters in the given string.
273 */
274 i3String *i3string_escape_markup(i3String *str);
275
276 /**
277 * Returns the number of glyphs in an i3String.
278 *
279 */
280 size_t i3string_get_num_glyphs(i3String *str);
281
282 /**
283 * Connects to the i3 IPC socket and returns the file descriptor for the
284 * socket. die()s if anything goes wrong.
285 *
286 */
287 int ipc_connect(const char *socket_path);
288
289 /**
290 * Formats a message (payload) of the given size and type and sends it to i3 via
291 * the given socket file descriptor.
292 *
293 * Returns -1 when write() fails, errno will remain.
294 * Returns 0 on success.
295 *
296 */
297 int ipc_send_message(int sockfd, const uint32_t message_size,
298 const uint32_t message_type, const uint8_t *payload);
299
300 /**
301 * Reads a message from the given socket file descriptor and stores its length
302 * (reply_length) as well as a pointer to its contents (reply).
303 *
304 * Returns -1 when read() fails, errno will remain.
305 * Returns -2 when the IPC protocol is violated (invalid magic, unexpected
306 * message type, EOF instead of a message). Additionally, the error will be
307 * printed to stderr.
308 * Returns 0 on success.
309 *
310 */
311 int ipc_recv_message(int sockfd, uint32_t *message_type,
312 uint32_t *reply_length, uint8_t **reply);
313
314 /**
315 * Generates a configure_notify event and sends it to the given window
316 * Applications need this to think they’ve configured themselves correctly.
317 * The truth is, however, that we will manage them.
318 *
319 */
320 void fake_configure_notify(xcb_connection_t *conn, xcb_rectangle_t r, xcb_window_t window, int border_width);
321
322 #define HAS_G_UTF8_MAKE_VALID GLIB_CHECK_VERSION(2, 52, 0)
323 #if !HAS_G_UTF8_MAKE_VALID
324 gchar *g_utf8_make_valid(const gchar *str, gssize len);
325 #endif
326
327 /**
328 * Returns the colorpixel to use for the given hex color (think of HTML). Only
329 * works for true-color (vast majority of cases) at the moment, avoiding a
330 * roundtrip to X11.
331 *
332 * The hex_color has to start with #, for example #FF00FF.
333 *
334 * NOTE that get_colorpixel() does _NOT_ check the given color code for validity.
335 * This has to be done by the caller.
336 *
337 * NOTE that this function may in the future rely on a global xcb_connection_t
338 * variable called 'conn' to be present.
339 *
340 */
341 uint32_t get_colorpixel(const char *hex) __attribute__((const));
342
343 #if defined(__APPLE__)
344
345 /**
346 * Taken from FreeBSD
347 * Returns a pointer to a new string which is a duplicate of the
348 * string, but only copies at most n characters.
349 *
350 */
351 char *strndup(const char *str, size_t n);
352
353 #endif
354
355 /**
356 * All-in-one function which returns the modifier mask (XCB_MOD_MASK_*) for the
357 * given keysymbol, for example for XCB_NUM_LOCK (usually configured to mod2).
358 *
359 * This function initiates one round-trip. Use get_mod_mask_for() directly if
360 * you already have the modifier mapping and key symbols.
361 *
362 */
363 uint32_t aio_get_mod_mask_for(uint32_t keysym, xcb_key_symbols_t *symbols);
364
365 /**
366 * Returns the modifier mask (XCB_MOD_MASK_*) for the given keysymbol, for
367 * example for XCB_NUM_LOCK (usually configured to mod2).
368 *
369 * This function does not initiate any round-trips.
370 *
371 */
372 uint32_t get_mod_mask_for(uint32_t keysym,
373 xcb_key_symbols_t *symbols,
374 xcb_get_modifier_mapping_reply_t *modmap_reply);
375
376 /**
377 * Loads a font for usage, also getting its height. If fallback is true,
378 * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any
379 * font was previously loaded, it will be freed.
380 *
381 */
382 i3Font load_font(const char *pattern, const bool fallback);
383
384 /**
385 * Defines the font to be used for the forthcoming calls.
386 *
387 */
388 void set_font(i3Font *font);
389
390 /**
391 * Frees the resources taken by the current font. If no font was previously
392 * loaded, it simply returns.
393 *
394 */
395 void free_font(void);
396
397 /**
398 * Converts the given string to UTF-8 from UCS-2 big endian. The return value
399 * must be freed after use.
400 *
401 */
402 char *convert_ucs2_to_utf8(xcb_char2b_t *text, size_t num_glyphs);
403
404 /**
405 * Converts the given string to UCS-2 big endian for use with
406 * xcb_image_text_16(). The amount of real glyphs is stored in real_strlen,
407 * a buffer containing the UCS-2 encoded string (16 bit per glyph) is
408 * returned. It has to be freed when done.
409 *
410 */
411 xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen);
412
413 /* Represents a color split by color channel. */
414 typedef struct color_t {
415 double red;
416 double green;
417 double blue;
418 double alpha;
419
420 /* The colorpixel we use for direct XCB calls. */
421 uint32_t colorpixel;
422 } color_t;
423
424 #define COLOR_TRANSPARENT ((color_t){.red = 0.0, .green = 0.0, .blue = 0.0, .colorpixel = 0})
425
426 /**
427 * Defines the colors to be used for the forthcoming draw_text calls.
428 *
429 */
430 void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background);
431
432 /**
433 * Returns true if and only if the current font is a pango font.
434 *
435 */
436 bool font_is_pango(void);
437
438 /**
439 * Draws text onto the specified X drawable (normally a pixmap) at the
440 * specified coordinates (from the top left corner of the leftmost, uppermost
441 * glyph) and using the provided gc.
442 *
443 * Text must be specified as an i3String.
444 *
445 */
446 void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
447 xcb_visualtype_t *visual, int x, int y, int max_width);
448
449 /**
450 * ASCII version of draw_text to print static strings.
451 *
452 */
453 void draw_text_ascii(const char *text, xcb_drawable_t drawable,
454 xcb_gcontext_t gc, int x, int y, int max_width);
455
456 /**
457 * Predict the text width in pixels for the given text. Text must be
458 * specified as an i3String.
459 *
460 */
461 int predict_text_width(i3String *text);
462
463 /**
464 * Returns the visual type associated with the given screen.
465 *
466 */
467 xcb_visualtype_t *get_visualtype(xcb_screen_t *screen);
468
469 /**
470 * Returns true if this version of i3 is a debug build (anything which is not a
471 * release version), based on the git version number.
472 *
473 */
474 bool is_debug_build(void) __attribute__((const));
475
476 /**
477 * Returns the name of a temporary file with the specified prefix.
478 *
479 */
480 char *get_process_filename(const char *prefix);
481
482 /**
483 * This function returns the absolute path to the executable it is running in.
484 *
485 * The implementation follows https://stackoverflow.com/a/933996/712014
486 *
487 * Returned value must be freed by the caller.
488 */
489 char *get_exe_path(const char *argv0);
490
491 /**
492 * Initialize the DPI setting.
493 * This will use the 'Xft.dpi' X resource if available and fall back to
494 * guessing the correct value otherwise.
495 */
496 void init_dpi(void);
497
498 /**
499 * This function returns the value of the DPI setting.
500 *
501 */
502 long get_dpi_value(void);
503
504 /**
505 * Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI
506 * screen) to a corresponding amount of physical pixels on a standard or retina
507 * screen, e.g. 5 pixels on a 227 DPI MacBook Pro 13" Retina screen.
508 *
509 */
510 int logical_px(const int logical);
511
512 /**
513 * This function resolves ~ in pathnames.
514 * It may resolve wildcards in the first part of the path, but if no match
515 * or multiple matches are found, it just returns a copy of path as given.
516 *
517 */
518 char *resolve_tilde(const char *path);
519
520 /**
521 * Get the path of the first configuration file found. If override_configpath is
522 * specified, that path is returned and saved for further calls. Otherwise,
523 * checks the home directory first, then the system directory, always taking
524 * into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
525 * $XDG_CONFIG_DIRS).
526 *
527 */
528 char *get_config_path(const char *override_configpath, bool use_system_paths);
529
530 #if !defined(__sun)
531 /**
532 * Emulates mkdir -p (creates any missing folders)
533 *
534 */
535 int mkdirp(const char *path, mode_t mode);
536 #endif
537
538 /** Helper structure for usage in format_placeholders(). */
539 typedef struct placeholder_t {
540 /* The placeholder to be replaced, e.g., "%title". */
541 char *name;
542 /* The value this placeholder should be replaced with. */
543 char *value;
544 } placeholder_t;
545
546 /**
547 * Replaces occurrences of the defined placeholders in the format string.
548 *
549 */
550 char *format_placeholders(char *format, placeholder_t *placeholders, int num);
551
552 /* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989
553 * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */
554 #define CAIRO_SURFACE_FLUSH(surface) \
555 do { \
556 cairo_surface_flush(surface); \
557 cairo_surface_flush(surface); \
558 } while (0)
559
560 /* A wrapper grouping an XCB drawable and both a graphics context
561 * and the corresponding cairo objects representing it. */
562 typedef struct surface_t {
563 /* The drawable which is being represented. */
564 xcb_drawable_t id;
565
566 /* A classic XCB graphics context. */
567 xcb_gcontext_t gc;
568
569 xcb_visualtype_t *visual_type;
570
571 int width;
572 int height;
573
574 /* A cairo surface representing the drawable. */
575 cairo_surface_t *surface;
576
577 /* The cairo object representing the drawable. In general,
578 * this is what one should use for any drawing operation. */
579 cairo_t *cr;
580 } surface_t;
581
582 /**
583 * Initialize the surface to represent the given drawable.
584 *
585 */
586 void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable,
587 xcb_visualtype_t *visual, int width, int height);
588
589 /**
590 * Resize the surface to the given size.
591 *
592 */
593 void draw_util_surface_set_size(surface_t *surface, int width, int height);
594
595 /**
596 * Destroys the surface.
597 *
598 */
599 void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface);
600
601 /**
602 * Parses the given color in hex format to an internal color representation.
603 * Note that the input must begin with a hash sign, e.g., "#3fbc59".
604 *
605 */
606 color_t draw_util_hex_to_color(const char *color);
607
608 /**
609 * Draw the given text using libi3.
610 * This function also marks the surface dirty which is needed if other means of
611 * drawing are used. This will be the case when using XCB to draw text.
612 *
613 */
614 void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width);
615
616 /**
617 * Draws a filled rectangle.
618 * This function is a convenience wrapper and takes care of flushing the
619 * surface as well as restoring the cairo state.
620 *
621 */
622 void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h);
623
624 /**
625 * Clears a surface with the given color.
626 *
627 */
628 void draw_util_clear_surface(surface_t *surface, color_t color);
629
630 /**
631 * Copies a surface onto another surface.
632 *
633 */
634 void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y,
635 double dest_x, double dest_y, double width, double height);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * load_layout.c: Restore (parts of) the layout, for example after an inplace
7 * restart.
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 typedef enum {
15 // We could not determine the content of the JSON file. This typically
16 // means it’s unreadable or contains garbage.
17 JSON_CONTENT_UNKNOWN = 0,
18
19 // The JSON file contains a “normal” container, i.e. a container to be
20 // appended to an existing workspace (or split container!).
21 JSON_CONTENT_CON = 1,
22
23 // The JSON file contains a workspace container, which needs to be appended
24 // to the output (next to the other workspaces) with special care to avoid
25 // naming conflicts and ensuring that the workspace _has_ a name.
26 JSON_CONTENT_WORKSPACE = 2,
27 } json_content_t;
28
29 /* Parses the given JSON file until it encounters the first “type” property to
30 * determine whether the file contains workspaces or regular containers, which
31 * is important to know when deciding where (and how) to append the contents.
32 * */
33 json_content_t json_determine_content(const char *buf, const size_t len);
34
35 /**
36 * Returns true if the provided JSON could be parsed by yajl.
37 *
38 */
39 bool json_validate(const char *buf, const size_t len);
40
41 void tree_append_json(Con *con, const char *buf, const size_t len, char **errormsg);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * log.c: Logging functions.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <stdarg.h>
14 #include <stdbool.h>
15
16 /* We will include libi3.h which define its own version of LOG, ELOG.
17 * We want *our* version, so we undef the libi3 one. */
18 #if defined(LOG)
19 #undef LOG
20 #endif
21 #if defined(ELOG)
22 #undef ELOG
23 #endif
24 #if defined(DLOG)
25 #undef DLOG
26 #endif
27 /** ##__VA_ARGS__ means: leave out __VA_ARGS__ completely if it is empty, that
28 is, delete the preceding comma */
29 #define LOG(fmt, ...) verboselog(fmt, ##__VA_ARGS__)
30 #define ELOG(fmt, ...) errorlog("ERROR: " fmt, ##__VA_ARGS__)
31 #define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, STRIPPED__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
32
33 extern char *errorfilename;
34 extern char *shmlogname;
35 extern int shmlog_size;
36
37 /**
38 * Initializes logging by creating an error logfile in /tmp (or
39 * XDG_RUNTIME_DIR, see get_process_filename()).
40 *
41 */
42 void init_logging(void);
43
44 /**
45 * Opens the logbuffer.
46 *
47 */
48 void open_logbuffer(void);
49
50 /**
51 * Closes the logbuffer.
52 *
53 */
54 void close_logbuffer(void);
55
56 /**
57 * Checks if debug logging is active.
58 *
59 */
60 bool get_debug_logging(void);
61
62 /**
63 * Set debug logging.
64 *
65 */
66 void set_debug_logging(const bool _debug_logging);
67
68 /**
69 * Set verbosity of i3. If verbose is set to true, informative messages will
70 * be printed to stdout. If verbose is set to false, only errors will be
71 * printed.
72 *
73 */
74 void set_verbosity(bool _verbose);
75
76 /**
77 * Logs the given message to stdout while prefixing the current time to it,
78 * but only if debug logging was activated.
79 *
80 */
81 void debuglog(char *fmt, ...)
82 __attribute__((format(printf, 1, 2)));
83
84 /**
85 * Logs the given message to stdout while prefixing the current time to it.
86 *
87 */
88 void errorlog(char *fmt, ...)
89 __attribute__((format(printf, 1, 2)));
90
91 /**
92 * Logs the given message to stdout while prefixing the current time to it,
93 * but only if verbose mode is activated.
94 *
95 */
96 void verboselog(char *fmt, ...)
97 __attribute__((format(printf, 1, 2)));
98
99 /**
100 * Deletes the unused log files. Useful if i3 exits immediately, eg.
101 * because --get-socketpath was called. We don't care for syscall
102 * failures. This function is invoked automatically when exiting.
103 */
104 void purge_zerobyte_logfile(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * main.c: Initialization, main loop
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Enable or disable the main X11 event handling function.
15 * This is used by drag_pointer() which has its own, modal event handler, which
16 * takes precedence over the normal event handler.
17 *
18 */
19 void main_set_x11_cb(bool enable);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * manage.c: Initially managing new windows (or existing ones on restart).
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include "data.h"
14
15 /**
16 * Go through all existing windows (if the window manager is restarted) and
17 * manage them
18 *
19 */
20 void manage_existing_windows(xcb_window_t root);
21
22 /**
23 * Restores the geometry of each window by reparenting it to the root window
24 * at the position of its frame.
25 *
26 * This is to be called *only* before exiting/restarting i3 because of evil
27 * side-effects which are to be expected when continuing to run i3.
28 *
29 */
30 void restore_geometry(void);
31
32 /**
33 * Do some sanity checks and then reparent the window.
34 *
35 */
36 void manage_window(xcb_window_t window,
37 xcb_get_window_attributes_cookie_t cookie,
38 bool needs_to_be_mapped);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * A "match" is a data structure which acts like a mask or expression to match
7 * certain windows or not. For example, when using commands, you can specify a
8 * command like this: [title="*Firefox*"] kill. The title member of the match
9 * data structure will then be filled and i3 will check each window using
10 * match_matches_window() to find the windows affected by this command.
11 *
12 */
13 #pragma once
14
15 #include <config.h>
16
17 /**
18 * Initializes the Match data structure. This function is necessary because the
19 * members representing boolean values (like dock) need to be initialized with
20 * -1 instead of 0.
21 *
22 */
23 void match_init(Match *match);
24
25 /**
26 * Check if a match is empty. This is necessary while parsing commands to see
27 * whether the user specified a match at all.
28 *
29 */
30 bool match_is_empty(Match *match);
31
32 /**
33 * Copies the data of a match from src to dest.
34 *
35 */
36 void match_copy(Match *dest, Match *src);
37
38 /**
39 * Check if a match data structure matches the given window.
40 *
41 */
42 bool match_matches_window(Match *match, i3Window *window);
43
44 /**
45 * Frees the given match. It must not be used afterwards!
46 *
47 */
48 void match_free(Match *match);
49
50 /**
51 * Interprets a ctype=cvalue pair and adds it to the given match specification.
52 *
53 */
54 void match_parse_property(Match *match, const char *ctype, const char *cvalue);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * move.c: Moving containers into some direction.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT,
15 * TOK_UP, TOK_DOWN from cmdparse.l)
16 *
17 */
18 void tree_move(Con *con, int direction);
19
20 typedef enum { BEFORE,
21 AFTER } position_t;
22
23 /**
24 * This function detaches 'con' from its parent and inserts it either before or
25 * after 'target'.
26 *
27 */
28 void insert_con_into(Con *con, Con *target, position_t position);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * output.c: Output (monitor) related functions.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Returns the output container below the given output container.
15 *
16 */
17 Con *output_get_content(Con *output);
18
19 /**
20 * Returns an 'output' corresponding to one of left/right/down/up or a specific
21 * output name.
22 *
23 */
24 Output *get_output_from_string(Output *current_output, const char *output_str);
25
26 /**
27 * Retrieves the primary name of an output.
28 *
29 */
30 char *output_primary_name(Output *output);
31
32 /**
33 * Returns the output for the given con.
34 *
35 */
36 Output *get_output_for_con(Con *con);
37
38 /**
39 * Iterates over all outputs and pushes sticky windows to the currently visible
40 * workspace on that output.
41 *
42 * old_focus is used to determine if a sticky window is going to be focused.
43 * old_focus might be different than the currently focused container because the
44 * caller might need to temporarily change the focus and then call
45 * output_push_sticky_windows. For example, workspace_show needs to set focus to
46 * one of its descendants first, then call output_push_sticky_windows that
47 * should focus a sticky window if it was the focused in the previous workspace.
48 *
49 */
50 void output_push_sticky_windows(Con *old_focus);
0 /* $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $ */
1 /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * @(#)queue.h 8.5 (Berkeley) 8/20/94
32 */
33
34 #pragma once
35
36 /*
37 * This file defines five types of data structures: singly-linked lists,
38 * lists, simple queues, tail queues, and circular queues.
39 *
40 *
41 * A singly-linked list is headed by a single forward pointer. The elements
42 * are singly linked for minimum space and pointer manipulation overhead at
43 * the expense of O(n) removal for arbitrary elements. New elements can be
44 * added to the list after an existing element or at the head of the list.
45 * Elements being removed from the head of the list should use the explicit
46 * macro for this purpose for optimum efficiency. A singly-linked list may
47 * only be traversed in the forward direction. Singly-linked lists are ideal
48 * for applications with large datasets and few or no removals or for
49 * implementing a LIFO queue.
50 *
51 * A list is headed by a single forward pointer (or an array of forward
52 * pointers for a hash table header). The elements are doubly linked
53 * so that an arbitrary element can be removed without a need to
54 * traverse the list. New elements can be added to the list before
55 * or after an existing element or at the head of the list. A list
56 * may only be traversed in the forward direction.
57 *
58 * A simple queue is headed by a pair of pointers, one the head of the
59 * list and the other to the tail of the list. The elements are singly
60 * linked to save space, so elements can only be removed from the
61 * head of the list. New elements can be added to the list before or after
62 * an existing element, at the head of the list, or at the end of the
63 * list. A simple queue may only be traversed in the forward direction.
64 *
65 * A tail queue is headed by a pair of pointers, one to the head of the
66 * list and the other to the tail of the list. The elements are doubly
67 * linked so that an arbitrary element can be removed without a need to
68 * traverse the list. New elements can be added to the list before or
69 * after an existing element, at the head of the list, or at the end of
70 * the list. A tail queue may be traversed in either direction.
71 *
72 * A circle queue is headed by a pair of pointers, one to the head of the
73 * list and the other to the tail of the list. The elements are doubly
74 * linked so that an arbitrary element can be removed without a need to
75 * traverse the list. New elements can be added to the list before or after
76 * an existing element, at the head of the list, or at the end of the list.
77 * A circle queue may be traversed in either direction, but has a more
78 * complex end of list detection.
79 *
80 * For details on the use of these macros, see the queue(3) manual page.
81 */
82
83 #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
84 #define _Q_INVALIDATE(a) (a) = ((void *)-1)
85 #else
86 #define _Q_INVALIDATE(a)
87 #endif
88
89 /*
90 * Singly-linked List definitions.
91 */
92 #define SLIST_HEAD(name, type) \
93 struct name { \
94 struct type *slh_first; /* first element */ \
95 }
96
97 #define SLIST_HEAD_INITIALIZER(head) \
98 { NULL }
99
100 #define SLIST_ENTRY(type) \
101 struct { \
102 struct type *sle_next; /* next element */ \
103 }
104
105 /*
106 * Singly-linked List access methods.
107 */
108 #define SLIST_FIRST(head) ((head)->slh_first)
109 #define SLIST_END(head) NULL
110 #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
111 #define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
112
113 #define SLIST_FOREACH(var, head, field) \
114 for ((var) = SLIST_FIRST(head); \
115 (var) != SLIST_END(head); \
116 (var) = SLIST_NEXT(var, field))
117
118 #define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
119 for ((varp) = &SLIST_FIRST((head)); \
120 ((var) = *(varp)) != SLIST_END(head); \
121 (varp) = &SLIST_NEXT((var), field))
122
123 /*
124 * Singly-linked List functions.
125 */
126 #define SLIST_INIT(head) \
127 { \
128 SLIST_FIRST(head) = SLIST_END(head); \
129 }
130
131 #define SLIST_INSERT_AFTER(slistelm, elm, field) \
132 do { \
133 (elm)->field.sle_next = (slistelm)->field.sle_next; \
134 (slistelm)->field.sle_next = (elm); \
135 } while (0)
136
137 #define SLIST_INSERT_HEAD(head, elm, field) \
138 do { \
139 (elm)->field.sle_next = (head)->slh_first; \
140 (head)->slh_first = (elm); \
141 } while (0)
142
143 #define SLIST_REMOVE_NEXT(head, elm, field) \
144 do { \
145 (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
146 } while (0)
147
148 #define SLIST_REMOVE_HEAD(head, field) \
149 do { \
150 (head)->slh_first = (head)->slh_first->field.sle_next; \
151 } while (0)
152
153 #define SLIST_REMOVE(head, elm, type, field) \
154 do { \
155 if ((head)->slh_first == (elm)) { \
156 SLIST_REMOVE_HEAD((head), field); \
157 } else { \
158 struct type *curelm = (head)->slh_first; \
159 \
160 while (curelm->field.sle_next != (elm)) \
161 curelm = curelm->field.sle_next; \
162 curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
163 _Q_INVALIDATE((elm)->field.sle_next); \
164 } \
165 } while (0)
166
167 /*
168 * List definitions.
169 */
170 #define LIST_HEAD(name, type) \
171 struct name { \
172 struct type *lh_first; /* first element */ \
173 }
174
175 #define LIST_HEAD_INITIALIZER(head) \
176 { NULL }
177
178 #define LIST_ENTRY(type) \
179 struct { \
180 struct type *le_next; /* next element */ \
181 struct type **le_prev; /* address of previous next element */ \
182 }
183
184 /*
185 * List access methods
186 */
187 #define LIST_FIRST(head) ((head)->lh_first)
188 #define LIST_END(head) NULL
189 #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
190 #define LIST_NEXT(elm, field) ((elm)->field.le_next)
191
192 #define LIST_FOREACH(var, head, field) \
193 for ((var) = LIST_FIRST(head); \
194 (var) != LIST_END(head); \
195 (var) = LIST_NEXT(var, field))
196
197 /*
198 * List functions.
199 */
200 #define LIST_INIT(head) \
201 do { \
202 LIST_FIRST(head) = LIST_END(head); \
203 } while (0)
204
205 #define LIST_INSERT_AFTER(listelm, elm, field) \
206 do { \
207 if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
208 (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
209 (listelm)->field.le_next = (elm); \
210 (elm)->field.le_prev = &(listelm)->field.le_next; \
211 } while (0)
212
213 #define LIST_INSERT_BEFORE(listelm, elm, field) \
214 do { \
215 (elm)->field.le_prev = (listelm)->field.le_prev; \
216 (elm)->field.le_next = (listelm); \
217 *(listelm)->field.le_prev = (elm); \
218 (listelm)->field.le_prev = &(elm)->field.le_next; \
219 } while (0)
220
221 #define LIST_INSERT_HEAD(head, elm, field) \
222 do { \
223 if (((elm)->field.le_next = (head)->lh_first) != NULL) \
224 (head)->lh_first->field.le_prev = &(elm)->field.le_next; \
225 (head)->lh_first = (elm); \
226 (elm)->field.le_prev = &(head)->lh_first; \
227 } while (0)
228
229 #define LIST_REMOVE(elm, field) \
230 do { \
231 if ((elm)->field.le_next != NULL) \
232 (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
233 *(elm)->field.le_prev = (elm)->field.le_next; \
234 _Q_INVALIDATE((elm)->field.le_prev); \
235 _Q_INVALIDATE((elm)->field.le_next); \
236 } while (0)
237
238 #define LIST_REPLACE(elm, elm2, field) \
239 do { \
240 if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
241 (elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
242 (elm2)->field.le_prev = (elm)->field.le_prev; \
243 *(elm2)->field.le_prev = (elm2); \
244 _Q_INVALIDATE((elm)->field.le_prev); \
245 _Q_INVALIDATE((elm)->field.le_next); \
246 } while (0)
247
248 /*
249 * Simple queue definitions.
250 */
251 #define SIMPLEQ_HEAD(name, type) \
252 struct name { \
253 struct type *sqh_first; /* first element */ \
254 struct type **sqh_last; /* addr of last next element */ \
255 }
256
257 #define SIMPLEQ_HEAD_INITIALIZER(head) \
258 { NULL, &(head).sqh_first }
259
260 #define SIMPLEQ_ENTRY(type) \
261 struct { \
262 struct type *sqe_next; /* next element */ \
263 }
264
265 /*
266 * Simple queue access methods.
267 */
268 #define SIMPLEQ_FIRST(head) ((head)->sqh_first)
269 #define SIMPLEQ_END(head) NULL
270 #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
271 #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
272
273 #define SIMPLEQ_FOREACH(var, head, field) \
274 for ((var) = SIMPLEQ_FIRST(head); \
275 (var) != SIMPLEQ_END(head); \
276 (var) = SIMPLEQ_NEXT(var, field))
277
278 /*
279 * Simple queue functions.
280 */
281 #define SIMPLEQ_INIT(head) \
282 do { \
283 (head)->sqh_first = NULL; \
284 (head)->sqh_last = &(head)->sqh_first; \
285 } while (0)
286
287 #define SIMPLEQ_INSERT_HEAD(head, elm, field) \
288 do { \
289 if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
290 (head)->sqh_last = &(elm)->field.sqe_next; \
291 (head)->sqh_first = (elm); \
292 } while (0)
293
294 #define SIMPLEQ_INSERT_TAIL(head, elm, field) \
295 do { \
296 (elm)->field.sqe_next = NULL; \
297 *(head)->sqh_last = (elm); \
298 (head)->sqh_last = &(elm)->field.sqe_next; \
299 } while (0)
300
301 #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \
302 do { \
303 if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
304 (head)->sqh_last = &(elm)->field.sqe_next; \
305 (listelm)->field.sqe_next = (elm); \
306 } while (0)
307
308 #define SIMPLEQ_REMOVE_HEAD(head, field) \
309 do { \
310 if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
311 (head)->sqh_last = &(head)->sqh_first; \
312 } while (0)
313
314 /*
315 * Tail queue definitions.
316 */
317 #define TAILQ_HEAD(name, type) \
318 struct name { \
319 struct type *tqh_first; /* first element */ \
320 struct type **tqh_last; /* addr of last next element */ \
321 }
322
323 #define TAILQ_HEAD_INITIALIZER(head) \
324 { NULL, &(head).tqh_first }
325
326 #define TAILQ_ENTRY(type) \
327 struct { \
328 struct type *tqe_next; /* next element */ \
329 struct type **tqe_prev; /* address of previous next element */ \
330 }
331
332 /*
333 * tail queue access methods
334 */
335 #define TAILQ_FIRST(head) ((head)->tqh_first)
336 #define TAILQ_END(head) NULL
337 #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
338 #define TAILQ_LAST(head, headname) \
339 (*(((struct headname *)((head)->tqh_last))->tqh_last))
340 /* XXX */
341 #define TAILQ_PREV(elm, headname, field) \
342 (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
343 #define TAILQ_EMPTY(head) \
344 (TAILQ_FIRST(head) == TAILQ_END(head))
345
346 #define TAILQ_FOREACH(var, head, field) \
347 for ((var) = TAILQ_FIRST(head); \
348 (var) != TAILQ_END(head); \
349 (var) = TAILQ_NEXT(var, field))
350
351 #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
352 for ((var) = TAILQ_LAST(head, headname); \
353 (var) != TAILQ_END(head); \
354 (var) = TAILQ_PREV(var, headname, field))
355
356 /*
357 * Tail queue functions.
358 */
359 #define TAILQ_INIT(head) \
360 do { \
361 (head)->tqh_first = NULL; \
362 (head)->tqh_last = &(head)->tqh_first; \
363 } while (0)
364
365 #define TAILQ_INSERT_HEAD(head, elm, field) \
366 do { \
367 if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
368 (head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
369 else \
370 (head)->tqh_last = &(elm)->field.tqe_next; \
371 (head)->tqh_first = (elm); \
372 (elm)->field.tqe_prev = &(head)->tqh_first; \
373 } while (0)
374
375 #define TAILQ_INSERT_TAIL(head, elm, field) \
376 do { \
377 (elm)->field.tqe_next = NULL; \
378 (elm)->field.tqe_prev = (head)->tqh_last; \
379 *(head)->tqh_last = (elm); \
380 (head)->tqh_last = &(elm)->field.tqe_next; \
381 } while (0)
382
383 #define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
384 do { \
385 if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
386 (elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
387 else \
388 (head)->tqh_last = &(elm)->field.tqe_next; \
389 (listelm)->field.tqe_next = (elm); \
390 (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
391 } while (0)
392
393 #define TAILQ_INSERT_BEFORE(listelm, elm, field) \
394 do { \
395 (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
396 (elm)->field.tqe_next = (listelm); \
397 *(listelm)->field.tqe_prev = (elm); \
398 (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
399 } while (0)
400
401 #define TAILQ_REMOVE(head, elm, field) \
402 do { \
403 if (((elm)->field.tqe_next) != NULL) \
404 (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
405 else \
406 (head)->tqh_last = (elm)->field.tqe_prev; \
407 *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
408 _Q_INVALIDATE((elm)->field.tqe_prev); \
409 _Q_INVALIDATE((elm)->field.tqe_next); \
410 } while (0)
411
412 #define TAILQ_REPLACE(head, elm, elm2, field) \
413 do { \
414 if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
415 (elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
416 else \
417 (head)->tqh_last = &(elm2)->field.tqe_next; \
418 (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
419 *(elm2)->field.tqe_prev = (elm2); \
420 _Q_INVALIDATE((elm)->field.tqe_prev); \
421 _Q_INVALIDATE((elm)->field.tqe_next); \
422 } while (0)
423
424 /* Swaps two consecutive elements. 'second' *MUST* follow 'first' */
425 #define TAILQ_SWAP(first, second, head, field) \
426 do { \
427 *((first)->field.tqe_prev) = (second); \
428 (second)->field.tqe_prev = (first)->field.tqe_prev; \
429 (first)->field.tqe_prev = &((second)->field.tqe_next); \
430 (first)->field.tqe_next = (second)->field.tqe_next; \
431 if ((second)->field.tqe_next) \
432 (second)->field.tqe_next->field.tqe_prev = &((first)->field.tqe_next); \
433 (second)->field.tqe_next = first; \
434 if ((head)->tqh_last == &((second)->field.tqe_next)) \
435 (head)->tqh_last = &((first)->field.tqe_next); \
436 } while (0)
437
438 /*
439 * Circular queue definitions.
440 */
441 #define CIRCLEQ_HEAD(name, type) \
442 struct name { \
443 struct type *cqh_first; /* first element */ \
444 struct type *cqh_last; /* last element */ \
445 }
446
447 #define CIRCLEQ_HEAD_INITIALIZER(head) \
448 { \
449 CIRCLEQ_END(&head) \
450 , CIRCLEQ_END(&head) \
451 }
452
453 #define CIRCLEQ_ENTRY(type) \
454 struct { \
455 struct type *cqe_next; /* next element */ \
456 struct type *cqe_prev; /* previous element */ \
457 }
458
459 /*
460 * Circular queue access methods
461 */
462 #define CIRCLEQ_FIRST(head) ((head)->cqh_first)
463 #define CIRCLEQ_LAST(head) ((head)->cqh_last)
464 #define CIRCLEQ_END(head) ((void *)(head))
465 #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
466 #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
467 #define CIRCLEQ_EMPTY(head) \
468 (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
469
470 #define CIRCLEQ_FOREACH(var, head, field) \
471 for ((var) = CIRCLEQ_FIRST(head); \
472 (var) != CIRCLEQ_END(head); \
473 (var) = CIRCLEQ_NEXT(var, field))
474
475 #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
476 for ((var) = CIRCLEQ_LAST(head); \
477 (var) != CIRCLEQ_END(head); \
478 (var) = CIRCLEQ_PREV(var, field))
479
480 /*
481 * Circular queue functions.
482 */
483 #define CIRCLEQ_INIT(head) \
484 do { \
485 (head)->cqh_first = CIRCLEQ_END(head); \
486 (head)->cqh_last = CIRCLEQ_END(head); \
487 } while (0)
488
489 #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) \
490 do { \
491 (elm)->field.cqe_next = (listelm)->field.cqe_next; \
492 (elm)->field.cqe_prev = (listelm); \
493 if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
494 (head)->cqh_last = (elm); \
495 else \
496 (listelm)->field.cqe_next->field.cqe_prev = (elm); \
497 (listelm)->field.cqe_next = (elm); \
498 } while (0)
499
500 #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) \
501 do { \
502 (elm)->field.cqe_next = (listelm); \
503 (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
504 if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
505 (head)->cqh_first = (elm); \
506 else \
507 (listelm)->field.cqe_prev->field.cqe_next = (elm); \
508 (listelm)->field.cqe_prev = (elm); \
509 } while (0)
510
511 #define CIRCLEQ_INSERT_HEAD(head, elm, field) \
512 do { \
513 (elm)->field.cqe_next = (head)->cqh_first; \
514 (elm)->field.cqe_prev = CIRCLEQ_END(head); \
515 if ((head)->cqh_last == CIRCLEQ_END(head)) \
516 (head)->cqh_last = (elm); \
517 else \
518 (head)->cqh_first->field.cqe_prev = (elm); \
519 (head)->cqh_first = (elm); \
520 } while (0)
521
522 #define CIRCLEQ_INSERT_TAIL(head, elm, field) \
523 do { \
524 (elm)->field.cqe_next = CIRCLEQ_END(head); \
525 (elm)->field.cqe_prev = (head)->cqh_last; \
526 if ((head)->cqh_first == CIRCLEQ_END(head)) \
527 (head)->cqh_first = (elm); \
528 else \
529 (head)->cqh_last->field.cqe_next = (elm); \
530 (head)->cqh_last = (elm); \
531 } while (0)
532
533 #define CIRCLEQ_REMOVE(head, elm, field) \
534 do { \
535 if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
536 (head)->cqh_last = (elm)->field.cqe_prev; \
537 else \
538 (elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
539 if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
540 (head)->cqh_first = (elm)->field.cqe_next; \
541 else \
542 (elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
543 _Q_INVALIDATE((elm)->field.cqe_prev); \
544 _Q_INVALIDATE((elm)->field.cqe_next); \
545 } while (0)
546
547 #define CIRCLEQ_REPLACE(head, elm, elm2, field) \
548 do { \
549 if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
550 (head)->cqh_last = (elm2); \
551 else \
552 (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
553 if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
554 (head)->cqh_first = (elm2); \
555 else \
556 (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
557 _Q_INVALIDATE((elm)->field.cqe_prev); \
558 _Q_INVALIDATE((elm)->field.cqe_next); \
559 } while (0)
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * For more information on RandR, please see the X.org RandR specification at
7 * https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
8 * (take your time to read it completely, it answers all questions).
9 *
10 */
11 #pragma once
12
13 #include <config.h>
14
15 #include "data.h"
16 #include <xcb/randr.h>
17
18 TAILQ_HEAD(outputs_head, xoutput);
19 extern struct outputs_head outputs;
20
21 typedef enum {
22 CLOSEST_OUTPUT = 0,
23 FARTHEST_OUTPUT = 1
24 } output_close_far_t;
25
26 /**
27 * We have just established a connection to the X server and need the initial
28 * XRandR information to setup workspaces for each screen.
29 *
30 */
31 void randr_init(int *event_base, const bool disable_randr15);
32
33 /**
34 * Initializes a CT_OUTPUT Con (searches existing ones from inplace restart
35 * before) to use for the given Output.
36 *
37 */
38 void output_init_con(Output *output);
39
40 /**
41 * Initializes at least one workspace for this output, trying the following
42 * steps until there is at least one workspace:
43 *
44 * • Move existing workspaces, which are assigned to be on the given output, to
45 * the output.
46 * • Create the first assigned workspace for this output.
47 * • Create the first unused workspace.
48 *
49 */
50 void init_ws_for_output(Output *output, Con *content);
51
52 /**
53 * Initializes the specified output, assigning the specified workspace to it.
54 *
55 */
56 //void initialize_output(xcb_connection_t *conn, Output *output, Workspace *workspace);
57
58 /**
59 * (Re-)queries the outputs via RandR and stores them in the list of outputs.
60 *
61 */
62 void randr_query_outputs(void);
63
64 /**
65 * Disables the output and moves its content.
66 *
67 */
68 void randr_disable_output(Output *output);
69
70 /**
71 * Returns the first output which is active.
72 *
73 */
74 Output *get_first_output(void);
75
76 /**
77 * Returns the output with the given name or NULL.
78 * If require_active is true, only active outputs are considered.
79 *
80 */
81 Output *get_output_by_name(const char *name, const bool require_active);
82
83 /**
84 * Returns the active (!) output which contains the coordinates x, y or NULL
85 * if there is no output which contains these coordinates.
86 *
87 */
88 Output *get_output_containing(unsigned int x, unsigned int y);
89
90 /**
91 * Returns the active output which contains the midpoint of the given rect. If
92 * such an output doesn't exist, returns the output which contains most of the
93 * rectangle or NULL if there is no output which intersects with it.
94 *
95 */
96 Output *get_output_from_rect(Rect rect);
97
98 /**
99 * Returns the active output which spans exactly the area specified by
100 * rect or NULL if there is no output like this.
101 *
102 */
103 Output *get_output_with_dimensions(Rect rect);
104
105 /**
106 * In output_containing_rect, we check if any active output contains part of the container.
107 * We do this by checking if the output rect is intersected by the Rect.
108 * This is the 2-dimensional counterpart of get_output_containing.
109 * Returns the output with the maximum intersecting area.
110 *
111 */
112 Output *output_containing_rect(Rect rect);
113
114 /**
115 * Gets the output which is the next one in the given direction.
116 *
117 * If close_far == CLOSEST_OUTPUT, then the output next to the current one will
118 * selected. If close_far == FARTHEST_OUTPUT, the output which is the last one
119 * in the given direction will be selected.
120 *
121 * NULL will be returned when no active outputs are present in the direction
122 * specified (note that ‘current’ counts as such an output).
123 *
124 */
125 Output *get_output_next(direction_t direction, Output *current, output_close_far_t close_far);
126
127 /**
128 * Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
129 *
130 * For example if get_output_next(D_DOWN, x, FARTHEST_OUTPUT) = NULL, then
131 * get_output_next_wrap(D_DOWN, x) will return the topmost output.
132 *
133 * This function always returns a output: if no active outputs can be found,
134 * current itself is returned.
135 *
136 */
137 Output *get_output_next_wrap(direction_t direction, Output *current);
138
139 /**
140 * Creates an output covering the root window.
141 *
142 */
143 Output *create_root_output(xcb_connection_t *conn);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * regex.c: Interface to libPCRE (perl compatible regular expressions).
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Creates a new 'regex' struct containing the given pattern and a PCRE
15 * compiled regular expression. Also, calls pcre_study because this regex will
16 * most likely be used often (like for every new window and on every relevant
17 * property change of existing windows).
18 *
19 * Returns NULL if the pattern could not be compiled into a regular expression
20 * (and ELOGs an appropriate error message).
21 *
22 */
23 struct regex *regex_new(const char *pattern);
24
25 /**
26 * Frees the given regular expression. It must not be used afterwards!
27 *
28 */
29 void regex_free(struct regex *regex);
30
31 /**
32 * Checks if the given regular expression matches the given input and returns
33 * true if it does. In either case, it logs the outcome using LOG(), so it will
34 * be visible without debug logging.
35 *
36 */
37 bool regex_matches(struct regex *regex, const char *input);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * render.c: Renders (determines position/sizes) the layout tree, updating the
7 * various rects. Needs to be pushed to X11 (see x.c) to be visible.
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 /**
15 * This is used to keep a state to pass around when rendering a con in render_con().
16 *
17 */
18 typedef struct render_params {
19 /* A copy of the coordinates of the container which is being rendered. */
20 int x;
21 int y;
22
23 /* The computed height for decorations. */
24 int deco_height;
25 /* Container rect, subtract container border. This is the actually usable space
26 * inside this container for clients. */
27 Rect rect;
28 /* The number of children of the container which is being rendered. */
29 int children;
30 /* A precalculated list of sizes of each child. */
31 int *sizes;
32 } render_params;
33
34 /**
35 * "Renders" the given container (and its children), meaning that all rects are
36 * updated correctly. Note that this function does not call any xcb_*
37 * functions, so the changes are completely done in memory only (and
38 * side-effect free). As soon as you call x_push_changes(), the changes will be
39 * updated in X11.
40 *
41 */
42 void render_con(Con *con, bool render_fullscreen);
43
44 /**
45 * Returns the height for the decorations
46 *
47 */
48 int render_deco_height(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * resize.c: Interactive resizing.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides);
14
15 void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
16
17 /**
18 * Resize the two given containers using the given amount of pixels or
19 * percentage points. One of the two needs to be 0. A positive amount means
20 * growing the first container while a negative means shrinking it.
21 * Returns false when the resize would result in one of the two containers
22 * having less than 1 pixel of size.
23 *
24 */
25 bool resize_neighboring_cons(Con *first, Con *second, int px, int ppt);
26
27 /**
28 * Calculate the minimum percent needed for the given container to be at least 1
29 * pixel.
30 *
31 */
32 double percent_for_1px(Con *con);
33
34 /**
35 * Calculate the given container's new percent given a change in pixels.
36 *
37 */
38 double px_resize_to_percent(Con *con, int px_diff);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * restore_layout.c: Everything for restored containers that is not pure state
7 * parsing (which can be found in load_layout.c).
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 /**
15 * Opens a separate connection to X11 for placeholder windows when restoring
16 * layouts. This is done as a safety measure (users can xkill a placeholder
17 * window without killing their window manager) and for better isolation, both
18 * on the wire to X11 and thus also in the code.
19 *
20 */
21 void restore_connect(void);
22
23 /**
24 * Open placeholder windows for all children of parent. The placeholder window
25 * will vanish as soon as a real window is swallowed by the container. Until
26 * then, it exposes the criteria that must be fulfilled for a window to be
27 * swallowed by this container.
28 *
29 */
30 void restore_open_placeholder_windows(Con *con);
31
32 /**
33 * Kill the placeholder window, if placeholder refers to a placeholder window.
34 * This function is called when manage.c puts a window into an existing
35 * container. In order not to leak resources, we need to destroy the window and
36 * all associated X11 objects (pixmap/gc).
37 *
38 */
39 bool restore_kill_placeholder(xcb_window_t placeholder);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * scratchpad.c: Scratchpad functions (TODO: more description)
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Moves the specified window to the __i3_scratch workspace, making it floating
15 * and setting the appropriate scratchpad_state.
16 *
17 * Gets called upon the command 'move scratchpad'.
18 *
19 */
20 void scratchpad_move(Con *con);
21
22 /**
23 * Either shows the top-most scratchpad window (con == NULL) or shows the
24 * specified con (if it is scratchpad window).
25 *
26 * When called with con == NULL and the currently focused window is a
27 * scratchpad window, this serves as a shortcut to hide it again (so the user
28 * can press the same key to quickly look something up).
29 *
30 */
31 bool scratchpad_show(Con *con);
32
33 /**
34 * When starting i3 initially (and after each change to the connected outputs),
35 * this function fixes the resolution of the __i3 pseudo-output. When that
36 * resolution is not set to a function which shares a common divisor with every
37 * active output’s resolution, floating point calculation errors will lead to
38 * the scratchpad window moving when shown repeatedly.
39 *
40 */
41 void scratchpad_fix_resolution(void);
0 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
1
2 #pragma once
3
4 /***
5 Copyright 2010 Lennart Poettering
6
7 Permission is hereby granted, free of charge, to any person
8 obtaining a copy of this software and associated documentation files
9 (the "Software"), to deal in the Software without restriction,
10 including without limitation the rights to use, copy, modify, merge,
11 publish, distribute, sublicense, and/or sell copies of the Software,
12 and to permit persons to whom the Software is furnished to do so,
13 subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 SOFTWARE.
26 ***/
27
28 #include <sys/types.h>
29 #include <inttypes.h>
30
31 #ifdef __cplusplus
32 extern "C" {
33 #endif
34
35 /*
36 Reference implementation of a few systemd related interfaces for
37 writing daemons. These interfaces are trivial to implement. To
38 simplify porting we provide this reference implementation.
39 Applications are welcome to reimplement the algorithms described
40 here if they do not want to include these two source files.
41
42 The following functionality is provided:
43
44 - Support for logging with log levels on stderr
45 - File descriptor passing for socket-based activation
46 - Daemon startup and status notification
47 - Detection of systemd boots
48
49 You may compile this with -DDISABLE_SYSTEMD to disable systemd
50 support. This makes all those calls NOPs that are directly related to
51 systemd (i.e. only sd_is_xxx() will stay useful).
52
53 Since this is drop-in code we don't want any of our symbols to be
54 exported in any case. Hence we declare hidden visibility for all of
55 them.
56
57 You may find an up-to-date version of these source files online:
58
59 https://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h
60 https://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c
61
62 This should compile on non-Linux systems, too, but with the
63 exception of the sd_is_xxx() calls all functions will become NOPs.
64
65 See sd-daemon(7) for more information.
66 */
67
68 #ifndef _sd_printf_attr_
69 #if __GNUC__ >= 4
70 #define _sd_printf_attr_(a, b) __attribute__((format(printf, a, b)))
71 #else
72 #define _sd_printf_attr_(a, b)
73 #endif
74 #endif
75
76 #ifndef _sd_hidden_
77 #if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
78 #define _sd_hidden_ __attribute__((visibility("hidden")))
79 #else
80 #define _sd_hidden_
81 #endif
82 #endif
83
84 /*
85 Log levels for usage on stderr:
86
87 fprintf(stderr, SD_NOTICE "Hello World!\n");
88
89 This is similar to printk() usage in the kernel.
90 */
91 #define SD_EMERG "<0>" /* system is unusable */
92 #define SD_ALERT "<1>" /* action must be taken immediately */
93 #define SD_CRIT "<2>" /* critical conditions */
94 #define SD_ERR "<3>" /* error conditions */
95 #define SD_WARNING "<4>" /* warning conditions */
96 #define SD_NOTICE "<5>" /* normal but significant condition */
97 #define SD_INFO "<6>" /* informational */
98 #define SD_DEBUG "<7>" /* debug-level messages */
99
100 /* The first passed file descriptor is fd 3 */
101 #define SD_LISTEN_FDS_START 3
102
103 /*
104 Returns how many file descriptors have been passed, or a negative
105 errno code on failure. Optionally, removes the $LISTEN_FDS and
106 $LISTEN_PID file descriptors from the environment (recommended, but
107 problematic in threaded environments). If r is the return value of
108 this function you'll find the file descriptors passed as fds
109 SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative
110 errno style error code on failure. This function call ensures that
111 the FD_CLOEXEC flag is set for the passed file descriptors, to make
112 sure they are not passed on to child processes. If FD_CLOEXEC shall
113 not be set, the caller needs to unset it after this call for all file
114 descriptors that are used.
115
116 See sd_listen_fds(3) for more information.
117 */
118 int sd_listen_fds(int unset_environment) _sd_hidden_;
119
120 /*
121 Helper call for identifying a passed file descriptor. Returns 1 if
122 the file descriptor is a FIFO in the file system stored under the
123 specified path, 0 otherwise. If path is NULL a path name check will
124 not be done and the call only verifies if the file descriptor
125 refers to a FIFO. Returns a negative errno style error code on
126 failure.
127
128 See sd_is_fifo(3) for more information.
129 */
130 int sd_is_fifo(int fd, const char *path) _sd_hidden_;
131
132 /*
133 Helper call for identifying a passed file descriptor. Returns 1 if
134 the file descriptor is a socket of the specified family (AF_INET,
135 ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If
136 family is 0 a socket family check will not be done. If type is 0 a
137 socket type check will not be done and the call only verifies if
138 the file descriptor refers to a socket. If listening is > 0 it is
139 verified that the socket is in listening mode. (i.e. listen() has
140 been called) If listening is == 0 it is verified that the socket is
141 not in listening mode. If listening is < 0 no listening mode check
142 is done. Returns a negative errno style error code on failure.
143
144 See sd_is_socket(3) for more information.
145 */
146 int sd_is_socket(int fd, int family, int type, int listening) _sd_hidden_;
147
148 /*
149 Helper call for identifying a passed file descriptor. Returns 1 if
150 the file descriptor is an Internet socket, of the specified family
151 (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM,
152 SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version
153 check is not done. If type is 0 a socket type check will not be
154 done. If port is 0 a socket port check will not be done. The
155 listening flag is used the same way as in sd_is_socket(). Returns a
156 negative errno style error code on failure.
157
158 See sd_is_socket_inet(3) for more information.
159 */
160 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) _sd_hidden_;
161
162 /*
163 Helper call for identifying a passed file descriptor. Returns 1 if
164 the file descriptor is an AF_UNIX socket of the specified type
165 (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0
166 a socket type check will not be done. If path is NULL a socket path
167 check will not be done. For normal AF_UNIX sockets set length to
168 0. For abstract namespace sockets set length to the length of the
169 socket name (including the initial 0 byte), and pass the full
170 socket path in path (including the initial 0 byte). The listening
171 flag is used the same way as in sd_is_socket(). Returns a negative
172 errno style error code on failure.
173
174 See sd_is_socket_unix(3) for more information.
175 */
176 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) _sd_hidden_;
177
178 /*
179 Informs systemd about changed daemon state. This takes a number of
180 newline separated environment-style variable assignments in a
181 string. The following variables are known:
182
183 READY=1 Tells systemd that daemon startup is finished (only
184 relevant for services of Type=notify). The passed
185 argument is a boolean "1" or "0". Since there is
186 little value in signaling non-readiness the only
187 value daemons should send is "READY=1".
188
189 STATUS=... Passes a single-line status string back to systemd
190 that describes the daemon state. This is free-from
191 and can be used for various purposes: general state
192 feedback, fsck-like programs could pass completion
193 percentages and failing programs could pass a human
194 readable error message. Example: "STATUS=Completed
195 66% of file system check..."
196
197 ERRNO=... If a daemon fails, the errno-style error code,
198 formatted as string. Example: "ERRNO=2" for ENOENT.
199
200 BUSERROR=... If a daemon fails, the D-Bus error-style error
201 code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut"
202
203 MAINPID=... The main pid of a daemon, in case systemd did not
204 fork off the process itself. Example: "MAINPID=4711"
205
206 Daemons can choose to send additional variables. However, it is
207 recommended to prefix variable names not listed above with X_.
208
209 Returns a negative errno-style error code on failure. Returns > 0
210 if systemd could be notified, 0 if it couldn't possibly because
211 systemd is not running.
212
213 Example: When a daemon finished starting up, it could issue this
214 call to notify systemd about it:
215
216 sd_notify(0, "READY=1");
217
218 See sd_notifyf() for more complete examples.
219
220 See sd_notify(3) for more information.
221 */
222 int sd_notify(int unset_environment, const char *state) _sd_hidden_;
223
224 /*
225 Similar to sd_notify() but takes a format string.
226
227 Example 1: A daemon could send the following after initialization:
228
229 sd_notifyf(0, "READY=1\n"
230 "STATUS=Processing requests...\n"
231 "MAINPID=%lu",
232 (unsigned long) getpid());
233
234 Example 2: A daemon could send the following shortly before
235 exiting, on failure:
236
237 sd_notifyf(0, "STATUS=Failed to start up: %s\n"
238 "ERRNO=%i",
239 strerror(errno),
240 errno);
241
242 See sd_notifyf(3) for more information.
243 */
244 int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2, 3) _sd_hidden_;
245
246 /*
247 Returns > 0 if the system was booted with systemd. Returns < 0 on
248 error. Returns 0 if the system was not booted with systemd. Note
249 that all of the functions above handle non-systemd boots just
250 fine. You should NOT protect them with a call to this function. Also
251 note that this function checks whether the system, not the user
252 session is controlled by systemd. However the functions above work
253 for both user and system services.
254
255 See sd_booted(3) for more information.
256 */
257 int sd_booted(void) _sd_hidden_;
258
259 #ifdef __cplusplus
260 }
261 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * The format of the shmlog data structure which i3 development versions use by
7 * default (ringbuffer for storing the debug log).
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 #include <stdint.h>
15 #if !defined(__OpenBSD__)
16 #include <pthread.h>
17 #endif
18
19 /* Default shmlog size if not set by user. */
20 extern const int default_shmlog_size;
21
22 /**
23 * Header of the shmlog file. Used by i3/src/log.c and i3/i3-dump-log/main.c.
24 *
25 */
26 typedef struct i3_shmlog_header {
27 /* Byte offset where the next line will be written to. */
28 uint32_t offset_next_write;
29
30 /* Byte offset where the last wrap occurred. */
31 uint32_t offset_last_wrap;
32
33 /* The size of the logfile in bytes. Since the size is limited to 25 MiB
34 * an uint32_t is sufficient. */
35 uint32_t size;
36
37 /* wrap counter. We need it to reliably signal to clients that we just
38 * wrapped (clients cannot use offset_last_wrap because that might
39 * coincidentally be exactly the same as previously). Overflows can happen
40 * and don’t matter — clients use an equality check (==). */
41 uint32_t wrap_count;
42
43 #if !defined(__OpenBSD__)
44 /* pthread condvar which will be broadcasted whenever there is a new
45 * message in the log. i3-dump-log uses this to implement -f (follow, like
46 * tail -f) in an efficient way. */
47 pthread_cond_t condvar;
48 #endif
49 } i3_shmlog_header;
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #pragma once
8
9 #include <config.h>
10
11 /**
12 * Configured a signal handler to gracefully handle crashes and allow the user
13 * to generate a backtrace and rescue their session.
14 *
15 */
16 void setup_signal_handler(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * startup.c: Startup notification code. Ensures a startup notification context
7 * is setup when launching applications. We store the current
8 * workspace to open windows in that startup notification context on
9 * the appropriate workspace.
10 *
11 */
12 #pragma once
13
14 #include <config.h>
15
16 #define SN_API_NOT_YET_FROZEN 1
17 #include <libsn/sn-monitor.h>
18
19 /**
20 * Starts the given application by passing it through a shell. We use double
21 * fork to avoid zombie processes. As the started application’s parent exits
22 * (immediately), the application is reparented to init (process-id 1), which
23 * correctly handles children, so we don’t have to do it :-).
24 *
25 * The shell used to start applications is the system's bourne shell (i.e.,
26 * /bin/sh).
27 *
28 * The no_startup_id flag determines whether a startup notification context
29 * (and ID) should be created, which is the default and encouraged behavior.
30 *
31 */
32 void start_application(const char *command, bool no_startup_id);
33
34 /**
35 * Deletes a startup sequence, ignoring whether its timeout has elapsed.
36 * Useful when e.g. a window is moved between workspaces and its children
37 * shouldn't spawn on the original workspace.
38 *
39 */
40 void startup_sequence_delete(struct Startup_Sequence *sequence);
41
42 /**
43 * Called by libstartup-notification when something happens
44 *
45 */
46 void startup_monitor_event(SnMonitorEvent *event, void *userdata);
47
48 /**
49 * Renames workspaces that are mentioned in the startup sequences.
50 *
51 */
52 void startup_sequence_rename_workspace(const char *old_name, const char *new_name);
53
54 /**
55 * Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
56 *
57 */
58 struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
59 xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader);
60
61 /**
62 * Checks if the given window belongs to a startup notification by checking if
63 * the _NET_STARTUP_ID property is set on the window (or on its leader, if it’s
64 * unset).
65 *
66 * If so, returns the workspace on which the startup was initiated.
67 * Returns NULL otherwise.
68 *
69 */
70 char *startup_workspace_for_window(i3Window *cwindow, xcb_get_property_reply_t *startup_id_reply);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * sync.c: i3 sync protocol: https://i3wm.org/docs/testsuite.html#i3_sync
7 *
8 */
9 #pragma once
10
11 #include <xcb/xcb.h>
12
13 void sync_respond(xcb_window_t window, uint32_t rnd);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * tree.c: Everything that primarily modifies the layout tree data structure.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 extern Con *croot;
14 /* TODO: i am not sure yet how much access to the focused container should
15 * be permitted to source files */
16 extern Con *focused;
17 TAILQ_HEAD(all_cons_head, Con);
18 extern struct all_cons_head all_cons;
19
20 /**
21 * Initializes the tree by creating the root node, adding all RandR outputs
22 * to the tree (that means randr_init() has to be called before) and
23 * assigning a workspace to each RandR output.
24 *
25 */
26 void tree_init(xcb_get_geometry_reply_t *geometry);
27
28 /**
29 * Opens an empty container in the current container
30 *
31 */
32 Con *tree_open_con(Con *con, i3Window *window);
33
34 /**
35 * Splits (horizontally or vertically) the given container by creating a new
36 * container which contains the old one and the future ones.
37 *
38 */
39 void tree_split(Con *con, orientation_t orientation);
40
41 /**
42 * Moves focus one level up. Returns true if focus changed.
43 *
44 */
45 bool level_up(void);
46
47 /**
48 * Moves focus one level down. Returns true if focus changed.
49 *
50 */
51 bool level_down(void);
52
53 /**
54 * Renders the tree, that is rendering all outputs using render_con() and
55 * pushing the changes to X11 using x_push_changes().
56 *
57 */
58 void tree_render(void);
59
60 /**
61 * Changes focus in the given way (next/previous) and given orientation
62 * (horizontal/vertical).
63 *
64 */
65 void tree_next(char way, orientation_t orientation);
66
67 /**
68 * Closes the given container including all children.
69 * Returns true if the container was killed or false if just WM_DELETE was sent
70 * and the window is expected to kill itself.
71 *
72 * The dont_kill_parent flag is specified when the function calls itself
73 * recursively while deleting a containers children.
74 *
75 * The force_set_focus flag is specified in the case of killing a floating
76 * window: tree_close_internal() will be invoked for the CT_FLOATINGCON (the parent
77 * container) and focus should be set there.
78 *
79 */
80 bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent);
81
82 /**
83 * Loads tree from ~/.i3/_restart.json (used for in-place restarts).
84 *
85 */
86 bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry);
87
88 /**
89 * tree_flatten() removes pairs of redundant split containers, e.g.:
90 * [workspace, horizontal]
91 * [v-split] [child3]
92 * [h-split]
93 * [child1] [child2]
94 * In this example, the v-split and h-split container are redundant.
95 * Such a situation can be created by moving containers in a direction which is
96 * not the orientation of their parent container. i3 needs to create a new
97 * split container then and if you move containers this way multiple times,
98 * redundant chains of split-containers can be the result.
99 *
100 */
101 void tree_flatten(Con *child);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * util.c: Utility functions, which can be useful everywhere within i3 (see
7 * also libi3).
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 #include <err.h>
15
16 #include "data.h"
17
18 #define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
19 #define exit_if_null(pointer, ...) \
20 { \
21 if (pointer == NULL) \
22 die(__VA_ARGS__); \
23 }
24 #define STARTS_WITH(string, needle) (strncasecmp((string), (needle), strlen((needle))) == 0)
25 #define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
26 #define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
27
28 #define NODES_FOREACH(head) \
29 for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
30 TAILQ_FOREACH(child, &((head)->nodes_head), nodes)
31
32 #define NODES_FOREACH_REVERSE(head) \
33 for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
34 TAILQ_FOREACH_REVERSE(child, &((head)->nodes_head), nodes_head, nodes)
35
36 /* greps the ->nodes of the given head and returns the first node that matches the given condition */
37 #define GREP_FIRST(dest, head, condition) \
38 NODES_FOREACH(head) { \
39 if (!(condition)) \
40 continue; \
41 \
42 (dest) = child; \
43 break; \
44 }
45
46 #define FREE(pointer) \
47 do { \
48 free(pointer); \
49 pointer = NULL; \
50 } while (0)
51
52 #define CALL(obj, member, ...) obj->member(obj, ##__VA_ARGS__)
53
54 #define SWAP(first, second, type) \
55 do { \
56 type tmp_SWAP = first; \
57 first = second; \
58 second = tmp_SWAP; \
59 } while (0)
60
61 int min(int a, int b);
62 int max(int a, int b);
63 bool rect_contains(Rect rect, uint32_t x, uint32_t y);
64 Rect rect_add(Rect a, Rect b);
65 Rect rect_sub(Rect a, Rect b);
66
67 /**
68 * Returns true if the name consists of only digits.
69 *
70 */
71 __attribute__((pure)) bool name_is_digits(const char *name);
72
73 /**
74 * Set 'out' to the layout_t value for the given layout. The function
75 * returns true on success or false if the passed string is not a valid
76 * layout name.
77 *
78 */
79 bool layout_from_name(const char *layout_str, layout_t *out);
80
81 /**
82 * Parses the workspace name as a number. Returns -1 if the workspace should be
83 * interpreted as a "named workspace".
84 *
85 */
86 long ws_name_to_number(const char *name);
87
88 /**
89 * Updates *destination with new_value and returns true if it was changed or false
90 * if it was the same
91 *
92 */
93 bool update_if_necessary(uint32_t *destination, const uint32_t new_value);
94
95 /**
96 * exec()s an i3 utility, for example the config file migration script or
97 * i3-nagbar. This function first searches $PATH for the given utility named,
98 * then falls back to the dirname() of the i3 executable path and then falls
99 * back to the dirname() of the target of /proc/self/exe (on linux).
100 *
101 * This function should be called after fork()ing.
102 *
103 * The first argument of the given argv vector will be overwritten with the
104 * executable name, so pass NULL.
105 *
106 * If the utility cannot be found in any of these locations, it exits with
107 * return code 2.
108 *
109 */
110 void exec_i3_utility(char *name, char *argv[]);
111
112 /**
113 * Checks if the given path exists by calling stat().
114 *
115 */
116 bool path_exists(const char *path);
117
118 /**
119 * Restart i3 in-place
120 * appends -a to argument list to disable autostart
121 *
122 */
123 void i3_restart(bool forget_layout);
124
125 #if defined(__OpenBSD__) || defined(__APPLE__)
126
127 /**
128 * Taken from FreeBSD
129 * Find the first occurrence of the byte string s in byte string l.
130 *
131 */
132 void *memmem(const void *l, size_t l_len, const void *s, size_t s_len);
133
134 #endif
135
136 /**
137 * Escapes the given string if a pango font is currently used.
138 * If the string has to be escaped, the input string will be free'd.
139 *
140 */
141 char *pango_escape_markup(char *input);
142
143 /**
144 * Starts an i3-nagbar instance with the given parameters. Takes care of
145 * handling SIGCHLD and killing i3-nagbar when i3 exits.
146 *
147 * The resulting PID will be stored in *nagbar_pid and can be used with
148 * kill_nagbar() to kill the bar later on.
149 *
150 */
151 void start_nagbar(pid_t *nagbar_pid, char *argv[]);
152
153 /**
154 * Kills the i3-nagbar process, if *nagbar_pid != -1.
155 *
156 * If wait_for_it is set (restarting i3), this function will waitpid(),
157 * otherwise, ev is assumed to handle it (reloading).
158 *
159 */
160 void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it);
161
162 /**
163 * Converts a string into a long using strtol().
164 * This is a convenience wrapper checking the parsing result. It returns true
165 * if the number could be parsed.
166 */
167 bool parse_long(const char *str, long *out, int base);
168
169 /**
170 * Slurp reads path in its entirety into buf, returning the length of the file
171 * or -1 if the file could not be read. buf is set to a buffer of appropriate
172 * size, or NULL if -1 is returned.
173 *
174 */
175 ssize_t slurp(const char *path, char **buf);
176
177 /**
178 * Convert a direction to its corresponding orientation.
179 *
180 */
181 orientation_t orientation_from_direction(direction_t direction);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * window.c: Updates window attributes (X11 hints/properties).
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 /**
14 * Frees an i3Window and all its members.
15 *
16 */
17 void window_free(i3Window *win);
18
19 /**
20 * Updates the WM_CLASS (consisting of the class and instance) for the
21 * given window.
22 *
23 */
24 void window_update_class(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt);
25
26 /**
27 * Updates the name by using _NET_WM_NAME (encoded in UTF-8) for the given
28 * window. Further updates using window_update_name_legacy will be ignored.
29 *
30 */
31 void window_update_name(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt);
32
33 /**
34 * Updates the name by using WM_NAME (encoded in COMPOUND_TEXT). We do not
35 * touch what the client sends us but pass it to xcb_image_text_8. To get
36 * proper unicode rendering, the application has to use _NET_WM_NAME (see
37 * window_update_name()).
38 *
39 */
40 void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt);
41
42 /**
43 * Updates the CLIENT_LEADER (logical parent window).
44 *
45 */
46 void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop);
47
48 /**
49 * Updates the TRANSIENT_FOR (logical parent window).
50 *
51 */
52 void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop);
53
54 /**
55 * Updates the _NET_WM_STRUT_PARTIAL (reserved pixels at the screen edges)
56 *
57 */
58 void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop);
59
60 /**
61 * Updates the WM_WINDOW_ROLE
62 *
63 */
64 void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt);
65
66 /**
67 * Updates the _NET_WM_WINDOW_TYPE property.
68 *
69 */
70 void window_update_type(i3Window *window, xcb_get_property_reply_t *reply);
71
72 /**
73 * Updates the WM_HINTS (we only care about the input focus handling part).
74 *
75 */
76 void window_update_hints(i3Window *win, xcb_get_property_reply_t *prop, bool *urgency_hint);
77
78 /**
79 * Updates the MOTIF_WM_HINTS. The container's border style should be set to
80 * `motif_border_style' if border style is not BS_NORMAL.
81 *
82 * i3 only uses this hint when it specifies a window should have no
83 * title bar, or no decorations at all, which is how most window managers
84 * handle it.
85 *
86 * The EWMH spec intended to replace Motif hints with _NET_WM_WINDOW_TYPE, but
87 * it is still in use by popular widget toolkits such as GTK+ and Java AWT.
88 *
89 */
90 void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * workspace.c: Modifying workspaces, accessing them, moving containers to
7 * workspaces.
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 #include "data.h"
15 #include "tree.h"
16 #include "randr.h"
17
18 /* We use NET_WM_DESKTOP_NONE for cases where we cannot determine the EWMH
19 * desktop index for a window. We cannot use a negative value like -1 since we
20 * need to use uint32_t as we actually need the full range of it. This is
21 * technically dangerous, but it's safe to assume that we will never have more
22 * than 4294967279 workspaces open at a time. */
23 #define NET_WM_DESKTOP_NONE 0xFFFFFFF0
24 #define NET_WM_DESKTOP_ALL 0xFFFFFFFF
25
26 /**
27 * Returns the workspace with the given name or NULL if such a workspace does
28 * not exist.
29 *
30 */
31 Con *get_existing_workspace_by_name(const char *name);
32
33 /**
34 * Returns the workspace with the given number or NULL if such a workspace does
35 * not exist.
36 *
37 */
38 Con *get_existing_workspace_by_num(int num);
39
40 /**
41 * Returns true if the first output assigned to a workspace with the given
42 * workspace assignment is the same as the given output.
43 *
44 */
45 bool output_triggers_assignment(Output *output, struct Workspace_Assignment *assignment);
46
47 /**
48 * Returns a pointer to the workspace with the given number (starting at 0),
49 * creating the workspace if necessary (by allocating the necessary amount of
50 * memory and initializing the data structures correctly).
51 *
52 * If created is not NULL, *created will be set to whether or not the
53 * workspace has just been created.
54 *
55 */
56 Con *workspace_get(const char *num, bool *created);
57
58 /**
59 * Extracts workspace names from keybindings (e.g. “web” from “bindsym $mod+1
60 * workspace web”), so that when an output needs a workspace, i3 can start with
61 * the first configured one. Needs to be called before reorder_bindings() so
62 * that the config-file order is used, not the i3-internal order.
63 *
64 */
65 void extract_workspace_names_from_bindings(void);
66
67 /**
68 * Returns a pointer to a new workspace in the given output. The workspace
69 * is created attached to the tree hierarchy through the given content
70 * container.
71 *
72 */
73 Con *create_workspace_on_output(Output *output, Con *content);
74
75 /**
76 * Returns true if the workspace is currently visible. Especially important for
77 * multi-monitor environments, as they can have multiple currenlty active
78 * workspaces.
79 *
80 */
81 bool workspace_is_visible(Con *ws);
82
83 /**
84 * Switches to the given workspace
85 *
86 */
87 void workspace_show(Con *ws);
88
89 /**
90 * Looks up the workspace by name and switches to it.
91 *
92 */
93 void workspace_show_by_name(const char *num);
94
95 /**
96 * Returns the next workspace.
97 *
98 */
99 Con *workspace_next(void);
100
101 /**
102 * Returns the previous workspace.
103 *
104 */
105 Con *workspace_prev(void);
106
107 /**
108 * Returns the next workspace on the same output
109 *
110 */
111 Con *workspace_next_on_output(void);
112
113 /**
114 * Returns the previous workspace on the same output
115 *
116 */
117 Con *workspace_prev_on_output(void);
118
119 /**
120 * Focuses the previously focused workspace.
121 *
122 */
123 void workspace_back_and_forth(void);
124
125 /**
126 * Returns the previously focused workspace con, or NULL if unavailable.
127 *
128 */
129 Con *workspace_back_and_forth_get(void);
130
131 #if 0
132 /**
133 * Assigns the given workspace to the given screen by correctly updating its
134 * state and reconfiguring all the clients on this workspace.
135 *
136 * This is called when initializing a screen and when re-assigning it to a
137 * different screen which just got available (if you configured it to be on
138 * screen 1 and you just plugged in screen 1).
139 *
140 */
141 void workspace_assign_to(Workspace *ws, Output *screen, bool hide_it);
142
143 /**
144 * Initializes the given workspace if it is not already initialized. The given
145 * screen is to be understood as a fallback, if the workspace itself either
146 * was not assigned to a particular screen or cannot be placed there because
147 * the screen is not attached at the moment.
148 *
149 */
150 void workspace_initialize(Workspace *ws, Output *screen, bool recheck);
151
152 /**
153 * Gets the first unused workspace for the given screen, taking into account
154 * the preferred_screen setting of every workspace (workspace assignments).
155 *
156 */
157 Workspace *get_first_workspace_for_output(Output *screen);
158
159 /**
160 * Unmaps all clients (and stack windows) of the given workspace.
161 *
162 * This needs to be called separately when temporarily rendering a workspace
163 * which is not the active workspace to force reconfiguration of all clients,
164 * like in src/xinerama.c when re-assigning a workspace to another screen.
165 *
166 */
167 void workspace_unmap_clients(xcb_connection_t *conn, Workspace *u_ws);
168
169 /**
170 * Maps all clients (and stack windows) of the given workspace.
171 *
172 */
173 void workspace_map_clients(xcb_connection_t *conn, Workspace *ws);
174 #endif
175
176 /**
177 * Goes through all clients on the given workspace and updates the workspace’s
178 * urgent flag accordingly.
179 *
180 */
181 void workspace_update_urgent_flag(Con *ws);
182
183 /**
184 * 'Forces' workspace orientation by moving all cons into a new split-con with
185 * the same orientation as the workspace and then changing the workspace
186 * orientation.
187 *
188 */
189 void ws_force_orientation(Con *ws, orientation_t orientation);
190
191 /**
192 * Called when a new con (with a window, not an empty or split con) should be
193 * attached to the workspace (for example when managing a new window or when
194 * moving an existing window to the workspace level).
195 *
196 * Depending on the workspace_layout setting, this function either returns the
197 * workspace itself (default layout) or creates a new stacked/tabbed con and
198 * returns that.
199 *
200 */
201 Con *workspace_attach_to(Con *ws);
202
203 /**
204 * Creates a new container and re-parents all of children from the given
205 * workspace into it.
206 *
207 * The container inherits the layout from the workspace.
208 */
209 Con *workspace_encapsulate(Con *ws);
210
211 /**
212 * Move the given workspace to the specified output.
213 * This returns true if and only if moving the workspace was successful.
214 *
215 */
216 bool workspace_move_to_output(Con *ws, Output *output);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * x.c: Interface to X11, transfers our in-memory state to X11 (see also
7 * render.c). Basically a big state machine.
8 *
9 */
10 #pragma once
11
12 #include <config.h>
13
14 /** Stores the X11 window ID of the currently focused window */
15 extern xcb_window_t focused_id;
16
17 /**
18 * Initializes the X11 part for the given container. Called exactly once for
19 * every container from con_new().
20 *
21 */
22 void x_con_init(Con *con);
23
24 /**
25 * Moves a child window from Container src to Container dest.
26 *
27 */
28 void x_move_win(Con *src, Con *dest);
29
30 /**
31 * Reparents the child window of the given container (necessary for sticky
32 * containers). The reparenting happens in the next call of x_push_changes().
33 *
34 */
35 void x_reparent_child(Con *con, Con *old);
36
37 /**
38 * Re-initializes the associated X window state for this container. You have
39 * to call this when you assign a client to an empty container to ensure that
40 * its state gets updated correctly.
41 *
42 */
43 void x_reinit(Con *con);
44
45 /**
46 * Kills the window decoration associated with the given container.
47 *
48 */
49 void x_con_kill(Con *con);
50
51 /*
52 * Completely reinitializes the container's frame, without destroying the old window.
53 *
54 */
55 void x_con_reframe(Con *con);
56
57 /**
58 * Returns true if the client supports the given protocol atom (like WM_DELETE_WINDOW)
59 *
60 */
61 bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom);
62
63 /**
64 * Kills the given X11 window using WM_DELETE_WINDOW (if supported).
65 *
66 */
67 void x_window_kill(xcb_window_t window, kill_window_t kill_window);
68
69 /**
70 * Draws the decoration of the given container onto its parent.
71 *
72 */
73 void x_draw_decoration(Con *con);
74
75 /**
76 * Recursively calls x_draw_decoration. This cannot be done in x_push_node
77 * because x_push_node uses focus order to recurse (see the comment above)
78 * while drawing the decoration needs to happen in the actual order.
79 *
80 */
81 void x_deco_recurse(Con *con);
82
83 /**
84 * This function pushes the properties of each node of the layout tree to
85 * X11 if they have changed (like the map state, position of the window, …).
86 * It recursively traverses all children of the given node.
87 *
88 */
89 void x_push_node(Con *con);
90
91 /**
92 * Pushes all changes (state of each node, see x_push_node() and the window
93 * stack) to X11.
94 *
95 */
96 void x_push_changes(Con *con);
97
98 /**
99 * Raises the specified container in the internal stack of X windows. The
100 * next call to x_push_changes() will make the change visible in X11.
101 *
102 */
103 void x_raise_con(Con *con);
104
105 /**
106 * Sets the WM_NAME property (so, no UTF8, but used only for debugging anyways)
107 * of the given name. Used for properly tagging the windows for easily spotting
108 * i3 windows in xwininfo -root -all.
109 *
110 */
111 void x_set_name(Con *con, const char *name);
112
113 /**
114 * Set up the SHMLOG_PATH atom.
115 *
116 */
117 void update_shmlog_atom(void);
118
119 /**
120 * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH)
121 *
122 */
123 void x_set_i3_atoms(void);
124
125 /**
126 * Set warp_to coordinates. This will trigger on the next call to
127 * x_push_changes().
128 *
129 */
130 void x_set_warp_to(Rect *rect);
131
132 /**
133 * Applies the given mask to the event mask of every i3 window decoration X11
134 * window. This is useful to disable EnterNotify while resizing so that focus
135 * is untouched.
136 *
137 */
138 void x_mask_event_mask(uint32_t mask);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * xcb.c: Helper functions for easier usage of XCB
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include "data.h"
14 #include "xcursor.h"
15
16 #define _NET_WM_STATE_REMOVE 0
17 #define _NET_WM_STATE_ADD 1
18 #define _NET_WM_STATE_TOGGLE 2
19
20 /** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a
21 * constant for that. */
22 #define XCB_CURSOR_LEFT_PTR 68
23 #define XCB_CURSOR_SB_H_DOUBLE_ARROW 108
24 #define XCB_CURSOR_SB_V_DOUBLE_ARROW 116
25 #define XCB_CURSOR_WATCH 150
26
27 /* from X11/keysymdef.h */
28 #define XCB_NUM_LOCK 0xff7f
29
30 /* The event masks are defined here because we don’t only set them once but we
31 need to set slight variations of them (without XCB_EVENT_MASK_ENTER_WINDOW
32 while rendering the layout) */
33 /** The XCB_CW_EVENT_MASK for the child (= real window) */
34 #define CHILD_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
35 XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
36 XCB_EVENT_MASK_FOCUS_CHANGE)
37
38 /** The XCB_CW_EVENT_MASK for its frame */
39 #define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \
40 XCB_EVENT_MASK_BUTTON_RELEASE | \
41 XCB_EVENT_MASK_POINTER_MOTION | /* …mouse is moved */ \
42 XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \
43 XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* …the frame gets destroyed */ \
44 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | /* …the application tries to resize itself */ \
45 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \
46 XCB_EVENT_MASK_ENTER_WINDOW) /* …user moves cursor inside our window */
47
48 #define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
49 XCB_EVENT_MASK_BUTTON_PRESS | \
50 XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video \
51 projector), the root window gets a \
52 ConfigureNotify */ \
53 XCB_EVENT_MASK_POINTER_MOTION | \
54 XCB_EVENT_MASK_PROPERTY_CHANGE | \
55 XCB_EVENT_MASK_FOCUS_CHANGE | \
56 XCB_EVENT_MASK_ENTER_WINDOW)
57
58 #define xmacro(atom) xcb_atom_t A_##atom;
59 #include "atoms.xmacro"
60 #undef xmacro
61
62 extern unsigned int xcb_numlock_mask;
63
64 /**
65 * Convenience wrapper around xcb_create_window which takes care of depth,
66 * generating an ID and checking for errors.
67 *
68 */
69 xcb_window_t create_window(xcb_connection_t *conn, Rect r, uint16_t depth, xcb_visualid_t visual,
70 uint16_t window_class, enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values);
71
72 /**
73 * Generates a configure_notify_event with absolute coordinates (relative to
74 * the X root window, not to the client’s frame) for the given client.
75 *
76 */
77 void fake_absolute_configure_notify(Con *con);
78
79 /**
80 * Sends the WM_TAKE_FOCUS ClientMessage to the given window
81 *
82 */
83 void send_take_focus(xcb_window_t window, xcb_timestamp_t timestamp);
84
85 /**
86 * Configures the given window to have the size/position specified by given rect
87 *
88 */
89 void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r);
90
91 /**
92 * Returns the first supported _NET_WM_WINDOW_TYPE atom.
93 *
94 */
95 xcb_atom_t xcb_get_preferred_window_type(xcb_get_property_reply_t *reply);
96
97 /**
98 * Returns true if the given reply contains the given data.
99 *
100 */
101 bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
102
103 /**
104 * Set the cursor of the root window to the given cursor id.
105 * This function should only be used if xcursor_supported == false.
106 * Otherwise, use xcursor_set_root_cursor().
107 *
108 */
109 void xcb_set_root_cursor(int cursor);
110
111 /**
112 * Get depth of visual specified by visualid
113 *
114 */
115 uint16_t get_visual_depth(xcb_visualid_t visual_id);
116
117 /**
118 * Get visual type specified by visualid
119 *
120 */
121 xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id);
122
123 /**
124 * Get visualid with specified depth
125 *
126 */
127 xcb_visualid_t get_visualid_by_depth(uint16_t depth);
128
129 /**
130 * Add an atom to a list of atoms the given property defines.
131 * This is useful, for example, for manipulating _NET_WM_STATE.
132 *
133 */
134 void xcb_add_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom);
135
136 /**
137 * Remove an atom from a list of atoms the given property defines without
138 * removing any other potentially set atoms. This is useful, for example, for
139 * manipulating _NET_WM_STATE.
140 *
141 */
142 void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom);
143
144 /**
145 * Grab the specified buttons on a window when managing it.
146 *
147 */
148 void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, int *buttons);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * xcursor.c: libXcursor support for themed cursors.
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <xcb/xcb_cursor.h>
14
15 enum xcursor_cursor_t {
16 XCURSOR_CURSOR_POINTER = 0,
17 XCURSOR_CURSOR_RESIZE_HORIZONTAL,
18 XCURSOR_CURSOR_RESIZE_VERTICAL,
19 XCURSOR_CURSOR_TOP_LEFT_CORNER,
20 XCURSOR_CURSOR_TOP_RIGHT_CORNER,
21 XCURSOR_CURSOR_BOTTOM_LEFT_CORNER,
22 XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER,
23 XCURSOR_CURSOR_WATCH,
24 XCURSOR_CURSOR_MOVE,
25 XCURSOR_CURSOR_MAX
26 };
27
28 void xcursor_load_cursors(void);
29 xcb_cursor_t xcursor_get_cursor(enum xcursor_cursor_t c);
30 int xcursor_get_xcb_cursor(enum xcursor_cursor_t c);
31
32 /**
33 * Sets the cursor of the root window to the 'pointer' cursor.
34 *
35 * This function is called when i3 is initialized, because with some login
36 * managers, the root window will not have a cursor otherwise.
37 *
38 * We have a separate xcursor function to use the same X11 connection as the
39 * xcursor_load_cursors() function. If we mix the Xlib and the XCB connection,
40 * races might occur (even though we flush the Xlib connection).
41 *
42 */
43 void xcursor_set_root_cursor(int cursor_id);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * This is LEGACY code (we support RandR, which can do much more than
7 * Xinerama), but necessary for the poor users of the nVidia binary
8 * driver which does not support RandR in 2011 *sigh*.
9 *
10 */
11 #pragma once
12
13 #include <config.h>
14
15 #include "data.h"
16
17 /**
18 * We have just established a connection to the X server and need the initial
19 * Xinerama information to setup workspaces for each screen.
20 *
21 */
22 void xinerama_init(void);
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * yajl_utils.h
7 *
8 */
9 #pragma once
10
11 #include <config.h>
12
13 #include <yajl/yajl_gen.h>
14 #include <yajl/yajl_parse.h>
15 #include <yajl/yajl_version.h>
16
17 /* Shorter names for all those yajl_gen_* functions */
18 #define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
19 #define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
20
21 #define ygenalloc() yajl_gen_alloc(NULL)
22 #define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, client)
23 typedef size_t ylength;
0 #!/bin/sh
1 # install - install a program, script, or datafile
2
3 scriptversion=2018-03-11.20; # UTC
4
5 # This originates from X11R5 (mit/util/scripts/install.sh), which was
6 # later released in X11R6 (xc/config/util/install.sh) with the
7 # following copyright and license.
8 #
9 # Copyright (C) 1994 X Consortium
10 #
11 # Permission is hereby granted, free of charge, to any person obtaining a copy
12 # of this software and associated documentation files (the "Software"), to
13 # deal in the Software without restriction, including without limitation the
14 # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
15 # sell copies of the Software, and to permit persons to whom the Software is
16 # furnished to do so, subject to the following conditions:
17 #
18 # The above copyright notice and this permission notice shall be included in
19 # all copies or substantial portions of the Software.
20 #
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
25 # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
26 # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #
28 # Except as contained in this notice, the name of the X Consortium shall not
29 # be used in advertising or otherwise to promote the sale, use or other deal-
30 # ings in this Software without prior written authorization from the X Consor-
31 # tium.
32 #
33 #
34 # FSF changes to this file are in the public domain.
35 #
36 # Calling this script install-sh is preferred over install.sh, to prevent
37 # 'make' implicit rules from creating a file called install from it
38 # when there is no Makefile.
39 #
40 # This script is compatible with the BSD install script, but was written
41 # from scratch.
42
43 tab=' '
44 nl='
45 '
46 IFS=" $tab$nl"
47
48 # Set DOITPROG to "echo" to test this script.
49
50 doit=${DOITPROG-}
51 doit_exec=${doit:-exec}
52
53 # Put in absolute file names if you don't have them in your path;
54 # or use environment vars.
55
56 chgrpprog=${CHGRPPROG-chgrp}
57 chmodprog=${CHMODPROG-chmod}
58 chownprog=${CHOWNPROG-chown}
59 cmpprog=${CMPPROG-cmp}
60 cpprog=${CPPROG-cp}
61 mkdirprog=${MKDIRPROG-mkdir}
62 mvprog=${MVPROG-mv}
63 rmprog=${RMPROG-rm}
64 stripprog=${STRIPPROG-strip}
65
66 posix_mkdir=
67
68 # Desired mode of installed file.
69 mode=0755
70
71 chgrpcmd=
72 chmodcmd=$chmodprog
73 chowncmd=
74 mvcmd=$mvprog
75 rmcmd="$rmprog -f"
76 stripcmd=
77
78 src=
79 dst=
80 dir_arg=
81 dst_arg=
82
83 copy_on_change=false
84 is_target_a_directory=possibly
85
86 usage="\
87 Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
88 or: $0 [OPTION]... SRCFILES... DIRECTORY
89 or: $0 [OPTION]... -t DIRECTORY SRCFILES...
90 or: $0 [OPTION]... -d DIRECTORIES...
91
92 In the 1st form, copy SRCFILE to DSTFILE.
93 In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
94 In the 4th, create DIRECTORIES.
95
96 Options:
97 --help display this help and exit.
98 --version display version info and exit.
99
100 -c (ignored)
101 -C install only if different (preserve the last data modification time)
102 -d create directories instead of installing files.
103 -g GROUP $chgrpprog installed files to GROUP.
104 -m MODE $chmodprog installed files to MODE.
105 -o USER $chownprog installed files to USER.
106 -s $stripprog installed files.
107 -t DIRECTORY install into DIRECTORY.
108 -T report an error if DSTFILE is a directory.
109
110 Environment variables override the default commands:
111 CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
112 RMPROG STRIPPROG
113 "
114
115 while test $# -ne 0; do
116 case $1 in
117 -c) ;;
118
119 -C) copy_on_change=true;;
120
121 -d) dir_arg=true;;
122
123 -g) chgrpcmd="$chgrpprog $2"
124 shift;;
125
126 --help) echo "$usage"; exit $?;;
127
128 -m) mode=$2
129 case $mode in
130 *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
131 echo "$0: invalid mode: $mode" >&2
132 exit 1;;
133 esac
134 shift;;
135
136 -o) chowncmd="$chownprog $2"
137 shift;;
138
139 -s) stripcmd=$stripprog;;
140
141 -t)
142 is_target_a_directory=always
143 dst_arg=$2
144 # Protect names problematic for 'test' and other utilities.
145 case $dst_arg in
146 -* | [=\(\)!]) dst_arg=./$dst_arg;;
147 esac
148 shift;;
149
150 -T) is_target_a_directory=never;;
151
152 --version) echo "$0 $scriptversion"; exit $?;;
153
154 --) shift
155 break;;
156
157 -*) echo "$0: invalid option: $1" >&2
158 exit 1;;
159
160 *) break;;
161 esac
162 shift
163 done
164
165 # We allow the use of options -d and -T together, by making -d
166 # take the precedence; this is for compatibility with GNU install.
167
168 if test -n "$dir_arg"; then
169 if test -n "$dst_arg"; then
170 echo "$0: target directory not allowed when installing a directory." >&2
171 exit 1
172 fi
173 fi
174
175 if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
176 # When -d is used, all remaining arguments are directories to create.
177 # When -t is used, the destination is already specified.
178 # Otherwise, the last argument is the destination. Remove it from $@.
179 for arg
180 do
181 if test -n "$dst_arg"; then
182 # $@ is not empty: it contains at least $arg.
183 set fnord "$@" "$dst_arg"
184 shift # fnord
185 fi
186 shift # arg
187 dst_arg=$arg
188 # Protect names problematic for 'test' and other utilities.
189 case $dst_arg in
190 -* | [=\(\)!]) dst_arg=./$dst_arg;;
191 esac
192 done
193 fi
194
195 if test $# -eq 0; then
196 if test -z "$dir_arg"; then
197 echo "$0: no input file specified." >&2
198 exit 1
199 fi
200 # It's OK to call 'install-sh -d' without argument.
201 # This can happen when creating conditional directories.
202 exit 0
203 fi
204
205 if test -z "$dir_arg"; then
206 if test $# -gt 1 || test "$is_target_a_directory" = always; then
207 if test ! -d "$dst_arg"; then
208 echo "$0: $dst_arg: Is not a directory." >&2
209 exit 1
210 fi
211 fi
212 fi
213
214 if test -z "$dir_arg"; then
215 do_exit='(exit $ret); exit $ret'
216 trap "ret=129; $do_exit" 1
217 trap "ret=130; $do_exit" 2
218 trap "ret=141; $do_exit" 13
219 trap "ret=143; $do_exit" 15
220
221 # Set umask so as not to create temps with too-generous modes.
222 # However, 'strip' requires both read and write access to temps.
223 case $mode in
224 # Optimize common cases.
225 *644) cp_umask=133;;
226 *755) cp_umask=22;;
227
228 *[0-7])
229 if test -z "$stripcmd"; then
230 u_plus_rw=
231 else
232 u_plus_rw='% 200'
233 fi
234 cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
235 *)
236 if test -z "$stripcmd"; then
237 u_plus_rw=
238 else
239 u_plus_rw=,u+rw
240 fi
241 cp_umask=$mode$u_plus_rw;;
242 esac
243 fi
244
245 for src
246 do
247 # Protect names problematic for 'test' and other utilities.
248 case $src in
249 -* | [=\(\)!]) src=./$src;;
250 esac
251
252 if test -n "$dir_arg"; then
253 dst=$src
254 dstdir=$dst
255 test -d "$dstdir"
256 dstdir_status=$?
257 else
258
259 # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
260 # might cause directories to be created, which would be especially bad
261 # if $src (and thus $dsttmp) contains '*'.
262 if test ! -f "$src" && test ! -d "$src"; then
263 echo "$0: $src does not exist." >&2
264 exit 1
265 fi
266
267 if test -z "$dst_arg"; then
268 echo "$0: no destination specified." >&2
269 exit 1
270 fi
271 dst=$dst_arg
272
273 # If destination is a directory, append the input filename.
274 if test -d "$dst"; then
275 if test "$is_target_a_directory" = never; then
276 echo "$0: $dst_arg: Is a directory" >&2
277 exit 1
278 fi
279 dstdir=$dst
280 dstbase=`basename "$src"`
281 case $dst in
282 */) dst=$dst$dstbase;;
283 *) dst=$dst/$dstbase;;
284 esac
285 dstdir_status=0
286 else
287 dstdir=`dirname "$dst"`
288 test -d "$dstdir"
289 dstdir_status=$?
290 fi
291 fi
292
293 case $dstdir in
294 */) dstdirslash=$dstdir;;
295 *) dstdirslash=$dstdir/;;
296 esac
297
298 obsolete_mkdir_used=false
299
300 if test $dstdir_status != 0; then
301 case $posix_mkdir in
302 '')
303 # Create intermediate dirs using mode 755 as modified by the umask.
304 # This is like FreeBSD 'install' as of 1997-10-28.
305 umask=`umask`
306 case $stripcmd.$umask in
307 # Optimize common cases.
308 *[2367][2367]) mkdir_umask=$umask;;
309 .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
310
311 *[0-7])
312 mkdir_umask=`expr $umask + 22 \
313 - $umask % 100 % 40 + $umask % 20 \
314 - $umask % 10 % 4 + $umask % 2
315 `;;
316 *) mkdir_umask=$umask,go-w;;
317 esac
318
319 # With -d, create the new directory with the user-specified mode.
320 # Otherwise, rely on $mkdir_umask.
321 if test -n "$dir_arg"; then
322 mkdir_mode=-m$mode
323 else
324 mkdir_mode=
325 fi
326
327 posix_mkdir=false
328 case $umask in
329 *[123567][0-7][0-7])
330 # POSIX mkdir -p sets u+wx bits regardless of umask, which
331 # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
332 ;;
333 *)
334 # Note that $RANDOM variable is not portable (e.g. dash); Use it
335 # here however when possible just to lower collision chance.
336 tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
337
338 trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
339
340 # Because "mkdir -p" follows existing symlinks and we likely work
341 # directly in world-writeable /tmp, make sure that the '$tmpdir'
342 # directory is successfully created first before we actually test
343 # 'mkdir -p' feature.
344 if (umask $mkdir_umask &&
345 $mkdirprog $mkdir_mode "$tmpdir" &&
346 exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
347 then
348 if test -z "$dir_arg" || {
349 # Check for POSIX incompatibilities with -m.
350 # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
351 # other-writable bit of parent directory when it shouldn't.
352 # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
353 test_tmpdir="$tmpdir/a"
354 ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
355 case $ls_ld_tmpdir in
356 d????-?r-*) different_mode=700;;
357 d????-?--*) different_mode=755;;
358 *) false;;
359 esac &&
360 $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
361 ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
362 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
363 }
364 }
365 then posix_mkdir=:
366 fi
367 rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
368 else
369 # Remove any dirs left behind by ancient mkdir implementations.
370 rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
371 fi
372 trap '' 0;;
373 esac;;
374 esac
375
376 if
377 $posix_mkdir && (
378 umask $mkdir_umask &&
379 $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
380 )
381 then :
382 else
383
384 # The umask is ridiculous, or mkdir does not conform to POSIX,
385 # or it failed possibly due to a race condition. Create the
386 # directory the slow way, step by step, checking for races as we go.
387
388 case $dstdir in
389 /*) prefix='/';;
390 [-=\(\)!]*) prefix='./';;
391 *) prefix='';;
392 esac
393
394 oIFS=$IFS
395 IFS=/
396 set -f
397 set fnord $dstdir
398 shift
399 set +f
400 IFS=$oIFS
401
402 prefixes=
403
404 for d
405 do
406 test X"$d" = X && continue
407
408 prefix=$prefix$d
409 if test -d "$prefix"; then
410 prefixes=
411 else
412 if $posix_mkdir; then
413 (umask=$mkdir_umask &&
414 $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
415 # Don't fail if two instances are running concurrently.
416 test -d "$prefix" || exit 1
417 else
418 case $prefix in
419 *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
420 *) qprefix=$prefix;;
421 esac
422 prefixes="$prefixes '$qprefix'"
423 fi
424 fi
425 prefix=$prefix/
426 done
427
428 if test -n "$prefixes"; then
429 # Don't fail if two instances are running concurrently.
430 (umask $mkdir_umask &&
431 eval "\$doit_exec \$mkdirprog $prefixes") ||
432 test -d "$dstdir" || exit 1
433 obsolete_mkdir_used=true
434 fi
435 fi
436 fi
437
438 if test -n "$dir_arg"; then
439 { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
440 { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
441 { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
442 test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
443 else
444
445 # Make a couple of temp file names in the proper directory.
446 dsttmp=${dstdirslash}_inst.$$_
447 rmtmp=${dstdirslash}_rm.$$_
448
449 # Trap to clean up those temp files at exit.
450 trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
451
452 # Copy the file name to the temp name.
453 (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
454
455 # and set any options; do chmod last to preserve setuid bits.
456 #
457 # If any of these fail, we abort the whole thing. If we want to
458 # ignore errors from any of these, just make sure not to ignore
459 # errors from the above "$doit $cpprog $src $dsttmp" command.
460 #
461 { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
462 { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
463 { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
464 { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
465
466 # If -C, don't bother to copy if it wouldn't change the file.
467 if $copy_on_change &&
468 old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
469 new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
470 set -f &&
471 set X $old && old=:$2:$4:$5:$6 &&
472 set X $new && new=:$2:$4:$5:$6 &&
473 set +f &&
474 test "$old" = "$new" &&
475 $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
476 then
477 rm -f "$dsttmp"
478 else
479 # Rename the file to the real destination.
480 $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
481
482 # The rename failed, perhaps because mv can't rename something else
483 # to itself, or perhaps because mv is so ancient that it does not
484 # support -f.
485 {
486 # Now remove or move aside any old file at destination location.
487 # We try this two ways since rm can't unlink itself on some
488 # systems and the destination file might be busy for other
489 # reasons. In this case, the final cleanup might fail but the new
490 # file should still install successfully.
491 {
492 test ! -f "$dst" ||
493 $doit $rmcmd -f "$dst" 2>/dev/null ||
494 { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
495 { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
496 } ||
497 { echo "$0: cannot unlink or rename $dst" >&2
498 (exit 1); exit 1
499 }
500 } &&
501
502 # Now rename the file to the real destination.
503 $doit $mvcmd "$dsttmp" "$dst"
504 }
505 fi || exit 1
506
507 trap '' 0
508 fi
509 done
510
511 # Local variables:
512 # eval: (add-hook 'before-save-hook 'time-stamp)
513 # time-stamp-start: "scriptversion="
514 # time-stamp-format: "%:y-%02m-%02d.%02H"
515 # time-stamp-time-zone: "UTC0"
516 # time-stamp-end: "; # UTC"
517 # End:
0 Introduction
1 ============
2
3 libi3 is an *INTERNAL* library which contains functions that i3 and related
4 tools (i3-msg, i3-input, i3-nagbar, i3-config-wizard, i3bar) use.
5
6 It is NOT to be used by other programs.
7
8 Structure
9 =========
10
11 Every function gets its own .c file, which in turn gets compiled into an .o
12 object file. Afterwards, all .o files are archived into one static library
13 (libi3.a). This library will be linked into all i3 binaries. The linker is able
14 to eliminate unused .o files when linking, so only the functions which you
15 actually use will be included in the corresponding binary.
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <math.h>
10 #include <stdlib.h>
11 #include <xcb/xcb_xrm.h>
12
13 static long dpi;
14
15 static long init_dpi_fallback(void) {
16 return (double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters;
17 }
18
19 /*
20 * Initialize the DPI setting.
21 * This will use the 'Xft.dpi' X resource if available and fall back to
22 * guessing the correct value otherwise.
23 */
24 void init_dpi(void) {
25 xcb_xrm_database_t *database = NULL;
26 char *resource = NULL;
27
28 if (conn == NULL) {
29 goto init_dpi_end;
30 }
31
32 database = xcb_xrm_database_from_default(conn);
33 if (database == NULL) {
34 ELOG("Failed to open the resource database.\n");
35 goto init_dpi_end;
36 }
37
38 xcb_xrm_resource_get_string(database, "Xft.dpi", NULL, &resource);
39 if (resource == NULL) {
40 DLOG("Resource Xft.dpi not specified, skipping.\n");
41 goto init_dpi_end;
42 }
43
44 char *endptr;
45 double in_dpi = strtod(resource, &endptr);
46 if (in_dpi == HUGE_VAL || dpi < 0 || *endptr != '\0' || endptr == resource) {
47 ELOG("Xft.dpi = %s is an invalid number and couldn't be parsed.\n", resource);
48 dpi = 0;
49 goto init_dpi_end;
50 }
51 dpi = lround(in_dpi);
52
53 DLOG("Found Xft.dpi = %ld.\n", dpi);
54
55 init_dpi_end:
56 free(resource);
57
58 if (database != NULL) {
59 xcb_xrm_database_free(database);
60 }
61
62 if (dpi == 0) {
63 DLOG("Using fallback for calculating DPI.\n");
64 dpi = init_dpi_fallback();
65 DLOG("Using dpi = %ld\n", dpi);
66 }
67 }
68
69 /*
70 * This function returns the value of the DPI setting.
71 *
72 */
73 long get_dpi_value(void) {
74 return dpi;
75 }
76
77 /*
78 * Convert a logical amount of pixels (e.g. 2 pixels on a “standard” 96 DPI
79 * screen) to a corresponding amount of physical pixels on a standard or retina
80 * screen, e.g. 5 pixels on a 227 DPI MacBook Pro 13" Retina screen.
81 *
82 */
83 int logical_px(const int logical) {
84 if (root_screen == NULL) {
85 /* Dpi info may not be available when parsing a config without an X
86 * server, such as for config file validation. */
87 return logical;
88 }
89
90 /* There are many misconfigurations out there, i.e. systems with screens
91 * whose dpi is in fact higher than 96 dpi, but not significantly higher,
92 * so software was never adapted. We could tell people to reconfigure their
93 * systems to 96 dpi in order to get the behavior they expect/are used to,
94 * but since we can easily detect this case in code, let’s do it for them.
95 */
96 if ((dpi / 96.0) < 1.25)
97 return logical;
98 return ceil((dpi / 96.0) * logical);
99 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * © 2015 Ingo Bürk and contributors (see also: LICENSE)
4 *
5 * draw.c: Utility for drawing.
6 *
7 */
8 #include "libi3.h"
9
10 #include <stdlib.h>
11 #include <err.h>
12 #include <string.h>
13 #include <xcb/xcb.h>
14 #include <xcb/xcb_aux.h>
15 #include <cairo/cairo-xcb.h>
16
17 /* The default visual_type to use if none is specified when creating the surface. Must be defined globally. */
18 xcb_visualtype_t *visual_type;
19
20 /* Forward declarations */
21 static void draw_util_set_source_color(surface_t *surface, color_t color);
22
23 #define RETURN_UNLESS_SURFACE_INITIALIZED(surface) \
24 do { \
25 if ((surface)->id == XCB_NONE) { \
26 ELOG("Surface %p is not initialized, skipping drawing.\n", surface); \
27 return; \
28 } \
29 } while (0)
30
31 /*
32 * Initialize the surface to represent the given drawable.
33 *
34 */
35 void draw_util_surface_init(xcb_connection_t *conn, surface_t *surface, xcb_drawable_t drawable,
36 xcb_visualtype_t *visual, int width, int height) {
37 surface->id = drawable;
38 surface->visual_type = ((visual == NULL) ? visual_type : visual);
39 surface->width = width;
40 surface->height = height;
41
42 surface->gc = xcb_generate_id(conn);
43 xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(conn, surface->gc, surface->id, 0, NULL);
44
45 xcb_generic_error_t *error = xcb_request_check(conn, gc_cookie);
46 if (error != NULL) {
47 ELOG("Could not create graphical context. Error code: %d. Please report this bug.\n", error->error_code);
48 }
49
50 surface->surface = cairo_xcb_surface_create(conn, surface->id, surface->visual_type, width, height);
51 surface->cr = cairo_create(surface->surface);
52 }
53
54 /*
55 * Destroys the surface.
56 *
57 */
58 void draw_util_surface_free(xcb_connection_t *conn, surface_t *surface) {
59 xcb_free_gc(conn, surface->gc);
60 cairo_surface_destroy(surface->surface);
61 cairo_destroy(surface->cr);
62
63 /* We need to explicitly set these to NULL to avoid assertion errors in
64 * cairo when calling this multiple times. This can happen, for example,
65 * when setting the border of a window to none and then closing it. */
66 surface->surface = NULL;
67 surface->cr = NULL;
68 }
69
70 /*
71 * Resize the surface to the given size.
72 *
73 */
74 void draw_util_surface_set_size(surface_t *surface, int width, int height) {
75 surface->width = width;
76 surface->height = height;
77 cairo_xcb_surface_set_size(surface->surface, width, height);
78 }
79
80 /*
81 * Parses the given color in hex format to an internal color representation.
82 * Note that the input must begin with a hash sign, e.g., "#3fbc59".
83 *
84 */
85 color_t draw_util_hex_to_color(const char *color) {
86 if (strlen(color) < 6 || color[0] != '#') {
87 ELOG("Could not parse color: %s\n", color);
88 return draw_util_hex_to_color("#A9A9A9");
89 }
90
91 char alpha[2];
92 if (strlen(color) == strlen("#rrggbbaa")) {
93 alpha[0] = color[7];
94 alpha[1] = color[8];
95 } else {
96 alpha[0] = alpha[1] = 'F';
97 }
98
99 char groups[4][3] = {
100 {color[1], color[2], '\0'},
101 {color[3], color[4], '\0'},
102 {color[5], color[6], '\0'},
103 {alpha[0], alpha[1], '\0'}};
104
105 return (color_t){
106 .red = strtol(groups[0], NULL, 16) / 255.0,
107 .green = strtol(groups[1], NULL, 16) / 255.0,
108 .blue = strtol(groups[2], NULL, 16) / 255.0,
109 .alpha = strtol(groups[3], NULL, 16) / 255.0,
110 .colorpixel = get_colorpixel(color)};
111 }
112
113 /*
114 * Set the given color as the source color on the surface.
115 *
116 */
117 static void draw_util_set_source_color(surface_t *surface, color_t color) {
118 RETURN_UNLESS_SURFACE_INITIALIZED(surface);
119
120 cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha);
121 }
122
123 /*
124 * Draw the given text using libi3.
125 * This function also marks the surface dirty which is needed if other means of
126 * drawing are used. This will be the case when using XCB to draw text.
127 *
128 */
129 void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) {
130 RETURN_UNLESS_SURFACE_INITIALIZED(surface);
131
132 /* Flush any changes before we draw the text as this might use XCB directly. */
133 CAIRO_SURFACE_FLUSH(surface->surface);
134
135 set_font_colors(surface->gc, fg_color, bg_color);
136 draw_text(text, surface->id, surface->gc, surface->visual_type, x, y, max_width);
137
138 /* Notify cairo that we (possibly) used another way to draw on the surface. */
139 cairo_surface_mark_dirty(surface->surface);
140 }
141
142 /*
143 * Draws a filled rectangle.
144 * This function is a convenience wrapper and takes care of flushing the
145 * surface as well as restoring the cairo state.
146 *
147 */
148 void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) {
149 RETURN_UNLESS_SURFACE_INITIALIZED(surface);
150
151 cairo_save(surface->cr);
152
153 /* Using the SOURCE operator will copy both color and alpha information directly
154 * onto the surface rather than blending it. This is a bit more efficient and
155 * allows better color control for the user when using opacity. */
156 cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
157 draw_util_set_source_color(surface, color);
158
159 cairo_rectangle(surface->cr, x, y, w, h);
160 cairo_fill(surface->cr);
161
162 /* Make sure we flush the surface for any text drawing operations that could follow.
163 * Since we support drawing text via XCB, we need this. */
164 CAIRO_SURFACE_FLUSH(surface->surface);
165
166 cairo_restore(surface->cr);
167 }
168
169 /*
170 * Clears a surface with the given color.
171 *
172 */
173 void draw_util_clear_surface(surface_t *surface, color_t color) {
174 RETURN_UNLESS_SURFACE_INITIALIZED(surface);
175
176 cairo_save(surface->cr);
177
178 /* Using the SOURCE operator will copy both color and alpha information directly
179 * onto the surface rather than blending it. This is a bit more efficient and
180 * allows better color control for the user when using opacity. */
181 cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE);
182 draw_util_set_source_color(surface, color);
183
184 cairo_paint(surface->cr);
185
186 /* Make sure we flush the surface for any text drawing operations that could follow.
187 * Since we support drawing text via XCB, we need this. */
188 CAIRO_SURFACE_FLUSH(surface->surface);
189
190 cairo_restore(surface->cr);
191 }
192
193 /*
194 * Copies a surface onto another surface.
195 *
196 */
197 void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y,
198 double dest_x, double dest_y, double width, double height) {
199 RETURN_UNLESS_SURFACE_INITIALIZED(src);
200 RETURN_UNLESS_SURFACE_INITIALIZED(dest);
201
202 cairo_save(dest->cr);
203
204 /* Using the SOURCE operator will copy both color and alpha information directly
205 * onto the surface rather than blending it. This is a bit more efficient and
206 * allows better color control for the user when using opacity. */
207 cairo_set_operator(dest->cr, CAIRO_OPERATOR_SOURCE);
208 cairo_set_source_surface(dest->cr, src->surface, dest_x - src_x, dest_y - src_y);
209
210 cairo_rectangle(dest->cr, dest_x, dest_y, width, height);
211 cairo_fill(dest->cr);
212
213 /* Make sure we flush the surface for any text drawing operations that could follow.
214 * Since we support drawing text via XCB, we need this. */
215 CAIRO_SURFACE_FLUSH(src->surface);
216 CAIRO_SURFACE_FLUSH(dest->surface);
217
218 cairo_restore(dest->cr);
219 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <stdlib.h>
10 #include <stdbool.h>
11
12 #include <xcb/xcb.h>
13 #include <xcb/xproto.h>
14
15 /*
16 * Generates a configure_notify event and sends it to the given window
17 * Applications need this to think they’ve configured themselves correctly.
18 * The truth is, however, that we will manage them.
19 *
20 */
21 void fake_configure_notify(xcb_connection_t *conn, xcb_rectangle_t r, xcb_window_t window, int border_width) {
22 /* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
23 * In order to properly initialize these bytes, we allocate 32 bytes even
24 * though we only need less for an xcb_configure_notify_event_t */
25 void *event = scalloc(32, 1);
26 xcb_configure_notify_event_t *generated_event = event;
27
28 generated_event->event = window;
29 generated_event->window = window;
30 generated_event->response_type = XCB_CONFIGURE_NOTIFY;
31
32 generated_event->x = r.x;
33 generated_event->y = r.y;
34 generated_event->width = r.width;
35 generated_event->height = r.height;
36
37 generated_event->border_width = border_width;
38 generated_event->above_sibling = XCB_NONE;
39 generated_event->override_redirect = false;
40
41 xcb_send_event(conn, false, window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char *)generated_event);
42 xcb_flush(conn);
43
44 free(event);
45 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <assert.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdbool.h>
14 #include <err.h>
15
16 #include <cairo/cairo-xcb.h>
17 #include <pango/pangocairo.h>
18
19 static const i3Font *savedFont = NULL;
20
21 static xcb_visualtype_t *root_visual_type;
22 static double pango_font_red;
23 static double pango_font_green;
24 static double pango_font_blue;
25
26 static PangoLayout *create_layout_with_dpi(cairo_t *cr) {
27 PangoLayout *layout;
28 PangoContext *context;
29
30 context = pango_cairo_create_context(cr);
31 pango_cairo_context_set_resolution(context, get_dpi_value());
32 layout = pango_layout_new(context);
33 g_object_unref(context);
34
35 return layout;
36 }
37
38 /*
39 * Loads a Pango font description into an i3Font structure. Returns true
40 * on success, false otherwise.
41 *
42 */
43 static bool load_pango_font(i3Font *font, const char *desc) {
44 /* Load the font description */
45 font->specific.pango_desc = pango_font_description_from_string(desc);
46 if (!font->specific.pango_desc) {
47 ELOG("Could not open font %s with Pango, fallback to X font.\n", desc);
48 return false;
49 }
50
51 LOG("Using Pango font %s, size %d\n",
52 pango_font_description_get_family(font->specific.pango_desc),
53 pango_font_description_get_size(font->specific.pango_desc) / PANGO_SCALE);
54
55 /* We cache root_visual_type here, since you must call
56 * load_pango_font before any other pango function
57 * that would need root_visual_type */
58 root_visual_type = get_visualtype(root_screen);
59
60 /* Create a dummy Pango layout to compute the font height */
61 cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
62 cairo_t *cr = cairo_create(surface);
63 PangoLayout *layout = create_layout_with_dpi(cr);
64 pango_layout_set_font_description(layout, font->specific.pango_desc);
65
66 /* Get the font height */
67 gint height;
68 pango_layout_get_pixel_size(layout, NULL, &height);
69 font->height = height;
70
71 /* Free resources */
72 g_object_unref(layout);
73 cairo_destroy(cr);
74 cairo_surface_destroy(surface);
75
76 /* Set the font type and return successfully */
77 font->type = FONT_TYPE_PANGO;
78 return true;
79 }
80
81 /*
82 * Draws text using Pango rendering.
83 *
84 */
85 static void draw_text_pango(const char *text, size_t text_len,
86 xcb_drawable_t drawable, xcb_visualtype_t *visual, int x, int y,
87 int max_width, bool pango_markup) {
88 /* Create the Pango layout */
89 /* root_visual_type is cached in load_pango_font */
90 cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable,
91 visual, x + max_width, y + savedFont->height);
92 cairo_t *cr = cairo_create(surface);
93 PangoLayout *layout = create_layout_with_dpi(cr);
94 gint height;
95
96 pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
97 pango_layout_set_width(layout, max_width * PANGO_SCALE);
98 pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
99 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
100
101 if (pango_markup)
102 pango_layout_set_markup(layout, text, text_len);
103 else
104 pango_layout_set_text(layout, text, text_len);
105
106 /* Do the drawing */
107 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
108 cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
109 pango_cairo_update_layout(cr, layout);
110 pango_layout_get_pixel_size(layout, NULL, &height);
111 /* Center the piece of text vertically. */
112 int yoffset = (height - savedFont->height) / 2;
113 cairo_move_to(cr, x, y - yoffset);
114 pango_cairo_show_layout(cr, layout);
115
116 /* Free resources */
117 g_object_unref(layout);
118 cairo_destroy(cr);
119 cairo_surface_destroy(surface);
120 }
121
122 /*
123 * Calculate the text width using Pango rendering.
124 *
125 */
126 static int predict_text_width_pango(const char *text, size_t text_len, bool pango_markup) {
127 /* Create a dummy Pango layout */
128 /* root_visual_type is cached in load_pango_font */
129 cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1);
130 cairo_t *cr = cairo_create(surface);
131 PangoLayout *layout = create_layout_with_dpi(cr);
132
133 /* Get the font width */
134 gint width;
135 pango_layout_set_font_description(layout, savedFont->specific.pango_desc);
136
137 if (pango_markup)
138 pango_layout_set_markup(layout, text, text_len);
139 else
140 pango_layout_set_text(layout, text, text_len);
141
142 pango_cairo_update_layout(cr, layout);
143 pango_layout_get_pixel_size(layout, &width, NULL);
144
145 /* Free resources */
146 g_object_unref(layout);
147 cairo_destroy(cr);
148 cairo_surface_destroy(surface);
149
150 return width;
151 }
152
153 /*
154 * Loads a font for usage, also getting its metrics. If fallback is true,
155 * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any
156 * font was previously loaded, it will be freed.
157 *
158 */
159 i3Font load_font(const char *pattern, const bool fallback) {
160 /* if any font was previously loaded, free it now */
161 free_font();
162
163 i3Font font;
164 font.type = FONT_TYPE_NONE;
165
166 /* No XCB connction, return early because we're just validating the
167 * configuration file. */
168 if (conn == NULL) {
169 return font;
170 }
171
172 /* Try to load a pango font if specified */
173 if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) {
174 const char *font_pattern = pattern + strlen("pango:");
175 if (load_pango_font(&font, font_pattern)) {
176 font.pattern = sstrdup(pattern);
177 return font;
178 }
179 } else if (strlen(pattern) > strlen("xft:") && !strncmp(pattern, "xft:", strlen("xft:"))) {
180 const char *font_pattern = pattern + strlen("xft:");
181 if (load_pango_font(&font, font_pattern)) {
182 font.pattern = sstrdup(pattern);
183 return font;
184 }
185 }
186
187 /* Send all our requests first */
188 font.specific.xcb.id = xcb_generate_id(conn);
189 xcb_void_cookie_t font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
190 strlen(pattern), pattern);
191 xcb_query_font_cookie_t info_cookie = xcb_query_font(conn, font.specific.xcb.id);
192
193 /* Check for errors. If errors, fall back to default font. */
194 xcb_generic_error_t *error;
195 error = xcb_request_check(conn, font_cookie);
196
197 /* If we fail to open font, fall back to 'fixed' */
198 if (fallback && error != NULL) {
199 ELOG("Could not open font %s (X error %d). Trying fallback to 'fixed'.\n",
200 pattern, error->error_code);
201 pattern = "fixed";
202 font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
203 strlen(pattern), pattern);
204 info_cookie = xcb_query_font(conn, font.specific.xcb.id);
205
206 /* Check if we managed to open 'fixed' */
207 free(error);
208 error = xcb_request_check(conn, font_cookie);
209
210 /* Fall back to '-misc-*' if opening 'fixed' fails. */
211 if (error != NULL) {
212 ELOG("Could not open fallback font 'fixed', trying with '-misc-*'.\n");
213 pattern = "-misc-*";
214 font_cookie = xcb_open_font_checked(conn, font.specific.xcb.id,
215 strlen(pattern), pattern);
216 info_cookie = xcb_query_font(conn, font.specific.xcb.id);
217
218 free(error);
219 if ((error = xcb_request_check(conn, font_cookie)) != NULL)
220 errx(EXIT_FAILURE, "Could open neither requested font nor fallbacks "
221 "(fixed or -misc-*): X11 error %d",
222 error->error_code);
223 }
224 }
225 free(error);
226
227 font.pattern = sstrdup(pattern);
228 LOG("Using X font %s\n", pattern);
229
230 /* Get information (height/name) for this font */
231 if (!(font.specific.xcb.info = xcb_query_font_reply(conn, info_cookie, NULL)))
232 errx(EXIT_FAILURE, "Could not load font \"%s\"", pattern);
233
234 /* Get the font table, if possible */
235 if (xcb_query_font_char_infos_length(font.specific.xcb.info) == 0)
236 font.specific.xcb.table = NULL;
237 else
238 font.specific.xcb.table = xcb_query_font_char_infos(font.specific.xcb.info);
239
240 /* Calculate the font height */
241 font.height = font.specific.xcb.info->font_ascent + font.specific.xcb.info->font_descent;
242
243 /* Set the font type and return successfully */
244 font.type = FONT_TYPE_XCB;
245 return font;
246 }
247
248 /*
249 * Defines the font to be used for the forthcoming calls.
250 *
251 */
252 void set_font(i3Font *font) {
253 savedFont = font;
254 }
255
256 /*
257 * Frees the resources taken by the current font. If no font was previously
258 * loaded, it simply returns.
259 *
260 */
261 void free_font(void) {
262 /* if there is no saved font, simply return */
263 if (savedFont == NULL)
264 return;
265
266 free(savedFont->pattern);
267 switch (savedFont->type) {
268 case FONT_TYPE_NONE:
269 /* Nothing to do */
270 break;
271 case FONT_TYPE_XCB: {
272 /* Close the font and free the info */
273 xcb_close_font(conn, savedFont->specific.xcb.id);
274 free(savedFont->specific.xcb.info);
275 break;
276 }
277 case FONT_TYPE_PANGO:
278 /* Free the font description */
279 pango_font_description_free(savedFont->specific.pango_desc);
280 break;
281 }
282
283 savedFont = NULL;
284 }
285
286 /*
287 * Defines the colors to be used for the forthcoming draw_text calls.
288 *
289 */
290 void set_font_colors(xcb_gcontext_t gc, color_t foreground, color_t background) {
291 assert(savedFont != NULL);
292
293 switch (savedFont->type) {
294 case FONT_TYPE_NONE:
295 /* Nothing to do */
296 break;
297 case FONT_TYPE_XCB: {
298 /* Change the font and colors in the GC */
299 uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
300 uint32_t values[] = {foreground.colorpixel, background.colorpixel, savedFont->specific.xcb.id};
301 xcb_change_gc(conn, gc, mask, values);
302 break;
303 }
304 case FONT_TYPE_PANGO:
305 /* Save the foreground font */
306 pango_font_red = foreground.red;
307 pango_font_green = foreground.green;
308 pango_font_blue = foreground.blue;
309 break;
310 }
311 }
312
313 /*
314 * Returns true if and only if the current font is a pango font.
315 *
316 */
317 bool font_is_pango(void) {
318 return savedFont->type == FONT_TYPE_PANGO;
319 }
320
321 static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len);
322
323 static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable,
324 xcb_gcontext_t gc, int x, int y, int max_width) {
325 /* X11 coordinates for fonts start at the baseline */
326 int pos_y = y + savedFont->specific.xcb.info->font_ascent;
327
328 /* The X11 protocol limits text drawing to 255 chars, so we may need
329 * multiple calls */
330 int offset = 0;
331 for (;;) {
332 /* Calculate the size of this chunk */
333 int chunk_size = (text_len > 255 ? 255 : text_len);
334 const xcb_char2b_t *chunk = text + offset;
335
336 /* Draw it */
337 xcb_image_text_16(conn, chunk_size, drawable, gc, x, pos_y, chunk);
338
339 /* Advance the offset and length of the text to draw */
340 offset += chunk_size;
341 text_len -= chunk_size;
342
343 /* Check if we're done */
344 if (text_len == 0)
345 break;
346
347 /* Advance pos_x based on the predicted text width */
348 x += predict_text_width_xcb(chunk, chunk_size);
349 }
350 }
351
352 /*
353 * Draws text onto the specified X drawable (normally a pixmap) at the
354 * specified coordinates (from the top left corner of the leftmost, uppermost
355 * glyph) and using the provided gc.
356 *
357 * Text must be specified as an i3String.
358 *
359 */
360 void draw_text(i3String *text, xcb_drawable_t drawable, xcb_gcontext_t gc,
361 xcb_visualtype_t *visual, int x, int y, int max_width) {
362 assert(savedFont != NULL);
363 if (visual == NULL) {
364 visual = root_visual_type;
365 }
366
367 switch (savedFont->type) {
368 case FONT_TYPE_NONE:
369 /* Nothing to do */
370 return;
371 case FONT_TYPE_XCB:
372 draw_text_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text),
373 drawable, gc, x, y, max_width);
374 break;
375 case FONT_TYPE_PANGO:
376 /* Render the text using Pango */
377 draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
378 drawable, visual, x, y, max_width, i3string_is_markup(text));
379 return;
380 }
381 }
382
383 /*
384 * ASCII version of draw_text to print static strings.
385 *
386 */
387 void draw_text_ascii(const char *text, xcb_drawable_t drawable,
388 xcb_gcontext_t gc, int x, int y, int max_width) {
389 assert(savedFont != NULL);
390
391 switch (savedFont->type) {
392 case FONT_TYPE_NONE:
393 /* Nothing to do */
394 return;
395 case FONT_TYPE_XCB: {
396 size_t text_len = strlen(text);
397 if (text_len > 255) {
398 /* The text is too long to draw it directly to X */
399 i3String *str = i3string_from_utf8(text);
400 draw_text(str, drawable, gc, NULL, x, y, max_width);
401 i3string_free(str);
402 } else {
403 /* X11 coordinates for fonts start at the baseline */
404 int pos_y = y + savedFont->specific.xcb.info->font_ascent;
405
406 xcb_image_text_8(conn, text_len, drawable, gc, x, pos_y, text);
407 }
408 break;
409 }
410 case FONT_TYPE_PANGO:
411 /* Render the text using Pango */
412 draw_text_pango(text, strlen(text),
413 drawable, root_visual_type, x, y, max_width, false);
414 return;
415 }
416 }
417
418 static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) {
419 /* Make the user know we’re using the slow path, but only once. */
420 static bool first_invocation = true;
421 if (first_invocation) {
422 fprintf(stderr, "Using slow code path for text extents\n");
423 first_invocation = false;
424 }
425
426 /* Query the text width */
427 xcb_generic_error_t *error;
428 xcb_query_text_extents_cookie_t cookie = xcb_query_text_extents(conn,
429 savedFont->specific.xcb.id, text_len, (xcb_char2b_t *)text);
430 xcb_query_text_extents_reply_t *reply = xcb_query_text_extents_reply(conn,
431 cookie, &error);
432 if (reply == NULL) {
433 /* We return a safe estimate because a rendering error is better than
434 * a crash. Plus, the user will see the error in their log. */
435 fprintf(stderr, "Could not get text extents (X error code %d)\n",
436 error->error_code);
437 return savedFont->specific.xcb.info->max_bounds.character_width * text_len;
438 }
439
440 int width = reply->overall_width;
441 free(reply);
442 return width;
443 }
444
445 static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) {
446 if (text_len == 0)
447 return 0;
448
449 int width;
450 if (savedFont->specific.xcb.table == NULL) {
451 /* If we don't have a font table, fall back to querying the server */
452 width = xcb_query_text_width(input, text_len);
453 } else {
454 /* Save some pointers for convenience */
455 xcb_query_font_reply_t *font_info = savedFont->specific.xcb.info;
456 xcb_charinfo_t *font_table = savedFont->specific.xcb.table;
457
458 /* Calculate the width using the font table */
459 width = 0;
460 for (size_t i = 0; i < text_len; i++) {
461 xcb_charinfo_t *info;
462 int row = input[i].byte1;
463 int col = input[i].byte2;
464
465 if (row < font_info->min_byte1 ||
466 row > font_info->max_byte1 ||
467 col < font_info->min_char_or_byte2 ||
468 col > font_info->max_char_or_byte2)
469 continue;
470
471 /* Don't you ask me, how this one works… (Merovius) */
472 info = &font_table[((row - font_info->min_byte1) *
473 (font_info->max_char_or_byte2 - font_info->min_char_or_byte2 + 1)) +
474 (col - font_info->min_char_or_byte2)];
475
476 if (info->character_width != 0 ||
477 (info->right_side_bearing |
478 info->left_side_bearing |
479 info->ascent |
480 info->descent) != 0) {
481 width += info->character_width;
482 }
483 }
484 }
485
486 return width;
487 }
488
489 /*
490 * Predict the text width in pixels for the given text. Text must be
491 * specified as an i3String.
492 *
493 */
494 int predict_text_width(i3String *text) {
495 assert(savedFont != NULL);
496
497 switch (savedFont->type) {
498 case FONT_TYPE_NONE:
499 /* Nothing to do */
500 return 0;
501 case FONT_TYPE_XCB:
502 return predict_text_width_xcb(i3string_as_ucs2(text), i3string_get_num_glyphs(text));
503 case FONT_TYPE_PANGO:
504 /* Calculate extents using Pango */
505 return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text),
506 i3string_is_markup(text));
507 }
508 assert(false);
509 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <string.h>
12
13 #ifndef CS_STARTS_WITH
14 #define CS_STARTS_WITH(string, needle) (strncmp((string), (needle), strlen((needle))) == 0)
15 #endif
16
17 /*
18 * Replaces occurrences of the defined placeholders in the format string.
19 *
20 */
21 char *format_placeholders(char *format, placeholder_t *placeholders, int num) {
22 if (format == NULL)
23 return NULL;
24
25 /* We have to first iterate over the string to see how much buffer space
26 * we need to allocate. */
27 int buffer_len = strlen(format) + 1;
28 for (char *walk = format; *walk != '\0'; walk++) {
29 for (int i = 0; i < num; i++) {
30 if (!CS_STARTS_WITH(walk, placeholders[i].name))
31 continue;
32
33 buffer_len = buffer_len - strlen(placeholders[i].name) + strlen(placeholders[i].value);
34 walk += strlen(placeholders[i].name) - 1;
35 break;
36 }
37 }
38
39 /* Now we can parse the format string. */
40 char buffer[buffer_len];
41 char *outwalk = buffer;
42 for (char *walk = format; *walk != '\0'; walk++) {
43 if (*walk != '%') {
44 *(outwalk++) = *walk;
45 continue;
46 }
47
48 bool matched = false;
49 for (int i = 0; i < num; i++) {
50 if (!CS_STARTS_WITH(walk, placeholders[i].name)) {
51 continue;
52 }
53
54 matched = true;
55 outwalk += sprintf(outwalk, "%s", placeholders[i].value);
56 walk += strlen(placeholders[i].name) - 1;
57 break;
58 }
59
60 if (!matched)
61 *(outwalk++) = *walk;
62 }
63
64 *outwalk = '\0';
65 return sstrdup(buffer);
66 }
0 /* g_utf8_make_valid.c - Coerce string into UTF-8
1 *
2 * Copyright (C) 1999 Tom Tromey
3 * Copyright (C) 2000 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "libi3.h"
20
21 #include <string.h>
22 #include <glib.h>
23
24 /* Copied from:
25 * https://gitlab.gnome.org/GNOME/glib/blob/f928dfdf57bf92c883b53b16d7a9d49add504f52/glib/gutf8.c#L1752-1815 */
26 /* clang-format off */
27 #if !HAS_G_UTF8_MAKE_VALID
28 /**
29 * g_utf8_make_valid:
30 * @str: string to coerce into UTF-8
31 * @len: the maximum length of @str to use, in bytes. If @len < 0,
32 * then the string is nul-terminated.
33 *
34 * If the provided string is valid UTF-8, return a copy of it. If not,
35 * return a copy in which bytes that could not be interpreted as valid Unicode
36 * are replaced with the Unicode replacement character (U+FFFD).
37 *
38 * For example, this is an appropriate function to use if you have received
39 * a string that was incorrectly declared to be UTF-8, and you need a valid
40 * UTF-8 version of it that can be logged or displayed to the user, with the
41 * assumption that it is close enough to ASCII or UTF-8 to be mostly
42 * readable as-is.
43 *
44 * Returns: (transfer full): a valid UTF-8 string whose content resembles @str
45 *
46 * Since: 2.52
47 */
48 gchar *
49 g_utf8_make_valid (const gchar *str,
50 gssize len)
51 {
52 GString *string;
53 const gchar *remainder, *invalid;
54 gsize remaining_bytes, valid_bytes;
55
56 g_return_val_if_fail (str != NULL, NULL);
57
58 if (len < 0)
59 len = strlen (str);
60
61 string = NULL;
62 remainder = str;
63 remaining_bytes = len;
64
65 while (remaining_bytes != 0)
66 {
67 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
68 break;
69 valid_bytes = invalid - remainder;
70
71 if (string == NULL)
72 string = g_string_sized_new (remaining_bytes);
73
74 g_string_append_len (string, remainder, valid_bytes);
75 /* append U+FFFD REPLACEMENT CHARACTER */
76 g_string_append (string, "\357\277\275");
77
78 remaining_bytes -= valid_bytes + 1;
79 remainder = invalid + 1;
80 }
81
82 if (string == NULL)
83 return g_strndup (str, len);
84
85 g_string_append_len (string, remainder, remaining_bytes);
86 g_string_append_c (string, '\0');
87
88 g_assert (g_utf8_validate (string->str, -1, NULL));
89
90 return g_string_free (string, FALSE);
91 }
92 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <string.h>
12
13 #include "queue.h"
14 struct Colorpixel {
15 char hex[8];
16 uint32_t pixel;
17
18 SLIST_ENTRY(Colorpixel)
19 colorpixels;
20 };
21
22 SLIST_HEAD(colorpixel_head, Colorpixel)
23 colorpixels;
24
25 /*
26 * Returns the colorpixel to use for the given hex color (think of HTML).
27 *
28 * The hex_color has to start with #, for example #FF00FF.
29 *
30 * NOTE that get_colorpixel() does _NOT_ check the given color code for validity.
31 * This has to be done by the caller.
32 *
33 */
34 uint32_t get_colorpixel(const char *hex) {
35 char strgroups[3][3] = {
36 {hex[1], hex[2], '\0'},
37 {hex[3], hex[4], '\0'},
38 {hex[5], hex[6], '\0'}};
39 uint8_t r = strtol(strgroups[0], NULL, 16);
40 uint8_t g = strtol(strgroups[1], NULL, 16);
41 uint8_t b = strtol(strgroups[2], NULL, 16);
42
43 /* Shortcut: if our screen is true color, no need to do a roundtrip to X11 */
44 if (root_screen == NULL || root_screen->root_depth == 24 || root_screen->root_depth == 32) {
45 return (0xFFUL << 24) | (r << 16 | g << 8 | b);
46 }
47
48 /* Lookup this colorpixel in the cache */
49 struct Colorpixel *colorpixel;
50 SLIST_FOREACH(colorpixel, &(colorpixels), colorpixels) {
51 if (strcmp(colorpixel->hex, hex) == 0)
52 return colorpixel->pixel;
53 }
54
55 #define RGB_8_TO_16(i) (65535 * ((i)&0xFF) / 255)
56 int r16 = RGB_8_TO_16(r);
57 int g16 = RGB_8_TO_16(g);
58 int b16 = RGB_8_TO_16(b);
59
60 xcb_alloc_color_reply_t *reply;
61
62 reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, root_screen->default_colormap, r16, g16, b16),
63 NULL);
64
65 if (!reply) {
66 LOG("Could not allocate color\n");
67 exit(1);
68 }
69
70 uint32_t pixel = reply->pixel;
71 free(reply);
72
73 /* Store the result in the cache */
74 struct Colorpixel *cache_pixel = scalloc(1, sizeof(struct Colorpixel));
75
76 strncpy(cache_pixel->hex, hex, 7);
77 cache_pixel->hex[7] = '\0';
78
79 cache_pixel->pixel = pixel;
80
81 SLIST_INSERT_HEAD(&(colorpixels), cache_pixel, colorpixels);
82
83 return pixel;
84 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/stat.h>
12
13 /*
14 * Checks if the given path exists by calling stat().
15 *
16 */
17 static bool path_exists(const char *path) {
18 struct stat buf;
19 return (stat(path, &buf) == 0);
20 }
21
22 /*
23 * Get the path of the first configuration file found. If override_configpath is
24 * specified, that path is returned and saved for further calls. Otherwise,
25 * checks the home directory first, then the system directory, always taking
26 * into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
27 * $XDG_CONFIG_DIRS).
28 *
29 */
30 char *get_config_path(const char *override_configpath, bool use_system_paths) {
31 char *xdg_config_home, *xdg_config_dirs, *config_path;
32
33 static const char *saved_configpath = NULL;
34
35 if (override_configpath != NULL) {
36 saved_configpath = override_configpath;
37 return sstrdup(saved_configpath);
38 }
39
40 if (saved_configpath != NULL) {
41 return sstrdup(saved_configpath);
42 }
43
44 /* 1: check for $XDG_CONFIG_HOME/i3/config */
45 if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) {
46 xdg_config_home = "~/.config";
47 }
48
49 xdg_config_home = resolve_tilde(xdg_config_home);
50 sasprintf(&config_path, "%s/i3/config", xdg_config_home);
51 free(xdg_config_home);
52
53 if (path_exists(config_path)) {
54 return config_path;
55 }
56 free(config_path);
57
58 /* 2: check the traditional path under the home directory */
59 config_path = resolve_tilde("~/.i3/config");
60 if (path_exists(config_path)) {
61 return config_path;
62 }
63 free(config_path);
64
65 /* The below paths are considered system-level, and can be skipped if the
66 * caller only wants user-level configs. */
67 if (!use_system_paths) {
68 return NULL;
69 }
70
71 /* 3: check for $XDG_CONFIG_DIRS/i3/config */
72 if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL) {
73 xdg_config_dirs = SYSCONFDIR "/xdg";
74 }
75
76 char *buf = sstrdup(xdg_config_dirs);
77 char *tok = strtok(buf, ":");
78 while (tok != NULL) {
79 tok = resolve_tilde(tok);
80 sasprintf(&config_path, "%s/i3/config", tok);
81 free(tok);
82 if (path_exists(config_path)) {
83 free(buf);
84 return config_path;
85 }
86 free(config_path);
87 tok = strtok(NULL, ":");
88 }
89 free(buf);
90
91 /* 4: check the traditional path under /etc */
92 config_path = SYSCONFDIR "/i3/config";
93 if (path_exists(config_path)) {
94 return sstrdup(config_path);
95 }
96
97 return NULL;
98 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <unistd.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <errno.h>
15
16 /*
17 * This function returns the absolute path to the executable it is running in.
18 *
19 * The implementation follows https://stackoverflow.com/a/933996/712014
20 *
21 * Returned value must be freed by the caller.
22 */
23 char *get_exe_path(const char *argv0) {
24 size_t destpath_size = 1024;
25 size_t tmp_size = 1024;
26 char *destpath = smalloc(destpath_size);
27 char *tmp = smalloc(tmp_size);
28
29 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
30 /* Linux and Debian/kFreeBSD provide /proc/self/exe */
31 #if defined(__linux__) || defined(__FreeBSD_kernel__)
32 const char *exepath = "/proc/self/exe";
33 #elif defined(__FreeBSD__)
34 const char *exepath = "/proc/curproc/file";
35 #endif
36 ssize_t linksize;
37
38 while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
39 destpath_size = destpath_size * 2;
40 destpath = srealloc(destpath, destpath_size);
41 }
42 if (linksize != -1) {
43 /* readlink() does not NULL-terminate strings, so we have to. */
44 destpath[linksize] = '\0';
45 free(tmp);
46 return destpath;
47 }
48 #endif
49
50 /* argv[0] is most likely a full path if it starts with a slash. */
51 if (argv0[0] == '/') {
52 free(tmp);
53 free(destpath);
54 return sstrdup(argv0);
55 }
56
57 /* if argv[0] contains a /, prepend the working directory */
58 if (strchr(argv0, '/') != NULL) {
59 char *retgcwd;
60 while ((retgcwd = getcwd(tmp, tmp_size)) == NULL && errno == ERANGE) {
61 tmp_size = tmp_size * 2;
62 tmp = srealloc(tmp, tmp_size);
63 }
64 if (retgcwd != NULL) {
65 free(destpath);
66 sasprintf(&destpath, "%s/%s", tmp, argv0);
67 free(tmp);
68 return destpath;
69 }
70 }
71
72 /* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
73 char *path = getenv("PATH");
74 if (path == NULL) {
75 /* _CS_PATH is typically something like "/bin:/usr/bin" */
76 while (confstr(_CS_PATH, tmp, tmp_size) > tmp_size) {
77 tmp_size = tmp_size * 2;
78 tmp = srealloc(tmp, tmp_size);
79 }
80 sasprintf(&path, ":%s", tmp);
81 } else {
82 path = sstrdup(path);
83 }
84 const char *component;
85 char *str = path;
86 while (1) {
87 if ((component = strtok(str, ":")) == NULL)
88 break;
89 str = NULL;
90 free(destpath);
91 sasprintf(&destpath, "%s/%s", component, argv0);
92 /* Of course this is not 100% equivalent to actually exec()ing the
93 * binary, but meh. */
94 if (access(destpath, X_OK) == 0) {
95 free(path);
96 free(tmp);
97 return destpath;
98 }
99 }
100 free(destpath);
101 free(path);
102 free(tmp);
103
104 /* Last resort: maybe it’s in /usr/bin? */
105 return sstrdup("/usr/bin/i3-nagbar");
106 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <xcb/xcb.h>
12 #include <xcb/xcb_keysyms.h>
13
14 /*
15 * All-in-one function which returns the modifier mask (XCB_MOD_MASK_*) for the
16 * given keysymbol, for example for XCB_NUM_LOCK (usually configured to mod2).
17 *
18 * This function initiates one round-trip. Use get_mod_mask_for() directly if
19 * you already have the modifier mapping and key symbols.
20 *
21 */
22 uint32_t aio_get_mod_mask_for(uint32_t keysym, xcb_key_symbols_t *symbols) {
23 xcb_get_modifier_mapping_cookie_t cookie;
24 xcb_get_modifier_mapping_reply_t *modmap_r;
25
26 xcb_flush(conn);
27
28 /* Get the current modifier mapping (this is blocking!) */
29 cookie = xcb_get_modifier_mapping(conn);
30 if (!(modmap_r = xcb_get_modifier_mapping_reply(conn, cookie, NULL)))
31 return 0;
32
33 uint32_t result = get_mod_mask_for(keysym, symbols, modmap_r);
34 free(modmap_r);
35 return result;
36 }
37
38 /*
39 * Returns the modifier mask (XCB_MOD_MASK_*) for the given keysymbol, for
40 * example for XCB_NUM_LOCK (usually configured to mod2).
41 *
42 * This function does not initiate any round-trips.
43 *
44 */
45 uint32_t get_mod_mask_for(uint32_t keysym,
46 xcb_key_symbols_t *symbols,
47 xcb_get_modifier_mapping_reply_t *modmap_reply) {
48 xcb_keycode_t *codes, *modmap;
49 xcb_keycode_t mod_code;
50
51 modmap = xcb_get_modifier_mapping_keycodes(modmap_reply);
52
53 /* Get the list of keycodes for the given symbol */
54 if (!(codes = xcb_key_symbols_get_keycode(symbols, keysym)))
55 return 0;
56
57 /* Loop through all modifiers (Mod1-Mod5, Shift, Control, Lock) */
58 for (int mod = 0; mod < 8; mod++)
59 for (int j = 0; j < modmap_reply->keycodes_per_modifier; j++) {
60 /* Store the current keycode (for modifier 'mod') */
61 mod_code = modmap[(mod * modmap_reply->keycodes_per_modifier) + j];
62 /* Check if that keycode is in the list of previously resolved
63 * keycodes for our symbol. If so, return the modifier mask. */
64 for (xcb_keycode_t *code = codes; *code; code++) {
65 if (*code != mod_code)
66 continue;
67
68 free(codes);
69 /* This corresponds to the XCB_MOD_MASK_* constants */
70 return (1 << mod);
71 }
72 }
73
74 return 0;
75 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <assert.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdbool.h>
14 #include <err.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <pwd.h>
18 #include <unistd.h>
19 #include <err.h>
20
21 /*
22 * Returns the name of a temporary file with the specified prefix.
23 *
24 */
25 char *get_process_filename(const char *prefix) {
26 /* dir stores the directory path for this and all subsequent calls so that
27 * we only create a temporary directory once per i3 instance. */
28 static char *dir = NULL;
29 if (dir == NULL) {
30 /* Check if XDG_RUNTIME_DIR is set. If so, we use XDG_RUNTIME_DIR/i3 */
31 if ((dir = getenv("XDG_RUNTIME_DIR"))) {
32 char *tmp;
33 sasprintf(&tmp, "%s/i3", dir);
34 dir = tmp;
35 struct stat buf;
36 if (stat(dir, &buf) != 0) {
37 if (mkdir(dir, 0700) == -1) {
38 warn("Could not mkdir(%s)", dir);
39 errx(EXIT_FAILURE, "Check permissions of $XDG_RUNTIME_DIR = '%s'",
40 getenv("XDG_RUNTIME_DIR"));
41 perror("mkdir()");
42 return NULL;
43 }
44 }
45 } else {
46 /* If not, we create a (secure) temp directory using the template
47 * /tmp/i3-<user>.XXXXXX */
48 struct passwd *pw = getpwuid(getuid());
49 const char *username = pw ? pw->pw_name : "unknown";
50 sasprintf(&dir, "/tmp/i3-%s.XXXXXX", username);
51 /* mkdtemp modifies dir */
52 if (mkdtemp(dir) == NULL) {
53 perror("mkdtemp()");
54 return NULL;
55 }
56 }
57 }
58 char *filename;
59 sasprintf(&filename, "%s/%s.%d", dir, prefix, getpid());
60 return filename;
61 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 /*
10 * Returns the visual type associated with the given screen.
11 *
12 */
13 xcb_visualtype_t *get_visualtype(xcb_screen_t *screen) {
14 xcb_depth_iterator_t depth_iter;
15 for (depth_iter = xcb_screen_allowed_depths_iterator(screen);
16 depth_iter.rem;
17 xcb_depth_next(&depth_iter)) {
18 xcb_visualtype_iterator_t visual_iter;
19 for (visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
20 visual_iter.rem;
21 xcb_visualtype_next(&visual_iter)) {
22 if (screen->root_visual == visual_iter.data->visual_id)
23 return visual_iter.data;
24 }
25 }
26 return NULL;
27 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <string.h>
13 #include <err.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17
18 /*
19 * Connects to the i3 IPC socket and returns the file descriptor for the
20 * socket. die()s if anything goes wrong.
21 *
22 */
23 int ipc_connect(const char *socket_path) {
24 char *path = NULL;
25 if (socket_path != NULL) {
26 path = sstrdup(socket_path);
27 }
28
29 if (path == NULL) {
30 if ((path = getenv("I3SOCK")) != NULL) {
31 path = sstrdup(path);
32 }
33 }
34
35 if (path == NULL) {
36 path = root_atom_contents("I3_SOCKET_PATH", NULL, 0);
37 }
38
39 if (path == NULL) {
40 path = sstrdup("/tmp/i3-ipc.sock");
41 }
42
43 int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
44 if (sockfd == -1)
45 err(EXIT_FAILURE, "Could not create socket");
46
47 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
48
49 struct sockaddr_un addr;
50 memset(&addr, 0, sizeof(struct sockaddr_un));
51 addr.sun_family = AF_LOCAL;
52 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
53 if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
54 err(EXIT_FAILURE, "Could not connect to i3 on socket %s", path);
55 free(path);
56 return sockfd;
57 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <inttypes.h>
16
17 #include <i3/ipc.h>
18
19 /*
20 * Reads a message from the given socket file descriptor and stores its length
21 * (reply_length) as well as a pointer to its contents (reply).
22 *
23 * Returns -1 when read() fails, errno will remain.
24 * Returns -2 on EOF.
25 * Returns -3 when the IPC protocol is violated (invalid magic, unexpected
26 * message type, EOF instead of a message). Additionally, the error will be
27 * printed to stderr.
28 * Returns 0 on success.
29 *
30 */
31 int ipc_recv_message(int sockfd, uint32_t *message_type,
32 uint32_t *reply_length, uint8_t **reply) {
33 /* Read the message header first */
34 const uint32_t to_read = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) + sizeof(uint32_t);
35 char msg[to_read];
36 char *walk = msg;
37
38 uint32_t read_bytes = 0;
39 while (read_bytes < to_read) {
40 int n = read(sockfd, msg + read_bytes, to_read - read_bytes);
41 if (n == -1)
42 return -1;
43 if (n == 0) {
44 if (read_bytes == 0) {
45 return -2;
46 } else {
47 ELOG("IPC: unexpected EOF while reading header, got %" PRIu32 " bytes, want %" PRIu32 " bytes\n",
48 read_bytes, to_read);
49 return -3;
50 }
51 }
52
53 read_bytes += n;
54 }
55
56 if (memcmp(walk, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) {
57 ELOG("IPC: invalid magic in header, got \"%.*s\", want \"%s\"\n",
58 (int)strlen(I3_IPC_MAGIC), walk, I3_IPC_MAGIC);
59 return -3;
60 }
61
62 walk += strlen(I3_IPC_MAGIC);
63 memcpy(reply_length, walk, sizeof(uint32_t));
64 walk += sizeof(uint32_t);
65 if (message_type != NULL)
66 memcpy(message_type, walk, sizeof(uint32_t));
67
68 *reply = smalloc(*reply_length);
69
70 read_bytes = 0;
71 while (read_bytes < *reply_length) {
72 const int n = read(sockfd, *reply + read_bytes, *reply_length - read_bytes);
73 if (n == -1) {
74 if (errno == EINTR || errno == EAGAIN)
75 continue;
76 return -1;
77 }
78 if (n == 0) {
79 ELOG("IPC: unexpected EOF while reading payload, got %" PRIu32 " bytes, want %" PRIu32 " bytes\n",
80 read_bytes, *reply_length);
81 return -3;
82 }
83
84 read_bytes += n;
85 }
86
87 return 0;
88 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdint.h>
13 #include <err.h>
14 #include <errno.h>
15
16 #include <i3/ipc.h>
17
18 /*
19 * Formats a message (payload) of the given size and type and sends it to i3 via
20 * the given socket file descriptor.
21 *
22 * Returns -1 when write() fails, errno will remain.
23 * Returns 0 on success.
24 *
25 */
26 int ipc_send_message(int sockfd, const uint32_t message_size,
27 const uint32_t message_type, const uint8_t *payload) {
28 const i3_ipc_header_t header = {
29 /* We don’t use I3_IPC_MAGIC because it’s a 0-terminated C string. */
30 .magic = {'i', '3', '-', 'i', 'p', 'c'},
31 .size = message_size,
32 .type = message_type};
33
34 if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
35 return -1;
36
37 if (writeall(sockfd, payload, message_size) == -1)
38 return -1;
39
40 return 0;
41 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <string.h>
10 #include <stdbool.h>
11
12 /*
13 * Returns true if this version of i3 is a debug build (anything which is not a
14 * release version), based on the git version number.
15 *
16 */
17 bool is_debug_build(void) {
18 /* i3_version contains either something like this:
19 * "4.0.2 (2011-11-11, branch "release")".
20 * or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".
21 *
22 * So we check for the offset of the first opening round bracket to
23 * determine whether this is a git version or a release version. */
24 return ((strchr(I3_VERSION, '(') - I3_VERSION) > 10);
25 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13
14 /*
15 * Emulates mkdir -p (creates any missing folders)
16 *
17 */
18
19 #if !defined(__sun)
20 int mkdirp(const char *path, mode_t mode) {
21 if (mkdir(path, mode) == 0)
22 return 0;
23 if (errno == EEXIST) {
24 struct stat st;
25 /* Check that the named file actually is a directory. */
26 if (stat(path, &st)) {
27 ELOG("stat(%s) failed: %s\n", path, strerror(errno));
28 return -1;
29 }
30 if (!S_ISDIR(st.st_mode)) {
31 ELOG("mkdir(%s) failed: %s\n", path, strerror(ENOTDIR));
32 return -1;
33 }
34 return 0;
35 } else if (errno != ENOENT) {
36 ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
37 return -1;
38 }
39 char *copy = sstrdup(path);
40 /* strip trailing slashes, if any */
41 while (copy[strlen(copy) - 1] == '/')
42 copy[strlen(copy) - 1] = '\0';
43
44 char *sep = strrchr(copy, '/');
45 if (sep == NULL) {
46 free(copy);
47 return -1;
48 }
49 *sep = '\0';
50 int result = -1;
51 if (mkdirp(copy, mode) == 0)
52 result = mkdirp(path, mode);
53 free(copy);
54
55 return result;
56 }
57 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <err.h>
10 #include <glob.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 /*
15 * This function resolves ~ in pathnames.
16 * It may resolve wildcards in the first part of the path, but if no match
17 * or multiple matches are found, it just returns a copy of path as given.
18 *
19 */
20 char *resolve_tilde(const char *path) {
21 static glob_t globbuf;
22 char *head, *tail, *result;
23
24 tail = strchr(path, '/');
25 head = sstrndup(path, tail ? (size_t)(tail - path) : strlen(path));
26
27 int res = glob(head, GLOB_TILDE, NULL, &globbuf);
28 free(head);
29 /* no match, or many wildcard matches are bad */
30 if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
31 result = sstrdup(path);
32 else if (res != 0) {
33 err(EXIT_FAILURE, "glob() failed");
34 } else {
35 head = globbuf.gl_pathv[0];
36 result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1, 1);
37 strcpy(result, head);
38 if (tail) {
39 strcat(result, tail);
40 }
41 }
42 globfree(&globbuf);
43
44 return result;
45 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdbool.h>
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <math.h>
15
16 #include <xcb/xcb.h>
17 #include <xcb/xcb_aux.h>
18
19 /*
20 * Try to get the contents of the given atom (for example I3_SOCKET_PATH) from
21 * the X11 root window and return NULL if it doesn’t work.
22 *
23 * If the provided XCB connection is NULL, a new connection will be
24 * established.
25 *
26 * The memory for the contents is dynamically allocated and has to be
27 * free()d by the caller.
28 *
29 */
30 char *root_atom_contents(const char *atomname, xcb_connection_t *provided_conn, int screen) {
31 xcb_intern_atom_cookie_t atom_cookie;
32 xcb_intern_atom_reply_t *atom_reply;
33 char *content = NULL;
34 size_t content_max_words = 256;
35 xcb_connection_t *conn = provided_conn;
36
37 if (provided_conn == NULL &&
38 ((conn = xcb_connect(NULL, &screen)) == NULL ||
39 xcb_connection_has_error(conn))) {
40 return NULL;
41 }
42
43 atom_cookie = xcb_intern_atom(conn, 0, strlen(atomname), atomname);
44
45 xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen);
46 xcb_window_t root = root_screen->root;
47
48 atom_reply = xcb_intern_atom_reply(conn, atom_cookie, NULL);
49 if (atom_reply == NULL) {
50 goto out_conn;
51 }
52
53 xcb_get_property_cookie_t prop_cookie;
54 xcb_get_property_reply_t *prop_reply;
55 prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
56 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
57 prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
58 if (prop_reply == NULL) {
59 goto out_atom;
60 }
61 if (xcb_get_property_value_length(prop_reply) > 0 && prop_reply->bytes_after > 0) {
62 /* We received an incomplete value. Ask again but with a properly
63 * adjusted size. */
64 content_max_words += ceil(prop_reply->bytes_after / 4.0);
65 /* Repeat the request, with adjusted size */
66 free(prop_reply);
67 prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
68 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
69 prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
70 if (prop_reply == NULL) {
71 goto out_atom;
72 }
73 }
74 if (xcb_get_property_value_length(prop_reply) == 0) {
75 goto out;
76 }
77 if (prop_reply->type == XCB_ATOM_CARDINAL) {
78 /* We treat a CARDINAL as a >= 32-bit unsigned int. The only CARDINAL
79 * we query is I3_PID, which is 32-bit. */
80 sasprintf(&content, "%u", *((unsigned int *)xcb_get_property_value(prop_reply)));
81 } else {
82 sasprintf(&content, "%.*s", xcb_get_property_value_length(prop_reply),
83 (char *)xcb_get_property_value(prop_reply));
84 }
85
86 out:
87 free(prop_reply);
88 out_atom:
89 free(atom_reply);
90 out_conn:
91 if (provided_conn == NULL)
92 xcb_disconnect(conn);
93 return content;
94 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <stdarg.h>
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <err.h>
15 #include <errno.h>
16
17 /*
18 * The s* functions (safe) are wrappers around malloc, strdup, …, which exits if one of
19 * the called functions returns NULL, meaning that there is no more memory available
20 *
21 */
22 void *smalloc(size_t size) {
23 void *result = malloc(size);
24 if (result == NULL)
25 err(EXIT_FAILURE, "malloc(%zd)", size);
26 return result;
27 }
28
29 void *scalloc(size_t num, size_t size) {
30 void *result = calloc(num, size);
31 if (result == NULL)
32 err(EXIT_FAILURE, "calloc(%zd, %zd)", num, size);
33 return result;
34 }
35
36 void *srealloc(void *ptr, size_t size) {
37 void *result = realloc(ptr, size);
38 if (result == NULL && size > 0)
39 err(EXIT_FAILURE, "realloc(%zd)", size);
40 return result;
41 }
42
43 char *sstrdup(const char *str) {
44 char *result = strdup(str);
45 if (result == NULL)
46 err(EXIT_FAILURE, "strdup()");
47 return result;
48 }
49
50 char *sstrndup(const char *str, size_t size) {
51 char *result = strndup(str, size);
52 if (result == NULL)
53 err(EXIT_FAILURE, "strndup()");
54 return result;
55 }
56
57 int sasprintf(char **strp, const char *fmt, ...) {
58 va_list args;
59 int result;
60
61 va_start(args, fmt);
62 if ((result = vasprintf(strp, fmt, args)) == -1)
63 err(EXIT_FAILURE, "asprintf(%s)", fmt);
64 va_end(args);
65 return result;
66 }
67
68 ssize_t writeall(int fd, const void *buf, size_t count) {
69 size_t written = 0;
70
71 while (written < count) {
72 const ssize_t n = write(fd, ((char *)buf) + written, count - written);
73 if (n == -1) {
74 if (errno == EINTR || errno == EAGAIN)
75 continue;
76 return n;
77 }
78 written += (size_t)n;
79 }
80
81 return written;
82 }
83
84 ssize_t writeall_nonblock(int fd, const void *buf, size_t count) {
85 size_t written = 0;
86
87 while (written < count) {
88 const ssize_t n = write(fd, ((char *)buf) + written, count - written);
89 if (n == -1) {
90 if (errno == EAGAIN) {
91 return written;
92 } else if (errno == EINTR) {
93 continue;
94 } else {
95 return n;
96 }
97 }
98 written += (size_t)n;
99 }
100 return written;
101 }
102
103 ssize_t swrite(int fd, const void *buf, size_t count) {
104 ssize_t n;
105
106 n = writeall(fd, buf, count);
107 if (n == -1)
108 err(EXIT_FAILURE, "Failed to write %d", fd);
109 else
110 return n;
111 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * string.c: Define an i3String type to automagically handle UTF-8/UCS-2
7 * conversions. Some font backends need UCS-2 (X core fonts),
8 * others want UTF-8 (Pango).
9 *
10 */
11 #include "libi3.h"
12
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include <glib.h>
17
18 struct _i3String {
19 char *utf8;
20 xcb_char2b_t *ucs2;
21 size_t num_glyphs;
22 size_t num_bytes;
23 bool pango_markup;
24 };
25
26 /*
27 * Build an i3String from an UTF-8 encoded string.
28 * Returns the newly-allocated i3String.
29 *
30 */
31 i3String *i3string_from_utf8(const char *from_utf8) {
32 return i3string_from_utf8_with_length(from_utf8, -1);
33 }
34
35 /*
36 * Build an i3String from an UTF-8 encoded string in Pango markup.
37 *
38 */
39 i3String *i3string_from_markup(const char *from_markup) {
40 i3String *str = i3string_from_utf8(from_markup);
41
42 /* Set the markup flag */
43 str->pango_markup = true;
44
45 return str;
46 }
47
48 /*
49 * Build an i3String from an UTF-8 encoded string with fixed length.
50 * To be used when no proper NULL-termination is available.
51 * Returns the newly-allocated i3String.
52 *
53 */
54 i3String *i3string_from_utf8_with_length(const char *from_utf8, ssize_t num_bytes) {
55 i3String *str = scalloc(1, sizeof(i3String));
56
57 /* g_utf8_make_valid NULL-terminates the string. */
58 str->utf8 = g_utf8_make_valid(from_utf8, num_bytes);
59
60 /* num_bytes < 0 means NULL-terminated string, need to calculate length */
61 str->num_bytes = num_bytes < 0 ? strlen(str->utf8) : (size_t)num_bytes;
62
63 return str;
64 }
65
66 /*
67 * Build an i3String from an UTF-8 encoded string in Pango markup with fixed
68 * length.
69 *
70 */
71 i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes) {
72 i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes);
73
74 /* set the markup flag */
75 str->pango_markup = true;
76
77 return str;
78 }
79
80 /*
81 * Build an i3String from an UCS-2 encoded string.
82 * Returns the newly-allocated i3String.
83 *
84 */
85 i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
86 i3String *str = scalloc(1, sizeof(i3String));
87
88 /* Copy the actual text to our i3String */
89 str->ucs2 = scalloc(num_glyphs, sizeof(xcb_char2b_t));
90 memcpy(str->ucs2, from_ucs2, num_glyphs * sizeof(xcb_char2b_t));
91
92 /* Store the length */
93 str->num_glyphs = num_glyphs;
94
95 str->utf8 = NULL;
96 str->num_bytes = 0;
97
98 return str;
99 }
100
101 /*
102 * Copies the given i3string.
103 * Note that this will not free the source string.
104 */
105 i3String *i3string_copy(i3String *str) {
106 i3String *copy = i3string_from_utf8(i3string_as_utf8(str));
107 copy->pango_markup = str->pango_markup;
108 return copy;
109 }
110
111 /*
112 * Free an i3String.
113 *
114 */
115 void i3string_free(i3String *str) {
116 if (str == NULL)
117 return;
118 free(str->utf8);
119 free(str->ucs2);
120 free(str);
121 }
122
123 static void i3string_ensure_utf8(i3String *str) {
124 if (str->utf8 != NULL)
125 return;
126 if ((str->utf8 = convert_ucs2_to_utf8(str->ucs2, str->num_glyphs)) != NULL)
127 str->num_bytes = strlen(str->utf8);
128 }
129
130 static void i3string_ensure_ucs2(i3String *str) {
131 if (str->ucs2 != NULL)
132 return;
133 str->ucs2 = convert_utf8_to_ucs2(str->utf8, &str->num_glyphs);
134 }
135
136 /*
137 * Returns the UTF-8 encoded version of the i3String.
138 *
139 */
140 const char *i3string_as_utf8(i3String *str) {
141 i3string_ensure_utf8(str);
142 return str->utf8;
143 }
144
145 /*
146 * Returns the UCS-2 encoded version of the i3String.
147 *
148 */
149 const xcb_char2b_t *i3string_as_ucs2(i3String *str) {
150 i3string_ensure_ucs2(str);
151 return str->ucs2;
152 }
153
154 /*
155 * Returns the number of bytes (UTF-8 encoded) in an i3String.
156 *
157 */
158 size_t i3string_get_num_bytes(i3String *str) {
159 i3string_ensure_utf8(str);
160 return str->num_bytes;
161 }
162
163 /*
164 * Whether the given i3String is in Pango markup.
165 */
166 bool i3string_is_markup(i3String *str) {
167 return str->pango_markup;
168 }
169
170 /*
171 * Set whether the i3String should use Pango markup.
172 */
173 void i3string_set_markup(i3String *str, bool pango_markup) {
174 str->pango_markup = pango_markup;
175 }
176
177 /*
178 * Escape pango markup characters in the given string.
179 */
180 i3String *i3string_escape_markup(i3String *str) {
181 const char *text = i3string_as_utf8(str);
182 char *escaped = g_markup_escape_text(text, -1);
183 i3String *result = i3string_from_utf8(escaped);
184 free(escaped);
185 return result;
186 }
187
188 /*
189 * Returns the number of glyphs in an i3String.
190 *
191 */
192 size_t i3string_get_num_glyphs(i3String *str) {
193 i3string_ensure_ucs2(str);
194 return str->num_glyphs;
195 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <sys/types.h>
10 #include <string.h>
11
12 #if defined(__APPLE__)
13
14 /*
15 * Taken from FreeBSD
16 * Returns a pointer to a new string which is a duplicate of the
17 * string, but only copies at most n characters.
18 *
19 */
20 char *strndup(const char *str, size_t n) {
21 size_t len;
22 char *copy;
23
24 for (len = 0; len < n && str[len]; len++)
25 continue;
26
27 copy = smalloc(len + 1);
28 memcpy(copy, str, len);
29 copy[len] = '\0';
30 return (copy);
31 }
32
33 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "libi3.h"
8
9 #include <err.h>
10 #include <errno.h>
11 #include <iconv.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 static iconv_t utf8_conversion_descriptor = (iconv_t)-1;
16 static iconv_t ucs2_conversion_descriptor = (iconv_t)-1;
17
18 /*
19 * Converts the given string to UTF-8 from UCS-2 big endian. The return value
20 * must be freed after use.
21 *
22 */
23 char *convert_ucs2_to_utf8(xcb_char2b_t *text, size_t num_glyphs) {
24 /* Allocate the output buffer (UTF-8 is at most 4 bytes per glyph) */
25 size_t buffer_size = num_glyphs * 4 + 1;
26 char *buffer = scalloc(buffer_size, 1);
27
28 /* We need to use an additional pointer, because iconv() modifies it */
29 char *output = buffer;
30 size_t output_size = buffer_size - 1;
31
32 if (utf8_conversion_descriptor == (iconv_t)-1) {
33 /* Get a new conversion descriptor */
34 utf8_conversion_descriptor = iconv_open("UTF-8", "UCS-2BE");
35 if (utf8_conversion_descriptor == (iconv_t)-1)
36 err(EXIT_FAILURE, "Error opening the conversion context");
37 } else {
38 /* Reset the existing conversion descriptor */
39 iconv(utf8_conversion_descriptor, NULL, NULL, NULL, NULL);
40 }
41
42 /* Do the conversion */
43 size_t input_len = num_glyphs * sizeof(xcb_char2b_t);
44 size_t rc = iconv(utf8_conversion_descriptor, (char **)&text,
45 &input_len, &output, &output_size);
46 if (rc == (size_t)-1) {
47 perror("Converting to UTF-8 failed");
48 free(buffer);
49 return NULL;
50 }
51
52 return buffer;
53 }
54
55 /*
56 * Converts the given string to UCS-2 big endian for use with
57 * xcb_image_text_16(). The amount of real glyphs is stored in real_strlen,
58 * a buffer containing the UCS-2 encoded string (16 bit per glyph) is
59 * returned. It has to be freed when done.
60 *
61 */
62 xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen) {
63 /* Calculate the input buffer size (UTF-8 is strlen-safe) */
64 size_t input_size = strlen(input);
65
66 /* Calculate the output buffer size and allocate the buffer */
67 size_t buffer_size = input_size * sizeof(xcb_char2b_t);
68 xcb_char2b_t *buffer = smalloc(buffer_size);
69
70 /* We need to use an additional pointer, because iconv() modifies it */
71 size_t output_size = buffer_size;
72 xcb_char2b_t *output = buffer;
73
74 if (ucs2_conversion_descriptor == (iconv_t)-1) {
75 /* Get a new conversion descriptor */
76 ucs2_conversion_descriptor = iconv_open("UCS-2BE", "UTF-8");
77 if (ucs2_conversion_descriptor == (iconv_t)-1)
78 err(EXIT_FAILURE, "Error opening the conversion context");
79 } else {
80 /* Reset the existing conversion descriptor */
81 iconv(ucs2_conversion_descriptor, NULL, NULL, NULL, NULL);
82 }
83
84 /* Do the conversion */
85 size_t rc = iconv(ucs2_conversion_descriptor, &input, &input_size, (char **)&output, &output_size);
86 if (rc == (size_t)-1) {
87 perror("Converting to UCS-2 failed");
88 free(buffer);
89 if (real_strlen != NULL)
90 *real_strlen = 0;
91 return NULL;
92 }
93
94 /* Return the resulting string's length */
95 if (real_strlen != NULL)
96 *real_strlen = (buffer_size - output_size) / sizeof(xcb_char2b_t);
97
98 return buffer;
99 }
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
7 #
8 # DESCRIPTION
9 #
10 # FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
11 # added in between.
12 #
13 # If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
14 # CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
15 # FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
16 # FLAG.
17 #
18 # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
19 #
20 # LICENSE
21 #
22 # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
23 # Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
24 #
25 # This program is free software: you can redistribute it and/or modify it
26 # under the terms of the GNU General Public License as published by the
27 # Free Software Foundation, either version 3 of the License, or (at your
28 # option) any later version.
29 #
30 # This program is distributed in the hope that it will be useful, but
31 # WITHOUT ANY WARRANTY; without even the implied warranty of
32 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
33 # Public License for more details.
34 #
35 # You should have received a copy of the GNU General Public License along
36 # with this program. If not, see <http://www.gnu.org/licenses/>.
37 #
38 # As a special exception, the respective Autoconf Macro's copyright owner
39 # gives unlimited permission to copy, distribute and modify the configure
40 # scripts that are the output of Autoconf when processing the Macro. You
41 # need not follow the terms of the GNU General Public License when using
42 # or distributing such scripts, even though portions of the text of the
43 # Macro appear in them. The GNU General Public License (GPL) does govern
44 # all other use of the material that constitutes the Autoconf Macro.
45 #
46 # This special exception to the GPL applies to versions of the Autoconf
47 # Macro released by the Autoconf Archive. When you make and distribute a
48 # modified version of the Autoconf Macro, you may extend this special
49 # exception to the GPL to apply to your modified version as well.
50
51 #serial 6
52
53 AC_DEFUN([AX_APPEND_FLAG],
54 [dnl
55 AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
56 AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
57 AS_VAR_SET_IF(FLAGS,[
58 AS_CASE([" AS_VAR_GET(FLAGS) "],
59 [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
60 [
61 AS_VAR_APPEND(FLAGS,[" $1"])
62 AC_RUN_LOG([: FLAGS="$FLAGS"])
63 ])
64 ],
65 [
66 AS_VAR_SET(FLAGS,[$1])
67 AC_RUN_LOG([: FLAGS="$FLAGS"])
68 ])
69 AS_VAR_POPDEF([FLAGS])dnl
70 ])dnl AX_APPEND_FLAG
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_cflags_warn_all.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])]
7 # AX_CXXFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])]
8 # AX_FCFLAGS_WARN_ALL [(shellvar [,default, [A/NA]])]
9 #
10 # DESCRIPTION
11 #
12 # Try to find a compiler option that enables most reasonable warnings.
13 #
14 # For the GNU compiler it will be -Wall (and -ansi -pedantic) The result
15 # is added to the shellvar being CFLAGS, CXXFLAGS, or FCFLAGS by default.
16 #
17 # Currently this macro knows about the GCC, Solaris, Digital Unix, AIX,
18 # HP-UX, IRIX, NEC SX-5 (Super-UX 10), Cray J90 (Unicos 10.0.0.8), and
19 # Intel compilers. For a given compiler, the Fortran flags are much more
20 # experimental than their C equivalents.
21 #
22 # - $1 shell-variable-to-add-to : CFLAGS, CXXFLAGS, or FCFLAGS
23 # - $2 add-value-if-not-found : nothing
24 # - $3 action-if-found : add value to shellvariable
25 # - $4 action-if-not-found : nothing
26 #
27 # NOTE: These macros depend on AX_APPEND_FLAG.
28 #
29 # LICENSE
30 #
31 # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
32 # Copyright (c) 2010 Rhys Ulerich <rhys.ulerich@gmail.com>
33 #
34 # This program is free software; you can redistribute it and/or modify it
35 # under the terms of the GNU General Public License as published by the
36 # Free Software Foundation; either version 3 of the License, or (at your
37 # option) any later version.
38 #
39 # This program is distributed in the hope that it will be useful, but
40 # WITHOUT ANY WARRANTY; without even the implied warranty of
41 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
42 # Public License for more details.
43 #
44 # You should have received a copy of the GNU General Public License along
45 # with this program. If not, see <http://www.gnu.org/licenses/>.
46 #
47 # As a special exception, the respective Autoconf Macro's copyright owner
48 # gives unlimited permission to copy, distribute and modify the configure
49 # scripts that are the output of Autoconf when processing the Macro. You
50 # need not follow the terms of the GNU General Public License when using
51 # or distributing such scripts, even though portions of the text of the
52 # Macro appear in them. The GNU General Public License (GPL) does govern
53 # all other use of the material that constitutes the Autoconf Macro.
54 #
55 # This special exception to the GPL applies to versions of the Autoconf
56 # Macro released by the Autoconf Archive. When you make and distribute a
57 # modified version of the Autoconf Macro, you may extend this special
58 # exception to the GPL to apply to your modified version as well.
59
60 #serial 15
61
62 AC_DEFUN([AX_FLAGS_WARN_ALL],[dnl
63 AS_VAR_PUSHDEF([FLAGS],[_AC_LANG_PREFIX[]FLAGS])dnl
64 AS_VAR_PUSHDEF([VAR],[ac_cv_[]_AC_LANG_ABBREV[]flags_warn_all])dnl
65 AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for maximum warnings],
66 VAR,[VAR="no, unknown"
67 ac_save_[]FLAGS="$[]FLAGS"
68 for ac_arg dnl
69 in "-warn all % -warn all" dnl Intel
70 "-pedantic % -Wall" dnl GCC
71 "-xstrconst % -v" dnl Solaris C
72 "-std1 % -verbose -w0 -warnprotos" dnl Digital Unix
73 "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" dnl AIX
74 "-ansi -ansiE % -fullwarn" dnl IRIX
75 "+ESlit % +w1" dnl HP-UX C
76 "-Xc % -pvctl[,]fullmsg" dnl NEC SX-5 (Super-UX 10)
77 "-h conform % -h msglevel 2" dnl Cray C (Unicos)
78 #
79 do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
80 AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
81 [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
82 done
83 FLAGS="$ac_save_[]FLAGS"
84 ])
85 AS_VAR_POPDEF([FLAGS])dnl
86 AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
87 case ".$VAR" in
88 .ok|.ok,*) m4_ifvaln($3,$3) ;;
89 .|.no|.no,*) m4_default($4,[m4_ifval($2,[AX_APPEND_FLAG([$2], [$1])])]) ;;
90 *) m4_default($3,[AX_APPEND_FLAG([$VAR], [$1])]) ;;
91 esac
92 AS_VAR_POPDEF([VAR])dnl
93 ])dnl AX_FLAGS_WARN_ALL
94 dnl implementation tactics:
95 dnl the for-argument contains a list of options. The first part of
96 dnl these does only exist to detect the compiler - usually it is
97 dnl a global option to enable -ansi or -extrawarnings. All other
98 dnl compilers will fail about it. That was needed since a lot of
99 dnl compilers will give false positives for some option-syntax
100 dnl like -Woption or -Xoption as they think of it is a pass-through
101 dnl to later compile stages or something. The "%" is used as a
102 dnl delimiter. A non-option comment can be given after "%%" marks
103 dnl which will be shown but not added to the respective C/CXXFLAGS.
104
105 AC_DEFUN([AX_CFLAGS_WARN_ALL],[dnl
106 AC_LANG_PUSH([C])
107 AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
108 AC_LANG_POP([C])
109 ])
110
111 AC_DEFUN([AX_CXXFLAGS_WARN_ALL],[dnl
112 AC_LANG_PUSH([C++])
113 AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
114 AC_LANG_POP([C++])
115 ])
116
117 AC_DEFUN([AX_FCFLAGS_WARN_ALL],[dnl
118 AC_LANG_PUSH([Fortran])
119 AX_FLAGS_WARN_ALL([$1], [$2], [$3], [$4])
120 AC_LANG_POP([Fortran])
121 ])
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
7 #
8 # DESCRIPTION
9 #
10 # Check whether the given FLAG works with the current language's compiler
11 # or gives an error. (Warnings, however, are ignored)
12 #
13 # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
14 # success/failure.
15 #
16 # If EXTRA-FLAGS is defined, it is added to the current language's default
17 # flags (e.g. CFLAGS) when the check is done. The check is thus made with
18 # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
19 # force the compiler to issue an error when a bad flag is given.
20 #
21 # INPUT gives an alternative input source to AC_COMPILE_IFELSE.
22 #
23 # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
24 # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
25 #
26 # LICENSE
27 #
28 # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
29 # Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
30 #
31 # This program is free software: you can redistribute it and/or modify it
32 # under the terms of the GNU General Public License as published by the
33 # Free Software Foundation, either version 3 of the License, or (at your
34 # option) any later version.
35 #
36 # This program is distributed in the hope that it will be useful, but
37 # WITHOUT ANY WARRANTY; without even the implied warranty of
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
39 # Public License for more details.
40 #
41 # You should have received a copy of the GNU General Public License along
42 # with this program. If not, see <http://www.gnu.org/licenses/>.
43 #
44 # As a special exception, the respective Autoconf Macro's copyright owner
45 # gives unlimited permission to copy, distribute and modify the configure
46 # scripts that are the output of Autoconf when processing the Macro. You
47 # need not follow the terms of the GNU General Public License when using
48 # or distributing such scripts, even though portions of the text of the
49 # Macro appear in them. The GNU General Public License (GPL) does govern
50 # all other use of the material that constitutes the Autoconf Macro.
51 #
52 # This special exception to the GPL applies to versions of the Autoconf
53 # Macro released by the Autoconf Archive. When you make and distribute a
54 # modified version of the Autoconf Macro, you may extend this special
55 # exception to the GPL to apply to your modified version as well.
56
57 #serial 4
58
59 AC_DEFUN([AX_CHECK_COMPILE_FLAG],
60 [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
61 AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
62 AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
63 ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
64 _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
65 AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
66 [AS_VAR_SET(CACHEVAR,[yes])],
67 [AS_VAR_SET(CACHEVAR,[no])])
68 _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
69 AS_VAR_IF(CACHEVAR,yes,
70 [m4_default([$2], :)],
71 [m4_default([$3], :)])
72 AS_VAR_POPDEF([CACHEVAR])dnl
73 ])dnl AX_CHECK_COMPILE_FLAGS
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE])
7 #
8 # DESCRIPTION
9 #
10 # Check for the presence of an --enable-debug option to configure, with
11 # the specified default value used when the option is not present. Return
12 # the value in the variable $ax_enable_debug.
13 #
14 # Specifying 'yes' adds '-g -O0' to the compilation flags for all
15 # languages. Specifying 'info' adds '-g' to the compilation flags.
16 # Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to
17 # the linking flags. Otherwise, nothing is added.
18 #
19 # Define the variables listed in the second argument if debug is enabled,
20 # defaulting to no variables. Defines the variables listed in the third
21 # argument if debug is disabled, defaulting to NDEBUG. All lists of
22 # variables should be space-separated.
23 #
24 # If debug is not enabled, ensure AC_PROG_* will not add debugging flags.
25 # Should be invoked prior to any AC_PROG_* compiler checks.
26 #
27 # IS-RELEASE can be used to change the default to 'no' when making a
28 # release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it
29 # uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE
30 # macro, there is no need to pass this parameter.
31 #
32 # AX_IS_RELEASE([git-directory])
33 # AX_CHECK_ENABLE_DEBUG()
34 #
35 # LICENSE
36 #
37 # Copyright (c) 2011 Rhys Ulerich <rhys.ulerich@gmail.com>
38 # Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
39 #
40 # Copying and distribution of this file, with or without modification, are
41 # permitted in any medium without royalty provided the copyright notice
42 # and this notice are preserved.
43
44 #serial 5
45
46 AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[
47 AC_BEFORE([$0],[AC_PROG_CC])dnl
48 AC_BEFORE([$0],[AC_PROG_CXX])dnl
49 AC_BEFORE([$0],[AC_PROG_F77])dnl
50 AC_BEFORE([$0],[AC_PROG_FC])dnl
51
52 AC_MSG_CHECKING(whether to enable debugging)
53
54 ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1])))
55 ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],,
56 [$ax_is_release],
57 [$4])))
58
59 # If this is a release, override the default.
60 AS_IF([test "$ax_enable_debug_is_release" = "yes"],
61 [ax_enable_debug_default="no"])
62
63 m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))])
64 m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))])
65
66 AC_ARG_ENABLE(debug,
67 [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])],
68 [],enable_debug=$ax_enable_debug_default)
69
70 # empty mean debug yes
71 AS_IF([test "x$enable_debug" = "x"],
72 [enable_debug="yes"])
73
74 # case of debug
75 AS_CASE([$enable_debug],
76 [yes],[
77 AC_MSG_RESULT(yes)
78 CFLAGS="${CFLAGS} -g -O0"
79 CXXFLAGS="${CXXFLAGS} -g -O0"
80 FFLAGS="${FFLAGS} -g -O0"
81 FCFLAGS="${FCFLAGS} -g -O0"
82 OBJCFLAGS="${OBJCFLAGS} -g -O0"
83 ],
84 [info],[
85 AC_MSG_RESULT(info)
86 CFLAGS="${CFLAGS} -g"
87 CXXFLAGS="${CXXFLAGS} -g"
88 FFLAGS="${FFLAGS} -g"
89 FCFLAGS="${FCFLAGS} -g"
90 OBJCFLAGS="${OBJCFLAGS} -g"
91 ],
92 [profile],[
93 AC_MSG_RESULT(profile)
94 CFLAGS="${CFLAGS} -g -pg"
95 CXXFLAGS="${CXXFLAGS} -g -pg"
96 FFLAGS="${FFLAGS} -g -pg"
97 FCFLAGS="${FCFLAGS} -g -pg"
98 OBJCFLAGS="${OBJCFLAGS} -g -pg"
99 LDFLAGS="${LDFLAGS} -pg"
100 ],
101 [
102 AC_MSG_RESULT(no)
103 dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags
104 dnl by setting any unset environment flag variables
105 AS_IF([test "x${CFLAGS+set}" != "xset"],
106 [CFLAGS=""])
107 AS_IF([test "x${CXXFLAGS+set}" != "xset"],
108 [CXXFLAGS=""])
109 AS_IF([test "x${FFLAGS+set}" != "xset"],
110 [FFLAGS=""])
111 AS_IF([test "x${FCFLAGS+set}" != "xset"],
112 [FCFLAGS=""])
113 AS_IF([test "x${OBJCFLAGS+set}" != "xset"],
114 [OBJCFLAGS=""])
115 ])
116
117 dnl Define various variables if debugging is disabled.
118 dnl assert.h is a NOP if NDEBUG is defined, so define it by default.
119 AS_IF([test "x$enable_debug" = "xyes"],
120 [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])],
121 [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])])
122 ax_enable_debug=$enable_debug
123 ])
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CHECK_GNU_MAKE()
7 #
8 # DESCRIPTION
9 #
10 # This macro searches for a GNU version of make. If a match is found:
11 #
12 # * The makefile variable `ifGNUmake' is set to the empty string, otherwise
13 # it is set to "#". This is useful for including a special features in a
14 # Makefile, which cannot be handled by other versions of make.
15 # * The variable `_cv_gnu_make_command` is set to the command to invoke
16 # GNU make if it exists, the empty string otherwise.
17 # * The variable `ax_cv_gnu_make_command` is set to the command to invoke
18 # GNU make by copying `_cv_gnu_make_command`, otherwise it is unset.
19 # * If GNU Make is found, its version is extracted from the output of
20 # `make --version` as the last field of a record of space-separated
21 # columns and saved into the variable `ax_check_gnu_make_version`.
22 #
23 # Here is an example of its use:
24 #
25 # Makefile.in might contain:
26 #
27 # # A failsafe way of putting a dependency rule into a makefile
28 # $(DEPEND):
29 # $(CC) -MM $(srcdir)/*.c > $(DEPEND)
30 #
31 # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND)))
32 # @ifGNUmake@ include $(DEPEND)
33 # @ifGNUmake@ endif
34 #
35 # Then configure.in would normally contain:
36 #
37 # AX_CHECK_GNU_MAKE()
38 # AC_OUTPUT(Makefile)
39 #
40 # Then perhaps to cause gnu make to override any other make, we could do
41 # something like this (note that GNU make always looks for GNUmakefile
42 # first):
43 #
44 # if ! test x$_cv_gnu_make_command = x ; then
45 # mv Makefile GNUmakefile
46 # echo .DEFAULT: > Makefile ;
47 # echo \ $_cv_gnu_make_command \$@ >> Makefile;
48 # fi
49 #
50 # Then, if any (well almost any) other make is called, and GNU make also
51 # exists, then the other make wraps the GNU make.
52 #
53 # LICENSE
54 #
55 # Copyright (c) 2008 John Darrington <j.darrington@elvis.murdoch.edu.au>
56 # Copyright (c) 2015 Enrico M. Crisostomo <enrico.m.crisostomo@gmail.com>
57 #
58 # Copying and distribution of this file, with or without modification, are
59 # permitted in any medium without royalty provided the copyright notice
60 # and this notice are preserved. This file is offered as-is, without any
61 # warranty.
62
63 #serial 8
64
65 AC_DEFUN([AX_CHECK_GNU_MAKE],dnl
66 [AC_PROG_AWK
67 AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl
68 _cv_gnu_make_command="" ;
69 dnl Search all the common names for GNU make
70 for a in "$MAKE" make gmake gnumake ; do
71 if test -z "$a" ; then continue ; fi ;
72 if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then
73 _cv_gnu_make_command=$a ;
74 AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make")
75 ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }')
76 break ;
77 fi
78 done ;])
79 dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise
80 AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])])
81 AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])])
82 AC_SUBST([ifGNUmake])
83 ])
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
7 #
8 # DESCRIPTION
9 #
10 # Check whether the given FLAG works with the linker or gives an error.
11 # (Warnings, however, are ignored)
12 #
13 # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
14 # success/failure.
15 #
16 # If EXTRA-FLAGS is defined, it is added to the linker's default flags
17 # when the check is done. The check is thus made with the flags: "LDFLAGS
18 # EXTRA-FLAGS FLAG". This can for example be used to force the linker to
19 # issue an error when a bad flag is given.
20 #
21 # INPUT gives an alternative input source to AC_LINK_IFELSE.
22 #
23 # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
24 # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
25 #
26 # LICENSE
27 #
28 # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
29 # Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
30 #
31 # This program is free software: you can redistribute it and/or modify it
32 # under the terms of the GNU General Public License as published by the
33 # Free Software Foundation, either version 3 of the License, or (at your
34 # option) any later version.
35 #
36 # This program is distributed in the hope that it will be useful, but
37 # WITHOUT ANY WARRANTY; without even the implied warranty of
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
39 # Public License for more details.
40 #
41 # You should have received a copy of the GNU General Public License along
42 # with this program. If not, see <http://www.gnu.org/licenses/>.
43 #
44 # As a special exception, the respective Autoconf Macro's copyright owner
45 # gives unlimited permission to copy, distribute and modify the configure
46 # scripts that are the output of Autoconf when processing the Macro. You
47 # need not follow the terms of the GNU General Public License when using
48 # or distributing such scripts, even though portions of the text of the
49 # Macro appear in them. The GNU General Public License (GPL) does govern
50 # all other use of the material that constitutes the Autoconf Macro.
51 #
52 # This special exception to the GPL applies to versions of the Autoconf
53 # Macro released by the Autoconf Archive. When you make and distribute a
54 # modified version of the Autoconf Macro, you may extend this special
55 # exception to the GPL to apply to your modified version as well.
56
57 #serial 4
58
59 AC_DEFUN([AX_CHECK_LINK_FLAG],
60 [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
61 AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
62 AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
63 ax_check_save_flags=$LDFLAGS
64 LDFLAGS="$LDFLAGS $4 $1"
65 AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
66 [AS_VAR_SET(CACHEVAR,[yes])],
67 [AS_VAR_SET(CACHEVAR,[no])])
68 LDFLAGS=$ax_check_save_flags])
69 AS_VAR_IF(CACHEVAR,yes,
70 [m4_default([$2], :)],
71 [m4_default([$3], :)])
72 AS_VAR_POPDEF([CACHEVAR])dnl
73 ])dnl AX_CHECK_LINK_FLAGS
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CODE_COVERAGE()
7 #
8 # DESCRIPTION
9 #
10 # Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS,
11 # CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LDFLAGS which should be
12 # included in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LDFLAGS variables of
13 # every build target (program or library) which should be built with code
14 # coverage support. Also defines CODE_COVERAGE_RULES which should be
15 # substituted in your Makefile; and $enable_code_coverage which can be
16 # used in subsequent configure output. CODE_COVERAGE_ENABLED is defined
17 # and substituted, and corresponds to the value of the
18 # --enable-code-coverage option, which defaults to being disabled.
19 #
20 # Test also for gcov program and create GCOV variable that could be
21 # substituted.
22 #
23 # Note that all optimisation flags in CFLAGS must be disabled when code
24 # coverage is enabled.
25 #
26 # Usage example:
27 #
28 # configure.ac:
29 #
30 # AX_CODE_COVERAGE
31 #
32 # Makefile.am:
33 #
34 # @CODE_COVERAGE_RULES@
35 # my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ...
36 # my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ...
37 # my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
38 # my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ...
39 #
40 # This results in a "check-code-coverage" rule being added to any
41 # Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
42 # has been configured with --enable-code-coverage). Running `make
43 # check-code-coverage` in that directory will run the module's test suite
44 # (`make check`) and build a code coverage report detailing the code which
45 # was touched, then print the URI for the report.
46 #
47 # This code was derived from Makefile.decl in GLib, originally licenced
48 # under LGPLv2.1+.
49 #
50 # LICENSE
51 #
52 # Copyright (c) 2012, 2016 Philip Withnall
53 # Copyright (c) 2012 Xan Lopez
54 # Copyright (c) 2012 Christian Persch
55 # Copyright (c) 2012 Paolo Borelli
56 # Copyright (c) 2012 Dan Winship
57 # Copyright (c) 2015 Bastien ROUCARIES
58 #
59 # This library is free software; you can redistribute it and/or modify it
60 # under the terms of the GNU Lesser General Public License as published by
61 # the Free Software Foundation; either version 2.1 of the License, or (at
62 # your option) any later version.
63 #
64 # This library is distributed in the hope that it will be useful, but
65 # WITHOUT ANY WARRANTY; without even the implied warranty of
66 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
67 # General Public License for more details.
68 #
69 # You should have received a copy of the GNU Lesser General Public License
70 # along with this program. If not, see <http://www.gnu.org/licenses/>.
71
72 #serial 15
73
74 AC_DEFUN([AX_CODE_COVERAGE],[
75 dnl Check for --enable-code-coverage
76 AC_REQUIRE([AC_PROG_SED])
77
78 # allow to override gcov location
79 AC_ARG_WITH([gcov],
80 [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
81 [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
82 [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
83
84 AC_MSG_CHECKING([whether to build with code coverage support])
85 AC_ARG_ENABLE([code-coverage],
86 AS_HELP_STRING([--enable-code-coverage],
87 [Whether to enable code coverage support]),,
88 enable_code_coverage=no)
89
90 AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
91 AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
92 AC_MSG_RESULT($enable_code_coverage)
93
94 AS_IF([ test "$enable_code_coverage" = "yes" ], [
95 # check for gcov
96 AC_CHECK_TOOL([GCOV],
97 [$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
98 [:])
99 AS_IF([test "X$GCOV" = "X:"],
100 [AC_MSG_ERROR([gcov is needed to do coverage])])
101 AC_SUBST([GCOV])
102
103 dnl Check if gcc is being used
104 AS_IF([ test "$GCC" = "no" ], [
105 AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
106 ])
107
108 # List of supported lcov versions.
109 lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11 1.12"
110
111 AC_CHECK_PROG([LCOV], [lcov], [lcov])
112 AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
113
114 AS_IF([ test "$LCOV" ], [
115 AC_CACHE_CHECK([for lcov version], ax_cv_lcov_version, [
116 ax_cv_lcov_version=invalid
117 lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
118 for lcov_check_version in $lcov_version_list; do
119 if test "$lcov_version" = "$lcov_check_version"; then
120 ax_cv_lcov_version="$lcov_check_version (ok)"
121 fi
122 done
123 ])
124 ], [
125 lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
126 AC_MSG_ERROR([$lcov_msg])
127 ])
128
129 case $ax_cv_lcov_version in
130 ""|invalid[)]
131 lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
132 AC_MSG_ERROR([$lcov_msg])
133 LCOV="exit 0;"
134 ;;
135 esac
136
137 AS_IF([ test -z "$GENHTML" ], [
138 AC_MSG_ERROR([Could not find genhtml from the lcov package])
139 ])
140
141 dnl Build the code coverage flags
142 CODE_COVERAGE_CPPFLAGS="-DNDEBUG"
143 CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
144 CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
145 CODE_COVERAGE_LDFLAGS="-lgcov"
146
147 AC_SUBST([CODE_COVERAGE_CPPFLAGS])
148 AC_SUBST([CODE_COVERAGE_CFLAGS])
149 AC_SUBST([CODE_COVERAGE_CXXFLAGS])
150 AC_SUBST([CODE_COVERAGE_LDFLAGS])
151 ])
152
153 [CODE_COVERAGE_RULES='
154 # Code coverage
155 #
156 # Optional:
157 # - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
158 # Multiple directories may be specified, separated by whitespace.
159 # (Default: $(top_builddir))
160 # - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
161 # by lcov for code coverage. (Default:
162 # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
163 # - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
164 # reports to be created. (Default:
165 # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
166 # - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage,
167 # set to 0 to disable it and leave empty to stay with the default.
168 # (Default: empty)
169 # - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov
170 # instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
171 # - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov
172 # instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
173 # - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
174 # - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the
175 # collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
176 # - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov
177 # instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
178 # - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering
179 # lcov instance. (Default: empty)
180 # - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov
181 # instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
182 # - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the
183 # genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE)
184 # - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
185 # instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT)
186 # - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
187 #
188 # The generated report will be titled using the $(PACKAGE_NAME) and
189 # $(PACKAGE_VERSION). In order to add the current git hash to the title,
190 # use the git-version-gen script, available online.
191
192 # Optional variables
193 CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
194 CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
195 CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
196 CODE_COVERAGE_BRANCH_COVERAGE ?=
197 CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
198 --rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
199 CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT)
200 CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
201 CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
202 CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
203 CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?=
204 CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT)
205 CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\
206 $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\
207 --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE))
208 CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS)
209 CODE_COVERAGE_IGNORE_PATTERN ?=
210
211 code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V))
212 code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY))
213 code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\
214 $(CODE_COVERAGE_OUTPUT_FILE);
215 code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V))
216 code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY))
217 code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\
218 $(CODE_COVERAGE_IGNORE_PATTERN);
219 code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V))
220 code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY))
221 code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY);
222 code_coverage_quiet = $(code_coverage_quiet_$(V))
223 code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
224 code_coverage_quiet_0 = --quiet
225
226 # sanitizes the test-name: replaces with underscores: dashes and dots
227 code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1)))
228
229 # Use recursive makes in order to ignore errors during check
230 check-code-coverage:
231 ifeq ($(CODE_COVERAGE_ENABLED),yes)
232 -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check
233 $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
234 else
235 @echo "Need to reconfigure with --enable-code-coverage"
236 endif
237
238 # Capture code coverage data
239 code-coverage-capture: code-coverage-capture-hook
240 ifeq ($(CODE_COVERAGE_ENABLED),yes)
241 $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS)
242 $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS)
243 -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
244 $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
245 @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
246 else
247 @echo "Need to reconfigure with --enable-code-coverage"
248 endif
249
250 # Hook rule executed before code-coverage-capture, overridable by the user
251 code-coverage-capture-hook:
252
253 ifeq ($(CODE_COVERAGE_ENABLED),yes)
254 clean: code-coverage-clean
255 code-coverage-clean:
256 -$(LCOV) --directory $(top_builddir) -z
257 -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
258 -find . -name "*.gcda" -o -name "*.gcov" -delete
259 endif
260
261 GITIGNOREFILES ?=
262 GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
263
264 A''M_DISTCHECK_CONFIGURE_FLAGS ?=
265 A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
266
267 .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
268 ']
269
270 AC_SUBST([CODE_COVERAGE_RULES])
271 m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
272 ])
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_configure_args.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_CONFIGURE_ARGS
7 #
8 # DESCRIPTION
9 #
10 # Helper macro for AX_ENABLE_BUILDDIR.
11 #
12 # The traditional way of starting a subdir-configure is running the script
13 # with ${1+"$@"} but since autoconf 2.60 this is broken. Instead we have
14 # to rely on eval'ing $ac_configure_args however some old autoconf
15 # versions do not provide that. To ensure maximum portability of autoconf
16 # extension macros this helper can be AC_REQUIRE'd so that
17 # $ac_configure_args will alsways be present.
18 #
19 # Sadly, the traditional "exec $SHELL" of the enable_builddir macros is
20 # spoiled now and must be replaced by "eval + exit $?".
21 #
22 # Example:
23 #
24 # AC_DEFUN([AX_ENABLE_SUBDIR],[dnl
25 # AC_REQUIRE([AX_CONFIGURE_ARGS])dnl
26 # eval $SHELL $ac_configure_args || exit $?
27 # ...])
28 #
29 # LICENSE
30 #
31 # Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
32 #
33 # This program is free software; you can redistribute it and/or modify it
34 # under the terms of the GNU General Public License as published by the
35 # Free Software Foundation; either version 3 of the License, or (at your
36 # option) any later version.
37 #
38 # This program is distributed in the hope that it will be useful, but
39 # WITHOUT ANY WARRANTY; without even the implied warranty of
40 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
41 # Public License for more details.
42 #
43 # You should have received a copy of the GNU General Public License along
44 # with this program. If not, see <http://www.gnu.org/licenses/>.
45 #
46 # As a special exception, the respective Autoconf Macro's copyright owner
47 # gives unlimited permission to copy, distribute and modify the configure
48 # scripts that are the output of Autoconf when processing the Macro. You
49 # need not follow the terms of the GNU General Public License when using
50 # or distributing such scripts, even though portions of the text of the
51 # Macro appear in them. The GNU General Public License (GPL) does govern
52 # all other use of the material that constitutes the Autoconf Macro.
53 #
54 # This special exception to the GPL applies to versions of the Autoconf
55 # Macro released by the Autoconf Archive. When you make and distribute a
56 # modified version of the Autoconf Macro, you may extend this special
57 # exception to the GPL to apply to your modified version as well.
58
59 #serial 9
60
61 AC_DEFUN([AX_CONFIGURE_ARGS],[
62 # [$]@ is unsable in 2.60+ but earlier autoconf had no ac_configure_args
63 if test "${ac_configure_args+set}" != "set" ; then
64 ac_configure_args=
65 for ac_arg in ${1+"[$]@"}; do
66 ac_configure_args="$ac_configure_args '$ac_arg'"
67 done
68 fi
69 ])
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_enable_builddir.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_ENABLE_BUILDDIR [(dirstring-or-command [,Makefile.mk [,-all]])]
7 #
8 # DESCRIPTION
9 #
10 # If the current configure was run within the srcdir then we move all
11 # configure-files into a subdir and let the configure steps continue
12 # there. We provide an option --disable-builddir to suppress the move into
13 # a separate builddir.
14 #
15 # Defaults:
16 #
17 # $1 = $host (overridden with $HOST)
18 # $2 = Makefile.mk
19 # $3 = -all
20 #
21 # This macro must be called before AM_INIT_AUTOMAKE. It creates a default
22 # toplevel srcdir Makefile from the information found in the created
23 # toplevel builddir Makefile. It just copies the variables and
24 # rule-targets, each extended with a default rule-execution that recurses
25 # into the build directory of the current "HOST". You can override the
26 # auto-dection through `config.guess` and build-time of course, as in
27 #
28 # make HOST=i386-mingw-cross
29 #
30 # which can of course set at configure time as well using
31 #
32 # configure --host=i386-mingw-cross
33 #
34 # After the default has been created, additional rules can be appended
35 # that will not just recurse into the subdirectories and only ever exist
36 # in the srcdir toplevel makefile - these parts are read from the $2 =
37 # Makefile.mk file
38 #
39 # The automatic rules are usually scanning the toplevel Makefile for lines
40 # like '#### $host |$builddir' to recognize the place where to recurse
41 # into. Usually, the last one is the only one used. However, almost all
42 # targets have an additional "*-all" rule which makes the script to
43 # recurse into _all_ variants of the current HOST (!!) setting. The "-all"
44 # suffix can be overriden for the macro as well.
45 #
46 # a special rule is only given for things like "dist" that will copy the
47 # tarball from the builddir to the sourcedir (or $(PUB)) for reason of
48 # convenience.
49 #
50 # LICENSE
51 #
52 # Copyright (c) 2009 Guido U. Draheim <guidod@gmx.de>
53 # Copyright (c) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
54 #
55 # This program is free software; you can redistribute it and/or modify it
56 # under the terms of the GNU General Public License as published by the
57 # Free Software Foundation; either version 3 of the License, or (at your
58 # option) any later version.
59 #
60 # This program is distributed in the hope that it will be useful, but
61 # WITHOUT ANY WARRANTY; without even the implied warranty of
62 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
63 # Public License for more details.
64 #
65 # You should have received a copy of the GNU General Public License along
66 # with this program. If not, see <http://www.gnu.org/licenses/>.
67 #
68 # As a special exception, the respective Autoconf Macro's copyright owner
69 # gives unlimited permission to copy, distribute and modify the configure
70 # scripts that are the output of Autoconf when processing the Macro. You
71 # need not follow the terms of the GNU General Public License when using
72 # or distributing such scripts, even though portions of the text of the
73 # Macro appear in them. The GNU General Public License (GPL) does govern
74 # all other use of the material that constitutes the Autoconf Macro.
75 #
76 # This special exception to the GPL applies to versions of the Autoconf
77 # Macro released by the Autoconf Archive. When you make and distribute a
78 # modified version of the Autoconf Macro, you may extend this special
79 # exception to the GPL to apply to your modified version as well.
80
81 #serial 25
82
83 AC_DEFUN([AX_ENABLE_BUILDDIR],[
84 AC_REQUIRE([AC_CANONICAL_HOST])[]dnl
85 AC_REQUIRE([AC_CANONICAL_TARGET])[]dnl
86 AC_REQUIRE([AX_CONFIGURE_ARGS])[]dnl
87 AC_REQUIRE([AM_AUX_DIR_EXPAND])[]dnl
88 AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl
89 AS_VAR_PUSHDEF([SUB],[ax_enable_builddir])dnl
90 AS_VAR_PUSHDEF([AUX],[ax_enable_builddir_auxdir])dnl
91 AS_VAR_PUSHDEF([SED],[ax_enable_builddir_sed])dnl
92 SUB="."
93 AC_ARG_ENABLE([builddir], AS_HELP_STRING(
94 [--disable-builddir],[disable automatic build in subdir of sources])
95 ,[SUB="$enableval"], [SUB="auto"])
96 if test ".$ac_srcdir_defaulted" != ".no" ; then
97 if test ".$srcdir" = ".." ; then
98 if test -f config.status ; then
99 AC_MSG_NOTICE(toplevel srcdir already configured... skipping subdir build)
100 else
101 test ".$SUB" = "." && SUB="."
102 test ".$SUB" = ".no" && SUB="."
103 test ".$TARGET" = "." && TARGET="$target"
104 test ".$SUB" = ".auto" && SUB="m4_ifval([$1], [$1],[$TARGET])"
105 if test ".$SUB" != ".." ; then # we know where to go and
106 AS_MKDIR_P([$SUB])
107 echo __.$SUB.__ > $SUB/conftest.tmp
108 cd $SUB
109 if grep __.$SUB.__ conftest.tmp >/dev/null 2>/dev/null ; then
110 rm conftest.tmp
111 AC_MSG_RESULT([continue configure in default builddir "./$SUB"])
112 else
113 AC_MSG_ERROR([could not change to default builddir "./$SUB"])
114 fi
115 srcdir=`echo "$SUB" |
116 sed -e 's,^\./,,;s,[[^/]]$,&/,;s,[[^/]]*/,../,g;s,[[/]]$,,;'`
117 # going to restart from subdirectory location
118 test -f $srcdir/config.log && mv $srcdir/config.log .
119 test -f $srcdir/confdefs.h && mv $srcdir/confdefs.h .
120 test -f $srcdir/conftest.log && mv $srcdir/conftest.log .
121 test -f $srcdir/$cache_file && mv $srcdir/$cache_file .
122 AC_MSG_RESULT(....exec $SHELL $srcdir/[$]0 "--srcdir=$srcdir" "--enable-builddir=$SUB" ${1+"[$]@"})
123 case "[$]0" in # restart
124 [[\\/]]* | ?:[[\\/]]*) # Asbolute name
125 eval $SHELL "'[$]0'" "'--srcdir=$srcdir'" "'--enable-builddir=$SUB'" $ac_configure_args ;;
126 *) eval $SHELL "'$srcdir/[$]0'" "'--srcdir=$srcdir'" "'--enable-builddir=$SUB'" $ac_configure_args ;;
127 esac ; exit $?
128 fi
129 fi
130 fi fi
131 test ".$SUB" = ".auto" && SUB="."
132 dnl ac_path_prog uses "set dummy" to override $@ which would defeat the "exec"
133 AC_PATH_PROG(SED,gsed sed, sed)
134 AUX="$am_aux_dir"
135 AS_VAR_POPDEF([SED])dnl
136 AS_VAR_POPDEF([AUX])dnl
137 AS_VAR_POPDEF([SUB])dnl
138 AC_CONFIG_COMMANDS([buildir],[dnl .............. config.status ..............
139 AS_VAR_PUSHDEF([SUB],[ax_enable_builddir])dnl
140 AS_VAR_PUSHDEF([TOP],[top_srcdir])dnl
141 AS_VAR_PUSHDEF([SRC],[ac_top_srcdir])dnl
142 AS_VAR_PUSHDEF([AUX],[ax_enable_builddir_auxdir])dnl
143 AS_VAR_PUSHDEF([SED],[ax_enable_builddir_sed])dnl
144 pushdef([END],[Makefile.mk])dnl
145 pushdef([_ALL],[ifelse([$3],,[-all],[$3])])dnl
146 SRC="$ax_enable_builddir_srcdir"
147 if test ".$SUB" = ".." ; then
148 if test -f "$TOP/Makefile" ; then
149 AC_MSG_NOTICE([skipping TOP/Makefile - left untouched])
150 else
151 AC_MSG_NOTICE([skipping TOP/Makefile - not created])
152 fi
153 else
154 if test -f "$SRC/Makefile" ; then
155 a=`grep "^VERSION " "$SRC/Makefile"` ; b=`grep "^VERSION " Makefile`
156 test "$a" != "$b" && rm "$SRC/Makefile"
157 fi
158 if test -f "$SRC/Makefile" ; then
159 echo "$SRC/Makefile : $SRC/Makefile.in" > $tmp/conftemp.mk
160 echo " []@ echo 'REMOVED,,,' >\$[]@" >> $tmp/conftemp.mk
161 eval "${MAKE-make} -f $tmp/conftemp.mk 2>/dev/null >/dev/null"
162 if grep '^REMOVED,,,' "$SRC/Makefile" >/dev/null
163 then rm $SRC/Makefile ; fi
164 cp $tmp/conftemp.mk $SRC/makefiles.mk~ ## DEBUGGING
165 fi
166 if test ! -f "$SRC/Makefile" ; then
167 AC_MSG_NOTICE([create TOP/Makefile guessed from local Makefile])
168 x='`' ; cat >$tmp/conftemp.sed <<_EOF
169 /^\$/n
170 x
171 /^\$/bS
172 x
173 /\\\\\$/{H;d;}
174 {H;s/.*//;x;}
175 bM
176 :S
177 x
178 /\\\\\$/{h;d;}
179 {h;s/.*//;x;}
180 :M
181 s/\\(\\n\\) /\\1 /g
182 /^ /d
183 /^[[ ]]*[[\\#]]/d
184 /^VPATH *=/d
185 s/^srcdir *=.*/srcdir = ./
186 s/^top_srcdir *=.*/top_srcdir = ./
187 /[[:=]]/!d
188 /^\\./d
189 dnl Now handle rules (i.e. lines containing ":" but not " = ").
190 / = /b
191 / .= /b
192 /:/!b
193 s/:.*/:/
194 s/ / /g
195 s/ \\([[a-z]][[a-z-]]*[[a-zA-Z0-9]]\\)\\([[ :]]\\)/ \\1 \\1[]_ALL\\2/g
196 s/^\\([[a-z]][[a-z-]]*[[a-zA-Z0-9]]\\)\\([[ :]]\\)/\\1 \\1[]_ALL\\2/
197 s/ / /g
198 /^all all[]_ALL[[ :]]/i\\
199 all-configured : all[]_ALL
200 dnl dist-all exists... and would make for dist-all-all
201 s/ [[a-zA-Z0-9-]]*[]_ALL [[a-zA-Z0-9-]]*[]_ALL[]_ALL//g
202 /[]_ALL[]_ALL/d
203 a\\
204 @ HOST="\$(HOST)\" \\\\\\
205 ; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\
206 ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
207 ; use=$x basename "\$\@" _ALL $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\
208 ; echo "MAKE \$\$HOST : \$\$n * \$\@"; if test "\$\$n" -eq "0" ; then : \\\\\\
209 ; BUILD=$x grep "^####.*|" Makefile |tail -1| sed -e 's/.*|//' $x ; fi \\\\\\
210 ; test ".\$\$BUILD" = "." && BUILD="." \\\\\\
211 ; test "\$\$use" = "\$\@" && BUILD=$x echo "\$\$BUILD" | tail -1 $x \\\\\\
212 ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
213 ; (cd "\$\$i" && test ! -f configure && \$(MAKE) \$\$use) || exit; done
214 dnl special rule add-on: "dist" copies the tarball to $(PUB). (source tree)
215 /dist[]_ALL *:/a\\
216 @ HOST="\$(HOST)\" \\\\\\
217 ; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\
218 ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
219 ; found=$x echo \$\$BUILD | wc -w $x \\\\\\
220 ; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).tar.*" \\\\\\
221 ; if test "\$\$found" -eq "0" ; then : \\\\\\
222 ; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\
223 ; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
224 ; for f in \$\$i/\$(PACKAGE)-\$(VERSION).tar.* \\\\\\
225 ; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done
226 dnl special rule add-on: "dist-foo" copies all the archives to $(PUB). (source tree)
227 /dist-[[a-zA-Z0-9]]*[]_ALL *:/a\\
228 @ HOST="\$(HOST)\" \\\\\\
229 ; test ".\$\$HOST" = "." && HOST=$x sh ./config.guess $x \\\\\\
230 ; BUILD=$x grep "^#### \$\$HOST " Makefile | sed -e 's/.*|//' $x \\\\\\
231 ; found=$x echo \$\$BUILD | wc -w $x \\\\\\
232 ; echo "MAKE \$\$HOST : \$\$found \$(PACKAGE)-\$(VERSION).*" \\\\\\
233 ; if test "\$\$found" -eq "0" ; then : \\\\\\
234 ; BUILD=$x grep "^#### .*|" Makefile |tail -1| sed -e 's/.*|//' $x \\\\\\
235 ; fi ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
236 ; for f in \$\$i/\$(PACKAGE)-\$(VERSION).* \\\\\\
237 ; do test -f "\$\$f" && mv "\$\$f" \$(PUB). ; done ; break ; done
238 dnl special rule add-on: "distclean" removes all local builddirs completely
239 /distclean[]_ALL *:/a\\
240 @ HOST="\$(HOST)\" \\\\\\
241 ; test ".\$\$HOST" = "." && HOST=$x sh $AUX/config.guess $x \\\\\\
242 ; BUILD=$x grep "^#### .*|" Makefile | sed -e 's/.*|//' $x \\\\\\
243 ; use=$x basename "\$\@" _ALL $x; n=$x echo \$\$BUILD | wc -w $x \\\\\\
244 ; echo "MAKE \$\$HOST : \$\$n * \$\@ (all local builds)" \\\\\\
245 ; test ".\$\$BUILD" = "." && BUILD="." \\\\\\
246 ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
247 ; echo "# rm -r \$\$i"; done ; echo "# (sleep 3)" ; sleep 3 \\\\\\
248 ; for i in \$\$BUILD ; do test ".\$\$i" = "." && continue \\\\\\
249 ; echo "\$\$i" | grep "^/" > /dev/null && continue \\\\\\
250 ; echo "\$\$i" | grep "^../" > /dev/null && continue \\\\\\
251 ; echo "rm -r \$\$i"; (rm -r "\$\$i") ; done ; rm Makefile
252 _EOF
253 cp "$tmp/conftemp.sed" "$SRC/makefile.sed~" ## DEBUGGING
254 $SED -f $tmp/conftemp.sed Makefile >$SRC/Makefile
255 if test -f "$SRC/m4_ifval([$2],[$2],[END])" ; then
256 AC_MSG_NOTICE([extend TOP/Makefile with TOP/m4_ifval([$2],[$2],[END])])
257 cat $SRC/END >>$SRC/Makefile
258 fi ; xxxx="####"
259 echo "$xxxx CONFIGURATIONS FOR TOPLEVEL MAKEFILE: " >>$SRC/Makefile
260 # sanity check
261 if grep '^; echo "MAKE ' $SRC/Makefile >/dev/null ; then
262 AC_MSG_NOTICE([buggy sed found - it deletes tab in "a" text parts])
263 $SED -e '/^@ HOST=/s/^/ /' -e '/^; /s/^/ /' $SRC/Makefile \
264 >$SRC/Makefile~
265 (test -s $SRC/Makefile~ && mv $SRC/Makefile~ $SRC/Makefile) 2>/dev/null
266 fi
267 else
268 xxxx="\\#\\#\\#\\#"
269 # echo "/^$xxxx *$ax_enable_builddir_host /d" >$tmp/conftemp.sed
270 echo "s!^$xxxx [[^|]]* | *$SUB *\$!$xxxx ...... $SUB!" >$tmp/conftemp.sed
271 $SED -f "$tmp/conftemp.sed" "$SRC/Makefile" >$tmp/mkfile.tmp
272 cp "$tmp/conftemp.sed" "$SRC/makefiles.sed~" ## DEBUGGING
273 cp "$tmp/mkfile.tmp" "$SRC/makefiles.out~" ## DEBUGGING
274 if cmp -s "$SRC/Makefile" "$tmp/mkfile.tmp" 2>/dev/null ; then
275 AC_MSG_NOTICE([keeping TOP/Makefile from earlier configure])
276 rm "$tmp/mkfile.tmp"
277 else
278 AC_MSG_NOTICE([reusing TOP/Makefile from earlier configure])
279 mv "$tmp/mkfile.tmp" "$SRC/Makefile"
280 fi
281 fi
282 AC_MSG_NOTICE([build in $SUB (HOST=$ax_enable_builddir_host)])
283 xxxx="####"
284 echo "$xxxx" "$ax_enable_builddir_host" "|$SUB" >>$SRC/Makefile
285 fi
286 popdef([END])dnl
287 AS_VAR_POPDEF([SED])dnl
288 AS_VAR_POPDEF([AUX])dnl
289 AS_VAR_POPDEF([SRC])dnl
290 AS_VAR_POPDEF([TOP])dnl
291 AS_VAR_POPDEF([SUB])dnl
292 ],[dnl
293 ax_enable_builddir_srcdir="$srcdir" # $srcdir
294 ax_enable_builddir_host="$HOST" # $HOST / $host
295 ax_enable_builddir_version="$VERSION" # $VERSION
296 ax_enable_builddir_package="$PACKAGE" # $PACKAGE
297 ax_enable_builddir_auxdir="$ax_enable_builddir_auxdir" # $AUX
298 ax_enable_builddir_sed="$ax_enable_builddir_sed" # $SED
299 ax_enable_builddir="$ax_enable_builddir" # $SUB
300 ])dnl
301 ])
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_extend_srcdir.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_EXTEND_SRCDIR
7 #
8 # DESCRIPTION
9 #
10 # The AX_EXTEND_SRCDIR macro extends $srcdir by one path component.
11 #
12 # As an example, when working in /home/michael/i3-4.12/build and calling
13 # ../configure, your $srcdir is "..". After calling AX_EXTEND_SRCDIR,
14 # $srcdir will be set to "../../i3-4.12".
15 #
16 # The result of extending $srcdir is that filenames (e.g. in the output of
17 # the "backtrace" gdb command) will include one more path component of the
18 # absolute source path. The additional path component makes it easy for
19 # users to recognize which files belong to the PACKAGE, and -- provided a
20 # dist tarball was unpacked -- which version of PACKAGE was used.
21 #
22 # As an example, in "backtrace", you will see:
23 #
24 # #0 main (argc=1, argv=0x7fffffff1fc8) at ../../i3-4.12/src/main.c:187
25 #
26 # instead of:
27 #
28 # #0 main (argc=1, argv=0x7fffffff1fc8) at ../src/main.c:187
29 #
30 # In case your code uses the __FILE__ preprocessor directive to refer to
31 # the filename of the current source file (e.g. in debug messages), using
32 # the extended path might be undesirable. For this purpose,
33 # AX_EXTEND_SRCDIR defines the output variable AX_EXTEND_SRCDIR_CPPFLAGS,
34 # which can be added to AM_CPPFLAGS in Makefile.am in order to define the
35 # preprocessor directive STRIPPED__FILE__. As an example, when compiling
36 # the file "../../i3-4.12/src/main.c", STRIPPED__FILE__ evaluates to
37 # "main.c".
38 #
39 # There are some caveats: When $srcdir is "." (i.e. when ./configure was
40 # called instead of ../configure in a separate build directory),
41 # AX_EXTEND_SRCDIR will still extend $srcdir, but the intended effect will
42 # not be achieved because of the way automake specifies file paths:
43 # automake defines COMPILE to use "`test -f '$source' || echo
44 # '\$(srcdir)/'`$source" in order to prefer files in the current directory
45 # over specifying $srcdir explicitly.
46 #
47 # The AX_EXTEND_SRCDIR author is not aware of any way to influence this
48 # automake behavior. Patches very welcome.
49 #
50 # To work around this issue, you can use AX_ENABLE_BUILDDIR i.e. by adding
51 # the following code to configure.ac:
52 #
53 # AX_ENABLE_BUILDDIR
54 # dnl ...
55 # AX_EXTEND_SRCDIR
56 #
57 # Then also add this bit to Makefile.am (if you wish to use
58 # STRIPPED__FILE__ in your code):
59 #
60 # AM_CPPFLAGS = @AX_EXTEND_SRCDIR_CPPFLAGS@
61 #
62 # LICENSE
63 #
64 # Copyright (c) 2016 Michael Stapelberg <michael@i3wm.org>
65 #
66 # Copying and distribution of this file, with or without modification, are
67 # permitted in any medium without royalty provided the copyright notice
68 # and this notice are preserved. This file is offered as-is, without any
69 # warranty.
70
71 #serial 3
72
73 AC_DEFUN([AX_EXTEND_SRCDIR],
74 [dnl
75 AS_CASE([$srcdir],
76 [.|.*|/*],
77 [
78 # pwd -P is specified in IEEE 1003.1 from 2004
79 as_dir=`cd "$srcdir" && pwd -P`
80 as_base=`AS_BASENAME([$as_dir])`
81 srcdir=${srcdir}/../${as_base}
82
83 AC_SUBST([AX_EXTEND_SRCDIR_CPPFLAGS], ["-DSTRIPPED__FILE__=AS_ESCAPE([\"$$(basename $<)\"])"])
84 ])
85 ])dnl AX_EXTEND_SRCDIR
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_pthread.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
7 #
8 # DESCRIPTION
9 #
10 # This macro figures out how to build C programs using POSIX threads. It
11 # sets the PTHREAD_LIBS output variable to the threads library and linker
12 # flags, and the PTHREAD_CFLAGS output variable to any special C compiler
13 # flags that are needed. (The user can also force certain compiler
14 # flags/libs to be tested by setting these environment variables.)
15 #
16 # Also sets PTHREAD_CC to any special C compiler that is needed for
17 # multi-threaded programs (defaults to the value of CC otherwise). (This
18 # is necessary on AIX to use the special cc_r compiler alias.)
19 #
20 # NOTE: You are assumed to not only compile your program with these flags,
21 # but also to link with them as well. For example, you might link with
22 # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
23 #
24 # If you are only building threaded programs, you may wish to use these
25 # variables in your default LIBS, CFLAGS, and CC:
26 #
27 # LIBS="$PTHREAD_LIBS $LIBS"
28 # CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
29 # CC="$PTHREAD_CC"
30 #
31 # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
32 # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
33 # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
34 #
35 # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
36 # PTHREAD_PRIO_INHERIT symbol is defined when compiling with
37 # PTHREAD_CFLAGS.
38 #
39 # ACTION-IF-FOUND is a list of shell commands to run if a threads library
40 # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
41 # is not found. If ACTION-IF-FOUND is not specified, the default action
42 # will define HAVE_PTHREAD.
43 #
44 # Please let the authors know if this macro fails on any platform, or if
45 # you have any other suggestions or comments. This macro was based on work
46 # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
47 # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
48 # Alejandro Forero Cuervo to the autoconf macro repository. We are also
49 # grateful for the helpful feedback of numerous users.
50 #
51 # Updated for Autoconf 2.68 by Daniel Richard G.
52 #
53 # LICENSE
54 #
55 # Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
56 # Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
57 #
58 # This program is free software: you can redistribute it and/or modify it
59 # under the terms of the GNU General Public License as published by the
60 # Free Software Foundation, either version 3 of the License, or (at your
61 # option) any later version.
62 #
63 # This program is distributed in the hope that it will be useful, but
64 # WITHOUT ANY WARRANTY; without even the implied warranty of
65 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
66 # Public License for more details.
67 #
68 # You should have received a copy of the GNU General Public License along
69 # with this program. If not, see <http://www.gnu.org/licenses/>.
70 #
71 # As a special exception, the respective Autoconf Macro's copyright owner
72 # gives unlimited permission to copy, distribute and modify the configure
73 # scripts that are the output of Autoconf when processing the Macro. You
74 # need not follow the terms of the GNU General Public License when using
75 # or distributing such scripts, even though portions of the text of the
76 # Macro appear in them. The GNU General Public License (GPL) does govern
77 # all other use of the material that constitutes the Autoconf Macro.
78 #
79 # This special exception to the GPL applies to versions of the Autoconf
80 # Macro released by the Autoconf Archive. When you make and distribute a
81 # modified version of the Autoconf Macro, you may extend this special
82 # exception to the GPL to apply to your modified version as well.
83
84 #serial 23
85
86 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
87 AC_DEFUN([AX_PTHREAD], [
88 AC_REQUIRE([AC_CANONICAL_HOST])
89 AC_REQUIRE([AC_PROG_CC])
90 AC_REQUIRE([AC_PROG_SED])
91 AC_LANG_PUSH([C])
92 ax_pthread_ok=no
93
94 # We used to check for pthread.h first, but this fails if pthread.h
95 # requires special compiler flags (e.g. on Tru64 or Sequent).
96 # It gets checked for in the link test anyway.
97
98 # First of all, check if the user has set any of the PTHREAD_LIBS,
99 # etcetera environment variables, and if threads linking works using
100 # them:
101 if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
102 ax_pthread_save_CC="$CC"
103 ax_pthread_save_CFLAGS="$CFLAGS"
104 ax_pthread_save_LIBS="$LIBS"
105 AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
106 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
107 LIBS="$PTHREAD_LIBS $LIBS"
108 AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
109 AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
110 AC_MSG_RESULT([$ax_pthread_ok])
111 if test "x$ax_pthread_ok" = "xno"; then
112 PTHREAD_LIBS=""
113 PTHREAD_CFLAGS=""
114 fi
115 CC="$ax_pthread_save_CC"
116 CFLAGS="$ax_pthread_save_CFLAGS"
117 LIBS="$ax_pthread_save_LIBS"
118 fi
119
120 # We must check for the threads library under a number of different
121 # names; the ordering is very important because some systems
122 # (e.g. DEC) have both -lpthread and -lpthreads, where one of the
123 # libraries is broken (non-POSIX).
124
125 # Create a list of thread flags to try. Items starting with a "-" are
126 # C compiler flags, and other items are library names, except for "none"
127 # which indicates that we try without any flags at all, and "pthread-config"
128 # which is a program returning the flags for the Pth emulation library.
129
130 ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
131
132 # The ordering *is* (sometimes) important. Some notes on the
133 # individual items follow:
134
135 # pthreads: AIX (must check this before -lpthread)
136 # none: in case threads are in libc; should be tried before -Kthread and
137 # other compiler flags to prevent continual compiler warnings
138 # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
139 # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
140 # (Note: HP C rejects this with "bad form for `-t' option")
141 # -pthreads: Solaris/gcc (Note: HP C also rejects)
142 # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
143 # doesn't hurt to check since this sometimes defines pthreads and
144 # -D_REENTRANT too), HP C (must be checked before -lpthread, which
145 # is present but should not be used directly; and before -mthreads,
146 # because the compiler interprets this as "-mt" + "-hreads")
147 # -mthreads: Mingw32/gcc, Lynx/gcc
148 # pthread: Linux, etcetera
149 # --thread-safe: KAI C++
150 # pthread-config: use pthread-config program (for GNU Pth library)
151
152 case $host_os in
153
154 freebsd*)
155
156 # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
157 # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
158
159 ax_pthread_flags="-kthread lthread $ax_pthread_flags"
160 ;;
161
162 hpux*)
163
164 # From the cc(1) man page: "[-mt] Sets various -D flags to enable
165 # multi-threading and also sets -lpthread."
166
167 ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
168 ;;
169
170 openedition*)
171
172 # IBM z/OS requires a feature-test macro to be defined in order to
173 # enable POSIX threads at all, so give the user a hint if this is
174 # not set. (We don't define these ourselves, as they can affect
175 # other portions of the system API in unpredictable ways.)
176
177 AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
178 [
179 # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
180 AX_PTHREAD_ZOS_MISSING
181 # endif
182 ],
183 [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
184 ;;
185
186 solaris*)
187
188 # On Solaris (at least, for some versions), libc contains stubbed
189 # (non-functional) versions of the pthreads routines, so link-based
190 # tests will erroneously succeed. (N.B.: The stubs are missing
191 # pthread_cleanup_push, or rather a function called by this macro,
192 # so we could check for that, but who knows whether they'll stub
193 # that too in a future libc.) So we'll check first for the
194 # standard Solaris way of linking pthreads (-mt -lpthread).
195
196 ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
197 ;;
198 esac
199
200 # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
201
202 AS_IF([test "x$GCC" = "xyes"],
203 [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
204
205 # The presence of a feature test macro requesting re-entrant function
206 # definitions is, on some systems, a strong hint that pthreads support is
207 # correctly enabled
208
209 case $host_os in
210 darwin* | hpux* | linux* | osf* | solaris*)
211 ax_pthread_check_macro="_REENTRANT"
212 ;;
213
214 aix*)
215 ax_pthread_check_macro="_THREAD_SAFE"
216 ;;
217
218 *)
219 ax_pthread_check_macro="--"
220 ;;
221 esac
222 AS_IF([test "x$ax_pthread_check_macro" = "x--"],
223 [ax_pthread_check_cond=0],
224 [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
225
226 # Are we compiling with Clang?
227
228 AC_CACHE_CHECK([whether $CC is Clang],
229 [ax_cv_PTHREAD_CLANG],
230 [ax_cv_PTHREAD_CLANG=no
231 # Note that Autoconf sets GCC=yes for Clang as well as GCC
232 if test "x$GCC" = "xyes"; then
233 AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
234 [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
235 # if defined(__clang__) && defined(__llvm__)
236 AX_PTHREAD_CC_IS_CLANG
237 # endif
238 ],
239 [ax_cv_PTHREAD_CLANG=yes])
240 fi
241 ])
242 ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
243
244 ax_pthread_clang_warning=no
245
246 # Clang needs special handling, because older versions handle the -pthread
247 # option in a rather... idiosyncratic way
248
249 if test "x$ax_pthread_clang" = "xyes"; then
250
251 # Clang takes -pthread; it has never supported any other flag
252
253 # (Note 1: This will need to be revisited if a system that Clang
254 # supports has POSIX threads in a separate library. This tends not
255 # to be the way of modern systems, but it's conceivable.)
256
257 # (Note 2: On some systems, notably Darwin, -pthread is not needed
258 # to get POSIX threads support; the API is always present and
259 # active. We could reasonably leave PTHREAD_CFLAGS empty. But
260 # -pthread does define _REENTRANT, and while the Darwin headers
261 # ignore this macro, third-party headers might not.)
262
263 PTHREAD_CFLAGS="-pthread"
264 PTHREAD_LIBS=
265
266 ax_pthread_ok=yes
267
268 # However, older versions of Clang make a point of warning the user
269 # that, in an invocation where only linking and no compilation is
270 # taking place, the -pthread option has no effect ("argument unused
271 # during compilation"). They expect -pthread to be passed in only
272 # when source code is being compiled.
273 #
274 # Problem is, this is at odds with the way Automake and most other
275 # C build frameworks function, which is that the same flags used in
276 # compilation (CFLAGS) are also used in linking. Many systems
277 # supported by AX_PTHREAD require exactly this for POSIX threads
278 # support, and in fact it is often not straightforward to specify a
279 # flag that is used only in the compilation phase and not in
280 # linking. Such a scenario is extremely rare in practice.
281 #
282 # Even though use of the -pthread flag in linking would only print
283 # a warning, this can be a nuisance for well-run software projects
284 # that build with -Werror. So if the active version of Clang has
285 # this misfeature, we search for an option to squash it.
286
287 AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
288 [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
289 [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
290 # Create an alternate version of $ac_link that compiles and
291 # links in two steps (.c -> .o, .o -> exe) instead of one
292 # (.c -> exe), because the warning occurs only in the second
293 # step
294 ax_pthread_save_ac_link="$ac_link"
295 ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
296 ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
297 ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
298 ax_pthread_save_CFLAGS="$CFLAGS"
299 for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
300 AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
301 CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
302 ac_link="$ax_pthread_save_ac_link"
303 AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
304 [ac_link="$ax_pthread_2step_ac_link"
305 AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
306 [break])
307 ])
308 done
309 ac_link="$ax_pthread_save_ac_link"
310 CFLAGS="$ax_pthread_save_CFLAGS"
311 AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
312 ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
313 ])
314
315 case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
316 no | unknown) ;;
317 *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
318 esac
319
320 fi # $ax_pthread_clang = yes
321
322 if test "x$ax_pthread_ok" = "xno"; then
323 for ax_pthread_try_flag in $ax_pthread_flags; do
324
325 case $ax_pthread_try_flag in
326 none)
327 AC_MSG_CHECKING([whether pthreads work without any flags])
328 ;;
329
330 -mt,pthread)
331 AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
332 PTHREAD_CFLAGS="-mt"
333 PTHREAD_LIBS="-lpthread"
334 ;;
335
336 -*)
337 AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
338 PTHREAD_CFLAGS="$ax_pthread_try_flag"
339 ;;
340
341 pthread-config)
342 AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
343 AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
344 PTHREAD_CFLAGS="`pthread-config --cflags`"
345 PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
346 ;;
347
348 *)
349 AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
350 PTHREAD_LIBS="-l$ax_pthread_try_flag"
351 ;;
352 esac
353
354 ax_pthread_save_CFLAGS="$CFLAGS"
355 ax_pthread_save_LIBS="$LIBS"
356 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
357 LIBS="$PTHREAD_LIBS $LIBS"
358
359 # Check for various functions. We must include pthread.h,
360 # since some functions may be macros. (On the Sequent, we
361 # need a special flag -Kthread to make this header compile.)
362 # We check for pthread_join because it is in -lpthread on IRIX
363 # while pthread_create is in libc. We check for pthread_attr_init
364 # due to DEC craziness with -lpthreads. We check for
365 # pthread_cleanup_push because it is one of the few pthread
366 # functions on Solaris that doesn't have a non-functional libc stub.
367 # We try pthread_create on general principles.
368
369 AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
370 # if $ax_pthread_check_cond
371 # error "$ax_pthread_check_macro must be defined"
372 # endif
373 static void routine(void *a) { a = 0; }
374 static void *start_routine(void *a) { return a; }],
375 [pthread_t th; pthread_attr_t attr;
376 pthread_create(&th, 0, start_routine, 0);
377 pthread_join(th, 0);
378 pthread_attr_init(&attr);
379 pthread_cleanup_push(routine, 0);
380 pthread_cleanup_pop(0) /* ; */])],
381 [ax_pthread_ok=yes],
382 [])
383
384 CFLAGS="$ax_pthread_save_CFLAGS"
385 LIBS="$ax_pthread_save_LIBS"
386
387 AC_MSG_RESULT([$ax_pthread_ok])
388 AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
389
390 PTHREAD_LIBS=""
391 PTHREAD_CFLAGS=""
392 done
393 fi
394
395 # Various other checks:
396 if test "x$ax_pthread_ok" = "xyes"; then
397 ax_pthread_save_CFLAGS="$CFLAGS"
398 ax_pthread_save_LIBS="$LIBS"
399 CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
400 LIBS="$PTHREAD_LIBS $LIBS"
401
402 # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
403 AC_CACHE_CHECK([for joinable pthread attribute],
404 [ax_cv_PTHREAD_JOINABLE_ATTR],
405 [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
406 for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
407 AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
408 [int attr = $ax_pthread_attr; return attr /* ; */])],
409 [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
410 [])
411 done
412 ])
413 AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
414 test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
415 test "x$ax_pthread_joinable_attr_defined" != "xyes"],
416 [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
417 [$ax_cv_PTHREAD_JOINABLE_ATTR],
418 [Define to necessary symbol if this constant
419 uses a non-standard name on your system.])
420 ax_pthread_joinable_attr_defined=yes
421 ])
422
423 AC_CACHE_CHECK([whether more special flags are required for pthreads],
424 [ax_cv_PTHREAD_SPECIAL_FLAGS],
425 [ax_cv_PTHREAD_SPECIAL_FLAGS=no
426 case $host_os in
427 solaris*)
428 ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
429 ;;
430 esac
431 ])
432 AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
433 test "x$ax_pthread_special_flags_added" != "xyes"],
434 [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
435 ax_pthread_special_flags_added=yes])
436
437 AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
438 [ax_cv_PTHREAD_PRIO_INHERIT],
439 [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
440 [[int i = PTHREAD_PRIO_INHERIT;]])],
441 [ax_cv_PTHREAD_PRIO_INHERIT=yes],
442 [ax_cv_PTHREAD_PRIO_INHERIT=no])
443 ])
444 AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
445 test "x$ax_pthread_prio_inherit_defined" != "xyes"],
446 [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
447 ax_pthread_prio_inherit_defined=yes
448 ])
449
450 CFLAGS="$ax_pthread_save_CFLAGS"
451 LIBS="$ax_pthread_save_LIBS"
452
453 # More AIX lossage: compile with *_r variant
454 if test "x$GCC" != "xyes"; then
455 case $host_os in
456 aix*)
457 AS_CASE(["x/$CC"],
458 [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
459 [#handle absolute path differently from PATH based program lookup
460 AS_CASE(["x$CC"],
461 [x/*],
462 [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
463 [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
464 ;;
465 esac
466 fi
467 fi
468
469 test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
470
471 AC_SUBST([PTHREAD_LIBS])
472 AC_SUBST([PTHREAD_CFLAGS])
473 AC_SUBST([PTHREAD_CC])
474
475 # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
476 if test "x$ax_pthread_ok" = "xyes"; then
477 ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
478 :
479 else
480 ax_pthread_ok=no
481 $2
482 fi
483 AC_LANG_POP
484 ])dnl AX_PTHREAD
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_require_defined.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_REQUIRE_DEFINED(MACRO)
7 #
8 # DESCRIPTION
9 #
10 # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
11 # been defined and thus are available for use. This avoids random issues
12 # where a macro isn't expanded. Instead the configure script emits a
13 # non-fatal:
14 #
15 # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
16 #
17 # It's like AC_REQUIRE except it doesn't expand the required macro.
18 #
19 # Here's an example:
20 #
21 # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
22 #
23 # LICENSE
24 #
25 # Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
26 #
27 # Copying and distribution of this file, with or without modification, are
28 # permitted in any medium without royalty provided the copyright notice
29 # and this notice are preserved. This file is offered as-is, without any
30 # warranty.
31
32 #serial 1
33
34 AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
35 m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
36 ])dnl AX_REQUIRE_DEFINED
0 # ===========================================================================
1 # http://www.gnu.org/software/autoconf-archive/ax_sanitizers.html
2 # ===========================================================================
3 #
4 # SYNOPSIS
5 #
6 # AX_SANITIZERS([SANITIZERS], [ENABLED-BY-DEFAULT], [ACTION-SUCCESS])
7 #
8 # DESCRIPTION
9 #
10 # Offers users to enable one or more sanitizers (see
11 # https://github.com/google/sanitizers) with the corresponding
12 # --enable-<sanitizer>-sanitizer option.
13 #
14 # SANITIZERS is a whitespace-separated list of sanitizers to offer via
15 # --enable-<sanitizer>-sanitizer options, e.g. "address memory" for the
16 # address sanitizer and the memory sanitizer. If SANITIZERS is not specified,
17 # all known sanitizers to AX_SANITIZERS will be offered, which at the time of
18 # writing are "address memory undefined".
19 # NOTE that SANITIZERS is expanded at autoconf time, not at configure time,
20 # i.e. you cannot use shell variables in SANITIZERS.
21 #
22 # ENABLED-BY-DEFAULT is a whitespace-separated list of sanitizers which
23 # should be enabled by default, e.g. "memory undefined". Note that not all
24 # sanitizers can be combined, e.g. memory sanitizer cannot be enabled when
25 # address sanitizer is already enabled.
26 # Set ENABLED-BY-DEFAULT to a single whitespace in order to disable all
27 # sanitizers by default.
28 # ENABLED-BY-DEFAULT is expanded at configure time, so you can use shell
29 # variables.
30 #
31 # ACTION-SUCCESS allows to specify shell commands to execute on success, i.e.
32 # when one of the sanitizers was successfully enabled. This is a good place
33 # to call AC_DEFINE for any precompiler constants you might need to make your
34 # code play nice with sanitizers.
35 #
36 # The variable ax_enabled_sanitizers contains a whitespace-separated list of
37 # all enabled sanitizers, so that you can print them at the end of configure,
38 # if you wish.
39 #
40 # The additional --enable-sanitizers option allows users to enable/disable
41 # all sanitizers, effectively overriding ENABLED-BY-DEFAULT.
42 #
43 # EXAMPLES
44 #
45 # AX_SANITIZERS([address])
46 # dnl offer users to enable address sanitizer via --enable-address-sanitizer
47 #
48 # is_debug_build=…
49 # if test "x$is_debug_build" = "xyes"; then
50 # default_sanitizers="address memory"
51 # else
52 # default_sanitizers=
53 # fi
54 # AX_SANITIZERS([address memory], [$default_sanitizers])
55 # dnl enable address sanitizer and memory sanitizer by default for debug
56 # dnl builds, e.g. when building from git instead of a dist tarball.
57 #
58 # AX_SANITIZERS(, , [
59 # AC_DEFINE([SANITIZERS_ENABLED],
60 # [],
61 # [At least one sanitizer was enabled])])
62 # dnl enable all sanitizers known to AX_SANITIZERS by default and set the
63 # dnl SANITIZERS_ENABLED precompiler constant.
64 #
65 # AX_SANITIZERS(, [ ])
66 # dnl provide all sanitizers, but enable none by default.
67 #
68 # LICENSE
69 #
70 # Copyright (c) 2016 Michael Stapelberg <michael@i3wm.org>
71 #
72 # Copying and distribution of this file, with or without modification,
73 # are permitted in any medium without royalty provided the copyright
74 # notice and this notice are preserved. This file is offered as-is,
75 # without any warranty.
76
77 AC_DEFUN([AX_SANITIZERS],
78 [AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
79 AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
80 AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
81 AC_ARG_ENABLE(sanitizers,
82 AS_HELP_STRING(
83 [--enable-sanitizers],
84 [enable all known sanitizers]),
85 [ax_sanitizers_default=$enableval],
86 [ax_sanitizers_default=])
87 ax_enabled_sanitizers=
88 m4_foreach_w([mysan], m4_default($1, [address memory undefined]), [
89 dnl If ax_sanitizers_default is unset, i.e. the user neither explicitly
90 dnl enabled nor explicitly disabled all sanitizers, we get the default value
91 dnl for this sanitizer based on whether it is listed in ENABLED-BY-DEFAULT.
92 AS_IF([test "x$ax_sanitizers_default" = "x"], [dnl
93 ax_sanitizer_default=
94 for mycheck in m4_default([$2], [address memory undefined]); do
95 AS_IF([test "x$mycheck" = "x[]mysan"], [ax_sanitizer_default=yes])
96 done
97 AS_IF([test "x$ax_sanitizer_default" = "x"], [ax_sanitizer_default=no])
98 ],
99 [ax_sanitizer_default=$ax_sanitizers_default])
100 AC_ARG_ENABLE(mysan[]-sanitizer,
101 AS_HELP_STRING(
102 [--enable-[]mysan[]-sanitizer],
103 [enable -fsanitize=mysan]),
104 [ax_sanitizer_enabled=$enableval],
105 [ax_sanitizer_enabled=$ax_sanitizer_default])
106
107 AS_IF([test "x$ax_sanitizer_enabled" = "xyes"], [
108 dnl Not using AX_APPEND_COMPILE_FLAGS and AX_APPEND_LINK_FLAGS because they
109 dnl lack the ability to specify ACTION-SUCCESS.
110 AX_CHECK_COMPILE_FLAG([-fsanitize=[]mysan], [
111 AX_CHECK_LINK_FLAG([-fsanitize=[]mysan], [
112 AX_APPEND_FLAG([-fsanitize=[]mysan], [])
113 dnl If and only if libtool is being used, LDFLAGS needs to contain -Wc,-fsanitize=….
114 dnl See e.g. https://sources.debian.net/src/systemd/231-7/configure.ac/?hl=128#L135
115 dnl TODO: how can recognize that situation and add -Wc,?
116 AX_APPEND_FLAG([-fsanitize=[]mysan], [LDFLAGS])
117 dnl TODO: add -fPIE -pie for memory
118 # -fno-omit-frame-pointer results in nicer stack traces in error
119 # messages, see http://clang.llvm.org/docs/AddressSanitizer.html#usage
120 AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], [
121 AX_APPEND_FLAG([-fno-omit-frame-pointer], [])])
122 dnl TODO: at least for clang, we should specify exactly -O1, not -O2 or -O0, so that performance is reasonable but stacktraces are not tampered with (due to inlining), see http://clang.llvm.org/docs/AddressSanitizer.html#usage
123 m4_default([$3], :)
124 ax_enabled_sanitizers="[]mysan $ax_enabled_sanitizers"
125 ])
126 ])
127 ])
128 ])dnl
129 ])dnl AX_SANITIZERS
0 ifdef::doctype-manpage[]
1 ifdef::backend-docbook[]
2 [header]
3 template::[header-declarations]
4 <refentry>
5 <refmeta>
6 <refentrytitle>{mantitle}</refentrytitle>
7 <manvolnum>{manvolnum}</manvolnum>
8 <refmiscinfo class="source">i3</refmiscinfo>
9 <refmiscinfo class="version">@PACKAGE_VERSION@</refmiscinfo>
10 <refmiscinfo class="manual">i3 Manual</refmiscinfo>
11 </refmeta>
12 <refnamediv>
13 <refname>{manname}</refname>
14 <refpurpose>{manpurpose}</refpurpose>
15 </refnamediv>
16 endif::backend-docbook[]
17 endif::doctype-manpage[]
0 '\" t
1 .\" Title: i3-config-wizard
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-CONFIG\-WIZARD" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-config-wizard \- creates a keysym based config based on your layout
31 .SH "SYNOPSIS"
32 .sp
33 i3\-config\-wizard [\fB\-s\fR \fIsocket\fR] [\fB\-m\fR \fImodifier\fR] [\fB\-v\fR] [\fB\-h\fR]
34 .SH "OPTIONS"
35 .PP
36 \fB\-s, \-\-socket\fR \fIsocket\fR
37 .RS 4
38 Overwrites the path to the i3 IPC socket\&.
39 .RE
40 .PP
41 \fB\-m, \-\-modifier\fR \fImodifier\fR
42 .RS 4
43 Generates the configuration file headlessly\&. Accepts win or alt\&.
44 .RE
45 .PP
46 \fB\-v, \-\-version\fR
47 .RS 4
48 Display version number and exit\&.
49 .RE
50 .PP
51 \fB\-h, \-\-help\fR
52 .RS 4
53 Display a short help message and exit\&.
54 .RE
55 .SH "FILES"
56 .SS "/etc/i3/config\&.keycodes"
57 .sp
58 This file contains the default configuration with keycodes\&. All the bindcode lines will be transformed to bindsym and the user\-specified modifier will be used\&.
59 .SH "DESCRIPTION"
60 .sp
61 i3\-config\-wizard is started by i3 in its default config, unless /\&.i3/config exists\&. i3\-config\-wizard creates a keysym based i3 config file (based on /etc/i3/config\&.keycodes) in /\&.i3/config\&.
62 .sp
63 The advantage of using keysyms is that the config file is easy to read, understand and modify\&. However, if we shipped with a keysym based default config file, the key positions would not be consistent across different keyboard layouts (take for example the homerow for movement)\&. Therefore, we ship with a keycode based default config and let the wizard transform it according to your current keyboard layout\&.
64 .SH "SEE ALSO"
65 .sp
66 i3(1)
67 .SH "AUTHOR"
68 .sp
69 Michael Stapelberg and contributors
0 i3-config-wizard(1)
1 ===================
2 Michael Stapelberg <michael+i3@stapelberg.de>
3 v4.0, July 2011
4
5 == NAME
6
7 i3-config-wizard - creates a keysym based config based on your layout
8
9 == SYNOPSIS
10
11 i3-config-wizard [*-s* 'socket'] [*-m* 'modifier'] [*-v*] [*-h*]
12
13 == OPTIONS
14
15 *-s, --socket* 'socket'::
16 Overwrites the path to the i3 IPC socket.
17
18 *-m, --modifier* 'modifier'::
19 Generates the configuration file headlessly. Accepts win or alt.
20
21 *-v, --version*::
22 Display version number and exit.
23
24 *-h, --help*::
25 Display a short help message and exit.
26
27 == FILES
28
29 === /etc/i3/config.keycodes
30
31 This file contains the default configuration with keycodes. All the bindcode
32 lines will be transformed to bindsym and the user-specified modifier will be
33 used.
34
35 == DESCRIPTION
36
37 i3-config-wizard is started by i3 in its default config, unless ~/.i3/config
38 exists. i3-config-wizard creates a keysym based i3 config file (based on
39 /etc/i3/config.keycodes) in ~/.i3/config.
40
41 The advantage of using keysyms is that the config file is easy to read,
42 understand and modify. However, if we shipped with a keysym based default
43 config file, the key positions would not be consistent across different
44 keyboard layouts (take for example the homerow for movement). Therefore, we
45 ship with a keycode based default config and let the wizard transform it
46 according to your current keyboard layout.
47
48 == SEE ALSO
49
50 i3(1)
51
52 == AUTHOR
53
54 Michael Stapelberg and contributors
0 .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
1 .\"
2 .\" Standard preamble:
3 .\" ========================================================================
4 .de Sp \" Vertical space (when we can't use .PP)
5 .if t .sp .5v
6 .if n .sp
7 ..
8 .de Vb \" Begin verbatim text
9 .ft CW
10 .nf
11 .ne \\$1
12 ..
13 .de Ve \" End verbatim text
14 .ft R
15 .fi
16 ..
17 .\" Set up some character translations and predefined strings. \*(-- will
18 .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
19 .\" double quote, and \*(R" will give a right double quote. \*(C+ will
20 .\" give a nicer C++. Capital omega is used to do unbreakable dashes and
21 .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
22 .\" nothing in troff, for use with C<>.
23 .tr \(*W-
24 .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
25 .ie n \{\
26 . ds -- \(*W-
27 . ds PI pi
28 . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
29 . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
30 . ds L" ""
31 . ds R" ""
32 . ds C` ""
33 . ds C' ""
34 'br\}
35 .el\{\
36 . ds -- \|\(em\|
37 . ds PI \(*p
38 . ds L" ``
39 . ds R" ''
40 . ds C`
41 . ds C'
42 'br\}
43 .\"
44 .\" Escape single quotes in literal strings from groff's Unicode transform.
45 .ie \n(.g .ds Aq \(aq
46 .el .ds Aq '
47 .\"
48 .\" If the F register is >0, we'll generate index entries on stderr for
49 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
50 .\" entries marked with X<> in POD. Of course, you'll have to process the
51 .\" output yourself in some meaningful fashion.
52 .\"
53 .\" Avoid warning from groff about undefined register 'F'.
54 .de IX
55 ..
56 .nr rF 0
57 .if \n(.g .if rF .nr rF 1
58 .if (\n(rF:(\n(.g==0)) \{\
59 . if \nF \{\
60 . de IX
61 . tm Index:\\$1\t\\n%\t"\\$2"
62 ..
63 . if !\nF==2 \{\
64 . nr % 0
65 . nr F 2
66 . \}
67 . \}
68 .\}
69 .rr rF
70 .\" ========================================================================
71 .\"
72 .IX Title "I3-DMENU-DESKTOP 1"
73 .TH I3-DMENU-DESKTOP 1 "2019-01-27" "perl v5.28.1" "User Contributed Perl Documentation"
74 .\" For nroff, turn off justification. Always turn off hyphenation; it makes
75 .\" way too many mistakes in technical documents.
76 .if n .ad l
77 .nh
78 .SH "NAME"
79 .Vb 1
80 \& i3\-dmenu\-desktop \- run .desktop files with dmenu
81 .Ve
82 .SH "SYNOPSIS"
83 .IX Header "SYNOPSIS"
84 .Vb 1
85 \& i3\-dmenu\-desktop [\-\-dmenu=\*(Aqdmenu \-i\*(Aq] [\-\-entry\-type=name]
86 .Ve
87 .SH "DESCRIPTION"
88 .IX Header "DESCRIPTION"
89 i3\-dmenu\-desktop is a script which extracts the (localized) name from
90 application .desktop files, offers the user a choice via \fBdmenu\fR\|(1) and then
91 starts the chosen application via i3 (for startup notification support).
92 The advantage of using .desktop files instead of \fBdmenu_run\fR\|(1) is that dmenu_run
93 offers \fBall\fR binaries in your \f(CW$PATH\fR, including non-interactive utilities like
94 \&\*(L"sed\*(R". Also, .desktop files contain a proper name, information about whether
95 the application runs in a terminal and whether it supports startup
96 notifications.
97 .PP
98 The .desktop files are searched in \f(CW$XDG_DATA_HOME\fR/applications (by default
99 \&\f(CW$HOME\fR/.local/share/applications) and in the \*(L"applications\*(R" subdirectory of each
100 entry of \f(CW$XDG_DATA_DIRS\fR (by default /usr/local/share/:/usr/share/).
101 .PP
102 Files with the same name in \f(CW$XDG_DATA_HOME\fR/applications take precedence over
103 files in \f(CW$XDG_DATA_DIRS\fR, so that you can overwrite parts of the system-wide
104 \&.desktop files by copying them to your local directory and making changes.
105 .PP
106 i3\-dmenu\-desktop displays the \*(L"Name\*(R" value in the localized version depending
107 on \s-1LC_MESSAGES\s0 as specified in the Desktop Entry Specification.
108 .PP
109 You can pass a filename or \s-1URL\s0 (%f/%F and \f(CW%u\fR/%U field codes in the .desktop
110 file respectively) by appending it to the name of the application. E.g., if you
111 want to launch \*(L"\s-1GNU\s0 Emacs 24\*(R" with the patch /tmp/foobar.txt, you would type
112 \&\*(L"emacs\*(R", press \s-1TAB,\s0 type \*(L" /tmp/foobar.txt\*(R" and press \s-1ENTER.\s0
113 .PP
114 \&.desktop files with Terminal=true are started using \fBi3\-sensible\-terminal\fR\|(1).
115 .PP
116 \&.desktop files with NoDisplay=true or Hidden=true are skipped.
117 .PP
118 \&\s-1UTF\-8\s0 is supported, of course, but dmenu does not support displaying all
119 glyphs. E.g., xfce4\-terminal.desktop's Name[fi]=Pääte will be displayed just
120 fine, but not its Name[ru]=Терминал.
121 .SH "OPTIONS"
122 .IX Header "OPTIONS"
123 .IP "\fB\-\-dmenu=command\fR" 4
124 .IX Item "--dmenu=command"
125 Execute command instead of 'dmenu \-i'. This option can be used to pass custom
126 parameters to dmenu, or to make i3\-dmenu\-desktop start a custom (patched?)
127 version of dmenu.
128 .IP "\fB\-\-entry\-type=type\fR" 4
129 .IX Item "--entry-type=type"
130 Display the (localized) \*(L"Name\*(R" (type = name), the command (type = command) or
131 the (*.desktop) filename (type = filename) in dmenu. This option can be
132 specified multiple times.
133 .Sp
134 Examples are \*(L"\s-1GNU\s0 Image Manipulation Program\*(R" (type = name), \*(L"gimp\*(R" (type =
135 command), and \*(L"libreoffice-writer\*(R" (type = filename).
136 .SH "VERSION"
137 .IX Header "VERSION"
138 Version 1.5
139 .SH "AUTHOR"
140 .IX Header "AUTHOR"
141 Michael Stapelberg, \f(CW\*(C`<michael at i3wm.org>\*(C'\fR
142 .SH "LICENSE AND COPYRIGHT"
143 .IX Header "LICENSE AND COPYRIGHT"
144 Copyright 2012 Michael Stapelberg.
145 .PP
146 This program is free software; you can redistribute it and/or modify it
147 under the terms of the \s-1BSD\s0 license.
0 '\" t
1 .\" Title: i3-dump-log
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-DUMP\-LOG" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-dump-log \- dumps the i3 SHM log
31 .SH "SYNOPSIS"
32 .sp
33 i3\-dump\-log [\-s <socketpath>] [\-f]
34 .SH "DESCRIPTION"
35 .sp
36 Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store full debug log output\&. This is extremely helpful for bugreports and figuring out what is going on, without permanently logging to a file\&.
37 .sp
38 With i3\-dump\-log, you can dump the SHM log to stdout\&.
39 .sp
40 The \-f flag works like tail \-f, i\&.e\&. the process does not terminate after dumping the log, but prints new lines as they appear\&.
41 .SH "EXAMPLE"
42 .sp
43 i3\-dump\-log | gzip \-9 > /tmp/i3\-log\&.gz
44 .SH "SEE ALSO"
45 .sp
46 i3(1)
47 .SH "AUTHOR"
48 .sp
49 Michael Stapelberg and contributors
0 i3-dump-log(1)
1 ==============
2 Michael Stapelberg <michael@i3wm.org>
3 v4.6, September 2013
4
5 == NAME
6
7 i3-dump-log - dumps the i3 SHM log
8
9 == SYNOPSIS
10
11 i3-dump-log [-s <socketpath>] [-f]
12
13 == DESCRIPTION
14
15 Debug versions of i3 automatically use 1% of your RAM (but 25 MiB max) to store
16 full debug log output. This is extremely helpful for bugreports and
17 figuring out what is going on, without permanently logging to a file.
18
19 With i3-dump-log, you can dump the SHM log to stdout.
20
21 The -f flag works like tail -f, i.e. the process does not terminate after
22 dumping the log, but prints new lines as they appear.
23
24 == EXAMPLE
25
26 i3-dump-log | gzip -9 > /tmp/i3-log.gz
27
28 == SEE ALSO
29
30 i3(1)
31
32 == AUTHOR
33
34 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3-input
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-INPUT" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-input \- interactively take a command for i3 window manager
31 .SH "SYNOPSIS"
32 .sp
33 i3\-input [\-s <socket>] [\-F <format>] [\-l <limit>] [\-P <prompt>] [\-f <font>] [\-v]
34 .SH "DESCRIPTION"
35 .sp
36 i3\-input is a tool to take commands (or parts of a command) composed by the user, and send it/them to i3\&. This is useful, for example, for the mark/goto command\&.
37 .sp
38 You can press Escape to close i3\-input without sending any commands\&.
39 .SH "OPTIONS"
40 .PP
41 \-s <socket>
42 .RS 4
43 Specify the path to the i3 IPC socket (it should not be necessary to use this option, i3\-input will figure out the path on its own)\&.
44 .RE
45 .PP
46 \-F <format>
47 .RS 4
48 Every occurrence of "%s" in the <format> string is replaced by the user input, and the result is sent to i3 as a command\&. Default value is "%s"\&.
49 .RE
50 .PP
51 \-l <limit>
52 .RS 4
53 Set the maximum allowed length of the user input to <limit> characters\&. i3\-input will automatically issue the command when the user input reaches that length\&.
54 .RE
55 .PP
56 \-P <prompt>
57 .RS 4
58 Display the <prompt> string in front of user input text field\&. The prompt string is not included in the user input/command\&.
59 .RE
60 .PP
61 \-f <font>
62 .RS 4
63 Use the specified X11 core font (use
64 xfontsel
65 to chose a font)\&.
66 .RE
67 .PP
68 \-v
69 .RS 4
70 Show version and exit\&.
71 .RE
72 .SH "EXAMPLES"
73 .sp
74 Mark a container with a single character:
75 .sp
76 .if n \{\
77 .RS 4
78 .\}
79 .nf
80 i3\-input \-F \*(Aqmark %s\*(Aq \-l 1 \-P \*(AqMark: \*(Aq
81 .fi
82 .if n \{\
83 .RE
84 .\}
85 .sp
86 Go to the container marked with above example:
87 .sp
88 .if n \{\
89 .RS 4
90 .\}
91 .nf
92 i3\-input \-F \*(Aq[con_mark="%s"] focus\*(Aq \-l 1 \-P \*(AqGo to: \*(Aq
93 .fi
94 .if n \{\
95 .RE
96 .\}
97 .SH "ENVIRONMENT"
98 .SS "I3SOCK"
99 .sp
100 i3\-input handles the different sources of socket paths in the following order:
101 .sp
102 .RS 4
103 .ie n \{\
104 \h'-04'\(bu\h'+03'\c
105 .\}
106 .el \{\
107 .sp -1
108 .IP \(bu 2.3
109 .\}
110 I3SOCK environment variable
111 .RE
112 .sp
113 .RS 4
114 .ie n \{\
115 \h'-04'\(bu\h'+03'\c
116 .\}
117 .el \{\
118 .sp -1
119 .IP \(bu 2.3
120 .\}
121 I3SOCK gets overwritten by the \-s parameter, if specified
122 .RE
123 .sp
124 .RS 4
125 .ie n \{\
126 \h'-04'\(bu\h'+03'\c
127 .\}
128 .el \{\
129 .sp -1
130 .IP \(bu 2.3
131 .\}
132 if neither are available, i3\-input reads the socket path from the X11 property, which is the recommended way
133 .RE
134 .sp
135 .RS 4
136 .ie n \{\
137 \h'-04'\(bu\h'+03'\c
138 .\}
139 .el \{\
140 .sp -1
141 .IP \(bu 2.3
142 .\}
143 if everything fails, i3\-input tries
144 /tmp/i3\-ipc\&.sock
145 .RE
146 .sp
147 The socket path is necessary to connect to i3 and actually issue the command\&.
148 .SH "SEE ALSO"
149 .sp
150 i3(1)
151 .SH "AUTHOR"
152 .sp
153 Michael Stapelberg and contributors
0 i3-input(1)
1 ===========
2 Michael Stapelberg <michael+i3@stapelberg.de>
3 v4.1.2, April 2012
4
5 == NAME
6
7 i3-input - interactively take a command for i3 window manager
8
9 == SYNOPSIS
10
11 i3-input [-s <socket>] [-F <format>] [-l <limit>] [-P <prompt>] [-f <font>] [-v]
12
13 == DESCRIPTION
14
15 i3-input is a tool to take commands (or parts of a command) composed by
16 the user, and send it/them to i3. This is useful, for example, for the
17 mark/goto command.
18
19 You can press Escape to close i3-input without sending any commands.
20
21 == OPTIONS
22
23 -s <socket>::
24 Specify the path to the i3 IPC socket (it should not be necessary to use this
25 option, i3-input will figure out the path on its own).
26
27 -F <format>::
28 Every occurrence of "%s" in the <format> string is replaced by the user input,
29 and the result is sent to i3 as a command. Default value is "%s".
30
31 -l <limit>::
32 Set the maximum allowed length of the user input to <limit> characters.
33 i3-input will automatically issue the command when the user input reaches that
34 length.
35
36 -P <prompt>::
37 Display the <prompt> string in front of user input text field.
38 The prompt string is not included in the user input/command.
39
40 -f <font>::
41 Use the specified X11 core font (use +xfontsel+ to chose a font).
42
43 -v::
44 Show version and exit.
45
46 == EXAMPLES
47
48 Mark a container with a single character:
49 ------------------------------------------------
50 i3-input -F 'mark %s' -l 1 -P 'Mark: '
51 ------------------------------------------------
52
53 Go to the container marked with above example:
54 -----------------------------------------------------
55 i3-input -F '[con_mark="%s"] focus' -l 1 -P 'Go to: '
56 -----------------------------------------------------
57
58 == ENVIRONMENT
59
60 === I3SOCK
61
62 i3-input handles the different sources of socket paths in the following order:
63
64 * I3SOCK environment variable
65 * I3SOCK gets overwritten by the -s parameter, if specified
66 * if neither are available, i3-input reads the socket path from the X11
67 property, which is the recommended way
68 * if everything fails, i3-input tries +/tmp/i3-ipc.sock+
69
70 The socket path is necessary to connect to i3 and actually issue the command.
71
72 == SEE ALSO
73
74 i3(1)
75
76 == AUTHOR
77
78 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3-migrate-config-to-v4
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-MIGRATE\-CONFIG\" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-migrate-config-to-v4 \- migrates your i3 config file
31 .SH "SYNOPSIS"
32 .sp
33 .nf
34 mv ~/\&.i3/config ~/\&.i3/old\&.config
35 i3\-migrate\-config\-to\-v4 ~/\&.i3/old\&.config > ~/\&.i3/config
36 .fi
37 .SH "DESCRIPTION"
38 .sp
39 i3\-migrate\-config\-to\-v4 is a Perl script which migrates your old (< version 4) configuration files to a version 4 config file\&. The most significant changes are the new commands (see the release notes)\&.
40 .sp
41 This script will automatically be run by i3 when it detects an old config file\&. Please migrate your config file as soon as possible\&. We plan to include this script in all i3 release until 2012\-08\-01\&. Afterwards, old config files will no longer be supported\&.
42 .SH "SEE ALSO"
43 .sp
44 i3(1)
45 .SH "AUTHOR"
46 .sp
47 Michael Stapelberg and contributors
0 i3-migrate-config-to-v4(1)
1 ==========================
2 Michael Stapelberg <michael+i3@stapelberg.de>
3 v4.0, July 2011
4
5 == NAME
6
7 i3-migrate-config-to-v4 - migrates your i3 config file
8
9 == SYNOPSIS
10
11 -------------------------------------------------------
12 mv ~/.i3/config ~/.i3/old.config
13 i3-migrate-config-to-v4 ~/.i3/old.config > ~/.i3/config
14 -------------------------------------------------------
15
16 == DESCRIPTION
17
18 i3-migrate-config-to-v4 is a Perl script which migrates your old (< version 4)
19 configuration files to a version 4 config file. The most significant changes
20 are the new commands (see the release notes).
21
22 This script will automatically be run by i3 when it detects an old config file.
23 Please migrate your config file as soon as possible. We plan to include this
24 script in all i3 release until 2012-08-01. Afterwards, old config files will no
25 longer be supported.
26
27 == SEE ALSO
28
29 i3(1)
30
31 == AUTHOR
32
33 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3-msg
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-MSG" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-msg \- send messages to i3 window manager
31 .SH "SYNOPSIS"
32 .sp
33 i3\-msg [\-q] [\-v] [\-h] [\-s socket] [\-t type] [message]
34 .SH "OPTIONS"
35 .PP
36 \fB\-q, \-\-quiet\fR
37 .RS 4
38 Only send ipc message and suppress the output of the response\&.
39 .RE
40 .PP
41 \fB\-v, \-\-version\fR
42 .RS 4
43 Display version number and exit\&.
44 .RE
45 .PP
46 \fB\-h, \-\-help\fR
47 .RS 4
48 Display a short help\-message and exit\&.
49 .RE
50 .PP
51 \fB\-s, \-\-socket\fR \fIsock_path\fR
52 .RS 4
53 i3\-msg will use the environment variable I3SOCK or the socket path given here\&. If both fail, it will try to get the socket information from the root window and then try /tmp/i3\-ipc\&.sock before exiting with an error\&.
54 .RE
55 .PP
56 \fB\-t\fR \fItype\fR
57 .RS 4
58 Send ipc message, see below\&. This option defaults to "command"\&.
59 .RE
60 .PP
61 \fB\-m\fR, \fB\-\-monitor\fR
62 .RS 4
63 Instead of exiting right after receiving the first subscribed event, wait indefinitely for all of them\&. Can only be used with "\-t subscribe"\&. See the "subscribe" IPC message type below for details\&.
64 .RE
65 .PP
66 \fBmessage\fR
67 .RS 4
68 Send ipc message, see below\&.
69 .RE
70 .SH "IPC MESSAGE TYPES"
71 .PP
72 command
73 .RS 4
74 The payload of the message is a command for i3 (like the commands you can bind to keys in the configuration file) and will be executed directly after receiving it\&.
75 .RE
76 .PP
77 get_workspaces
78 .RS 4
79 Gets the current workspaces\&. The reply will be a JSON\-encoded list of workspaces\&.
80 .RE
81 .PP
82 get_outputs
83 .RS 4
84 Gets the current outputs\&. The reply will be a JSON\-encoded list of outputs (see the reply section of docs/ipc, e\&.g\&. at
85 \m[blue]\fBhttps://i3wm\&.org/docs/ipc\&.html#_receiving_replies_from_i3\fR\m[])\&.
86 .RE
87 .PP
88 get_tree
89 .RS 4
90 Gets the layout tree\&. i3 uses a tree as data structure which includes every container\&. The reply will be the JSON\-encoded tree\&.
91 .RE
92 .PP
93 get_marks
94 .RS 4
95 Gets a list of marks (identifiers for containers to easily jump to them later)\&. The reply will be a JSON\-encoded list of window marks\&.
96 .RE
97 .PP
98 get_bar_config
99 .RS 4
100 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\&.
101 .RE
102 .PP
103 get_binding_modes
104 .RS 4
105 Gets a list of configured binding modes\&.
106 .RE
107 .PP
108 get_version
109 .RS 4
110 Gets the version of i3\&. The reply will be a JSON\-encoded dictionary with the major, minor, patch and human\-readable version\&.
111 .RE
112 .PP
113 get_config
114 .RS 4
115 Gets the currently loaded i3 configuration\&.
116 .RE
117 .PP
118 send_tick
119 .RS 4
120 Sends a tick to all IPC connections which subscribe to tick events\&.
121 .RE
122 .PP
123 subscribe
124 .RS 4
125 The payload of the message describes the events to subscribe to\&. Upon reception, each event will be dumped as a JSON\-encoded object\&. See the \-m option for continuous monitoring\&.
126 .RE
127 .SH "DESCRIPTION"
128 .sp
129 i3\-msg is a sample implementation for a client using the unix socket IPC interface to i3\&.
130 .SH "EXAMPLES"
131 .sp
132 .if n \{\
133 .RS 4
134 .\}
135 .nf
136 # Use 1\-px border for current client
137 i3\-msg "border 1pixel"
138
139 # You can leave out the quotes
140 i3\-msg border normal
141
142 # Dump the layout tree
143 i3\-msg \-t get_tree
144
145 # Monitor window changes
146 i3\-msg \-t subscribe \-m \*(Aq[ "window" ]\*(Aq
147 .fi
148 .if n \{\
149 .RE
150 .\}
151 .SH "ENVIRONMENT"
152 .SS "I3SOCK"
153 .sp
154 If no ipc\-socket is specified on the commandline, this variable is used to determine the path, at which the unix domain socket is expected, on which to connect to i3\&.
155 .SH "SEE ALSO"
156 .sp
157 i3(1)
158 .SH "AUTHOR"
159 .sp
160 Michael Stapelberg and contributors
0 i3-msg(1)
1 =========
2 Michael Stapelberg <michael@i3wm.org>
3 v4.2, August 2012
4
5 == NAME
6
7 i3-msg - send messages to i3 window manager
8
9 == SYNOPSIS
10
11 i3-msg [-q] [-v] [-h] [-s socket] [-t type] [message]
12
13 == OPTIONS
14
15 *-q, --quiet*::
16 Only send ipc message and suppress the output of the response.
17
18 *-v, --version*::
19 Display version number and exit.
20
21 *-h, --help*::
22 Display a short help-message and exit.
23
24 *-s, --socket* 'sock_path'::
25 i3-msg will use the environment variable I3SOCK or the socket path
26 given here. If both fail, it will try to get the socket information
27 from the root window and then try /tmp/i3-ipc.sock before exiting
28 with an error.
29
30 *-t* 'type'::
31 Send ipc message, see below. This option defaults to "command".
32
33 *-m*, *--monitor*::
34 Instead of exiting right after receiving the first subscribed event,
35 wait indefinitely for all of them. Can only be used with "-t subscribe".
36 See the "subscribe" IPC message type below for details.
37
38 *message*::
39 Send ipc message, see below.
40
41 == IPC MESSAGE TYPES
42
43 command::
44 The payload of the message is a command for i3 (like the commands you can bind
45 to keys in the configuration file) and will be executed directly after
46 receiving it.
47
48 get_workspaces::
49 Gets the current workspaces. The reply will be a JSON-encoded list of
50 workspaces.
51
52 get_outputs::
53 Gets the current outputs. The reply will be a JSON-encoded list of outputs (see
54 the reply section of docs/ipc, e.g. at
55 https://i3wm.org/docs/ipc.html#_receiving_replies_from_i3).
56
57 get_tree::
58 Gets the layout tree. i3 uses a tree as data structure which includes every
59 container. The reply will be the JSON-encoded tree.
60
61 get_marks::
62 Gets a list of marks (identifiers for containers to easily jump to them later).
63 The reply will be a JSON-encoded list of window marks.
64
65 get_bar_config::
66 Gets the configuration (as JSON map) of the workspace bar with the given ID. If
67 no ID is provided, an array with all configured bar IDs is returned instead.
68
69 get_binding_modes::
70 Gets a list of configured binding modes.
71
72 get_version::
73 Gets the version of i3. The reply will be a JSON-encoded dictionary with the
74 major, minor, patch and human-readable version.
75
76 get_config::
77 Gets the currently loaded i3 configuration.
78
79 send_tick::
80 Sends a tick to all IPC connections which subscribe to tick events.
81
82 subscribe::
83 The payload of the message describes the events to subscribe to.
84 Upon reception, each event will be dumped as a JSON-encoded object.
85 See the -m option for continuous monitoring.
86
87 == DESCRIPTION
88
89 i3-msg is a sample implementation for a client using the unix socket IPC
90 interface to i3.
91
92 == EXAMPLES
93
94 ------------------------------------------------
95 # Use 1-px border for current client
96 i3-msg "border 1pixel"
97
98 # You can leave out the quotes
99 i3-msg border normal
100
101 # Dump the layout tree
102 i3-msg -t get_tree
103
104 # Monitor window changes
105 i3-msg -t subscribe -m '[ "window" ]'
106 ------------------------------------------------
107
108 == ENVIRONMENT
109
110 === I3SOCK
111
112 If no ipc-socket is specified on the commandline, this variable is used
113 to determine the path, at which the unix domain socket is expected, on which
114 to connect to i3.
115
116 == SEE ALSO
117
118 i3(1)
119
120 == AUTHOR
121
122 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3-nagbar
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-NAGBAR" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-nagbar \- displays an error bar on top of your screen
31 .SH "SYNOPSIS"
32 .sp
33 i3\-nagbar [\-m <message>] [\-b <button> <action>] [\-B <button> <action>] [\-t warning|error] [\-f <font>] [\-v]
34 .SH "OPTIONS"
35 .PP
36 \fB\-v, \-\-version\fR
37 .RS 4
38 Display version number and exit\&.
39 .RE
40 .PP
41 \fB\-h, \-\-help\fR
42 .RS 4
43 Display a short help\-message and exit\&.
44 .RE
45 .PP
46 \fB\-t, \-\-type\fR \fItype\fR
47 .RS 4
48 Display either a warning or error message\&. This only changes the color scheme for the i3\-nagbar\&. Default: error\&.
49 .RE
50 .PP
51 \fB\-m, \-\-message\fR \fImessage\fR
52 .RS 4
53 Display
54 \fImessage\fR
55 as text on the left of the i3\-nagbar\&.
56 .RE
57 .PP
58 \fB\-f, \-\-font\fR \fIfont\fR
59 .RS 4
60 Select font that is being used\&.
61 .RE
62 .PP
63 \fB\-b, \-\-button\fR \fIbutton\fR \fIaction\fR
64 .RS 4
65 Create a button with text
66 \fIbutton\fR\&. The
67 \fIaction\fR
68 are the shell commands that will be executed by this button\&. Multiple buttons can be defined\&. Will launch the shell commands inside a terminal emulator, using i3\-sensible\-terminal\&.
69 .RE
70 .PP
71 \fB\-B, \-\-button\-no\-terminal\fR \fIbutton\fR \fIaction\fR
72 .RS 4
73 Same as above, but will execute the shell commands directly, without launching a terminal emulator\&.
74 .RE
75 .SH "DESCRIPTION"
76 .sp
77 i3\-nagbar is used by i3 to tell you about errors in your configuration file (for example)\&. While these errors are logged to the logfile (if any), the past has proven that users are either not aware of their logfile or do not check it after modifying the configuration file\&.
78 .SH "EXAMPLE"
79 .sp
80 .if n \{\
81 .RS 4
82 .\}
83 .nf
84 i3\-nagbar \-m \*(AqYou have an error in your i3 config file!\*(Aq \e
85 \-b \*(Aqedit config\*(Aq \*(Aqi3\-sensible\-editor ~/\&.config/i3/config\*(Aq
86 .fi
87 .if n \{\
88 .RE
89 .\}
90 .SH "SEE ALSO"
91 .sp
92 i3(1)
93 .SH "AUTHOR"
94 .sp
95 Michael Stapelberg and contributors
0 i3-nagbar(1)
1 ============
2 Michael Stapelberg <michael+i3@stapelberg.de>
3 v4.0, July 2011
4
5 == NAME
6
7 i3-nagbar - displays an error bar on top of your screen
8
9 == SYNOPSIS
10
11 i3-nagbar [-m <message>] [-b <button> <action>] [-B <button> <action>] [-t warning|error] [-f <font>] [-v]
12
13 == OPTIONS
14
15 *-v, --version*::
16 Display version number and exit.
17
18 *-h, --help*::
19 Display a short help-message and exit.
20
21 *-t, --type* 'type'::
22 Display either a warning or error message. This only changes the color scheme
23 for the i3-nagbar. Default: error.
24
25 *-m, --message* 'message'::
26 Display 'message' as text on the left of the i3-nagbar.
27
28 *-f, --font* 'font'::
29 Select font that is being used.
30
31 *-b, --button* 'button' 'action'::
32 Create a button with text 'button'. The 'action' are the shell commands that
33 will be executed by this button. Multiple buttons can be defined.
34 Will launch the shell commands inside a terminal emulator, using
35 i3-sensible-terminal.
36
37 *-B, --button-no-terminal* 'button' 'action'::
38 Same as above, but will execute the shell commands directly, without launching a
39 terminal emulator.
40
41 == DESCRIPTION
42
43 i3-nagbar is used by i3 to tell you about errors in your configuration file
44 (for example). While these errors are logged to the logfile (if any), the past
45 has proven that users are either not aware of their logfile or do not check it
46 after modifying the configuration file.
47
48 == EXAMPLE
49
50 ------------------------------------------------
51 i3-nagbar -m 'You have an error in your i3 config file!' \
52 -b 'edit config' 'i3-sensible-editor ~/.config/i3/config'
53 ------------------------------------------------
54
55 == SEE ALSO
56
57 i3(1)
58
59 == AUTHOR
60
61 Michael Stapelberg and contributors
0 .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
1 .\"
2 .\" Standard preamble:
3 .\" ========================================================================
4 .de Sp \" Vertical space (when we can't use .PP)
5 .if t .sp .5v
6 .if n .sp
7 ..
8 .de Vb \" Begin verbatim text
9 .ft CW
10 .nf
11 .ne \\$1
12 ..
13 .de Ve \" End verbatim text
14 .ft R
15 .fi
16 ..
17 .\" Set up some character translations and predefined strings. \*(-- will
18 .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
19 .\" double quote, and \*(R" will give a right double quote. \*(C+ will
20 .\" give a nicer C++. Capital omega is used to do unbreakable dashes and
21 .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
22 .\" nothing in troff, for use with C<>.
23 .tr \(*W-
24 .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
25 .ie n \{\
26 . ds -- \(*W-
27 . ds PI pi
28 . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
29 . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
30 . ds L" ""
31 . ds R" ""
32 . ds C` ""
33 . ds C' ""
34 'br\}
35 .el\{\
36 . ds -- \|\(em\|
37 . ds PI \(*p
38 . ds L" ``
39 . ds R" ''
40 . ds C`
41 . ds C'
42 'br\}
43 .\"
44 .\" Escape single quotes in literal strings from groff's Unicode transform.
45 .ie \n(.g .ds Aq \(aq
46 .el .ds Aq '
47 .\"
48 .\" If the F register is >0, we'll generate index entries on stderr for
49 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
50 .\" entries marked with X<> in POD. Of course, you'll have to process the
51 .\" output yourself in some meaningful fashion.
52 .\"
53 .\" Avoid warning from groff about undefined register 'F'.
54 .de IX
55 ..
56 .nr rF 0
57 .if \n(.g .if rF .nr rF 1
58 .if (\n(rF:(\n(.g==0)) \{\
59 . if \nF \{\
60 . de IX
61 . tm Index:\\$1\t\\n%\t"\\$2"
62 ..
63 . if !\nF==2 \{\
64 . nr % 0
65 . nr F 2
66 . \}
67 . \}
68 .\}
69 .rr rF
70 .\" ========================================================================
71 .\"
72 .IX Title "I3-SAVE-TREE 1"
73 .TH I3-SAVE-TREE 1 "2019-01-27" "perl v5.28.1" "User Contributed Perl Documentation"
74 .\" For nroff, turn off justification. Always turn off hyphenation; it makes
75 .\" way too many mistakes in technical documents.
76 .if n .ad l
77 .nh
78 .SH "NAME"
79 .Vb 1
80 \& i3\-save\-tree \- save (parts of) the layout tree for restoring
81 .Ve
82 .SH "SYNOPSIS"
83 .IX Header "SYNOPSIS"
84 .Vb 1
85 \& i3\-save\-tree [\-\-workspace=name|number] [\-\-output=name]
86 .Ve
87 .SH "DESCRIPTION"
88 .IX Header "DESCRIPTION"
89 Dumps a workspace (or an entire output) to stdout. The data is supposed to be
90 edited a bit by a human, then later fed to i3 via the append_layout command.
91 .PP
92 The append_layout command will create placeholder windows, arranged in the
93 layout the input file specifies. Each container should have a swallows
94 specification. When a window is mapped (made visible on the screen) that
95 matches the specification, i3 will put it into that place and kill the
96 placeholder.
97 .PP
98 If neither argument is specified, the currently focused workspace will be used.
99 .SH "OPTIONS"
100 .IX Header "OPTIONS"
101 .IP "\fB\-\-workspace=name|number\fR" 4
102 .IX Item "--workspace=name|number"
103 Specifies the workspace that should be dumped, e.g. 1. This can either be a
104 name or the number of a workspace.
105 .IP "\fB\-\-output=name\fR" 4
106 .IX Item "--output=name"
107 Specifies the output that should be dumped, e.g. \s-1LVDS\-1.\s0
108 .SH "VERSION"
109 .IX Header "VERSION"
110 Version 0.1
111 .SH "AUTHOR"
112 .IX Header "AUTHOR"
113 Michael Stapelberg, \f(CW\*(C`<michael at i3wm.org>\*(C'\fR
114 .SH "LICENSE AND COPYRIGHT"
115 .IX Header "LICENSE AND COPYRIGHT"
116 Copyright 2013 Michael Stapelberg.
117 .PP
118 This program is free software; you can redistribute it and/or modify it
119 under the terms of the \s-1BSD\s0 license.
0 '\" t
1 .\" Title: i3-sensible-editor
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-SENSIBLE\-EDITOR" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-sensible-editor \- launches $EDITOR with fallbacks
31 .SH "SYNOPSIS"
32 .sp
33 i3\-sensible\-editor [arguments]
34 .SH "DESCRIPTION"
35 .sp
36 i3\-sensible\-editor is used by i3\-nagbar(1) when you click on the edit button\&.
37 .sp
38 It tries to start one of the following (in that order):
39 .sp
40 .RS 4
41 .ie n \{\
42 \h'-04'\(bu\h'+03'\c
43 .\}
44 .el \{\
45 .sp -1
46 .IP \(bu 2.3
47 .\}
48 $VISUAL
49 .RE
50 .sp
51 .RS 4
52 .ie n \{\
53 \h'-04'\(bu\h'+03'\c
54 .\}
55 .el \{\
56 .sp -1
57 .IP \(bu 2.3
58 .\}
59 $EDITOR
60 .RE
61 .sp
62 .RS 4
63 .ie n \{\
64 \h'-04'\(bu\h'+03'\c
65 .\}
66 .el \{\
67 .sp -1
68 .IP \(bu 2.3
69 .\}
70 nano
71 .RE
72 .sp
73 .RS 4
74 .ie n \{\
75 \h'-04'\(bu\h'+03'\c
76 .\}
77 .el \{\
78 .sp -1
79 .IP \(bu 2.3
80 .\}
81 nvim
82 .RE
83 .sp
84 .RS 4
85 .ie n \{\
86 \h'-04'\(bu\h'+03'\c
87 .\}
88 .el \{\
89 .sp -1
90 .IP \(bu 2.3
91 .\}
92 vim
93 .RE
94 .sp
95 .RS 4
96 .ie n \{\
97 \h'-04'\(bu\h'+03'\c
98 .\}
99 .el \{\
100 .sp -1
101 .IP \(bu 2.3
102 .\}
103 vi
104 .RE
105 .sp
106 .RS 4
107 .ie n \{\
108 \h'-04'\(bu\h'+03'\c
109 .\}
110 .el \{\
111 .sp -1
112 .IP \(bu 2.3
113 .\}
114 emacs
115 .RE
116 .sp
117 .RS 4
118 .ie n \{\
119 \h'-04'\(bu\h'+03'\c
120 .\}
121 .el \{\
122 .sp -1
123 .IP \(bu 2.3
124 .\}
125 pico
126 .RE
127 .sp
128 .RS 4
129 .ie n \{\
130 \h'-04'\(bu\h'+03'\c
131 .\}
132 .el \{\
133 .sp -1
134 .IP \(bu 2.3
135 .\}
136 qe
137 .RE
138 .sp
139 .RS 4
140 .ie n \{\
141 \h'-04'\(bu\h'+03'\c
142 .\}
143 .el \{\
144 .sp -1
145 .IP \(bu 2.3
146 .\}
147 mg
148 .RE
149 .sp
150 .RS 4
151 .ie n \{\
152 \h'-04'\(bu\h'+03'\c
153 .\}
154 .el \{\
155 .sp -1
156 .IP \(bu 2.3
157 .\}
158 jed
159 .RE
160 .sp
161 .RS 4
162 .ie n \{\
163 \h'-04'\(bu\h'+03'\c
164 .\}
165 .el \{\
166 .sp -1
167 .IP \(bu 2.3
168 .\}
169 gedit
170 .RE
171 .sp
172 .RS 4
173 .ie n \{\
174 \h'-04'\(bu\h'+03'\c
175 .\}
176 .el \{\
177 .sp -1
178 .IP \(bu 2.3
179 .\}
180 mcedit
181 .RE
182 .sp
183 .RS 4
184 .ie n \{\
185 \h'-04'\(bu\h'+03'\c
186 .\}
187 .el \{\
188 .sp -1
189 .IP \(bu 2.3
190 .\}
191 gvim
192 .RE
193 .sp
194 Please don\(cqt complain about the order: If the user has any preference, they will have $VISUAL or $EDITOR set\&.
195 .SH "SEE ALSO"
196 .sp
197 i3(1)
198 .SH "AUTHOR"
199 .sp
200 Michael Stapelberg and contributors
0 i3-sensible-editor(1)
1 =====================
2 Michael Stapelberg <michael+i3@stapelberg.de>
3 v4.1, November 2011
4
5 == NAME
6
7 i3-sensible-editor - launches $EDITOR with fallbacks
8
9 == SYNOPSIS
10
11 i3-sensible-editor [arguments]
12
13 == DESCRIPTION
14
15 i3-sensible-editor is used by i3-nagbar(1) when you click on the edit button.
16
17 It tries to start one of the following (in that order):
18
19 * $VISUAL
20 * $EDITOR
21 * nano
22 * nvim
23 * vim
24 * vi
25 * emacs
26 * pico
27 * qe
28 * mg
29 * jed
30 * gedit
31 * mcedit
32 * gvim
33
34 Please don’t complain about the order: If the user has any preference, they will
35 have $VISUAL or $EDITOR set.
36
37 == SEE ALSO
38
39 i3(1)
40
41 == AUTHOR
42
43 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3-sensible-pager
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-SENSIBLE\-PAGER" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-sensible-pager \- launches $PAGER with fallbacks
31 .SH "SYNOPSIS"
32 .sp
33 i3\-sensible\-pager [arguments]
34 .SH "DESCRIPTION"
35 .sp
36 i3\-sensible\-pager is used by i3\-nagbar(1) when you click on the view button\&.
37 .sp
38 It tries to start one of the following (in that order):
39 .sp
40 .RS 4
41 .ie n \{\
42 \h'-04'\(bu\h'+03'\c
43 .\}
44 .el \{\
45 .sp -1
46 .IP \(bu 2.3
47 .\}
48 $PAGER
49 .RE
50 .sp
51 .RS 4
52 .ie n \{\
53 \h'-04'\(bu\h'+03'\c
54 .\}
55 .el \{\
56 .sp -1
57 .IP \(bu 2.3
58 .\}
59 less
60 .RE
61 .sp
62 .RS 4
63 .ie n \{\
64 \h'-04'\(bu\h'+03'\c
65 .\}
66 .el \{\
67 .sp -1
68 .IP \(bu 2.3
69 .\}
70 most
71 .RE
72 .sp
73 .RS 4
74 .ie n \{\
75 \h'-04'\(bu\h'+03'\c
76 .\}
77 .el \{\
78 .sp -1
79 .IP \(bu 2.3
80 .\}
81 w3m
82 .RE
83 .sp
84 .RS 4
85 .ie n \{\
86 \h'-04'\(bu\h'+03'\c
87 .\}
88 .el \{\
89 .sp -1
90 .IP \(bu 2.3
91 .\}
92 i3\-sensible\-editor(1)
93 .RE
94 .sp
95 Please don\(cqt complain about the order: If the user has any preference, they will have $PAGER set\&.
96 .SH "SEE ALSO"
97 .sp
98 i3(1)
99 .SH "AUTHOR"
100 .sp
101 Michael Stapelberg and contributors
0 i3-sensible-pager(1)
1 ====================
2 Michael Stapelberg <michael+i3@stapelberg.de>
3 v4.1, November 2011
4
5 == NAME
6
7 i3-sensible-pager - launches $PAGER with fallbacks
8
9 == SYNOPSIS
10
11 i3-sensible-pager [arguments]
12
13 == DESCRIPTION
14
15 i3-sensible-pager is used by i3-nagbar(1) when you click on the view button.
16
17 It tries to start one of the following (in that order):
18
19 * $PAGER
20 * less
21 * most
22 * w3m
23 * i3-sensible-editor(1)
24
25 Please don’t complain about the order: If the user has any preference, they will
26 have $PAGER set.
27
28 == SEE ALSO
29
30 i3(1)
31
32 == AUTHOR
33
34 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3-sensible-terminal
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3\-SENSIBLE\-TERMIN" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3-sensible-terminal \- launches $TERMINAL with fallbacks
31 .SH "SYNOPSIS"
32 .sp
33 i3\-sensible\-terminal [arguments]
34 .SH "DESCRIPTION"
35 .sp
36 i3\-sensible\-terminal is invoked in the i3 default config to start a terminal\&. This wrapper script is necessary since there is no distribution\-independent terminal launcher (but for example Debian has x\-terminal\-emulator)\&. Distribution packagers are responsible for shipping this script in a way which is appropriate for the distribution\&.
37 .sp
38 It tries to start one of the following (in that order):
39 .sp
40 .RS 4
41 .ie n \{\
42 \h'-04'\(bu\h'+03'\c
43 .\}
44 .el \{\
45 .sp -1
46 .IP \(bu 2.3
47 .\}
48 $TERMINAL (this is a non\-standard variable)
49 .RE
50 .sp
51 .RS 4
52 .ie n \{\
53 \h'-04'\(bu\h'+03'\c
54 .\}
55 .el \{\
56 .sp -1
57 .IP \(bu 2.3
58 .\}
59 x\-terminal\-emulator (only present on Debian and derivatives)
60 .RE
61 .sp
62 .RS 4
63 .ie n \{\
64 \h'-04'\(bu\h'+03'\c
65 .\}
66 .el \{\
67 .sp -1
68 .IP \(bu 2.3
69 .\}
70 urxvt
71 .RE
72 .sp
73 .RS 4
74 .ie n \{\
75 \h'-04'\(bu\h'+03'\c
76 .\}
77 .el \{\
78 .sp -1
79 .IP \(bu 2.3
80 .\}
81 rxvt
82 .RE
83 .sp
84 .RS 4
85 .ie n \{\
86 \h'-04'\(bu\h'+03'\c
87 .\}
88 .el \{\
89 .sp -1
90 .IP \(bu 2.3
91 .\}
92 termit
93 .RE
94 .sp
95 .RS 4
96 .ie n \{\
97 \h'-04'\(bu\h'+03'\c
98 .\}
99 .el \{\
100 .sp -1
101 .IP \(bu 2.3
102 .\}
103 terminator
104 .RE
105 .sp
106 .RS 4
107 .ie n \{\
108 \h'-04'\(bu\h'+03'\c
109 .\}
110 .el \{\
111 .sp -1
112 .IP \(bu 2.3
113 .\}
114 Eterm
115 .RE
116 .sp
117 .RS 4
118 .ie n \{\
119 \h'-04'\(bu\h'+03'\c
120 .\}
121 .el \{\
122 .sp -1
123 .IP \(bu 2.3
124 .\}
125 aterm
126 .RE
127 .sp
128 .RS 4
129 .ie n \{\
130 \h'-04'\(bu\h'+03'\c
131 .\}
132 .el \{\
133 .sp -1
134 .IP \(bu 2.3
135 .\}
136 uxterm
137 .RE
138 .sp
139 .RS 4
140 .ie n \{\
141 \h'-04'\(bu\h'+03'\c
142 .\}
143 .el \{\
144 .sp -1
145 .IP \(bu 2.3
146 .\}
147 xterm
148 .RE
149 .sp
150 .RS 4
151 .ie n \{\
152 \h'-04'\(bu\h'+03'\c
153 .\}
154 .el \{\
155 .sp -1
156 .IP \(bu 2.3
157 .\}
158 gnome\-terminal
159 .RE
160 .sp
161 .RS 4
162 .ie n \{\
163 \h'-04'\(bu\h'+03'\c
164 .\}
165 .el \{\
166 .sp -1
167 .IP \(bu 2.3
168 .\}
169 roxterm
170 .RE
171 .sp
172 .RS 4
173 .ie n \{\
174 \h'-04'\(bu\h'+03'\c
175 .\}
176 .el \{\
177 .sp -1
178 .IP \(bu 2.3
179 .\}
180 xfce4\-terminal
181 .RE
182 .sp
183 .RS 4
184 .ie n \{\
185 \h'-04'\(bu\h'+03'\c
186 .\}
187 .el \{\
188 .sp -1
189 .IP \(bu 2.3
190 .\}
191 termite
192 .RE
193 .sp
194 .RS 4
195 .ie n \{\
196 \h'-04'\(bu\h'+03'\c
197 .\}
198 .el \{\
199 .sp -1
200 .IP \(bu 2.3
201 .\}
202 lxterminal
203 .RE
204 .sp
205 .RS 4
206 .ie n \{\
207 \h'-04'\(bu\h'+03'\c
208 .\}
209 .el \{\
210 .sp -1
211 .IP \(bu 2.3
212 .\}
213 mate\-terminal
214 .RE
215 .sp
216 .RS 4
217 .ie n \{\
218 \h'-04'\(bu\h'+03'\c
219 .\}
220 .el \{\
221 .sp -1
222 .IP \(bu 2.3
223 .\}
224 terminology
225 .RE
226 .sp
227 .RS 4
228 .ie n \{\
229 \h'-04'\(bu\h'+03'\c
230 .\}
231 .el \{\
232 .sp -1
233 .IP \(bu 2.3
234 .\}
235 st
236 .RE
237 .sp
238 .RS 4
239 .ie n \{\
240 \h'-04'\(bu\h'+03'\c
241 .\}
242 .el \{\
243 .sp -1
244 .IP \(bu 2.3
245 .\}
246 qterminal
247 .RE
248 .sp
249 .RS 4
250 .ie n \{\
251 \h'-04'\(bu\h'+03'\c
252 .\}
253 .el \{\
254 .sp -1
255 .IP \(bu 2.3
256 .\}
257 lilyterm
258 .RE
259 .sp
260 .RS 4
261 .ie n \{\
262 \h'-04'\(bu\h'+03'\c
263 .\}
264 .el \{\
265 .sp -1
266 .IP \(bu 2.3
267 .\}
268 tilix
269 .RE
270 .sp
271 .RS 4
272 .ie n \{\
273 \h'-04'\(bu\h'+03'\c
274 .\}
275 .el \{\
276 .sp -1
277 .IP \(bu 2.3
278 .\}
279 terminix
280 .RE
281 .sp
282 .RS 4
283 .ie n \{\
284 \h'-04'\(bu\h'+03'\c
285 .\}
286 .el \{\
287 .sp -1
288 .IP \(bu 2.3
289 .\}
290 konsole
291 .RE
292 .sp
293 .RS 4
294 .ie n \{\
295 \h'-04'\(bu\h'+03'\c
296 .\}
297 .el \{\
298 .sp -1
299 .IP \(bu 2.3
300 .\}
301 kitty
302 .RE
303 .sp
304 .RS 4
305 .ie n \{\
306 \h'-04'\(bu\h'+03'\c
307 .\}
308 .el \{\
309 .sp -1
310 .IP \(bu 2.3
311 .\}
312 guake
313 .RE
314 .sp
315 .RS 4
316 .ie n \{\
317 \h'-04'\(bu\h'+03'\c
318 .\}
319 .el \{\
320 .sp -1
321 .IP \(bu 2.3
322 .\}
323 tilda
324 .RE
325 .sp
326 .RS 4
327 .ie n \{\
328 \h'-04'\(bu\h'+03'\c
329 .\}
330 .el \{\
331 .sp -1
332 .IP \(bu 2.3
333 .\}
334 alacritty
335 .RE
336 .sp
337 .RS 4
338 .ie n \{\
339 \h'-04'\(bu\h'+03'\c
340 .\}
341 .el \{\
342 .sp -1
343 .IP \(bu 2.3
344 .\}
345 hyper
346 .RE
347 .sp
348 Please don\(cqt complain about the order: If the user has any preference, they will have $TERMINAL set or modified their i3 configuration file\&.
349 .SH "SEE ALSO"
350 .sp
351 i3(1)
352 .SH "AUTHOR"
353 .sp
354 Michael Stapelberg and contributors
0 i3-sensible-terminal(1)
1 =======================
2 Michael Stapelberg <michael@i3wm.org>
3 v4.2, August 2012
4
5 == NAME
6
7 i3-sensible-terminal - launches $TERMINAL with fallbacks
8
9 == SYNOPSIS
10
11 i3-sensible-terminal [arguments]
12
13 == DESCRIPTION
14
15 i3-sensible-terminal is invoked in the i3 default config to start a terminal.
16 This wrapper script is necessary since there is no distribution-independent
17 terminal launcher (but for example Debian has x-terminal-emulator).
18 Distribution packagers are responsible for shipping this script in a way which
19 is appropriate for the distribution.
20
21 It tries to start one of the following (in that order):
22
23 * $TERMINAL (this is a non-standard variable)
24 * x-terminal-emulator (only present on Debian and derivatives)
25 * urxvt
26 * rxvt
27 * termit
28 * terminator
29 * Eterm
30 * aterm
31 * uxterm
32 * xterm
33 * gnome-terminal
34 * roxterm
35 * xfce4-terminal
36 * termite
37 * lxterminal
38 * mate-terminal
39 * terminology
40 * st
41 * qterminal
42 * lilyterm
43 * tilix
44 * terminix
45 * konsole
46 * kitty
47 * guake
48 * tilda
49 * alacritty
50 * hyper
51
52 Please don’t complain about the order: If the user has any preference, they will
53 have $TERMINAL set or modified their i3 configuration file.
54
55 == SEE ALSO
56
57 i3(1)
58
59 == AUTHOR
60
61 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3
2 .\" Author: [see the "AUTHOR" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3 \- an improved dynamic, tiling window manager
31 .SH "SYNOPSIS"
32 .sp
33 i3 [\-a] [\-c configfile] [\-C] [\-d all] [\-v] [\-V]
34 .SH "OPTIONS"
35 .PP
36 \-a
37 .RS 4
38 Disables autostart\&.
39 .RE
40 .PP
41 \-c
42 .RS 4
43 Specifies an alternate configuration file path\&.
44 .RE
45 .PP
46 \-C
47 .RS 4
48 Check the configuration file for validity and exit\&.
49 .RE
50 .PP
51 \-d all
52 .RS 4
53 Enables debug logging\&. The
54 \fIall\fR
55 parameter is present for historical reasons\&.
56 .RE
57 .PP
58 \-v
59 .RS 4
60 Display version number (and date of the last commit)\&.
61 .RE
62 .PP
63 \-V
64 .RS 4
65 Be verbose\&.
66 .RE
67 .PP
68 \-\-force\-xinerama
69 .RS 4
70 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\&.
71 .RE
72 .PP
73 \-\-get\-socketpath
74 .RS 4
75 Retrieve the i3 IPC socket path from X11, print it, then exit\&.
76 .RE
77 .PP
78 \-\-shmlog\-size <limit>
79 .RS 4
80 Limits the size of the i3 SHM log to <limit> bytes\&. Setting this to 0 disables SHM logging entirely\&. The default is 0 bytes\&.
81 .RE
82 .SH "DESCRIPTION"
83 .SS "INTRODUCTION"
84 .sp
85 i3 was created because wmii, our favorite window manager at the time, didn\(cqt provide some features we wanted (multi\-monitor done right, for example), had some bugs, didn\(cqt progress since quite some time and wasn\(cqt easy to hack at all (source code comments/documentation completely lacking)\&. Still, we think the wmii developers and contributors did a great job\&. Thank you for inspiring us to create i3\&.
86 .sp
87 Please be aware that i3 is primarily targeted at advanced users and developers\&.
88 .SS "IMPORTANT NOTE TO nVidia BINARY DRIVER USERS"
89 .sp
90 If you are using the nVidia binary graphics driver (also known as \fIblob\fR) before version 302\&.17, you need to use the \-\-force\-xinerama flag (in your ~/\&.xsession) when starting i3, like so:
91 .sp
92 .if n \{\
93 .RS 4
94 .\}
95 .nf
96 exec i3 \-\-force\-xinerama \-V >>~/\&.i3/i3log 2>&1
97 .fi
98 .if n \{\
99 .RE
100 .\}
101 .sp
102 See also docs/multi\-monitor for the full explanation\&.
103 .SS "TERMINOLOGY"
104 .PP
105 Tree
106 .RS 4
107 i3 keeps your layout in a tree data structure\&.
108 .RE
109 .PP
110 Window
111 .RS 4
112 An X11 window, like the Firefox browser window or a terminal emulator\&.
113 .RE
114 .PP
115 Floating Window
116 .RS 4
117 A window which "floats" on top of other windows\&. This style is used by i3 to display X11 windows with type "dialog", such as the "Print" or "Open File" dialog boxes in many GUI applications\&. Use of floating windows can be fine\-tuned with the for_window command (see HTML userguide)\&.
118 .RE
119 .PP
120 Split container
121 .RS 4
122 A split container contains multiple other split containers or windows\&.
123 .sp
124 Containers can be used in various layouts\&. The default mode is called "default" and just resizes each client equally so that it fits\&.
125 .RE
126 .PP
127 Workspace
128 .RS 4
129 A workspace is a set of containers\&. Other window managers call this "Virtual Desktops"\&.
130 .sp
131 In i3, each workspace is assigned to a specific virtual screen\&. By default, screen 1 has workspace 1, screen 2 has workspace 2 and so on\&... However, when you create a new workspace (by simply switching to it), it\(cqll be assigned the screen you are currently on\&.
132 .RE
133 .PP
134 Output
135 .RS 4
136 Using XRandR, you can have an X11 screen spanning multiple real monitors\&. Furthermore, you can set them up in cloning mode or with positions (monitor 1 is left of monitor 2)\&.
137 .sp
138 i3 uses the RandR API to query which outputs are available and which screens are connected to these outputs\&.
139 .RE
140 .SH "KEYBINDINGS"
141 .sp
142 Here is a short overview of the default keybindings:
143 .PP
144 Mod1+Enter
145 .RS 4
146 Open a new terminal emulator window\&.
147 .RE
148 .PP
149 Mod1+d
150 .RS 4
151 Open dmenu for starting any application by typing (part of) its name\&.
152 .RE
153 .PP
154 j/k/l/;
155 .RS 4
156 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\&.
157 .RE
158 .PP
159 Mod1+<direction>
160 .RS 4
161 Focus window in <direction>\&.
162 .RE
163 .PP
164 Mod1+Shift+<direction>
165 .RS 4
166 Move window to <direction>\&.
167 .RE
168 .PP
169 Mod1+<number>
170 .RS 4
171 Switch to workspace <number>\&.
172 .RE
173 .PP
174 Mod1+Shift+<number>
175 .RS 4
176 Move window to workspace <number>\&.
177 .RE
178 .PP
179 Mod1+f
180 .RS 4
181 Toggle fullscreen mode\&.
182 .RE
183 .PP
184 Mod1+s
185 .RS 4
186 Enable stacking layout for the current container\&.
187 .RE
188 .PP
189 Mod1+e
190 .RS 4
191 Enable default layout for the current container\&.
192 .RE
193 .PP
194 Mod1+w
195 .RS 4
196 Enable tabbed layout for the current container\&.
197 .RE
198 .PP
199 Mod1+Shift+Space
200 .RS 4
201 Toggle tiling/floating for the current container\&.
202 .RE
203 .PP
204 Mod1+Space
205 .RS 4
206 Select the first tiling container if the current container is floating and vice\-versa\&.
207 .RE
208 .PP
209 Mod1+Shift+q
210 .RS 4
211 Kills the current window\&. This is equivalent to "clicking on the close button", meaning a polite request to the application to close this window\&. For example, Firefox will save its session upon such a request\&. If the application does not support that, the window will be killed and it depends on the application what happens\&.
212 .RE
213 .PP
214 Mod1+Shift+r
215 .RS 4
216 Restarts i3 in place\&. Your layout will be preserved\&.
217 .RE
218 .PP
219 Mod1+Shift+e
220 .RS 4
221 Exits i3\&.
222 .RE
223 .SH "FILES"
224 .SS "~/\&.i3/config (or ~/\&.config/i3/config)"
225 .sp
226 When starting, i3 looks for configuration files in the following order:
227 .sp
228 .RS 4
229 .ie n \{\
230 \h'-04' 1.\h'+01'\c
231 .\}
232 .el \{\
233 .sp -1
234 .IP " 1." 4.2
235 .\}
236 ~/\&.config/i3/config (or $XDG_CONFIG_HOME/i3/config if set)
237 .RE
238 .sp
239 .RS 4
240 .ie n \{\
241 \h'-04' 2.\h'+01'\c
242 .\}
243 .el \{\
244 .sp -1
245 .IP " 2." 4.2
246 .\}
247 ~/\&.i3/config
248 .RE
249 .sp
250 .RS 4
251 .ie n \{\
252 \h'-04' 3.\h'+01'\c
253 .\}
254 .el \{\
255 .sp -1
256 .IP " 3." 4.2
257 .\}
258 /etc/xdg/i3/config (or $XDG_CONFIG_DIRS/i3/config if set)
259 .RE
260 .sp
261 .RS 4
262 .ie n \{\
263 \h'-04' 4.\h'+01'\c
264 .\}
265 .el \{\
266 .sp -1
267 .IP " 4." 4.2
268 .\}
269 /etc/i3/config
270 .RE
271 .sp
272 You can specify a custom path using the \-c option\&.
273 .PP
274 \fBSample configuration\fR.
275 .sp
276 .if n \{\
277 .RS 4
278 .\}
279 .nf
280 # i3 config file (v4)
281
282 # Font for window titles\&. Will also be used by the bar unless a different font
283 # is used in the bar {} block below\&.
284 # This font is widely installed, provides lots of unicode glyphs, right\-to\-left
285 # text rendering and scalability on retina/hidpi displays (thanks to pango)\&.
286 font pango:DejaVu Sans Mono 8
287 # Before i3 v4\&.8, we used to recommend this one as the default:
288 # font \-misc\-fixed\-medium\-r\-normal\-\-13\-120\-75\-75\-C\-70\-iso10646\-1
289 # The font above is very space\-efficient, that is, it looks good, sharp and
290 # clear in small sizes\&. However, its unicode glyph coverage is limited, the old
291 # X core fonts rendering does not support right\-to\-left and this being a bitmap
292 # font, it doesn\(cqt scale on retina/hidpi displays\&.
293
294 # use Mouse+Mod1 to drag floating windows to their wanted position
295 floating_modifier Mod1
296
297 # start a terminal
298 bindsym Mod1+Return exec /usr/bin/urxvt
299
300 # kill focused window
301 bindsym Mod1+Shift+q kill
302
303 # start dmenu (a program launcher)
304 bindsym Mod1+d exec /usr/bin/dmenu_run
305
306 # change focus
307 bindsym Mod1+j focus left
308 bindsym Mod1+k focus down
309 bindsym Mod1+l focus up
310 bindsym Mod1+semicolon focus right
311
312 # alternatively, you can use the cursor keys:
313 bindsym Mod1+Left focus left
314 bindsym Mod1+Down focus down
315 bindsym Mod1+Up focus up
316 bindsym Mod1+Right focus right
317
318 # move focused window
319 bindsym Mod1+Shift+j move left
320 bindsym Mod1+Shift+k move down
321 bindsym Mod1+Shift+l move up
322 bindsym Mod1+Shift+semicolon move right
323
324 # alternatively, you can use the cursor keys:
325 bindsym Mod1+Shift+Left move left
326 bindsym Mod1+Shift+Down move down
327 bindsym Mod1+Shift+Up move up
328 bindsym Mod1+Shift+Right move right
329
330 # split in horizontal orientation
331 bindsym Mod1+h split h
332
333 # split in vertical orientation
334 bindsym Mod1+v split v
335
336 # enter fullscreen mode for the focused container
337 bindsym Mod1+f fullscreen toggle
338
339 # change container layout (stacked, tabbed, default)
340 bindsym Mod1+s layout stacking
341 bindsym Mod1+w layout tabbed
342 bindsym Mod1+e layout default
343
344 # toggle tiling / floating
345 bindsym Mod1+Shift+space floating toggle
346
347 # change focus between tiling / floating windows
348 bindsym Mod1+space focus mode_toggle
349
350 # focus the parent container
351 bindsym Mod1+a focus parent
352
353 # focus the child container
354 #bindsym Mod1+d focus child
355
356 # switch to workspace
357 bindsym Mod1+1 workspace 1
358 bindsym Mod1+2 workspace 2
359 # \&.\&.
360
361 # move focused container to workspace
362 bindsym Mod1+Shift+1 move workspace 1
363 bindsym Mod1+Shift+2 move workspace 2
364 # \&.\&.\&.
365
366 # reload the configuration file
367 bindsym Mod1+Shift+c reload
368 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
369 bindsym Mod1+Shift+r restart
370 # exit i3 (logs you out of your X session)
371 bindsym Mod1+Shift+e exit
372
373 # display workspace buttons plus a statusline generated by i3status
374 bar {
375 status_command i3status
376 }
377 .fi
378 .if n \{\
379 .RE
380 .\}
381 .sp
382 .SS "~/\&.xsession"
383 .sp
384 This file is where you should configure your locales and start i3\&. It is run by your login manager (xdm, slim, gdm, \&...) as soon as you login\&.
385 .PP
386 \fBSample xsession\fR.
387 .sp
388 .if n \{\
389 .RS 4
390 .\}
391 .nf
392 # Disable DPMS turning off the screen
393 xset \-dpms
394 xset s off
395
396 # Disable bell
397 xset \-b
398
399 # Enable zapping (C\-A\-<Bksp> kills X)
400 setxkbmap \-option terminate:ctrl_alt_bksp
401
402 # Enforce correct locales from the beginning:
403 # LC_ALL is unset since it overwrites everything
404 # LANG=de_DE\&.UTF\-8 is used, except for:
405 # LC_MESSAGES=C never translates program output
406 # LC_TIME=en_DK leads to yyyy\-mm\-dd hh:mm date/time output
407 unset LC_ALL
408 export LANG=de_DE\&.UTF\-8
409 export LC_MESSAGES=C
410 export LC_TIME=en_DK\&.UTF\-8
411
412 # Use XToolkit in java applications
413 export AWT_TOOLKIT=XToolkit
414
415 # Set background color
416 xsetroot \-solid "#333333"
417
418 # Enable core dumps in case something goes wrong
419 ulimit \-c unlimited
420
421 # Start i3 and log to ~/\&.i3/logfile
422 echo "Starting at $(date)" >> ~/\&.i3/logfile
423 exec /usr/bin/i3 \-V \-d all >> ~/\&.i3/logfile
424 .fi
425 .if n \{\
426 .RE
427 .\}
428 .sp
429 .SH "ENVIRONMENT"
430 .SS "I3SOCK"
431 .sp
432 This variable overwrites the IPC socket path (placed in /tmp/i3\-%u\&.XXXXXX/ipc\-socket\&.%p by default, where %u is replaced with your UNIX username, %p is replaced with i3\(cqs PID and XXXXXX is a string of random characters from the portable filename character set (see mkdtemp(3)))\&. The IPC socket is used by external programs like i3\-msg(1) or i3bar(1)\&.
433 .SH "TODO"
434 .sp
435 There is still lot of work to do\&. Please check our bugtracker for up\-to\-date information about tasks which are still not finished\&.
436 .SH "SEE ALSO"
437 .sp
438 You should have a copy of the userguide (featuring nice screenshots/graphics which is why this is not integrated into this manpage), the debugging guide, and the "how to hack" guide\&. If you are building from source, run: make \-C docs
439 .sp
440 You can also access these documents online at \m[blue]\fBhttps://i3wm\&.org/\fR\m[]
441 .sp
442 i3\-input(1), i3\-msg(1), i3bar(1), i3\-nagbar(1), i3\-config\-wizard(1), i3\-migrate\-config\-to\-v4(1)
443 .SH "AUTHOR"
444 .sp
445 Michael Stapelberg and contributors
0 i3(1)
1 =====
2 Michael Stapelberg <michael@i3wm.org>
3 v4.3, September 2012
4
5 == NAME
6
7 i3 - an improved dynamic, tiling window manager
8
9 == SYNOPSIS
10
11 i3 [-a] [-c configfile] [-C] [-d all] [-v] [-V]
12
13 == OPTIONS
14
15 -a::
16 Disables autostart.
17
18 -c::
19 Specifies an alternate configuration file path.
20
21 -C::
22 Check the configuration file for validity and exit.
23
24 -d all::
25 Enables debug logging.
26 The 'all' parameter is present for historical reasons.
27
28 -v::
29 Display version number (and date of the last commit).
30
31 -V::
32 Be verbose.
33
34 --force-xinerama::
35 Use Xinerama instead of RandR. This option should only be used if you are stuck
36 with the old nVidia closed source driver (older than 302.17) which does not
37 support RandR.
38
39 --get-socketpath::
40 Retrieve the i3 IPC socket path from X11, print it, then exit.
41
42 --shmlog-size <limit>::
43 Limits the size of the i3 SHM log to <limit> bytes. Setting this to 0 disables
44 SHM logging entirely. The default is 0 bytes.
45
46 == DESCRIPTION
47
48 === INTRODUCTION
49
50 i3 was created because wmii, our favorite window manager at the time, didn’t
51 provide some features we wanted (multi-monitor done right, for example), had
52 some bugs, didn’t progress since quite some time and wasn’t easy to hack at all
53 (source code comments/documentation completely lacking). Still, we think the
54 wmii developers and contributors did a great job. Thank you for inspiring us to
55 create i3.
56
57 Please be aware that i3 is primarily targeted at advanced users and developers.
58
59 === IMPORTANT NOTE TO nVidia BINARY DRIVER USERS
60
61 If you are using the nVidia binary graphics driver (also known as 'blob')
62 before version 302.17, you need to use the +--force-xinerama+ flag (in your
63 ~/.xsession) when starting i3, like so:
64
65 ----------------------------------------------
66 exec i3 --force-xinerama -V >>~/.i3/i3log 2>&1
67 ----------------------------------------------
68
69 See also docs/multi-monitor for the full explanation.
70
71 === TERMINOLOGY
72
73 Tree::
74 i3 keeps your layout in a tree data structure.
75
76 Window::
77 An X11 window, like the Firefox browser window or a terminal emulator.
78
79 Floating Window::
80 A window which "floats" on top of other windows. This style is used by i3 to
81 display X11 windows with type "dialog", such as the "Print" or "Open File"
82 dialog boxes in many GUI applications. Use of floating windows can be
83 fine-tuned with the for_window command (see HTML userguide).
84
85 Split container::
86 A split container contains multiple other split containers or windows.
87 +
88 Containers can be used in various layouts. The default mode is called "default"
89 and just resizes each client equally so that it fits.
90
91 Workspace::
92 A workspace is a set of containers. Other window managers call this "Virtual
93 Desktops".
94 +
95 In i3, each workspace is assigned to a specific virtual screen. By default,
96 screen 1 has workspace 1, screen 2 has workspace 2 and so on… However, when you
97 create a new workspace (by simply switching to it), it’ll be assigned the
98 screen you are currently on.
99
100 Output::
101 Using XRandR, you can have an X11 screen spanning multiple real monitors.
102 Furthermore, you can set them up in cloning mode or with positions (monitor 1
103 is left of monitor 2).
104 +
105 i3 uses the RandR API to query which outputs are available and which screens
106 are connected to these outputs.
107
108 == KEYBINDINGS
109
110 Here is a short overview of the default keybindings:
111
112 Mod1+Enter::
113 Open a new terminal emulator window.
114
115 Mod1+d::
116 Open dmenu for starting any application by typing (part of) its name.
117
118 j/k/l/;::
119 Direction keys (left, down, up, right). They are on your homerow (see the mark
120 on your "j" key). Alternatively, you can use the cursor keys.
121
122 Mod1+<direction>::
123 Focus window in <direction>.
124
125 Mod1+Shift+<direction>::
126 Move window to <direction>.
127
128 Mod1+<number>::
129 Switch to workspace <number>.
130
131 Mod1+Shift+<number>::
132 Move window to workspace <number>.
133
134 Mod1+f::
135 Toggle fullscreen mode.
136
137 Mod1+s::
138 Enable stacking layout for the current container.
139
140 Mod1+e::
141 Enable default layout for the current container.
142
143 Mod1+w::
144 Enable tabbed layout for the current container.
145
146 Mod1+Shift+Space::
147 Toggle tiling/floating for the current container.
148
149 Mod1+Space::
150 Select the first tiling container if the current container is floating and
151 vice-versa.
152
153 Mod1+Shift+q::
154 Kills the current window. This is equivalent to "clicking on the close button",
155 meaning a polite request to the application to close this window. For example,
156 Firefox will save its session upon such a request. If the application does not
157 support that, the window will be killed and it depends on the application what
158 happens.
159
160 Mod1+Shift+r::
161 Restarts i3 in place. Your layout will be preserved.
162
163 Mod1+Shift+e::
164 Exits i3.
165
166 == FILES
167
168 === \~/.i3/config (or ~/.config/i3/config)
169
170 When starting, i3 looks for configuration files in the following order:
171
172 1. ~/.config/i3/config (or $XDG_CONFIG_HOME/i3/config if set)
173 2. ~/.i3/config
174 3. /etc/xdg/i3/config (or $XDG_CONFIG_DIRS/i3/config if set)
175 4. /etc/i3/config
176
177 You can specify a custom path using the -c option.
178
179 .Sample configuration
180 -------------------------------------------------------------
181 # i3 config file (v4)
182
183 # Font for window titles. Will also be used by the bar unless a different font
184 # is used in the bar {} block below.
185 # This font is widely installed, provides lots of unicode glyphs, right-to-left
186 # text rendering and scalability on retina/hidpi displays (thanks to pango).
187 font pango:DejaVu Sans Mono 8
188 # Before i3 v4.8, we used to recommend this one as the default:
189 # font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
190 # The font above is very space-efficient, that is, it looks good, sharp and
191 # clear in small sizes. However, its unicode glyph coverage is limited, the old
192 # X core fonts rendering does not support right-to-left and this being a bitmap
193 # font, it doesn’t scale on retina/hidpi displays.
194
195 # use Mouse+Mod1 to drag floating windows to their wanted position
196 floating_modifier Mod1
197
198 # start a terminal
199 bindsym Mod1+Return exec /usr/bin/urxvt
200
201 # kill focused window
202 bindsym Mod1+Shift+q kill
203
204 # start dmenu (a program launcher)
205 bindsym Mod1+d exec /usr/bin/dmenu_run
206
207 # change focus
208 bindsym Mod1+j focus left
209 bindsym Mod1+k focus down
210 bindsym Mod1+l focus up
211 bindsym Mod1+semicolon focus right
212
213 # alternatively, you can use the cursor keys:
214 bindsym Mod1+Left focus left
215 bindsym Mod1+Down focus down
216 bindsym Mod1+Up focus up
217 bindsym Mod1+Right focus right
218
219 # move focused window
220 bindsym Mod1+Shift+j move left
221 bindsym Mod1+Shift+k move down
222 bindsym Mod1+Shift+l move up
223 bindsym Mod1+Shift+semicolon move right
224
225 # alternatively, you can use the cursor keys:
226 bindsym Mod1+Shift+Left move left
227 bindsym Mod1+Shift+Down move down
228 bindsym Mod1+Shift+Up move up
229 bindsym Mod1+Shift+Right move right
230
231 # split in horizontal orientation
232 bindsym Mod1+h split h
233
234 # split in vertical orientation
235 bindsym Mod1+v split v
236
237 # enter fullscreen mode for the focused container
238 bindsym Mod1+f fullscreen toggle
239
240 # change container layout (stacked, tabbed, default)
241 bindsym Mod1+s layout stacking
242 bindsym Mod1+w layout tabbed
243 bindsym Mod1+e layout default
244
245 # toggle tiling / floating
246 bindsym Mod1+Shift+space floating toggle
247
248 # change focus between tiling / floating windows
249 bindsym Mod1+space focus mode_toggle
250
251 # focus the parent container
252 bindsym Mod1+a focus parent
253
254 # focus the child container
255 #bindsym Mod1+d focus child
256
257 # switch to workspace
258 bindsym Mod1+1 workspace 1
259 bindsym Mod1+2 workspace 2
260 # ..
261
262 # move focused container to workspace
263 bindsym Mod1+Shift+1 move workspace 1
264 bindsym Mod1+Shift+2 move workspace 2
265 # ...
266
267 # reload the configuration file
268 bindsym Mod1+Shift+c reload
269 # restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
270 bindsym Mod1+Shift+r restart
271 # exit i3 (logs you out of your X session)
272 bindsym Mod1+Shift+e exit
273
274 # display workspace buttons plus a statusline generated by i3status
275 bar {
276 status_command i3status
277 }
278 -------------------------------------------------------------
279
280 === ~/.xsession
281
282 This file is where you should configure your locales and start i3. It is run by
283 your login manager (xdm, slim, gdm, …) as soon as you login.
284
285 .Sample xsession
286 -------------------------------------------------------------
287 # Disable DPMS turning off the screen
288 xset -dpms
289 xset s off
290
291 # Disable bell
292 xset -b
293
294 # Enable zapping (C-A-<Bksp> kills X)
295 setxkbmap -option terminate:ctrl_alt_bksp
296
297 # Enforce correct locales from the beginning:
298 # LC_ALL is unset since it overwrites everything
299 # LANG=de_DE.UTF-8 is used, except for:
300 # LC_MESSAGES=C never translates program output
301 # LC_TIME=en_DK leads to yyyy-mm-dd hh:mm date/time output
302 unset LC_ALL
303 export LANG=de_DE.UTF-8
304 export LC_MESSAGES=C
305 export LC_TIME=en_DK.UTF-8
306
307 # Use XToolkit in java applications
308 export AWT_TOOLKIT=XToolkit
309
310 # Set background color
311 xsetroot -solid "#333333"
312
313 # Enable core dumps in case something goes wrong
314 ulimit -c unlimited
315
316 # Start i3 and log to ~/.i3/logfile
317 echo "Starting at $(date)" >> ~/.i3/logfile
318 exec /usr/bin/i3 -V -d all >> ~/.i3/logfile
319 -------------------------------------------------------------
320
321 == ENVIRONMENT
322
323 === I3SOCK
324
325 This variable overwrites the IPC socket path (placed in
326 /tmp/i3-%u.XXXXXX/ipc-socket.%p by default, where %u is replaced with your UNIX
327 username, %p is replaced with i3’s PID and XXXXXX is a string of random
328 characters from the portable filename character set (see mkdtemp(3))). The IPC
329 socket is used by external programs like i3-msg(1) or i3bar(1).
330
331 == TODO
332
333 There is still lot of work to do. Please check our bugtracker for up-to-date
334 information about tasks which are still not finished.
335
336 == SEE ALSO
337
338 You should have a copy of the userguide (featuring nice screenshots/graphics
339 which is why this is not integrated into this manpage), the debugging guide,
340 and the "how to hack" guide. If you are building from source, run:
341 +make -C docs+
342
343 You can also access these documents online at https://i3wm.org/
344
345 i3-input(1), i3-msg(1), i3bar(1), i3-nagbar(1), i3-config-wizard(1),
346 i3-migrate-config-to-v4(1)
347
348 == AUTHOR
349
350 Michael Stapelberg and contributors
0 '\" t
1 .\" Title: i3bar
2 .\" Author: [see the "AUTHORS" section]
3 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
4 .\" Date: 01/27/2019
5 .\" Manual: i3 Manual
6 .\" Source: i3 4.16.1
7 .\" Language: English
8 .\"
9 .TH "I3BAR" "1" "01/27/2019" "i3 4\&.16\&.1" "i3 Manual"
10 .\" -----------------------------------------------------------------
11 .\" * Define some portability stuff
12 .\" -----------------------------------------------------------------
13 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 .\" http://bugs.debian.org/507673
15 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
16 .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 .ie \n(.g .ds Aq \(aq
18 .el .ds Aq '
19 .\" -----------------------------------------------------------------
20 .\" * set default formatting
21 .\" -----------------------------------------------------------------
22 .\" disable hyphenation
23 .nh
24 .\" disable justification (adjust text to left margin only)
25 .ad l
26 .\" -----------------------------------------------------------------
27 .\" * MAIN CONTENT STARTS HERE *
28 .\" -----------------------------------------------------------------
29 .SH "NAME"
30 i3bar \- xcb\-based status\- and workspace\-bar
31 .SH "SYNOPSIS"
32 .sp
33 \fBi3bar\fR [\fB\-s\fR \fIsock_path\fR] [\fB\-b\fR \fIbar_id\fR] [\fB\-v\fR] [\fB\-h\fR]
34 .SH "WARNING"
35 .sp
36 i3bar will automatically be invoked by i3 for every \fIbar\fR configuration block\&.
37 .sp
38 Starting it manually is usually not what you want to do\&.
39 .sp
40 You have been warned!
41 .SH "OPTIONS"
42 .PP
43 \fB\-s, \-\-socket\fR \fIsock_path\fR
44 .RS 4
45 Overwrites the path to the i3 IPC socket\&.
46 .RE
47 .PP
48 \fB\-b, \-\-bar_id\fR \fIbar_id\fR
49 .RS 4
50 Specifies the bar ID for which to get the configuration from i3\&.
51 .RE
52 .PP
53 \fB\-v, \-\-version\fR
54 .RS 4
55 Display version number and exit\&.
56 .RE
57 .PP
58 \fB\-h, \-\-help\fR
59 .RS 4
60 Display a short help\-message and exit
61 .RE
62 .SH "DESCRIPTION"
63 .sp
64 \fBi3bar\fR displays a bar at the bottom (or top) of your monitor(s) containing workspace switching buttons and a statusline generated by i3status(1) or similar\&. It is automatically invoked (and configured through) i3\&.
65 .sp
66 i3bar supports colors via a JSON protocol starting from v4\&.2, see \m[blue]\fBhttps://i3wm\&.org/docs/i3bar\-protocol\&.html\fR\m[]
67 .SH "ENVIRONMENT"
68 .SS "I3SOCK"
69 .sp
70 Used as a fallback for the i3 IPC socket path if neither the commandline contains an argument nor the I3_SOCKET_PATH property is set on the X11 root window\&.
71 .SH "EXAMPLES"
72 .sp
73 Nothing to see here, move along\&. As stated above, you should not run i3bar manually\&.
74 .sp
75 Instead, see the i3 documentation, especially the User\(cqs Guide\&.
76 .SH "SEE ALSO"
77 .sp
78 i3status(1), j4status(1) or conky(1) for programs generating a statusline\&.
79 .sp
80 dzen2(1) or xmobar(1) for similar programs to i3bar\&.
81 .SH "AUTHORS"
82 .sp
83 Axel Wagner and contributors
0 i3bar(1)
1 ========
2 Axel Wagner <mail+i3bar@merovius.de>
3 v4.1, October 2011
4
5 == NAME
6
7 i3bar - xcb-based status- and workspace-bar
8
9 == SYNOPSIS
10
11 *i3bar* [*-s* 'sock_path'] [*-b* 'bar_id'] [*-v*] [*-h*]
12
13 == WARNING
14
15 i3bar will automatically be invoked by i3 for every 'bar' configuration block.
16
17 Starting it manually is usually not what you want to do.
18
19 You have been warned!
20
21 == OPTIONS
22
23 *-s, --socket* 'sock_path'::
24 Overwrites the path to the i3 IPC socket.
25
26 *-b, --bar_id* 'bar_id'::
27 Specifies the bar ID for which to get the configuration from i3.
28
29 *-v, --version*::
30 Display version number and exit.
31
32 *-h, --help*::
33 Display a short help-message and exit
34
35 == DESCRIPTION
36
37 *i3bar* displays a bar at the bottom (or top) of your monitor(s) containing
38 workspace switching buttons and a statusline generated by i3status(1) or
39 similar. It is automatically invoked (and configured through) i3.
40
41 i3bar supports colors via a JSON protocol starting from v4.2, see
42 https://i3wm.org/docs/i3bar-protocol.html
43
44 == ENVIRONMENT
45
46 === I3SOCK
47
48 Used as a fallback for the i3 IPC socket path if neither the commandline
49 contains an argument nor the I3_SOCKET_PATH property is set on the X11 root
50 window.
51
52 == EXAMPLES
53
54 Nothing to see here, move along. As stated above, you should not run i3bar manually.
55
56 Instead, see the i3 documentation, especially the User’s Guide.
57
58 == SEE ALSO
59
60 +i3status(1)+, +j4status(1)+ or +conky(1)+ for programs generating a statusline.
61
62 +dzen2(1)+ or +xmobar(1)+ for similar programs to i3bar.
63
64 == AUTHORS
65
66 Axel Wagner and contributors
0 #! /bin/sh
1 # Common wrapper for a few potentially missing GNU programs.
2
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 1996-2018 Free Software Foundation, Inc.
6 # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
7
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2, or (at your option)
11 # any later version.
12
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <https://www.gnu.org/licenses/>.
20
21 # As a special exception to the GNU General Public License, if you
22 # distribute this file as part of a program that contains a
23 # configuration script generated by Autoconf, you may include it under
24 # the same distribution terms that you use for the rest of that program.
25
26 if test $# -eq 0; then
27 echo 1>&2 "Try '$0 --help' for more information"
28 exit 1
29 fi
30
31 case $1 in
32
33 --is-lightweight)
34 # Used by our autoconf macros to check whether the available missing
35 # script is modern enough.
36 exit 0
37 ;;
38
39 --run)
40 # Back-compat with the calling convention used by older automake.
41 shift
42 ;;
43
44 -h|--h|--he|--hel|--help)
45 echo "\
46 $0 [OPTION]... PROGRAM [ARGUMENT]...
47
48 Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
49 to PROGRAM being missing or too old.
50
51 Options:
52 -h, --help display this help and exit
53 -v, --version output version information and exit
54
55 Supported PROGRAM values:
56 aclocal autoconf autoheader autom4te automake makeinfo
57 bison yacc flex lex help2man
58
59 Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
60 'g' are ignored when checking the name.
61
62 Send bug reports to <bug-automake@gnu.org>."
63 exit $?
64 ;;
65
66 -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
67 echo "missing $scriptversion (GNU Automake)"
68 exit $?
69 ;;
70
71 -*)
72 echo 1>&2 "$0: unknown '$1' option"
73 echo 1>&2 "Try '$0 --help' for more information"
74 exit 1
75 ;;
76
77 esac
78
79 # Run the given program, remember its exit status.
80 "$@"; st=$?
81
82 # If it succeeded, we are done.
83 test $st -eq 0 && exit 0
84
85 # Also exit now if we it failed (or wasn't found), and '--version' was
86 # passed; such an option is passed most likely to detect whether the
87 # program is present and works.
88 case $2 in --version|--help) exit $st;; esac
89
90 # Exit code 63 means version mismatch. This often happens when the user
91 # tries to use an ancient version of a tool on a file that requires a
92 # minimum version.
93 if test $st -eq 63; then
94 msg="probably too old"
95 elif test $st -eq 127; then
96 # Program was missing.
97 msg="missing on your system"
98 else
99 # Program was found and executed, but failed. Give up.
100 exit $st
101 fi
102
103 perl_URL=https://www.perl.org/
104 flex_URL=https://github.com/westes/flex
105 gnu_software_URL=https://www.gnu.org/software
106
107 program_details ()
108 {
109 case $1 in
110 aclocal|automake)
111 echo "The '$1' program is part of the GNU Automake package:"
112 echo "<$gnu_software_URL/automake>"
113 echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
114 echo "<$gnu_software_URL/autoconf>"
115 echo "<$gnu_software_URL/m4/>"
116 echo "<$perl_URL>"
117 ;;
118 autoconf|autom4te|autoheader)
119 echo "The '$1' program is part of the GNU Autoconf package:"
120 echo "<$gnu_software_URL/autoconf/>"
121 echo "It also requires GNU m4 and Perl in order to run:"
122 echo "<$gnu_software_URL/m4/>"
123 echo "<$perl_URL>"
124 ;;
125 esac
126 }
127
128 give_advice ()
129 {
130 # Normalize program name to check for.
131 normalized_program=`echo "$1" | sed '
132 s/^gnu-//; t
133 s/^gnu//; t
134 s/^g//; t'`
135
136 printf '%s\n' "'$1' is $msg."
137
138 configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
139 case $normalized_program in
140 autoconf*)
141 echo "You should only need it if you modified 'configure.ac',"
142 echo "or m4 files included by it."
143 program_details 'autoconf'
144 ;;
145 autoheader*)
146 echo "You should only need it if you modified 'acconfig.h' or"
147 echo "$configure_deps."
148 program_details 'autoheader'
149 ;;
150 automake*)
151 echo "You should only need it if you modified 'Makefile.am' or"
152 echo "$configure_deps."
153 program_details 'automake'
154 ;;
155 aclocal*)
156 echo "You should only need it if you modified 'acinclude.m4' or"
157 echo "$configure_deps."
158 program_details 'aclocal'
159 ;;
160 autom4te*)
161 echo "You might have modified some maintainer files that require"
162 echo "the 'autom4te' program to be rebuilt."
163 program_details 'autom4te'
164 ;;
165 bison*|yacc*)
166 echo "You should only need it if you modified a '.y' file."
167 echo "You may want to install the GNU Bison package:"
168 echo "<$gnu_software_URL/bison/>"
169 ;;
170 lex*|flex*)
171 echo "You should only need it if you modified a '.l' file."
172 echo "You may want to install the Fast Lexical Analyzer package:"
173 echo "<$flex_URL>"
174 ;;
175 help2man*)
176 echo "You should only need it if you modified a dependency" \
177 "of a man page."
178 echo "You may want to install the GNU Help2man package:"
179 echo "<$gnu_software_URL/help2man/>"
180 ;;
181 makeinfo*)
182 echo "You should only need it if you modified a '.texi' file, or"
183 echo "any other file indirectly affecting the aspect of the manual."
184 echo "You might want to install the Texinfo package:"
185 echo "<$gnu_software_URL/texinfo/>"
186 echo "The spurious makeinfo call might also be the consequence of"
187 echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
188 echo "want to install GNU make:"
189 echo "<$gnu_software_URL/make/>"
190 ;;
191 *)
192 echo "You might have modified some files without having the proper"
193 echo "tools for further handling them. Check the 'README' file, it"
194 echo "often tells you about the needed prerequisites for installing"
195 echo "this package. You may also peek at any GNU archive site, in"
196 echo "case some other package contains this missing '$1' program."
197 ;;
198 esac
199 }
200
201 give_advice "$1" | sed -e '1s/^/WARNING: /' \
202 -e '2,$s/^/ /' >&2
203
204 # Propagate the correct exit status (expected to be 127 for a program
205 # not found, 63 for a program that failed due to version mismatch).
206 exit $st
207
208 # Local variables:
209 # eval: (add-hook 'before-save-hook 'time-stamp)
210 # time-stamp-start: "scriptversion="
211 # time-stamp-format: "%:y-%02m-%02d.%02H"
212 # time-stamp-time-zone: "UTC0"
213 # time-stamp-end: "; # UTC"
214 # End:
0 static void GENERATED_call(const int call_identifier, struct CommandResultIR *result) {
1 switch (call_identifier) {
2 case 0:
3 result->next_state = INITIAL;
4 #ifndef TEST_PARSER
5 cmd_rename_workspace(&current_match, result, "to", get_string("new_name"));
6 #else
7 fprintf(stderr, "cmd_rename_workspace(%s, %s)\n", "to", get_string("new_name"));
8 #endif
9 break;
10 case 1:
11 result->next_state = INITIAL;
12 #ifndef TEST_PARSER
13 cmd_rename_workspace(&current_match, result, NULL, "to");
14 #else
15 fprintf(stderr, "cmd_rename_workspace(NULL, %s)\n", "to");
16 #endif
17 break;
18 case 2:
19 result->next_state = INITIAL;
20 #ifndef TEST_PARSER
21 cmd_rename_workspace(&current_match, result, get_string("old_name"), get_string("new_name"));
22 #else
23 fprintf(stderr, "cmd_rename_workspace(%s, %s)\n", get_string("old_name"), get_string("new_name"));
24 #endif
25 break;
26 case 3:
27 result->next_state = INITIAL;
28 #ifndef TEST_PARSER
29 cmd_rename_workspace(&current_match, result, NULL, get_string("new_name"));
30 #else
31 fprintf(stderr, "cmd_rename_workspace(NULL, %s)\n", get_string("new_name"));
32 #endif
33 break;
34 case 4:
35 result->next_state = INITIAL;
36 #ifndef TEST_PARSER
37 cmd_move_workspace_to_output(&current_match, result, get_string("output"));
38 #else
39 fprintf(stderr, "cmd_move_workspace_to_output(%s)\n", get_string("output"));
40 #endif
41 break;
42 case 5:
43 result->next_state = INITIAL;
44 #ifndef TEST_PARSER
45 cmd_move_con_to_workspace_number(&current_match, result, get_string("number"), get_string("no_auto_back_and_forth"));
46 #else
47 fprintf(stderr, "cmd_move_con_to_workspace_number(%s, %s)\n", get_string("number"), get_string("no_auto_back_and_forth"));
48 #endif
49 break;
50 case 6:
51 result->next_state = INITIAL;
52 #ifndef TEST_PARSER
53 cmd_resize(&current_match, result, get_string("way"), get_string("direction"), get_long("resize_px"), get_long("resize_ppt"));
54 #else
55 fprintf(stderr, "cmd_resize(%s, %s, %ld, %ld)\n", get_string("way"), get_string("direction"), get_long("resize_px"), get_long("resize_ppt"));
56 #endif
57 break;
58 case 7:
59 result->next_state = INITIAL;
60 #ifndef TEST_PARSER
61 cmd_resize(&current_match, result, get_string("way"), get_string("direction"), get_long("resize_px"), get_long("resize_ppt"));
62 #else
63 fprintf(stderr, "cmd_resize(%s, %s, %ld, %ld)\n", get_string("way"), get_string("direction"), get_long("resize_px"), get_long("resize_ppt"));
64 #endif
65 break;
66 case 8:
67 result->next_state = INITIAL;
68 #ifndef TEST_PARSER
69 cmd_move_window_to_position(&current_match, result, get_long("coord_x"), get_long("coord_y"));
70 #else
71 fprintf(stderr, "cmd_move_window_to_position(%ld, %ld)\n", get_long("coord_x"), get_long("coord_y"));
72 #endif
73 break;
74 case 9:
75 result->next_state = INITIAL;
76 #ifndef TEST_PARSER
77 cmd_move_window_to_position(&current_match, result, get_long("coord_x"), get_long("coord_y"));
78 #else
79 fprintf(stderr, "cmd_move_window_to_position(%ld, %ld)\n", get_long("coord_x"), get_long("coord_y"));
80 #endif
81 break;
82 case 10:
83 result->next_state = INITIAL;
84 #ifndef TEST_PARSER
85 cmd_fullscreen(&current_match, result, "toggle", get_string("mode"));
86 #else
87 fprintf(stderr, "cmd_fullscreen(%s, %s)\n", "toggle", get_string("mode"));
88 #endif
89 break;
90 case 11:
91 result->next_state = INITIAL;
92 #ifndef TEST_PARSER
93 cmd_fullscreen(&current_match, result, "toggle", "output");
94 #else
95 fprintf(stderr, "cmd_fullscreen(%s, %s)\n", "toggle", "output");
96 #endif
97 break;
98 case 12:
99 result->next_state = INITIAL;
100 #ifndef TEST_PARSER
101 cmd_move_direction(&current_match, result, get_string("direction"), get_long("pixels"));
102 #else
103 fprintf(stderr, "cmd_move_direction(%s, %ld)\n", get_string("direction"), get_long("pixels"));
104 #endif
105 break;
106 case 13:
107 result->next_state = INITIAL;
108 #ifndef TEST_PARSER
109 cmd_move_direction(&current_match, result, get_string("direction"), get_long("pixels"));
110 #else
111 fprintf(stderr, "cmd_move_direction(%s, %ld)\n", get_string("direction"), get_long("pixels"));
112 #endif
113 break;
114 case 14:
115 result->next_state = INITIAL;
116 #ifndef TEST_PARSER
117 cmd_move_window_to_center(&current_match, result, get_string("method"));
118 #else
119 fprintf(stderr, "cmd_move_window_to_center(%s)\n", get_string("method"));
120 #endif
121 break;
122 case 15:
123 result->next_state = INITIAL;
124 #ifndef TEST_PARSER
125 cmd_move_window_to_mouse(&current_match, result);
126 #else
127 fprintf(stderr, "cmd_move_window_to_mouse()\n");
128 #endif
129 break;
130 case 16:
131 result->next_state = INITIAL;
132 #ifndef TEST_PARSER
133 cmd_move_window_to_mouse(&current_match, result);
134 #else
135 fprintf(stderr, "cmd_move_window_to_mouse()\n");
136 #endif
137 break;
138 case 17:
139 result->next_state = INITIAL;
140 #ifndef TEST_PARSER
141 cmd_move_window_to_mouse(&current_match, result);
142 #else
143 fprintf(stderr, "cmd_move_window_to_mouse()\n");
144 #endif
145 break;
146 case 18:
147 result->next_state = INITIAL;
148 #ifndef TEST_PARSER
149 cmd_workspace_number(&current_match, result, get_string("workspace"), get_string("no_auto_back_and_forth"));
150 #else
151 fprintf(stderr, "cmd_workspace_number(%s, %s)\n", get_string("workspace"), get_string("no_auto_back_and_forth"));
152 #endif
153 break;
154 case 19:
155 result->next_state = INITIAL;
156 #ifndef TEST_PARSER
157 cmd_fullscreen(&current_match, result, get_string("action"), get_string("mode"));
158 #else
159 fprintf(stderr, "cmd_fullscreen(%s, %s)\n", get_string("action"), get_string("mode"));
160 #endif
161 break;
162 case 20:
163 result->next_state = INITIAL;
164 #ifndef TEST_PARSER
165 cmd_fullscreen(&current_match, result, get_string("action"), "output");
166 #else
167 fprintf(stderr, "cmd_fullscreen(%s, %s)\n", get_string("action"), "output");
168 #endif
169 break;
170 case 21:
171 result->next_state = INITIAL;
172 #ifndef TEST_PARSER
173 cmd_move_direction(&current_match, result, get_string("direction"), 10);
174 #else
175 fprintf(stderr, "cmd_move_direction(%s, %d)\n", get_string("direction"), 10);
176 #endif
177 break;
178 case 22:
179 result->next_state = INITIAL;
180 #ifndef TEST_PARSER
181 cmd_move_con_to_output(&current_match, result, get_string("output"));
182 #else
183 fprintf(stderr, "cmd_move_con_to_output(%s)\n", get_string("output"));
184 #endif
185 break;
186 case 23:
187 result->next_state = INITIAL;
188 #ifndef TEST_PARSER
189 cmd_move_con_to_workspace(&current_match, result, get_string("workspace"));
190 #else
191 fprintf(stderr, "cmd_move_con_to_workspace(%s)\n", get_string("workspace"));
192 #endif
193 break;
194 case 24:
195 result->next_state = INITIAL;
196 #ifndef TEST_PARSER
197 cmd_move_con_to_workspace(&current_match, result, get_string("workspace"));
198 #else
199 fprintf(stderr, "cmd_move_con_to_workspace(%s)\n", get_string("workspace"));
200 #endif
201 break;
202 case 25:
203 result->next_state = INITIAL;
204 #ifndef TEST_PARSER
205 cmd_move_con_to_workspace(&current_match, result, get_string("workspace"));
206 #else
207 fprintf(stderr, "cmd_move_con_to_workspace(%s)\n", get_string("workspace"));
208 #endif
209 break;
210 case 26:
211 result->next_state = INITIAL;
212 #ifndef TEST_PARSER
213 cmd_move_con_to_workspace(&current_match, result, get_string("workspace"));
214 #else
215 fprintf(stderr, "cmd_move_con_to_workspace(%s)\n", get_string("workspace"));
216 #endif
217 break;
218 case 27:
219 result->next_state = INITIAL;
220 #ifndef TEST_PARSER
221 cmd_move_con_to_workspace(&current_match, result, get_string("workspace"));
222 #else
223 fprintf(stderr, "cmd_move_con_to_workspace(%s)\n", get_string("workspace"));
224 #endif
225 break;
226 case 28:
227 result->next_state = INITIAL;
228 #ifndef TEST_PARSER
229 cmd_move_con_to_workspace_back_and_forth(&current_match, result);
230 #else
231 fprintf(stderr, "cmd_move_con_to_workspace_back_and_forth()\n");
232 #endif
233 break;
234 case 29:
235 result->next_state = INITIAL;
236 #ifndef TEST_PARSER
237 cmd_move_con_to_workspace_name(&current_match, result, get_string("workspace"), get_string("no_auto_back_and_forth"));
238 #else
239 fprintf(stderr, "cmd_move_con_to_workspace_name(%s, %s)\n", get_string("workspace"), get_string("no_auto_back_and_forth"));
240 #endif
241 break;
242 case 30:
243 result->next_state = INITIAL;
244 #ifndef TEST_PARSER
245 cmd_append_layout(&current_match, result, get_string("path"));
246 #else
247 fprintf(stderr, "cmd_append_layout(%s)\n", get_string("path"));
248 #endif
249 break;
250 case 31:
251 result->next_state = CRITERIA;
252 #ifndef TEST_PARSER
253 cmd_criteria_add(&current_match, result, get_string("ctype"), get_string("cvalue"));
254 #else
255 fprintf(stderr, "cmd_criteria_add(%s, %s)\n", get_string("ctype"), get_string("cvalue"));
256 #endif
257 break;
258 case 32:
259 result->next_state = INITIAL;
260 #ifndef TEST_PARSER
261 cmd_layout_toggle(&current_match, result, get_string("toggle_mode"));
262 #else
263 fprintf(stderr, "cmd_layout_toggle(%s)\n", get_string("toggle_mode"));
264 #endif
265 break;
266 case 33:
267 result->next_state = INITIAL;
268 #ifndef TEST_PARSER
269 cmd_layout_toggle(&current_match, result, get_string("toggle_mode"));
270 #else
271 fprintf(stderr, "cmd_layout_toggle(%s)\n", get_string("toggle_mode"));
272 #endif
273 break;
274 case 34:
275 result->next_state = INITIAL;
276 #ifndef TEST_PARSER
277 cmd_resize_set(&current_match, result, get_long("width"), get_string("mode_width"), get_long("height"), get_string("mode_height"));
278 #else
279 fprintf(stderr, "cmd_resize_set(%ld, %s, %ld, %s)\n", get_long("width"), get_string("mode_width"), get_long("height"), get_string("mode_height"));
280 #endif
281 break;
282 case 35:
283 result->next_state = INITIAL;
284 #ifndef TEST_PARSER
285 cmd_resize(&current_match, result, get_string("way"), get_string("direction"), get_long("resize_px"), 0);
286 #else
287 fprintf(stderr, "cmd_resize(%s, %s, %ld, %d)\n", get_string("way"), get_string("direction"), get_long("resize_px"), 0);
288 #endif
289 break;
290 case 36:
291 result->next_state = INITIAL;
292 #ifndef TEST_PARSER
293 cmd_swap(&current_match, result, get_string("mode"), get_string("arg"));
294 #else
295 fprintf(stderr, "cmd_swap(%s, %s)\n", get_string("mode"), get_string("arg"));
296 #endif
297 break;
298 case 37:
299 result->next_state = INITIAL;
300 #ifndef TEST_PARSER
301 cmd_border(&current_match, result, get_string("border_style"), -1);
302 #else
303 fprintf(stderr, "cmd_border(%s, %d)\n", get_string("border_style"), -1);
304 #endif
305 break;
306 case 38:
307 result->next_state = INITIAL;
308 #ifndef TEST_PARSER
309 cmd_border(&current_match, result, get_string("border_style"), get_long("border_width"));
310 #else
311 fprintf(stderr, "cmd_border(%s, %ld)\n", get_string("border_style"), get_long("border_width"));
312 #endif
313 break;
314 case 39:
315 result->next_state = INITIAL;
316 #ifndef TEST_PARSER
317 cmd_focus_output(&current_match, result, get_string("output"));
318 #else
319 fprintf(stderr, "cmd_focus_output(%s)\n", get_string("output"));
320 #endif
321 break;
322 case 40:
323 result->next_state = INITIAL;
324 #ifndef TEST_PARSER
325 cmd_move_con_to_mark(&current_match, result, get_string("mark"));
326 #else
327 fprintf(stderr, "cmd_move_con_to_mark(%s)\n", get_string("mark"));
328 #endif
329 break;
330 case 41:
331 result->next_state = INITIAL;
332 #ifndef TEST_PARSER
333 cmd_resize_set(&current_match, result, get_long("width"), get_string("mode_width"), 0, 0);
334 #else
335 fprintf(stderr, "cmd_resize_set(%ld, %s, %d, %d)\n", get_long("width"), get_string("mode_width"), 0, 0);
336 #endif
337 break;
338 case 42:
339 result->next_state = INITIAL;
340 #ifndef TEST_PARSER
341 cmd_title_format(&current_match, result, get_string("format"));
342 #else
343 fprintf(stderr, "cmd_title_format(%s)\n", get_string("format"));
344 #endif
345 break;
346 case 43:
347 result->next_state = INITIAL;
348 #ifndef TEST_PARSER
349 cmd_fullscreen(&current_match, result, get_string("action"), "output");
350 #else
351 fprintf(stderr, "cmd_fullscreen(%s, %s)\n", get_string("action"), "output");
352 #endif
353 break;
354 case 44:
355 result->next_state = INITIAL;
356 #ifndef TEST_PARSER
357 cmd_scratchpad_show(&current_match, result);
358 #else
359 fprintf(stderr, "cmd_scratchpad_show()\n");
360 #endif
361 break;
362 case 45:
363 result->next_state = INITIAL;
364 #ifndef TEST_PARSER
365 cmd_resize(&current_match, result, get_string("way"), get_string("direction"), 10, 10);
366 #else
367 fprintf(stderr, "cmd_resize(%s, %s, %d, %d)\n", get_string("way"), get_string("direction"), 10, 10);
368 #endif
369 break;
370 case 46:
371 result->next_state = INITIAL;
372 #ifndef TEST_PARSER
373 cmd_workspace(&current_match, result, get_string("direction"));
374 #else
375 fprintf(stderr, "cmd_workspace(%s)\n", get_string("direction"));
376 #endif
377 break;
378 case 47:
379 result->next_state = INITIAL;
380 #ifndef TEST_PARSER
381 cmd_workspace(&current_match, result, get_string("direction"));
382 #else
383 fprintf(stderr, "cmd_workspace(%s)\n", get_string("direction"));
384 #endif
385 break;
386 case 48:
387 result->next_state = INITIAL;
388 #ifndef TEST_PARSER
389 cmd_workspace(&current_match, result, get_string("direction"));
390 #else
391 fprintf(stderr, "cmd_workspace(%s)\n", get_string("direction"));
392 #endif
393 break;
394 case 49:
395 result->next_state = INITIAL;
396 #ifndef TEST_PARSER
397 cmd_workspace(&current_match, result, get_string("direction"));
398 #else
399 fprintf(stderr, "cmd_workspace(%s)\n", get_string("direction"));
400 #endif
401 break;
402 case 50:
403 result->next_state = INITIAL;
404 #ifndef TEST_PARSER
405 cmd_workspace_back_and_forth(&current_match, result);
406 #else
407 fprintf(stderr, "cmd_workspace_back_and_forth()\n");
408 #endif
409 break;
410 case 51:
411 result->next_state = INITIAL;
412 #ifndef TEST_PARSER
413 cmd_workspace_name(&current_match, result, get_string("workspace"), get_string("no_auto_back_and_forth"));
414 #else
415 fprintf(stderr, "cmd_workspace_name(%s, %s)\n", get_string("workspace"), get_string("no_auto_back_and_forth"));
416 #endif
417 break;
418 case 52:
419 result->next_state = INITIAL;
420 #ifndef TEST_PARSER
421 cmd_bar(&current_match, result, get_string("bar_type"), get_string("bar_value"), get_string("bar_id"));
422 #else
423 fprintf(stderr, "cmd_bar(%s, %s, %s)\n", get_string("bar_type"), get_string("bar_value"), get_string("bar_id"));
424 #endif
425 break;
426 case 53:
427 result->next_state = CRITERIA;
428 #ifndef TEST_PARSER
429 cmd_criteria_add(&current_match, result, get_string("ctype"), NULL);
430 #else
431 fprintf(stderr, "cmd_criteria_add(%s, NULL)\n", get_string("ctype"));
432 #endif
433 break;
434 case 54:
435 result->next_state = CRITERIA;
436 #ifndef TEST_PARSER
437 cmd_criteria_add(&current_match, result, get_string("ctype"), NULL);
438 #else
439 fprintf(stderr, "cmd_criteria_add(%s, NULL)\n", get_string("ctype"));
440 #endif
441 break;
442 case 55:
443 result->next_state = INITIAL;
444 #ifndef TEST_PARSER
445 cmd_criteria_match_windows(&current_match, result);
446 #else
447 fprintf(stderr, "cmd_criteria_match_windows()\n");
448 #endif
449 break;
450 case 56:
451 result->next_state = INITIAL;
452 #ifndef TEST_PARSER
453 cmd_debuglog(&current_match, result, get_string("argument"));
454 #else
455 fprintf(stderr, "cmd_debuglog(%s)\n", get_string("argument"));
456 #endif
457 break;
458 case 57:
459 result->next_state = INITIAL;
460 #ifndef TEST_PARSER
461 cmd_debuglog(&current_match, result, get_string("argument"));
462 #else
463 fprintf(stderr, "cmd_debuglog(%s)\n", get_string("argument"));
464 #endif
465 break;
466 case 58:
467 result->next_state = INITIAL;
468 #ifndef TEST_PARSER
469 cmd_debuglog(&current_match, result, get_string("argument"));
470 #else
471 fprintf(stderr, "cmd_debuglog(%s)\n", get_string("argument"));
472 #endif
473 break;
474 case 59:
475 result->next_state = INITIAL;
476 #ifndef TEST_PARSER
477 cmd_floating(&current_match, result, get_string("floating"));
478 #else
479 fprintf(stderr, "cmd_floating(%s)\n", get_string("floating"));
480 #endif
481 break;
482 case 60:
483 result->next_state = INITIAL;
484 #ifndef TEST_PARSER
485 cmd_floating(&current_match, result, get_string("floating"));
486 #else
487 fprintf(stderr, "cmd_floating(%s)\n", get_string("floating"));
488 #endif
489 break;
490 case 61:
491 result->next_state = INITIAL;
492 #ifndef TEST_PARSER
493 cmd_floating(&current_match, result, get_string("floating"));
494 #else
495 fprintf(stderr, "cmd_floating(%s)\n", get_string("floating"));
496 #endif
497 break;
498 case 62:
499 result->next_state = CRITERIA;
500 #ifndef TEST_PARSER
501 cmd_criteria_init(&current_match, result);
502 #else
503 fprintf(stderr, "cmd_criteria_init()\n");
504 #endif
505 break;
506 case 63:
507 result->next_state = INITIAL;
508 #ifndef TEST_PARSER
509 cmd_exit(&current_match, result);
510 #else
511 fprintf(stderr, "cmd_exit()\n");
512 #endif
513 break;
514 case 64:
515 result->next_state = INITIAL;
516 #ifndef TEST_PARSER
517 cmd_restart(&current_match, result);
518 #else
519 fprintf(stderr, "cmd_restart()\n");
520 #endif
521 break;
522 case 65:
523 result->next_state = INITIAL;
524 #ifndef TEST_PARSER
525 cmd_reload(&current_match, result);
526 #else
527 fprintf(stderr, "cmd_reload()\n");
528 #endif
529 break;
530 case 66:
531 result->next_state = INITIAL;
532 #ifndef TEST_PARSER
533 cmd_open(&current_match, result);
534 #else
535 fprintf(stderr, "cmd_open()\n");
536 #endif
537 break;
538 case 67:
539 result->next_state = INITIAL;
540 #ifndef TEST_PARSER
541 cmd_border(&current_match, result, get_string("border_style"), 0);
542 #else
543 fprintf(stderr, "cmd_border(%s, %d)\n", get_string("border_style"), 0);
544 #endif
545 break;
546 case 68:
547 result->next_state = INITIAL;
548 #ifndef TEST_PARSER
549 cmd_border(&current_match, result, "pixel", 1);
550 #else
551 fprintf(stderr, "cmd_border(%s, %d)\n", "pixel", 1);
552 #endif
553 break;
554 case 69:
555 result->next_state = INITIAL;
556 #ifndef TEST_PARSER
557 cmd_layout(&current_match, result, get_string("layout_mode"));
558 #else
559 fprintf(stderr, "cmd_layout(%s)\n", get_string("layout_mode"));
560 #endif
561 break;
562 case 70:
563 result->next_state = INITIAL;
564 #ifndef TEST_PARSER
565 cmd_layout(&current_match, result, get_string("layout_mode"));
566 #else
567 fprintf(stderr, "cmd_layout(%s)\n", get_string("layout_mode"));
568 #endif
569 break;
570 case 71:
571 result->next_state = INITIAL;
572 #ifndef TEST_PARSER
573 cmd_layout(&current_match, result, get_string("layout_mode"));
574 #else
575 fprintf(stderr, "cmd_layout(%s)\n", get_string("layout_mode"));
576 #endif
577 break;
578 case 72:
579 result->next_state = INITIAL;
580 #ifndef TEST_PARSER
581 cmd_layout(&current_match, result, get_string("layout_mode"));
582 #else
583 fprintf(stderr, "cmd_layout(%s)\n", get_string("layout_mode"));
584 #endif
585 break;
586 case 73:
587 result->next_state = INITIAL;
588 #ifndef TEST_PARSER
589 cmd_layout(&current_match, result, get_string("layout_mode"));
590 #else
591 fprintf(stderr, "cmd_layout(%s)\n", get_string("layout_mode"));
592 #endif
593 break;
594 case 74:
595 result->next_state = INITIAL;
596 #ifndef TEST_PARSER
597 cmd_layout(&current_match, result, get_string("layout_mode"));
598 #else
599 fprintf(stderr, "cmd_layout(%s)\n", get_string("layout_mode"));
600 #endif
601 break;
602 case 75:
603 result->next_state = INITIAL;
604 #ifndef TEST_PARSER
605 cmd_shmlog(&current_match, result, get_string("argument"));
606 #else
607 fprintf(stderr, "cmd_shmlog(%s)\n", get_string("argument"));
608 #endif
609 break;
610 case 76:
611 result->next_state = INITIAL;
612 #ifndef TEST_PARSER
613 cmd_sticky(&current_match, result, get_string("action"));
614 #else
615 fprintf(stderr, "cmd_sticky(%s)\n", get_string("action"));
616 #endif
617 break;
618 case 77:
619 result->next_state = INITIAL;
620 #ifndef TEST_PARSER
621 cmd_sticky(&current_match, result, get_string("action"));
622 #else
623 fprintf(stderr, "cmd_sticky(%s)\n", get_string("action"));
624 #endif
625 break;
626 case 78:
627 result->next_state = INITIAL;
628 #ifndef TEST_PARSER
629 cmd_sticky(&current_match, result, get_string("action"));
630 #else
631 fprintf(stderr, "cmd_sticky(%s)\n", get_string("action"));
632 #endif
633 break;
634 case 79:
635 result->next_state = INITIAL;
636 #ifndef TEST_PARSER
637 cmd_unmark(&current_match, result, get_string("mark"));
638 #else
639 fprintf(stderr, "cmd_unmark(%s)\n", get_string("mark"));
640 #endif
641 break;
642 case 80:
643 result->next_state = INITIAL;
644 #ifndef TEST_PARSER
645 cmd_unmark(&current_match, result, get_string("mark"));
646 #else
647 fprintf(stderr, "cmd_unmark(%s)\n", get_string("mark"));
648 #endif
649 break;
650 case 81:
651 result->next_state = INITIAL;
652 #ifndef TEST_PARSER
653 cmd_focus_direction(&current_match, result, get_string("direction"));
654 #else
655 fprintf(stderr, "cmd_focus_direction(%s)\n", get_string("direction"));
656 #endif
657 break;
658 case 82:
659 result->next_state = INITIAL;
660 #ifndef TEST_PARSER
661 cmd_focus_direction(&current_match, result, get_string("direction"));
662 #else
663 fprintf(stderr, "cmd_focus_direction(%s)\n", get_string("direction"));
664 #endif
665 break;
666 case 83:
667 result->next_state = INITIAL;
668 #ifndef TEST_PARSER
669 cmd_focus_direction(&current_match, result, get_string("direction"));
670 #else
671 fprintf(stderr, "cmd_focus_direction(%s)\n", get_string("direction"));
672 #endif
673 break;
674 case 84:
675 result->next_state = INITIAL;
676 #ifndef TEST_PARSER
677 cmd_focus_direction(&current_match, result, get_string("direction"));
678 #else
679 fprintf(stderr, "cmd_focus_direction(%s)\n", get_string("direction"));
680 #endif
681 break;
682 case 85:
683 result->next_state = INITIAL;
684 #ifndef TEST_PARSER
685 cmd_focus_window_mode(&current_match, result, get_string("window_mode"));
686 #else
687 fprintf(stderr, "cmd_focus_window_mode(%s)\n", get_string("window_mode"));
688 #endif
689 break;
690 case 86:
691 result->next_state = INITIAL;
692 #ifndef TEST_PARSER
693 cmd_focus_window_mode(&current_match, result, get_string("window_mode"));
694 #else
695 fprintf(stderr, "cmd_focus_window_mode(%s)\n", get_string("window_mode"));
696 #endif
697 break;
698 case 87:
699 result->next_state = INITIAL;
700 #ifndef TEST_PARSER
701 cmd_focus_window_mode(&current_match, result, get_string("window_mode"));
702 #else
703 fprintf(stderr, "cmd_focus_window_mode(%s)\n", get_string("window_mode"));
704 #endif
705 break;
706 case 88:
707 result->next_state = INITIAL;
708 #ifndef TEST_PARSER
709 cmd_focus_level(&current_match, result, get_string("level"));
710 #else
711 fprintf(stderr, "cmd_focus_level(%s)\n", get_string("level"));
712 #endif
713 break;
714 case 89:
715 result->next_state = INITIAL;
716 #ifndef TEST_PARSER
717 cmd_focus_level(&current_match, result, get_string("level"));
718 #else
719 fprintf(stderr, "cmd_focus_level(%s)\n", get_string("level"));
720 #endif
721 break;
722 case 90:
723 result->next_state = INITIAL;
724 #ifndef TEST_PARSER
725 cmd_focus(&current_match, result);
726 #else
727 fprintf(stderr, "cmd_focus()\n");
728 #endif
729 break;
730 case 91:
731 result->next_state = INITIAL;
732 #ifndef TEST_PARSER
733 cmd_split(&current_match, result, get_string("direction"));
734 #else
735 fprintf(stderr, "cmd_split(%s)\n", get_string("direction"));
736 #endif
737 break;
738 case 92:
739 result->next_state = INITIAL;
740 #ifndef TEST_PARSER
741 cmd_split(&current_match, result, get_string("direction"));
742 #else
743 fprintf(stderr, "cmd_split(%s)\n", get_string("direction"));
744 #endif
745 break;
746 case 93:
747 result->next_state = INITIAL;
748 #ifndef TEST_PARSER
749 cmd_split(&current_match, result, get_string("direction"));
750 #else
751 fprintf(stderr, "cmd_split(%s)\n", get_string("direction"));
752 #endif
753 break;
754 case 94:
755 result->next_state = INITIAL;
756 #ifndef TEST_PARSER
757 cmd_split(&current_match, result, get_string("direction"));
758 #else
759 fprintf(stderr, "cmd_split(%s)\n", get_string("direction"));
760 #endif
761 break;
762 case 95:
763 result->next_state = INITIAL;
764 #ifndef TEST_PARSER
765 cmd_split(&current_match, result, get_string("direction"));
766 #else
767 fprintf(stderr, "cmd_split(%s)\n", get_string("direction"));
768 #endif
769 break;
770 case 96:
771 result->next_state = INITIAL;
772 #ifndef TEST_PARSER
773 cmd_split(&current_match, result, get_string("direction"));
774 #else
775 fprintf(stderr, "cmd_split(%s)\n", get_string("direction"));
776 #endif
777 break;
778 case 97:
779 result->next_state = INITIAL;
780 #ifndef TEST_PARSER
781 cmd_exec(&current_match, result, get_string("nosn"), get_string("command"));
782 #else
783 fprintf(stderr, "cmd_exec(%s, %s)\n", get_string("nosn"), get_string("command"));
784 #endif
785 break;
786 case 98:
787 result->next_state = INITIAL;
788 #ifndef TEST_PARSER
789 cmd_kill(&current_match, result, get_string("kill_mode"));
790 #else
791 fprintf(stderr, "cmd_kill(%s)\n", get_string("kill_mode"));
792 #endif
793 break;
794 case 99:
795 result->next_state = INITIAL;
796 #ifndef TEST_PARSER
797 cmd_kill(&current_match, result, get_string("kill_mode"));
798 #else
799 fprintf(stderr, "cmd_kill(%s)\n", get_string("kill_mode"));
800 #endif
801 break;
802 case 100:
803 result->next_state = INITIAL;
804 #ifndef TEST_PARSER
805 cmd_kill(&current_match, result, get_string("kill_mode"));
806 #else
807 fprintf(stderr, "cmd_kill(%s)\n", get_string("kill_mode"));
808 #endif
809 break;
810 case 101:
811 result->next_state = INITIAL;
812 #ifndef TEST_PARSER
813 cmd_mark(&current_match, result, get_string("mark"), get_string("mode"), get_string("toggle"));
814 #else
815 fprintf(stderr, "cmd_mark(%s, %s, %s)\n", get_string("mark"), get_string("mode"), get_string("toggle"));
816 #endif
817 break;
818 case 102:
819 result->next_state = INITIAL;
820 #ifndef TEST_PARSER
821 cmd_mode(&current_match, result, get_string("mode"));
822 #else
823 fprintf(stderr, "cmd_mode(%s)\n", get_string("mode"));
824 #endif
825 break;
826 case 103:
827 result->next_state = INITIAL;
828 #ifndef TEST_PARSER
829 cmd_move_scratchpad(&current_match, result);
830 #else
831 fprintf(stderr, "cmd_move_scratchpad()\n");
832 #endif
833 break;
834 case 104:
835 result->next_state = INITIAL;
836 #ifndef TEST_PARSER
837 cmd_nop(&current_match, result, get_string("comment"));
838 #else
839 fprintf(stderr, "cmd_nop(%s)\n", get_string("comment"));
840 #endif
841 break;
842 case 105:
843 result->next_state = INITIAL;
844 #ifndef TEST_PARSER
845 cmd_nop(&current_match, result, NULL);
846 #else
847 fprintf(stderr, "cmd_nop(NULL)\n");
848 #endif
849 break;
850 default:
851 printf("BUG in the parser. state = %d\n", call_identifier);
852 assert(false);
853 }
854 }
0 typedef enum {
1 RENAME_WORKSPACE_LIKELY_TO_NEW_NAME = 0,
2 RENAME_WORKSPACE_TO_NEW_NAME = 1,
3 RENAME_WORKSPACE_LIKELY_TO = 2,
4 MOVE_TO_ABSOLUTE_POSITION = 3,
5 MOVE_WORKSPACE_TO_OUTPUT = 4,
6 RESIZE_HEIGHT_GET_NUMBER = 5,
7 MOVE_WORKSPACE_NUMBER = 6,
8 RENAME_WORKSPACE_TO = 7,
9 RESIZE_TILING_FINAL = 8,
10 MOVE_TO_POSITION_X = 9,
11 MOVE_TO_POSITION_Y = 10,
12 FULLSCREEN_COMPAT = 11,
13 MOVE_DIRECTION_PX = 12,
14 BAR_HIDDEN_STATE = 13,
15 MOVE_TO_POSITION = 14,
16 RENAME_WORKSPACE = 15,
17 RESIZE_DIRECTION = 16,
18 RESIZE_TILING_OR = 17,
19 WORKSPACE_NUMBER = 18,
20 FULLSCREEN_MODE = 19,
21 MOVE_DIRECTION = 20,
22 MOVE_TO_OUTPUT = 21,
23 MOVE_WORKSPACE = 22,
24 APPEND_LAYOUT = 23,
25 CRITERION_STR = 24,
26 LAYOUT_TOGGLE = 25,
27 RESIZE_HEIGHT = 26,
28 RESIZE_TILING = 27,
29 SWAP_ARGUMENT = 28,
30 BORDER_WIDTH = 29,
31 FOCUS_OUTPUT = 30,
32 MOVE_TO_MARK = 31,
33 RESIZE_WIDTH = 32,
34 TITLE_FORMAT = 33,
35 FULLSCREEN = 34,
36 RESIZE_SET = 35,
37 SCRATCHPAD = 36,
38 CRITERION = 37,
39 RESIZE_PX = 38,
40 WORKSPACE = 39,
41 BAR_MODE = 40,
42 BAR_W_ID = 41,
43 CRITERIA = 42,
44 DEBUGLOG = 43,
45 FLOATING = 44,
46 INITIAL = 45,
47 BORDER = 46,
48 LAYOUT = 47,
49 RENAME = 48,
50 RESIZE = 49,
51 SHMLOG = 50,
52 STICKY = 51,
53 UNMARK = 52,
54 FOCUS = 53,
55 SPLIT = 54,
56 EXEC = 55,
57 KILL = 56,
58 MARK = 57,
59 MODE = 58,
60 MOVE = 59,
61 SWAP = 60,
62 BAR = 61,
63 NOP = 62,
64 __CALL = 63
65 } cmdp_state;
0 static cmdp_token tokens_RENAME_WORKSPACE_LIKELY_TO_NEW_NAME[2] = {
1 { "string", "new_name", __CALL, { 0 } },
2 { "end", "", __CALL, { 1 } },
3 };
4 static cmdp_token tokens_RENAME_WORKSPACE_TO_NEW_NAME[1] = {
5 { "string", "new_name", __CALL, { 2 } },
6 };
7 static cmdp_token tokens_RENAME_WORKSPACE_LIKELY_TO[2] = {
8 { "'to ", "", RENAME_WORKSPACE_LIKELY_TO_NEW_NAME, { 0 } },
9 { "word", "new_name", __CALL, { 3 } },
10 };
11 static cmdp_token tokens_MOVE_TO_ABSOLUTE_POSITION[1] = {
12 { "'position", "", MOVE_TO_POSITION, { 0 } },
13 };
14 static cmdp_token tokens_MOVE_WORKSPACE_TO_OUTPUT[2] = {
15 { "'output", "", MOVE_WORKSPACE_TO_OUTPUT, { 0 } },
16 { "string", "output", __CALL, { 4 } },
17 };
18 static cmdp_token tokens_RESIZE_HEIGHT_GET_NUMBER[1] = {
19 { "number", "height", RESIZE_HEIGHT, { 0 } },
20 };
21 static cmdp_token tokens_MOVE_WORKSPACE_NUMBER[1] = {
22 { "string", "number", __CALL, { 5 } },
23 };
24 static cmdp_token tokens_RENAME_WORKSPACE_TO[1] = {
25 { "'to", "", RENAME_WORKSPACE_TO_NEW_NAME, { 0 } },
26 };
27 static cmdp_token tokens_RESIZE_TILING_FINAL[2] = {
28 { "'ppt", "", __CALL, { 6 } },
29 { "end", "", __CALL, { 7 } },
30 };
31 static cmdp_token tokens_MOVE_TO_POSITION_X[2] = {
32 { "'px", "", MOVE_TO_POSITION_X, { 0 } },
33 { "number", "coord_y", MOVE_TO_POSITION_Y, { 0 } },
34 };
35 static cmdp_token tokens_MOVE_TO_POSITION_Y[2] = {
36 { "'px", "", __CALL, { 8 } },
37 { "end", "", __CALL, { 9 } },
38 };
39 static cmdp_token tokens_FULLSCREEN_COMPAT[2] = {
40 { "'global", "mode", __CALL, { 10 } },
41 { "end", "", __CALL, { 11 } },
42 };
43 static cmdp_token tokens_MOVE_DIRECTION_PX[2] = {
44 { "'px", "", __CALL, { 12 } },
45 { "end", "", __CALL, { 13 } },
46 };
47 static cmdp_token tokens_BAR_HIDDEN_STATE[3] = {
48 { "'hide", "bar_value", BAR_W_ID, { 0 } },
49 { "'show", "bar_value", BAR_W_ID, { 0 } },
50 { "'toggle", "bar_value", BAR_W_ID, { 0 } },
51 };
52 static cmdp_token tokens_MOVE_TO_POSITION[5] = {
53 { "'center", "", __CALL, { 14 } },
54 { "'mouse", "", __CALL, { 15 } },
55 { "'cursor", "", __CALL, { 16 } },
56 { "'pointer", "", __CALL, { 17 } },
57 { "number", "coord_x", MOVE_TO_POSITION_X, { 0 } },
58 };
59 static cmdp_token tokens_RENAME_WORKSPACE[2] = {
60 { "'to", "", RENAME_WORKSPACE_LIKELY_TO, { 0 } },
61 { "word", "old_name", RENAME_WORKSPACE_TO, { 0 } },
62 };
63 static cmdp_token tokens_RESIZE_DIRECTION[6] = {
64 { "'up", "direction", RESIZE_PX, { 0 } },
65 { "'down", "direction", RESIZE_PX, { 0 } },
66 { "'left", "direction", RESIZE_PX, { 0 } },
67 { "'right", "direction", RESIZE_PX, { 0 } },
68 { "'width", "direction", RESIZE_PX, { 0 } },
69 { "'height", "direction", RESIZE_PX, { 0 } },
70 };
71 static cmdp_token tokens_RESIZE_TILING_OR[1] = {
72 { "number", "resize_ppt", RESIZE_TILING_FINAL, { 0 } },
73 };
74 static cmdp_token tokens_WORKSPACE_NUMBER[1] = {
75 { "string", "workspace", __CALL, { 18 } },
76 };
77 static cmdp_token tokens_FULLSCREEN_MODE[2] = {
78 { "'global", "mode", __CALL, { 19 } },
79 { "end", "", __CALL, { 20 } },
80 };
81 static cmdp_token tokens_MOVE_DIRECTION[2] = {
82 { "number", "pixels", MOVE_DIRECTION_PX, { 0 } },
83 { "end", "", __CALL, { 21 } },
84 };
85 static cmdp_token tokens_MOVE_TO_OUTPUT[1] = {
86 { "string", "output", __CALL, { 22 } },
87 };
88 static cmdp_token tokens_MOVE_WORKSPACE[9] = {
89 { "'to ", "", MOVE_WORKSPACE_TO_OUTPUT, { 0 } },
90 { "'next_on_output", "workspace", __CALL, { 23 } },
91 { "'prev_on_output", "workspace", __CALL, { 24 } },
92 { "'next", "workspace", __CALL, { 25 } },
93 { "'prev", "workspace", __CALL, { 26 } },
94 { "'current", "workspace", __CALL, { 27 } },
95 { "'back_and_forth", "", __CALL, { 28 } },
96 { "'number", "", MOVE_WORKSPACE_NUMBER, { 0 } },
97 { "string", "workspace", __CALL, { 29 } },
98 };
99 static cmdp_token tokens_APPEND_LAYOUT[1] = {
100 { "string", "path", __CALL, { 30 } },
101 };
102 static cmdp_token tokens_CRITERION_STR[1] = {
103 { "word", "cvalue", __CALL, { 31 } },
104 };
105 static cmdp_token tokens_LAYOUT_TOGGLE[2] = {
106 { "end", "", __CALL, { 32 } },
107 { "string", "toggle_mode", __CALL, { 33 } },
108 };
109 static cmdp_token tokens_RESIZE_HEIGHT[3] = {
110 { "'px", "mode_height", RESIZE_HEIGHT, { 0 } },
111 { "'ppt", "mode_height", RESIZE_HEIGHT, { 0 } },
112 { "end", "", __CALL, { 34 } },
113 };
114 static cmdp_token tokens_RESIZE_TILING[3] = {
115 { "'px", "", RESIZE_TILING, { 0 } },
116 { "'or", "", RESIZE_TILING_OR, { 0 } },
117 { "end", "", __CALL, { 35 } },
118 };
119 static cmdp_token tokens_SWAP_ARGUMENT[1] = {
120 { "string", "arg", __CALL, { 36 } },
121 };
122 static cmdp_token tokens_BORDER_WIDTH[2] = {
123 { "end", "", __CALL, { 37 } },
124 { "number", "border_width", __CALL, { 38 } },
125 };
126 static cmdp_token tokens_FOCUS_OUTPUT[1] = {
127 { "string", "output", __CALL, { 39 } },
128 };
129 static cmdp_token tokens_MOVE_TO_MARK[1] = {
130 { "string", "mark", __CALL, { 40 } },
131 };
132 static cmdp_token tokens_RESIZE_WIDTH[5] = {
133 { "'px", "mode_width", RESIZE_WIDTH, { 0 } },
134 { "'ppt", "mode_width", RESIZE_WIDTH, { 0 } },
135 { "end", "", __CALL, { 41 } },
136 { "'height", "", RESIZE_HEIGHT_GET_NUMBER, { 0 } },
137 { "number", "height", RESIZE_HEIGHT, { 0 } },
138 };
139 static cmdp_token tokens_TITLE_FORMAT[1] = {
140 { "string", "format", __CALL, { 42 } },
141 };
142 static cmdp_token tokens_FULLSCREEN[4] = {
143 { "'disable", "action", __CALL, { 43 } },
144 { "'enable", "action", FULLSCREEN_MODE, { 0 } },
145 { "'toggle", "action", FULLSCREEN_MODE, { 0 } },
146 { "'", "action", FULLSCREEN_COMPAT, { 0 } },
147 };
148 static cmdp_token tokens_RESIZE_SET[3] = {
149 { "'height", "", RESIZE_HEIGHT_GET_NUMBER, { 0 } },
150 { "'width", "", RESIZE_SET, { 0 } },
151 { "number", "width", RESIZE_WIDTH, { 0 } },
152 };
153 static cmdp_token tokens_SCRATCHPAD[1] = {
154 { "'show", "", __CALL, { 44 } },
155 };
156 static cmdp_token tokens_CRITERION[1] = {
157 { "'=", "", CRITERION_STR, { 0 } },
158 };
159 static cmdp_token tokens_RESIZE_PX[2] = {
160 { "number", "resize_px", RESIZE_TILING, { 0 } },
161 { "end", "", __CALL, { 45 } },
162 };
163 static cmdp_token tokens_WORKSPACE[8] = {
164 { "'--no-auto-back-and-forth", "no_auto_back_and_forth", WORKSPACE, { 0 } },
165 { "'next_on_output", "direction", __CALL, { 46 } },
166 { "'prev_on_output", "direction", __CALL, { 47 } },
167 { "'next", "direction", __CALL, { 48 } },
168 { "'prev", "direction", __CALL, { 49 } },
169 { "'back_and_forth", "", __CALL, { 50 } },
170 { "'number", "", WORKSPACE_NUMBER, { 0 } },
171 { "string", "workspace", __CALL, { 51 } },
172 };
173 static cmdp_token tokens_BAR_MODE[4] = {
174 { "'dock", "bar_value", BAR_W_ID, { 0 } },
175 { "'hide", "bar_value", BAR_W_ID, { 0 } },
176 { "'invisible", "bar_value", BAR_W_ID, { 0 } },
177 { "'toggle", "bar_value", BAR_W_ID, { 0 } },
178 };
179 static cmdp_token tokens_BAR_W_ID[2] = {
180 { "word", "bar_id", BAR_W_ID, { 0 } },
181 { "end", "", __CALL, { 52 } },
182 };
183 static cmdp_token tokens_CRITERIA[13] = {
184 { "'class", "ctype", CRITERION, { 0 } },
185 { "'instance", "ctype", CRITERION, { 0 } },
186 { "'window_role", "ctype", CRITERION, { 0 } },
187 { "'con_id", "ctype", CRITERION, { 0 } },
188 { "'id", "ctype", CRITERION, { 0 } },
189 { "'window_type", "ctype", CRITERION, { 0 } },
190 { "'con_mark", "ctype", CRITERION, { 0 } },
191 { "'title", "ctype", CRITERION, { 0 } },
192 { "'urgent", "ctype", CRITERION, { 0 } },
193 { "'workspace", "ctype", CRITERION, { 0 } },
194 { "'tiling", "ctype", __CALL, { 53 } },
195 { "'floating", "ctype", __CALL, { 54 } },
196 { "']", "", __CALL, { 55 } },
197 };
198 static cmdp_token tokens_DEBUGLOG[3] = {
199 { "'toggle", "argument", __CALL, { 56 } },
200 { "'on", "argument", __CALL, { 57 } },
201 { "'off", "argument", __CALL, { 58 } },
202 };
203 static cmdp_token tokens_FLOATING[3] = {
204 { "'enable", "floating", __CALL, { 59 } },
205 { "'disable", "floating", __CALL, { 60 } },
206 { "'toggle", "floating", __CALL, { 61 } },
207 };
208 static cmdp_token tokens_INITIAL[30] = {
209 { "end", "", INITIAL, { 0 } },
210 { "'[", "", __CALL, { 62 } },
211 { "'move", "", MOVE, { 0 } },
212 { "'exec", "", EXEC, { 0 } },
213 { "'exit", "", __CALL, { 63 } },
214 { "'restart", "", __CALL, { 64 } },
215 { "'reload", "", __CALL, { 65 } },
216 { "'shmlog", "", SHMLOG, { 0 } },
217 { "'debuglog", "", DEBUGLOG, { 0 } },
218 { "'border", "", BORDER, { 0 } },
219 { "'layout", "", LAYOUT, { 0 } },
220 { "'append_layout", "", APPEND_LAYOUT, { 0 } },
221 { "'workspace", "", WORKSPACE, { 0 } },
222 { "'focus", "", FOCUS, { 0 } },
223 { "'kill", "", KILL, { 0 } },
224 { "'open", "", __CALL, { 66 } },
225 { "'fullscreen", "", FULLSCREEN, { 0 } },
226 { "'sticky", "", STICKY, { 0 } },
227 { "'split", "", SPLIT, { 0 } },
228 { "'floating", "", FLOATING, { 0 } },
229 { "'mark", "", MARK, { 0 } },
230 { "'unmark", "", UNMARK, { 0 } },
231 { "'resize", "", RESIZE, { 0 } },
232 { "'rename", "", RENAME, { 0 } },
233 { "'nop", "", NOP, { 0 } },
234 { "'scratchpad", "", SCRATCHPAD, { 0 } },
235 { "'swap", "", SWAP, { 0 } },
236 { "'title_format", "", TITLE_FORMAT, { 0 } },
237 { "'mode", "", MODE, { 0 } },
238 { "'bar", "", BAR, { 0 } },
239 };
240 static cmdp_token tokens_BORDER[5] = {
241 { "'normal", "border_style", BORDER_WIDTH, { 0 } },
242 { "'pixel", "border_style", BORDER_WIDTH, { 0 } },
243 { "'toggle", "border_style", BORDER_WIDTH, { 0 } },
244 { "'none", "border_style", __CALL, { 67 } },
245 { "'1pixel", "", __CALL, { 68 } },
246 };
247 static cmdp_token tokens_LAYOUT[7] = {
248 { "'default", "layout_mode", __CALL, { 69 } },
249 { "'stacked", "layout_mode", __CALL, { 70 } },
250 { "'stacking", "layout_mode", __CALL, { 71 } },
251 { "'tabbed", "layout_mode", __CALL, { 72 } },
252 { "'splitv", "layout_mode", __CALL, { 73 } },
253 { "'splith", "layout_mode", __CALL, { 74 } },
254 { "'toggle", "", LAYOUT_TOGGLE, { 0 } },
255 };
256 static cmdp_token tokens_RENAME[1] = {
257 { "'workspace", "", RENAME_WORKSPACE, { 0 } },
258 };
259 static cmdp_token tokens_RESIZE[3] = {
260 { "'grow", "way", RESIZE_DIRECTION, { 0 } },
261 { "'shrink", "way", RESIZE_DIRECTION, { 0 } },
262 { "'set", "set", RESIZE_SET, { 0 } },
263 };
264 static cmdp_token tokens_SHMLOG[1] = {
265 { "string", "argument", __CALL, { 75 } },
266 };
267 static cmdp_token tokens_STICKY[3] = {
268 { "'enable", "action", __CALL, { 76 } },
269 { "'disable", "action", __CALL, { 77 } },
270 { "'toggle", "action", __CALL, { 78 } },
271 };
272 static cmdp_token tokens_UNMARK[2] = {
273 { "end", "", __CALL, { 79 } },
274 { "string", "mark", __CALL, { 80 } },
275 };
276 static cmdp_token tokens_FOCUS[11] = {
277 { "'left", "direction", __CALL, { 81 } },
278 { "'right", "direction", __CALL, { 82 } },
279 { "'up", "direction", __CALL, { 83 } },
280 { "'down", "direction", __CALL, { 84 } },
281 { "'output", "", FOCUS_OUTPUT, { 0 } },
282 { "'tiling", "window_mode", __CALL, { 85 } },
283 { "'floating", "window_mode", __CALL, { 86 } },
284 { "'mode_toggle", "window_mode", __CALL, { 87 } },
285 { "'parent", "level", __CALL, { 88 } },
286 { "'child", "level", __CALL, { 89 } },
287 { "end", "", __CALL, { 90 } },
288 };
289 static cmdp_token tokens_SPLIT[6] = {
290 { "'horizontal", "direction", __CALL, { 91 } },
291 { "'vertical", "direction", __CALL, { 92 } },
292 { "'toggle", "direction", __CALL, { 93 } },
293 { "'v", "direction", __CALL, { 94 } },
294 { "'h", "direction", __CALL, { 95 } },
295 { "'t", "direction", __CALL, { 96 } },
296 };
297 static cmdp_token tokens_EXEC[2] = {
298 { "'--no-startup-id", "nosn", EXEC, { 0 } },
299 { "string", "command", __CALL, { 97 } },
300 };
301 static cmdp_token tokens_KILL[3] = {
302 { "'window", "kill_mode", __CALL, { 98 } },
303 { "'client", "kill_mode", __CALL, { 99 } },
304 { "end", "", __CALL, { 100 } },
305 };
306 static cmdp_token tokens_MARK[4] = {
307 { "'--add", "mode", MARK, { 0 } },
308 { "'--replace", "mode", MARK, { 0 } },
309 { "'--toggle", "toggle", MARK, { 0 } },
310 { "string", "mark", __CALL, { 101 } },
311 };
312 static cmdp_token tokens_MODE[1] = {
313 { "string", "mode", __CALL, { 102 } },
314 };
315 static cmdp_token tokens_MOVE[14] = {
316 { "'window", "", MOVE, { 0 } },
317 { "'container", "", MOVE, { 0 } },
318 { "'to", "", MOVE, { 0 } },
319 { "'--no-auto-back-and-forth", "no_auto_back_and_forth", MOVE, { 0 } },
320 { "'workspace", "", MOVE_WORKSPACE, { 0 } },
321 { "'output", "", MOVE_TO_OUTPUT, { 0 } },
322 { "'mark", "", MOVE_TO_MARK, { 0 } },
323 { "'scratchpad", "", __CALL, { 103 } },
324 { "'left", "direction", MOVE_DIRECTION, { 0 } },
325 { "'right", "direction", MOVE_DIRECTION, { 0 } },
326 { "'up", "direction", MOVE_DIRECTION, { 0 } },
327 { "'down", "direction", MOVE_DIRECTION, { 0 } },
328 { "'position", "method", MOVE_TO_POSITION, { 0 } },
329 { "'absolute", "method", MOVE_TO_ABSOLUTE_POSITION, { 0 } },
330 };
331 static cmdp_token tokens_SWAP[5] = {
332 { "'container", "", SWAP, { 0 } },
333 { "'with", "", SWAP, { 0 } },
334 { "'id", "mode", SWAP_ARGUMENT, { 0 } },
335 { "'con_id", "mode", SWAP_ARGUMENT, { 0 } },
336 { "'mark", "mode", SWAP_ARGUMENT, { 0 } },
337 };
338 static cmdp_token tokens_BAR[2] = {
339 { "'hidden_state", "bar_type", BAR_HIDDEN_STATE, { 0 } },
340 { "'mode", "bar_type", BAR_MODE, { 0 } },
341 };
342 static cmdp_token tokens_NOP[2] = {
343 { "string", "comment", __CALL, { 104 } },
344 { "end", "", __CALL, { 105 } },
345 };
346 static cmdp_token_ptr tokens[63] = {
347 { tokens_RENAME_WORKSPACE_LIKELY_TO_NEW_NAME, 2 },
348 { tokens_RENAME_WORKSPACE_TO_NEW_NAME, 1 },
349 { tokens_RENAME_WORKSPACE_LIKELY_TO, 2 },
350 { tokens_MOVE_TO_ABSOLUTE_POSITION, 1 },
351 { tokens_MOVE_WORKSPACE_TO_OUTPUT, 2 },
352 { tokens_RESIZE_HEIGHT_GET_NUMBER, 1 },
353 { tokens_MOVE_WORKSPACE_NUMBER, 1 },
354 { tokens_RENAME_WORKSPACE_TO, 1 },
355 { tokens_RESIZE_TILING_FINAL, 2 },
356 { tokens_MOVE_TO_POSITION_X, 2 },
357 { tokens_MOVE_TO_POSITION_Y, 2 },
358 { tokens_FULLSCREEN_COMPAT, 2 },
359 { tokens_MOVE_DIRECTION_PX, 2 },
360 { tokens_BAR_HIDDEN_STATE, 3 },
361 { tokens_MOVE_TO_POSITION, 5 },
362 { tokens_RENAME_WORKSPACE, 2 },
363 { tokens_RESIZE_DIRECTION, 6 },
364 { tokens_RESIZE_TILING_OR, 1 },
365 { tokens_WORKSPACE_NUMBER, 1 },
366 { tokens_FULLSCREEN_MODE, 2 },
367 { tokens_MOVE_DIRECTION, 2 },
368 { tokens_MOVE_TO_OUTPUT, 1 },
369 { tokens_MOVE_WORKSPACE, 9 },
370 { tokens_APPEND_LAYOUT, 1 },
371 { tokens_CRITERION_STR, 1 },
372 { tokens_LAYOUT_TOGGLE, 2 },
373 { tokens_RESIZE_HEIGHT, 3 },
374 { tokens_RESIZE_TILING, 3 },
375 { tokens_SWAP_ARGUMENT, 1 },
376 { tokens_BORDER_WIDTH, 2 },
377 { tokens_FOCUS_OUTPUT, 1 },
378 { tokens_MOVE_TO_MARK, 1 },
379 { tokens_RESIZE_WIDTH, 5 },
380 { tokens_TITLE_FORMAT, 1 },
381 { tokens_FULLSCREEN, 4 },
382 { tokens_RESIZE_SET, 3 },
383 { tokens_SCRATCHPAD, 1 },
384 { tokens_CRITERION, 1 },
385 { tokens_RESIZE_PX, 2 },
386 { tokens_WORKSPACE, 8 },
387 { tokens_BAR_MODE, 4 },
388 { tokens_BAR_W_ID, 2 },
389 { tokens_CRITERIA, 13 },
390 { tokens_DEBUGLOG, 3 },
391 { tokens_FLOATING, 3 },
392 { tokens_INITIAL, 30 },
393 { tokens_BORDER, 5 },
394 { tokens_LAYOUT, 7 },
395 { tokens_RENAME, 1 },
396 { tokens_RESIZE, 3 },
397 { tokens_SHMLOG, 1 },
398 { tokens_STICKY, 3 },
399 { tokens_UNMARK, 2 },
400 { tokens_FOCUS, 11 },
401 { tokens_SPLIT, 6 },
402 { tokens_EXEC, 2 },
403 { tokens_KILL, 3 },
404 { tokens_MARK, 4 },
405 { tokens_MODE, 1 },
406 { tokens_MOVE, 14 },
407 { tokens_SWAP, 5 },
408 { tokens_BAR, 2 },
409 { tokens_NOP, 2 },
410 };
0 static void GENERATED_call(const int call_identifier, struct ConfigResultIR *result) {
1 switch (call_identifier) {
2 case 0:
3 result->next_state = INITIAL;
4 #ifndef TEST_PARSER
5 cfg_force_display_urgency_hint(&current_match, result, get_long("duration_ms"));
6 #else
7 fprintf(stderr, "cfg_force_display_urgency_hint(%ld)\n", get_long("duration_ms"));
8 #endif
9 break;
10 case 1:
11 result->next_state = INITIAL;
12 #ifndef TEST_PARSER
13 cfg_floating_maximum_size(&current_match, result, get_long("width"), get_long("height"));
14 #else
15 fprintf(stderr, "cfg_floating_maximum_size(%ld, %ld)\n", get_long("width"), get_long("height"));
16 #endif
17 break;
18 case 2:
19 result->next_state = INITIAL;
20 #ifndef TEST_PARSER
21 cfg_floating_minimum_size(&current_match, result, get_long("width"), get_long("height"));
22 #else
23 fprintf(stderr, "cfg_floating_minimum_size(%ld, %ld)\n", get_long("width"), get_long("height"));
24 #endif
25 break;
26 case 3:
27 result->next_state = BAR;
28 #ifndef TEST_PARSER
29 cfg_bar_strip_workspace_numbers(&current_match, result, get_string("value"));
30 #else
31 fprintf(stderr, "cfg_bar_strip_workspace_numbers(%s)\n", get_string("value"));
32 #endif
33 break;
34 case 4:
35 result->next_state = BAR;
36 #ifndef TEST_PARSER
37 cfg_bar_binding_mode_indicator(&current_match, result, get_string("value"));
38 #else
39 fprintf(stderr, "cfg_bar_binding_mode_indicator(%s)\n", get_string("value"));
40 #endif
41 break;
42 case 5:
43 result->next_state = INITIAL;
44 #ifndef TEST_PARSER
45 cfg_focus_on_window_activation(&current_match, result, get_string("mode"));
46 #else
47 fprintf(stderr, "cfg_focus_on_window_activation(%s)\n", get_string("mode"));
48 #endif
49 break;
50 case 6:
51 result->next_state = BAR;
52 #ifndef TEST_PARSER
53 cfg_bar_strip_workspace_name(&current_match, result, get_string("value"));
54 #else
55 fprintf(stderr, "cfg_bar_strip_workspace_name(%s)\n", get_string("value"));
56 #endif
57 break;
58 case 7:
59 result->next_state = INITIAL;
60 #ifndef TEST_PARSER
61 cfg_default_border(&current_match, result, get_string("windowtype"), get_string("border"), get_long("width"));
62 #else
63 fprintf(stderr, "cfg_default_border(%s, %s, %ld)\n", get_string("windowtype"), get_string("border"), get_long("width"));
64 #endif
65 break;
66 case 8:
67 result->next_state = INITIAL;
68 #ifndef TEST_PARSER
69 cfg_workspace_back_and_forth(&current_match, result, get_string("value"));
70 #else
71 fprintf(stderr, "cfg_workspace_back_and_forth(%s)\n", get_string("value"));
72 #endif
73 break;
74 case 9:
75 result->next_state = INITIAL;
76 #ifndef TEST_PARSER
77 cfg_assign(&current_match, result, get_string("number"), 1);
78 #else
79 fprintf(stderr, "cfg_assign(%s, %d)\n", get_string("number"), 1);
80 #endif
81 break;
82 case 10:
83 result->next_state = INITIAL;
84 #ifndef TEST_PARSER
85 cfg_popup_during_fullscreen(&current_match, result, get_string("value"));
86 #else
87 fprintf(stderr, "cfg_popup_during_fullscreen(%s)\n", get_string("value"));
88 #endif
89 break;
90 case 11:
91 result->next_state = INITIAL;
92 #ifndef TEST_PARSER
93 cfg_popup_during_fullscreen(&current_match, result, get_string("value"));
94 #else
95 fprintf(stderr, "cfg_popup_during_fullscreen(%s)\n", get_string("value"));
96 #endif
97 break;
98 case 12:
99 result->next_state = INITIAL;
100 #ifndef TEST_PARSER
101 cfg_popup_during_fullscreen(&current_match, result, get_string("value"));
102 #else
103 fprintf(stderr, "cfg_popup_during_fullscreen(%s)\n", get_string("value"));
104 #endif
105 break;
106 case 13:
107 result->next_state = BAR;
108 #ifndef TEST_PARSER
109 cfg_bar_workspace_buttons(&current_match, result, get_string("value"));
110 #else
111 fprintf(stderr, "cfg_bar_workspace_buttons(%s)\n", get_string("value"));
112 #endif
113 break;
114 case 14:
115 result->next_state = INITIAL;
116 #ifndef TEST_PARSER
117 cfg_default_border(&current_match, result, get_string("windowtype"), get_string("border"), 2);
118 #else
119 fprintf(stderr, "cfg_default_border(%s, %s, %d)\n", get_string("windowtype"), get_string("border"), 2);
120 #endif
121 break;
122 case 15:
123 result->next_state = BAR;
124 #ifndef TEST_PARSER
125 cfg_bar_separator_symbol(&current_match, result, get_string("separator"));
126 #else
127 fprintf(stderr, "cfg_bar_separator_symbol(%s)\n", get_string("separator"));
128 #endif
129 break;
130 case 16:
131 result->next_state = INITIAL;
132 #ifndef TEST_PARSER
133 cfg_force_focus_wrapping(&current_match, result, get_string("value"));
134 #else
135 fprintf(stderr, "cfg_force_focus_wrapping(%s)\n", get_string("value"));
136 #endif
137 break;
138 case 17:
139 result->next_state = INITIAL;
140 #ifndef TEST_PARSER
141 cfg_workspace(&current_match, result, get_string("workspace"), get_string("output"));
142 #else
143 fprintf(stderr, "cfg_workspace(%s, %s)\n", get_string("workspace"), get_string("output"));
144 #endif
145 break;
146 case 18:
147 result->next_state = BAR;
148 #ifndef TEST_PARSER
149 cfg_bar_bindsym(&current_match, result, get_string("button"), get_string("release"), get_string("command"));
150 #else
151 fprintf(stderr, "cfg_bar_bindsym(%s, %s, %s)\n", get_string("button"), get_string("release"), get_string("command"));
152 #endif
153 break;
154 case 19:
155 result->next_state = BAR;
156 #ifndef TEST_PARSER
157 cfg_bar_tray_padding(&current_match, result, get_long("padding_px"));
158 #else
159 fprintf(stderr, "cfg_bar_tray_padding(%ld)\n", get_long("padding_px"));
160 #endif
161 break;
162 case 20:
163 result->next_state = INITIAL;
164 #ifndef TEST_PARSER
165 cfg_default_orientation(&current_match, result, get_string("orientation"));
166 #else
167 fprintf(stderr, "cfg_default_orientation(%s)\n", get_string("orientation"));
168 #endif
169 break;
170 case 21:
171 result->next_state = INITIAL;
172 #ifndef TEST_PARSER
173 cfg_default_orientation(&current_match, result, get_string("orientation"));
174 #else
175 fprintf(stderr, "cfg_default_orientation(%s)\n", get_string("orientation"));
176 #endif
177 break;
178 case 22:
179 result->next_state = INITIAL;
180 #ifndef TEST_PARSER
181 cfg_default_orientation(&current_match, result, get_string("orientation"));
182 #else
183 fprintf(stderr, "cfg_default_orientation(%s)\n", get_string("orientation"));
184 #endif
185 break;
186 case 23:
187 result->next_state = INITIAL;
188 #ifndef TEST_PARSER
189 cfg_focus_follows_mouse(&current_match, result, get_string("value"));
190 #else
191 fprintf(stderr, "cfg_focus_follows_mouse(%s)\n", get_string("value"));
192 #endif
193 break;
194 case 24:
195 result->next_state = BAR;
196 #ifndef TEST_PARSER
197 cfg_bar_status_command(&current_match, result, get_string("command"));
198 #else
199 fprintf(stderr, "cfg_bar_status_command(%s)\n", get_string("command"));
200 #endif
201 break;
202 case 25:
203 result->next_state = BAR;
204 #ifndef TEST_PARSER
205 cfg_bar_wheel_down_cmd(&current_match, result, get_string("command"));
206 #else
207 fprintf(stderr, "cfg_bar_wheel_down_cmd(%s)\n", get_string("command"));
208 #endif
209 break;
210 case 26:
211 result->next_state = INITIAL;
212 #ifndef TEST_PARSER
213 cfg_color(&current_match, result, get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"), get_string("indicator"), get_string("child_border"));
214 #else
215 fprintf(stderr, "cfg_color(%s, %s, %s, %s, %s, %s)\n", get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"), get_string("indicator"), get_string("child_border"));
216 #endif
217 break;
218 case 27:
219 result->next_state = INITIAL;
220 #ifndef TEST_PARSER
221 cfg_color(&current_match, result, get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"), get_string("indicator"), NULL);
222 #else
223 fprintf(stderr, "cfg_color(%s, %s, %s, %s, %s, NULL)\n", get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"), get_string("indicator"));
224 #endif
225 break;
226 case 28:
227 result->next_state = INITIAL;
228 #ifndef TEST_PARSER
229 cfg_for_window(&current_match, result, get_string("command"));
230 #else
231 fprintf(stderr, "cfg_for_window(%s)\n", get_string("command"));
232 #endif
233 break;
234 case 29:
235 result->next_state = BAR_COLORS;
236 #ifndef TEST_PARSER
237 cfg_bar_color_single(&current_match, result, get_string("colorclass"), get_string("color"));
238 #else
239 fprintf(stderr, "cfg_bar_color_single(%s, %s)\n", get_string("colorclass"), get_string("color"));
240 #endif
241 break;
242 case 30:
243 result->next_state = INITIAL;
244 #ifndef TEST_PARSER
245 cfg_floating_modifier(&current_match, result, get_string("modifiers"));
246 #else
247 fprintf(stderr, "cfg_floating_modifier(%s)\n", get_string("modifiers"));
248 #endif
249 break;
250 case 31:
251 result->next_state = INITIAL;
252 #ifndef TEST_PARSER
253 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
254 #else
255 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
256 #endif
257 break;
258 case 32:
259 result->next_state = INITIAL;
260 #ifndef TEST_PARSER
261 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
262 #else
263 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
264 #endif
265 break;
266 case 33:
267 result->next_state = INITIAL;
268 #ifndef TEST_PARSER
269 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
270 #else
271 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
272 #endif
273 break;
274 case 34:
275 result->next_state = INITIAL;
276 #ifndef TEST_PARSER
277 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
278 #else
279 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
280 #endif
281 break;
282 case 35:
283 result->next_state = INITIAL;
284 #ifndef TEST_PARSER
285 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
286 #else
287 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
288 #endif
289 break;
290 case 36:
291 result->next_state = INITIAL;
292 #ifndef TEST_PARSER
293 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
294 #else
295 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
296 #endif
297 break;
298 case 37:
299 result->next_state = INITIAL;
300 #ifndef TEST_PARSER
301 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
302 #else
303 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
304 #endif
305 break;
306 case 38:
307 result->next_state = INITIAL;
308 #ifndef TEST_PARSER
309 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
310 #else
311 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
312 #endif
313 break;
314 case 39:
315 result->next_state = INITIAL;
316 #ifndef TEST_PARSER
317 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
318 #else
319 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
320 #endif
321 break;
322 case 40:
323 result->next_state = INITIAL;
324 #ifndef TEST_PARSER
325 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
326 #else
327 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
328 #endif
329 break;
330 case 41:
331 result->next_state = INITIAL;
332 #ifndef TEST_PARSER
333 cfg_hide_edge_borders(&current_match, result, get_string("hide_borders"));
334 #else
335 fprintf(stderr, "cfg_hide_edge_borders(%s)\n", get_string("hide_borders"));
336 #endif
337 break;
338 case 42:
339 result->next_state = INITIAL;
340 #ifndef TEST_PARSER
341 cfg_assign(&current_match, result, get_string("workspace"), 0);
342 #else
343 fprintf(stderr, "cfg_assign(%s, %d)\n", get_string("workspace"), 0);
344 #endif
345 break;
346 case 43:
347 result->next_state = BAR;
348 #ifndef TEST_PARSER
349 cfg_bar_hidden_state(&current_match, result, get_string("hidden_state"));
350 #else
351 fprintf(stderr, "cfg_bar_hidden_state(%s)\n", get_string("hidden_state"));
352 #endif
353 break;
354 case 44:
355 result->next_state = BAR;
356 #ifndef TEST_PARSER
357 cfg_bar_hidden_state(&current_match, result, get_string("hidden_state"));
358 #else
359 fprintf(stderr, "cfg_bar_hidden_state(%s)\n", get_string("hidden_state"));
360 #endif
361 break;
362 case 45:
363 result->next_state = BAR;
364 #ifndef TEST_PARSER
365 cfg_bar_wheel_up_cmd(&current_match, result, get_string("command"));
366 #else
367 fprintf(stderr, "cfg_bar_wheel_up_cmd(%s)\n", get_string("command"));
368 #endif
369 break;
370 case 46:
371 result->next_state = INITIAL;
372 #ifndef TEST_PARSER
373 cfg_ipc_kill_timeout(&current_match, result, get_long("timeout"));
374 #else
375 fprintf(stderr, "cfg_ipc_kill_timeout(%ld)\n", get_long("timeout"));
376 #endif
377 break;
378 case 47:
379 result->next_state = MODE;
380 #ifndef TEST_PARSER
381 cfg_mode_binding(&current_match, result, get_string("bindtype"), get_string("modifiers"), get_string("key"), get_string("release"), get_string("border"), get_string("whole_window"), get_string("exclude_titlebar"), get_string("command"));
382 #else
383 fprintf(stderr, "cfg_mode_binding(%s, %s, %s, %s, %s, %s, %s, %s)\n", get_string("bindtype"), get_string("modifiers"), get_string("key"), get_string("release"), get_string("border"), get_string("whole_window"), get_string("exclude_titlebar"), get_string("command"));
384 #endif
385 break;
386 case 48:
387 result->next_state = INITIAL;
388 #ifndef TEST_PARSER
389 cfg_workspace_layout(&current_match, result, get_string("layout"));
390 #else
391 fprintf(stderr, "cfg_workspace_layout(%s)\n", get_string("layout"));
392 #endif
393 break;
394 case 49:
395 result->next_state = INITIAL;
396 #ifndef TEST_PARSER
397 cfg_workspace_layout(&current_match, result, get_string("layout"));
398 #else
399 fprintf(stderr, "cfg_workspace_layout(%s)\n", get_string("layout"));
400 #endif
401 break;
402 case 50:
403 result->next_state = INITIAL;
404 #ifndef TEST_PARSER
405 cfg_workspace_layout(&current_match, result, get_string("layout"));
406 #else
407 fprintf(stderr, "cfg_workspace_layout(%s)\n", get_string("layout"));
408 #endif
409 break;
410 case 51:
411 result->next_state = INITIAL;
412 #ifndef TEST_PARSER
413 cfg_workspace_layout(&current_match, result, get_string("layout"));
414 #else
415 fprintf(stderr, "cfg_workspace_layout(%s)\n", get_string("layout"));
416 #endif
417 break;
418 case 52:
419 result->next_state = BAR;
420 #ifndef TEST_PARSER
421 cfg_bar_i3bar_command(&current_match, result, get_string("command"));
422 #else
423 fprintf(stderr, "cfg_bar_i3bar_command(%s)\n", get_string("command"));
424 #endif
425 break;
426 case 53:
427 result->next_state = BAR_COLORS;
428 #ifndef TEST_PARSER
429 cfg_bar_color(&current_match, result, get_string("colorclass"), get_string("border"), get_string("background"), NULL);
430 #else
431 fprintf(stderr, "cfg_bar_color(%s, %s, %s, NULL)\n", get_string("colorclass"), get_string("border"), get_string("background"));
432 #endif
433 break;
434 case 54:
435 result->next_state = BAR_COLORS;
436 #ifndef TEST_PARSER
437 cfg_bar_color(&current_match, result, get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"));
438 #else
439 fprintf(stderr, "cfg_bar_color(%s, %s, %s, %s)\n", get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"));
440 #endif
441 break;
442 case 55:
443 result->next_state = BAR;
444 #ifndef TEST_PARSER
445 cfg_bar_socket_path(&current_match, result, get_string("path"));
446 #else
447 fprintf(stderr, "cfg_bar_socket_path(%s)\n", get_string("path"));
448 #endif
449 break;
450 case 56:
451 result->next_state = BAR;
452 #ifndef TEST_PARSER
453 cfg_bar_tray_output(&current_match, result, get_string("output"));
454 #else
455 fprintf(stderr, "cfg_bar_tray_output(%s)\n", get_string("output"));
456 #endif
457 break;
458 case 57:
459 result->next_state = INITIAL;
460 #ifndef TEST_PARSER
461 cfg_color(&current_match, result, get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"), NULL, NULL);
462 #else
463 fprintf(stderr, "cfg_color(%s, %s, %s, %s, NULL, NULL)\n", get_string("colorclass"), get_string("border"), get_string("background"), get_string("text"));
464 #endif
465 break;
466 case 58:
467 result->next_state = INITIAL;
468 #ifndef TEST_PARSER
469 cfg_disable_randr15(&current_match, result, get_string("value"));
470 #else
471 fprintf(stderr, "cfg_disable_randr15(%s)\n", get_string("value"));
472 #endif
473 break;
474 case 59:
475 result->next_state = INITIAL;
476 #ifndef TEST_PARSER
477 cfg_default_border(&current_match, result, get_string("windowtype"), get_string("border"), -1);
478 #else
479 fprintf(stderr, "cfg_default_border(%s, %s, %d)\n", get_string("windowtype"), get_string("border"), -1);
480 #endif
481 break;
482 case 60:
483 result->next_state = INITIAL;
484 #ifndef TEST_PARSER
485 cfg_default_border(&current_match, result, get_string("windowtype"), get_string("border"), -1);
486 #else
487 fprintf(stderr, "cfg_default_border(%s, %s, %d)\n", get_string("windowtype"), get_string("border"), -1);
488 #endif
489 break;
490 case 61:
491 result->next_state = INITIAL;
492 #ifndef TEST_PARSER
493 cfg_focus_wrapping(&current_match, result, get_string("value"));
494 #else
495 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
496 #endif
497 break;
498 case 62:
499 result->next_state = INITIAL;
500 #ifndef TEST_PARSER
501 cfg_focus_wrapping(&current_match, result, get_string("value"));
502 #else
503 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
504 #endif
505 break;
506 case 63:
507 result->next_state = INITIAL;
508 #ifndef TEST_PARSER
509 cfg_focus_wrapping(&current_match, result, get_string("value"));
510 #else
511 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
512 #endif
513 break;
514 case 64:
515 result->next_state = INITIAL;
516 #ifndef TEST_PARSER
517 cfg_focus_wrapping(&current_match, result, get_string("value"));
518 #else
519 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
520 #endif
521 break;
522 case 65:
523 result->next_state = INITIAL;
524 #ifndef TEST_PARSER
525 cfg_focus_wrapping(&current_match, result, get_string("value"));
526 #else
527 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
528 #endif
529 break;
530 case 66:
531 result->next_state = INITIAL;
532 #ifndef TEST_PARSER
533 cfg_focus_wrapping(&current_match, result, get_string("value"));
534 #else
535 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
536 #endif
537 break;
538 case 67:
539 result->next_state = INITIAL;
540 #ifndef TEST_PARSER
541 cfg_focus_wrapping(&current_match, result, get_string("value"));
542 #else
543 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
544 #endif
545 break;
546 case 68:
547 result->next_state = INITIAL;
548 #ifndef TEST_PARSER
549 cfg_focus_wrapping(&current_match, result, get_string("value"));
550 #else
551 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
552 #endif
553 break;
554 case 69:
555 result->next_state = INITIAL;
556 #ifndef TEST_PARSER
557 cfg_focus_wrapping(&current_match, result, get_string("value"));
558 #else
559 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
560 #endif
561 break;
562 case 70:
563 result->next_state = INITIAL;
564 #ifndef TEST_PARSER
565 cfg_focus_wrapping(&current_match, result, get_string("value"));
566 #else
567 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
568 #endif
569 break;
570 case 71:
571 result->next_state = INITIAL;
572 #ifndef TEST_PARSER
573 cfg_focus_wrapping(&current_match, result, get_string("value"));
574 #else
575 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
576 #endif
577 break;
578 case 72:
579 result->next_state = INITIAL;
580 #ifndef TEST_PARSER
581 cfg_focus_wrapping(&current_match, result, get_string("value"));
582 #else
583 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
584 #endif
585 break;
586 case 73:
587 result->next_state = INITIAL;
588 #ifndef TEST_PARSER
589 cfg_focus_wrapping(&current_match, result, get_string("value"));
590 #else
591 fprintf(stderr, "cfg_focus_wrapping(%s)\n", get_string("value"));
592 #endif
593 break;
594 case 74:
595 result->next_state = INITIAL;
596 #ifndef TEST_PARSER
597 cfg_force_xinerama(&current_match, result, get_string("value"));
598 #else
599 fprintf(stderr, "cfg_force_xinerama(%s)\n", get_string("value"));
600 #endif
601 break;
602 case 75:
603 result->next_state = INITIAL;
604 #ifndef TEST_PARSER
605 cfg_assign_output(&current_match, result, get_string("output"));
606 #else
607 fprintf(stderr, "cfg_assign_output(%s)\n", get_string("output"));
608 #endif
609 break;
610 case 76:
611 result->next_state = CRITERIA;
612 #ifndef TEST_PARSER
613 cfg_criteria_add(&current_match, result, get_string("ctype"), get_string("cvalue"));
614 #else
615 fprintf(stderr, "cfg_criteria_add(%s, %s)\n", get_string("ctype"), get_string("cvalue"));
616 cfg_criteria_add(&current_match, result, get_string("ctype"), get_string("cvalue"));
617 #endif
618 break;
619 case 77:
620 result->next_state = INITIAL;
621 #ifndef TEST_PARSER
622 cfg_mouse_warping(&current_match, result, get_string("value"));
623 #else
624 fprintf(stderr, "cfg_mouse_warping(%s)\n", get_string("value"));
625 #endif
626 break;
627 case 78:
628 result->next_state = INITIAL;
629 #ifndef TEST_PARSER
630 cfg_mouse_warping(&current_match, result, get_string("value"));
631 #else
632 fprintf(stderr, "cfg_mouse_warping(%s)\n", get_string("value"));
633 #endif
634 break;
635 case 79:
636 result->next_state = INITIAL;
637 #ifndef TEST_PARSER
638 cfg_restart_state(&current_match, result, get_string("path"));
639 #else
640 fprintf(stderr, "cfg_restart_state(%s)\n", get_string("path"));
641 #endif
642 break;
643 case 80:
644 result->next_state = BAR;
645 #ifndef TEST_PARSER
646 cfg_bar_modifier(&current_match, result, NULL);
647 #else
648 fprintf(stderr, "cfg_bar_modifier(NULL)\n");
649 #endif
650 break;
651 case 81:
652 result->next_state = BAR;
653 #ifndef TEST_PARSER
654 cfg_bar_modifier(&current_match, result, NULL);
655 #else
656 fprintf(stderr, "cfg_bar_modifier(NULL)\n");
657 #endif
658 break;
659 case 82:
660 result->next_state = BAR;
661 #ifndef TEST_PARSER
662 cfg_bar_modifier(&current_match, result, get_string("modifiers"));
663 #else
664 fprintf(stderr, "cfg_bar_modifier(%s)\n", get_string("modifiers"));
665 #endif
666 break;
667 case 83:
668 result->next_state = BAR;
669 #ifndef TEST_PARSER
670 cfg_bar_position(&current_match, result, get_string("position"));
671 #else
672 fprintf(stderr, "cfg_bar_position(%s)\n", get_string("position"));
673 #endif
674 break;
675 case 84:
676 result->next_state = BAR;
677 #ifndef TEST_PARSER
678 cfg_bar_position(&current_match, result, get_string("position"));
679 #else
680 fprintf(stderr, "cfg_bar_position(%s)\n", get_string("position"));
681 #endif
682 break;
683 case 85:
684 result->next_state = INITIAL;
685 #ifndef TEST_PARSER
686 cfg_color_single(&current_match, result, get_string("colorclass"), get_string("color"));
687 #else
688 fprintf(stderr, "cfg_color_single(%s, %s)\n", get_string("colorclass"), get_string("color"));
689 #endif
690 break;
691 case 86:
692 result->next_state = INITIAL;
693 #ifndef TEST_PARSER
694 cfg_fake_outputs(&current_match, result, get_string("outputs"));
695 #else
696 fprintf(stderr, "cfg_fake_outputs(%s)\n", get_string("outputs"));
697 #endif
698 break;
699 case 87:
700 result->next_state = INITIAL;
701 #ifndef TEST_PARSER
702 cfg_no_focus(&current_match, result);
703 #else
704 fprintf(stderr, "cfg_no_focus()\n");
705 #endif
706 break;
707 case 88:
708 result->next_state = BAR;
709 #ifndef TEST_PARSER
710 cfg_bar_verbose(&current_match, result, get_string("value"));
711 #else
712 fprintf(stderr, "cfg_bar_verbose(%s)\n", get_string("value"));
713 #endif
714 break;
715 case 89:
716 result->next_state = INITIAL;
717 #ifndef TEST_PARSER
718 cfg_binding(&current_match, result, get_string("bindtype"), get_string("modifiers"), get_string("key"), get_string("release"), get_string("border"), get_string("whole_window"), get_string("exclude_titlebar"), get_string("command"));
719 #else
720 fprintf(stderr, "cfg_binding(%s, %s, %s, %s, %s, %s, %s, %s)\n", get_string("bindtype"), get_string("modifiers"), get_string("key"), get_string("release"), get_string("border"), get_string("whole_window"), get_string("exclude_titlebar"), get_string("command"));
721 #endif
722 break;
723 case 90:
724 result->next_state = INITIAL;
725 #ifndef TEST_PARSER
726 cfg_title_align(&current_match, result, get_string("alignment"));
727 #else
728 fprintf(stderr, "cfg_title_align(%s)\n", get_string("alignment"));
729 #endif
730 break;
731 case 91:
732 result->next_state = INITIAL;
733 #ifndef TEST_PARSER
734 cfg_title_align(&current_match, result, get_string("alignment"));
735 #else
736 fprintf(stderr, "cfg_title_align(%s)\n", get_string("alignment"));
737 #endif
738 break;
739 case 92:
740 result->next_state = INITIAL;
741 #ifndef TEST_PARSER
742 cfg_title_align(&current_match, result, get_string("alignment"));
743 #else
744 fprintf(stderr, "cfg_title_align(%s)\n", get_string("alignment"));
745 #endif
746 break;
747 case 93:
748 result->next_state = BAR;
749 #ifndef TEST_PARSER
750 cfg_bar_output(&current_match, result, get_string("output"));
751 #else
752 fprintf(stderr, "cfg_bar_output(%s)\n", get_string("output"));
753 #endif
754 break;
755 case 94:
756 result->next_state = CRITERIA;
757 #ifndef TEST_PARSER
758 cfg_criteria_init(&current_match, result, 30);
759 #else
760 fprintf(stderr, "cfg_criteria_init(%d)\n", 30);
761 cfg_criteria_init(&current_match, result, 30);
762 #endif
763 break;
764 case 95:
765 result->next_state = INITIAL;
766 #ifndef TEST_PARSER
767 cfg_ipc_socket(&current_match, result, get_string("path"));
768 #else
769 fprintf(stderr, "cfg_ipc_socket(%s)\n", get_string("path"));
770 #endif
771 break;
772 case 96:
773 result->next_state = INITIAL;
774 #ifndef TEST_PARSER
775 cfg_show_marks(&current_match, result, get_string("value"));
776 #else
777 fprintf(stderr, "cfg_show_marks(%s)\n", get_string("value"));
778 #endif
779 break;
780 case 97:
781 result->next_state = BAR;
782 #ifndef TEST_PARSER
783 cfg_bar_start(&current_match, result);
784 #else
785 fprintf(stderr, "cfg_bar_start()\n");
786 #endif
787 break;
788 case 98:
789 result->next_state = BAR;
790 #ifndef TEST_PARSER
791 cfg_bar_font(&current_match, result, get_string("font"));
792 #else
793 fprintf(stderr, "cfg_bar_font(%s)\n", get_string("font"));
794 #endif
795 break;
796 case 99:
797 result->next_state = BAR;
798 #ifndef TEST_PARSER
799 cfg_bar_mode(&current_match, result, get_string("mode"));
800 #else
801 fprintf(stderr, "cfg_bar_mode(%s)\n", get_string("mode"));
802 #endif
803 break;
804 case 100:
805 result->next_state = BAR;
806 #ifndef TEST_PARSER
807 cfg_bar_mode(&current_match, result, get_string("mode"));
808 #else
809 fprintf(stderr, "cfg_bar_mode(%s)\n", get_string("mode"));
810 #endif
811 break;
812 case 101:
813 result->next_state = BAR;
814 #ifndef TEST_PARSER
815 cfg_bar_mode(&current_match, result, get_string("mode"));
816 #else
817 fprintf(stderr, "cfg_bar_mode(%s)\n", get_string("mode"));
818 #endif
819 break;
820 case 102:
821 result->next_state = CRITERIA;
822 #ifndef TEST_PARSER
823 cfg_criteria_add(&current_match, result, get_string("ctype"), NULL);
824 #else
825 fprintf(stderr, "cfg_criteria_add(%s, NULL)\n", get_string("ctype"));
826 cfg_criteria_add(&current_match, result, get_string("ctype"), NULL);
827 #endif
828 break;
829 case 103:
830 result->next_state = CRITERIA;
831 #ifndef TEST_PARSER
832 cfg_criteria_add(&current_match, result, get_string("ctype"), NULL);
833 #else
834 fprintf(stderr, "cfg_criteria_add(%s, NULL)\n", get_string("ctype"));
835 cfg_criteria_add(&current_match, result, get_string("ctype"), NULL);
836 #endif
837 break;
838 case 104:
839 result->next_state = INITIAL;
840 #ifndef TEST_PARSER
841 cfg_criteria_pop_state(&current_match, result);
842 #else
843 fprintf(stderr, "cfg_criteria_pop_state()\n");
844 cfg_criteria_pop_state(&current_match, result);
845 #endif
846 break;
847 case 105:
848 result->next_state = MODEBRACE;
849 #ifndef TEST_PARSER
850 cfg_enter_mode(&current_match, result, get_string("pango_markup"), get_string("modename"));
851 #else
852 fprintf(stderr, "cfg_enter_mode(%s, %s)\n", get_string("pango_markup"), get_string("modename"));
853 #endif
854 break;
855 case 106:
856 result->next_state = CRITERIA;
857 #ifndef TEST_PARSER
858 cfg_criteria_init(&current_match, result, 66);
859 #else
860 fprintf(stderr, "cfg_criteria_init(%d)\n", 66);
861 cfg_criteria_init(&current_match, result, 66);
862 #endif
863 break;
864 case 107:
865 result->next_state = CRITERIA;
866 #ifndef TEST_PARSER
867 cfg_criteria_init(&current_match, result, 35);
868 #else
869 fprintf(stderr, "cfg_criteria_init(%d)\n", 35);
870 cfg_criteria_init(&current_match, result, 35);
871 #endif
872 break;
873 case 108:
874 result->next_state = BAR;
875 #ifndef TEST_PARSER
876 cfg_bar_id(&current_match, result, get_string("bar_id"));
877 #else
878 fprintf(stderr, "cfg_bar_id(%s)\n", get_string("bar_id"));
879 #endif
880 break;
881 case 109:
882 result->next_state = INITIAL;
883 #ifndef TEST_PARSER
884 cfg_exec(&current_match, result, get_string("exectype"), get_string("no_startup_id"), get_string("command"));
885 #else
886 fprintf(stderr, "cfg_exec(%s, %s, %s)\n", get_string("exectype"), get_string("no_startup_id"), get_string("command"));
887 #endif
888 break;
889 case 110:
890 result->next_state = INITIAL;
891 #ifndef TEST_PARSER
892 cfg_font(&current_match, result, get_string("font"));
893 #else
894 fprintf(stderr, "cfg_font(%s)\n", get_string("font"));
895 #endif
896 break;
897 case 111:
898 result->next_state = INITIAL;
899 #ifndef TEST_PARSER
900 cfg_bar_finish(&current_match, result);
901 #else
902 fprintf(stderr, "cfg_bar_finish()\n");
903 #endif
904 break;
905 default:
906 printf("BUG in the parser. state = %d\n", call_identifier);
907 assert(false);
908 }
909 }
0 typedef enum {
1 FORCE_DISPLAY_URGENCY_HINT_MS = 0,
2 FLOATING_MAXIMUM_SIZE_HEIGHT = 1,
3 FLOATING_MINIMUM_SIZE_HEIGHT = 2,
4 BAR_STRIP_WORKSPACE_NUMBERS = 3,
5 FLOATING_MAXIMUM_SIZE_WIDTH = 4,
6 FLOATING_MINIMUM_SIZE_WIDTH = 5,
7 BAR_BINDING_MODE_INDICATOR = 6,
8 FOCUS_ON_WINDOW_ACTIVATION = 7,
9 FORCE_DISPLAY_URGENCY_HINT = 8,
10 BAR_STRIP_WORKSPACE_NAME = 9,
11 DEFAULT_BORDER_PIXELS_PX = 10,
12 WORKSPACE_BACK_AND_FORTH = 11,
13 ASSIGN_WORKSPACE_NUMBER = 12,
14 FLOATING_MAXIMUM_SIZE_X = 13,
15 FLOATING_MINIMUM_SIZE_X = 14,
16 POPUP_DURING_FULLSCREEN = 15,
17 BAR_COLORS_IGNORE_LINE = 16,
18 BAR_COLORS_BACKGROUND = 17,
19 BAR_WORKSPACE_BUTTONS = 18,
20 DEFAULT_BORDER_PIXELS = 19,
21 BAR_SEPARATOR_SYMBOL = 20,
22 FORCE_FOCUS_WRAPPING = 21,
23 WORKSPACE_OUTPUT_STR = 22,
24 BAR_BINDSYM_COMMAND = 23,
25 BAR_TRAY_PADDING_PX = 24,
26 DEFAULT_ORIENTATION = 25,
27 FOCUS_FOLLOWS_MOUSE = 26,
28 BAR_STATUS_COMMAND = 27,
29 BAR_WHEEL_DOWN_CMD = 28,
30 COLOR_CHILD_BORDER = 29,
31 FOR_WINDOW_COMMAND = 30,
32 BAR_COLORS_BORDER = 31,
33 BAR_COLORS_SINGLE = 32,
34 FLOATING_MODIFIER = 33,
35 HIDE_EDGE_BORDERS = 34,
36 ASSIGN_WORKSPACE = 35,
37 BAR_COLORS_BRACE = 36,
38 BAR_HIDDEN_STATE = 37,
39 BAR_TRAY_PADDING = 38,
40 BAR_WHEEL_UP_CMD = 39,
41 COLOR_BACKGROUND = 40,
42 IPC_KILL_TIMEOUT = 41,
43 MODE_BINDCOMMAND = 42,
44 MODE_IGNORE_LINE = 43,
45 WORKSPACE_LAYOUT = 44,
46 WORKSPACE_OUTPUT = 45,
47 BAR_BAR_COMMAND = 46,
48 BAR_COLORS_TEXT = 47,
49 BAR_IGNORE_LINE = 48,
50 BAR_SOCKET_PATH = 49,
51 BAR_TRAY_OUTPUT = 50,
52 COLOR_INDICATOR = 51,
53 DISABLE_RANDR15 = 52,
54 DEFAULT_BORDER = 53,
55 FOCUS_WRAPPING = 54,
56 FORCE_XINERAMA = 55,
57 ASSIGN_OUTPUT = 56,
58 CRITERION_STR = 57,
59 MOUSE_WARPING = 58,
60 RESTART_STATE = 59,
61 BAR_MODIFIER = 60,
62 BAR_POSITION = 61,
63 COLOR_BORDER = 62,
64 COLOR_SINGLE = 63,
65 FAKE_OUTPUTS = 64,
66 MODE_BINDING = 65,
67 NO_FOCUS_END = 66,
68 BAR_BINDSYM = 67,
69 BAR_VERBOSE = 68,
70 BINDCOMMAND = 69,
71 IGNORE_LINE = 70,
72 TITLE_ALIGN = 71,
73 BAR_COLORS = 72,
74 BAR_OUTPUT = 73,
75 COLOR_TEXT = 74,
76 FOR_WINDOW = 75,
77 IPC_SOCKET = 76,
78 SHOW_MARKS = 77,
79 CRITERION = 78,
80 MODEBRACE = 79,
81 WORKSPACE = 80,
82 BARBRACE = 81,
83 BAR_FONT = 82,
84 BAR_MODE = 83,
85 CRITERIA = 84,
86 MODENAME = 85,
87 NO_FOCUS = 86,
88 BINDING = 87,
89 INITIAL = 88,
90 ASSIGN = 89,
91 BAR_ID = 90,
92 EXEC = 91,
93 FONT = 92,
94 MODE = 93,
95 BAR = 94,
96 __CALL = 95
97 } cmdp_state;
0 static cmdp_token tokens_FORCE_DISPLAY_URGENCY_HINT_MS[2] = {
1 { "'ms", "", FORCE_DISPLAY_URGENCY_HINT_MS, { 0 } },
2 { "end", "", __CALL, { 0 } },
3 };
4 static cmdp_token tokens_FLOATING_MAXIMUM_SIZE_HEIGHT[1] = {
5 { "number", "height", __CALL, { 1 } },
6 };
7 static cmdp_token tokens_FLOATING_MINIMUM_SIZE_HEIGHT[1] = {
8 { "number", "height", __CALL, { 2 } },
9 };
10 static cmdp_token tokens_BAR_STRIP_WORKSPACE_NUMBERS[1] = {
11 { "word", "value", __CALL, { 3 } },
12 };
13 static cmdp_token tokens_FLOATING_MAXIMUM_SIZE_WIDTH[1] = {
14 { "number", "width", FLOATING_MAXIMUM_SIZE_X, { 0 } },
15 };
16 static cmdp_token tokens_FLOATING_MINIMUM_SIZE_WIDTH[1] = {
17 { "number", "width", FLOATING_MINIMUM_SIZE_X, { 0 } },
18 };
19 static cmdp_token tokens_BAR_BINDING_MODE_INDICATOR[1] = {
20 { "word", "value", __CALL, { 4 } },
21 };
22 static cmdp_token tokens_FOCUS_ON_WINDOW_ACTIVATION[1] = {
23 { "word", "mode", __CALL, { 5 } },
24 };
25 static cmdp_token tokens_FORCE_DISPLAY_URGENCY_HINT[1] = {
26 { "number", "duration_ms", FORCE_DISPLAY_URGENCY_HINT_MS, { 0 } },
27 };
28 static cmdp_token tokens_BAR_STRIP_WORKSPACE_NAME[1] = {
29 { "word", "value", __CALL, { 6 } },
30 };
31 static cmdp_token tokens_DEFAULT_BORDER_PIXELS_PX[2] = {
32 { "'px", "", DEFAULT_BORDER_PIXELS_PX, { 0 } },
33 { "end", "", __CALL, { 7 } },
34 };
35 static cmdp_token tokens_WORKSPACE_BACK_AND_FORTH[1] = {
36 { "word", "value", __CALL, { 8 } },
37 };
38 static cmdp_token tokens_ASSIGN_WORKSPACE_NUMBER[1] = {
39 { "string", "number", __CALL, { 9 } },
40 };
41 static cmdp_token tokens_FLOATING_MAXIMUM_SIZE_X[1] = {
42 { "'x", "", FLOATING_MAXIMUM_SIZE_HEIGHT, { 0 } },
43 };
44 static cmdp_token tokens_FLOATING_MINIMUM_SIZE_X[1] = {
45 { "'x", "", FLOATING_MINIMUM_SIZE_HEIGHT, { 0 } },
46 };
47 static cmdp_token tokens_POPUP_DURING_FULLSCREEN[3] = {
48 { "'ignore", "value", __CALL, { 10 } },
49 { "'leave_fullscreen", "value", __CALL, { 11 } },
50 { "'smart", "value", __CALL, { 12 } },
51 };
52 static cmdp_token tokens_BAR_COLORS_IGNORE_LINE[1] = {
53 { "line", "", BAR_COLORS, { 0 } },
54 };
55 static cmdp_token tokens_BAR_COLORS_BACKGROUND[1] = {
56 { "word", "background", BAR_COLORS_TEXT, { 0 } },
57 };
58 static cmdp_token tokens_BAR_WORKSPACE_BUTTONS[1] = {
59 { "word", "value", __CALL, { 13 } },
60 };
61 static cmdp_token tokens_DEFAULT_BORDER_PIXELS[2] = {
62 { "end", "", __CALL, { 14 } },
63 { "number", "width", DEFAULT_BORDER_PIXELS_PX, { 0 } },
64 };
65 static cmdp_token tokens_BAR_SEPARATOR_SYMBOL[1] = {
66 { "string", "separator", __CALL, { 15 } },
67 };
68 static cmdp_token tokens_FORCE_FOCUS_WRAPPING[1] = {
69 { "word", "value", __CALL, { 16 } },
70 };
71 static cmdp_token tokens_WORKSPACE_OUTPUT_STR[1] = {
72 { "string", "output", __CALL, { 17 } },
73 };
74 static cmdp_token tokens_BAR_BINDSYM_COMMAND[2] = {
75 { "'--release", "release", BAR_BINDSYM_COMMAND, { 0 } },
76 { "string", "command", __CALL, { 18 } },
77 };
78 static cmdp_token tokens_BAR_TRAY_PADDING_PX[2] = {
79 { "'px", "", BAR_TRAY_PADDING_PX, { 0 } },
80 { "end", "", __CALL, { 19 } },
81 };
82 static cmdp_token tokens_DEFAULT_ORIENTATION[3] = {
83 { "'horizontal", "orientation", __CALL, { 20 } },
84 { "'vertical", "orientation", __CALL, { 21 } },
85 { "'auto", "orientation", __CALL, { 22 } },
86 };
87 static cmdp_token tokens_FOCUS_FOLLOWS_MOUSE[1] = {
88 { "word", "value", __CALL, { 23 } },
89 };
90 static cmdp_token tokens_BAR_STATUS_COMMAND[1] = {
91 { "string", "command", __CALL, { 24 } },
92 };
93 static cmdp_token tokens_BAR_WHEEL_DOWN_CMD[1] = {
94 { "string", "command", __CALL, { 25 } },
95 };
96 static cmdp_token tokens_COLOR_CHILD_BORDER[2] = {
97 { "word", "child_border", __CALL, { 26 } },
98 { "end", "", __CALL, { 27 } },
99 };
100 static cmdp_token tokens_FOR_WINDOW_COMMAND[1] = {
101 { "string", "command", __CALL, { 28 } },
102 };
103 static cmdp_token tokens_BAR_COLORS_BORDER[1] = {
104 { "word", "border", BAR_COLORS_BACKGROUND, { 0 } },
105 };
106 static cmdp_token tokens_BAR_COLORS_SINGLE[1] = {
107 { "word", "color", __CALL, { 29 } },
108 };
109 static cmdp_token tokens_FLOATING_MODIFIER[10] = {
110 { "'Mod1", "modifiers", FLOATING_MODIFIER, { 0 } },
111 { "'Mod2", "modifiers", FLOATING_MODIFIER, { 0 } },
112 { "'Mod3", "modifiers", FLOATING_MODIFIER, { 0 } },
113 { "'Mod4", "modifiers", FLOATING_MODIFIER, { 0 } },
114 { "'Mod5", "modifiers", FLOATING_MODIFIER, { 0 } },
115 { "'Shift", "modifiers", FLOATING_MODIFIER, { 0 } },
116 { "'Control", "modifiers", FLOATING_MODIFIER, { 0 } },
117 { "'Ctrl", "modifiers", FLOATING_MODIFIER, { 0 } },
118 { "'+", "", FLOATING_MODIFIER, { 0 } },
119 { "end", "", __CALL, { 30 } },
120 };
121 static cmdp_token tokens_HIDE_EDGE_BORDERS[11] = {
122 { "'none", "hide_borders", __CALL, { 31 } },
123 { "'vertical", "hide_borders", __CALL, { 32 } },
124 { "'horizontal", "hide_borders", __CALL, { 33 } },
125 { "'both", "hide_borders", __CALL, { 34 } },
126 { "'smart", "hide_borders", __CALL, { 35 } },
127 { "'1", "hide_borders", __CALL, { 36 } },
128 { "'yes", "hide_borders", __CALL, { 37 } },
129 { "'true", "hide_borders", __CALL, { 38 } },
130 { "'on", "hide_borders", __CALL, { 39 } },
131 { "'enable", "hide_borders", __CALL, { 40 } },
132 { "'active", "hide_borders", __CALL, { 41 } },
133 };
134 static cmdp_token tokens_ASSIGN_WORKSPACE[5] = {
135 { "'→", "", ASSIGN_WORKSPACE, { 0 } },
136 { "'output", "", ASSIGN_OUTPUT, { 0 } },
137 { "'workspace", "", ASSIGN_WORKSPACE, { 0 } },
138 { "'number", "", ASSIGN_WORKSPACE_NUMBER, { 0 } },
139 { "string", "workspace", __CALL, { 42 } },
140 };
141 static cmdp_token tokens_BAR_COLORS_BRACE[2] = {
142 { "end", "", BAR_COLORS_BRACE, { 0 } },
143 { "'{", "", BAR_COLORS, { 0 } },
144 };
145 static cmdp_token tokens_BAR_HIDDEN_STATE[2] = {
146 { "'hide", "hidden_state", __CALL, { 43 } },
147 { "'show", "hidden_state", __CALL, { 44 } },
148 };
149 static cmdp_token tokens_BAR_TRAY_PADDING[1] = {
150 { "number", "padding_px", BAR_TRAY_PADDING_PX, { 0 } },
151 };
152 static cmdp_token tokens_BAR_WHEEL_UP_CMD[1] = {
153 { "string", "command", __CALL, { 45 } },
154 };
155 static cmdp_token tokens_COLOR_BACKGROUND[1] = {
156 { "word", "background", COLOR_TEXT, { 0 } },
157 };
158 static cmdp_token tokens_IPC_KILL_TIMEOUT[1] = {
159 { "number", "timeout", __CALL, { 46 } },
160 };
161 static cmdp_token tokens_MODE_BINDCOMMAND[5] = {
162 { "'--release", "release", MODE_BINDCOMMAND, { 0 } },
163 { "'--border", "border", MODE_BINDCOMMAND, { 0 } },
164 { "'--whole-window", "whole_window", MODE_BINDCOMMAND, { 0 } },
165 { "'--exclude-titlebar", "exclude_titlebar", MODE_BINDCOMMAND, { 0 } },
166 { "string", "command", __CALL, { 47 } },
167 };
168 static cmdp_token tokens_MODE_IGNORE_LINE[1] = {
169 { "line", "", MODE, { 0 } },
170 };
171 static cmdp_token tokens_WORKSPACE_LAYOUT[4] = {
172 { "'default", "layout", __CALL, { 48 } },
173 { "'stacking", "layout", __CALL, { 49 } },
174 { "'stacked", "layout", __CALL, { 50 } },
175 { "'tabbed", "layout", __CALL, { 51 } },
176 };
177 static cmdp_token tokens_WORKSPACE_OUTPUT[1] = {
178 { "'output", "", WORKSPACE_OUTPUT_STR, { 0 } },
179 };
180 static cmdp_token tokens_BAR_BAR_COMMAND[1] = {
181 { "string", "command", __CALL, { 52 } },
182 };
183 static cmdp_token tokens_BAR_COLORS_TEXT[2] = {
184 { "end", "", __CALL, { 53 } },
185 { "word", "text", __CALL, { 54 } },
186 };
187 static cmdp_token tokens_BAR_IGNORE_LINE[1] = {
188 { "line", "", BAR, { 0 } },
189 };
190 static cmdp_token tokens_BAR_SOCKET_PATH[1] = {
191 { "string", "path", __CALL, { 55 } },
192 };
193 static cmdp_token tokens_BAR_TRAY_OUTPUT[1] = {
194 { "word", "output", __CALL, { 56 } },
195 };
196 static cmdp_token tokens_COLOR_INDICATOR[2] = {
197 { "word", "indicator", COLOR_CHILD_BORDER, { 0 } },
198 { "end", "", __CALL, { 57 } },
199 };
200 static cmdp_token tokens_DISABLE_RANDR15[1] = {
201 { "word", "value", __CALL, { 58 } },
202 };
203 static cmdp_token tokens_DEFAULT_BORDER[4] = {
204 { "'normal", "border", DEFAULT_BORDER_PIXELS, { 0 } },
205 { "'pixel", "border", DEFAULT_BORDER_PIXELS, { 0 } },
206 { "'1pixel", "border", __CALL, { 59 } },
207 { "'none", "border", __CALL, { 60 } },
208 };
209 static cmdp_token tokens_FOCUS_WRAPPING[13] = {
210 { "'1", "value", __CALL, { 61 } },
211 { "'yes", "value", __CALL, { 62 } },
212 { "'true", "value", __CALL, { 63 } },
213 { "'on", "value", __CALL, { 64 } },
214 { "'enable", "value", __CALL, { 65 } },
215 { "'active", "value", __CALL, { 66 } },
216 { "'0", "value", __CALL, { 67 } },
217 { "'no", "value", __CALL, { 68 } },
218 { "'false", "value", __CALL, { 69 } },
219 { "'off", "value", __CALL, { 70 } },
220 { "'disable", "value", __CALL, { 71 } },
221 { "'inactive", "value", __CALL, { 72 } },
222 { "'force", "value", __CALL, { 73 } },
223 };
224 static cmdp_token tokens_FORCE_XINERAMA[1] = {
225 { "word", "value", __CALL, { 74 } },
226 };
227 static cmdp_token tokens_ASSIGN_OUTPUT[1] = {
228 { "string", "output", __CALL, { 75 } },
229 };
230 static cmdp_token tokens_CRITERION_STR[1] = {
231 { "word", "cvalue", __CALL, { 76 } },
232 };
233 static cmdp_token tokens_MOUSE_WARPING[2] = {
234 { "'none", "value", __CALL, { 77 } },
235 { "'output", "value", __CALL, { 78 } },
236 };
237 static cmdp_token tokens_RESTART_STATE[1] = {
238 { "string", "path", __CALL, { 79 } },
239 };
240 static cmdp_token tokens_BAR_MODIFIER[12] = {
241 { "'off", "", __CALL, { 80 } },
242 { "'none", "", __CALL, { 81 } },
243 { "'Mod1", "modifiers", BAR_MODIFIER, { 0 } },
244 { "'Mod2", "modifiers", BAR_MODIFIER, { 0 } },
245 { "'Mod3", "modifiers", BAR_MODIFIER, { 0 } },
246 { "'Mod4", "modifiers", BAR_MODIFIER, { 0 } },
247 { "'Mod5", "modifiers", BAR_MODIFIER, { 0 } },
248 { "'Shift", "modifiers", BAR_MODIFIER, { 0 } },
249 { "'Control", "modifiers", BAR_MODIFIER, { 0 } },
250 { "'Ctrl", "modifiers", BAR_MODIFIER, { 0 } },
251 { "'+", "", BAR_MODIFIER, { 0 } },
252 { "end", "", __CALL, { 82 } },
253 };
254 static cmdp_token tokens_BAR_POSITION[2] = {
255 { "'top", "position", __CALL, { 83 } },
256 { "'bottom", "position", __CALL, { 84 } },
257 };
258 static cmdp_token tokens_COLOR_BORDER[1] = {
259 { "word", "border", COLOR_BACKGROUND, { 0 } },
260 };
261 static cmdp_token tokens_COLOR_SINGLE[1] = {
262 { "word", "color", __CALL, { 85 } },
263 };
264 static cmdp_token tokens_FAKE_OUTPUTS[1] = {
265 { "string", "outputs", __CALL, { 86 } },
266 };
267 static cmdp_token tokens_MODE_BINDING[20] = {
268 { "'--release", "release", MODE_BINDING, { 0 } },
269 { "'--border", "border", MODE_BINDING, { 0 } },
270 { "'--whole-window", "whole_window", MODE_BINDING, { 0 } },
271 { "'--exclude-titlebar", "exclude_titlebar", MODE_BINDING, { 0 } },
272 { "'Mod1", "modifiers", MODE_BINDING, { 0 } },
273 { "'Mod2", "modifiers", MODE_BINDING, { 0 } },
274 { "'Mod3", "modifiers", MODE_BINDING, { 0 } },
275 { "'Mod4", "modifiers", MODE_BINDING, { 0 } },
276 { "'Mod5", "modifiers", MODE_BINDING, { 0 } },
277 { "'Shift", "modifiers", MODE_BINDING, { 0 } },
278 { "'Control", "modifiers", MODE_BINDING, { 0 } },
279 { "'Ctrl", "modifiers", MODE_BINDING, { 0 } },
280 { "'Mode_switch", "modifiers", MODE_BINDING, { 0 } },
281 { "'Group1", "modifiers", MODE_BINDING, { 0 } },
282 { "'Group2", "modifiers", MODE_BINDING, { 0 } },
283 { "'Group3", "modifiers", MODE_BINDING, { 0 } },
284 { "'Group4", "modifiers", MODE_BINDING, { 0 } },
285 { "'$mod", "modifiers", MODE_BINDING, { 0 } },
286 { "'+", "", MODE_BINDING, { 0 } },
287 { "word", "key", MODE_BINDCOMMAND, { 0 } },
288 };
289 static cmdp_token tokens_NO_FOCUS_END[1] = {
290 { "end", "", __CALL, { 87 } },
291 };
292 static cmdp_token tokens_BAR_BINDSYM[2] = {
293 { "'--release", "release", BAR_BINDSYM, { 0 } },
294 { "word", "button", BAR_BINDSYM_COMMAND, { 0 } },
295 };
296 static cmdp_token tokens_BAR_VERBOSE[1] = {
297 { "word", "value", __CALL, { 88 } },
298 };
299 static cmdp_token tokens_BINDCOMMAND[5] = {
300 { "'--release", "release", BINDCOMMAND, { 0 } },
301 { "'--border", "border", BINDCOMMAND, { 0 } },
302 { "'--whole-window", "whole_window", BINDCOMMAND, { 0 } },
303 { "'--exclude-titlebar", "exclude_titlebar", BINDCOMMAND, { 0 } },
304 { "string", "command", __CALL, { 89 } },
305 };
306 static cmdp_token tokens_IGNORE_LINE[1] = {
307 { "line", "", INITIAL, { 0 } },
308 };
309 static cmdp_token tokens_TITLE_ALIGN[3] = {
310 { "'left", "alignment", __CALL, { 90 } },
311 { "'center", "alignment", __CALL, { 91 } },
312 { "'right", "alignment", __CALL, { 92 } },
313 };
314 static cmdp_token tokens_BAR_COLORS[15] = {
315 { "end", "", BAR_COLORS, { 0 } },
316 { "'#", "", BAR_COLORS_IGNORE_LINE, { 0 } },
317 { "'set", "", BAR_COLORS_IGNORE_LINE, { 0 } },
318 { "'background", "colorclass", BAR_COLORS_SINGLE, { 0 } },
319 { "'statusline", "colorclass", BAR_COLORS_SINGLE, { 0 } },
320 { "'separator", "colorclass", BAR_COLORS_SINGLE, { 0 } },
321 { "'focused_background", "colorclass", BAR_COLORS_SINGLE, { 0 } },
322 { "'focused_statusline", "colorclass", BAR_COLORS_SINGLE, { 0 } },
323 { "'focused_separator", "colorclass", BAR_COLORS_SINGLE, { 0 } },
324 { "'focused_workspace", "colorclass", BAR_COLORS_BORDER, { 0 } },
325 { "'active_workspace", "colorclass", BAR_COLORS_BORDER, { 0 } },
326 { "'inactive_workspace", "colorclass", BAR_COLORS_BORDER, { 0 } },
327 { "'urgent_workspace", "colorclass", BAR_COLORS_BORDER, { 0 } },
328 { "'binding_mode", "colorclass", BAR_COLORS_BORDER, { 0 } },
329 { "'}", "", BAR, { 0 } },
330 };
331 static cmdp_token tokens_BAR_OUTPUT[1] = {
332 { "string", "output", __CALL, { 93 } },
333 };
334 static cmdp_token tokens_COLOR_TEXT[1] = {
335 { "word", "text", COLOR_INDICATOR, { 0 } },
336 };
337 static cmdp_token tokens_FOR_WINDOW[1] = {
338 { "'[", "", __CALL, { 94 } },
339 };
340 static cmdp_token tokens_IPC_SOCKET[1] = {
341 { "string", "path", __CALL, { 95 } },
342 };
343 static cmdp_token tokens_SHOW_MARKS[1] = {
344 { "word", "value", __CALL, { 96 } },
345 };
346 static cmdp_token tokens_CRITERION[1] = {
347 { "'=", "", CRITERION_STR, { 0 } },
348 };
349 static cmdp_token tokens_MODEBRACE[2] = {
350 { "end", "", MODEBRACE, { 0 } },
351 { "'{", "", MODE, { 0 } },
352 };
353 static cmdp_token tokens_WORKSPACE[1] = {
354 { "word", "workspace", WORKSPACE_OUTPUT, { 0 } },
355 };
356 static cmdp_token tokens_BARBRACE[2] = {
357 { "end", "", BARBRACE, { 0 } },
358 { "'{", "", __CALL, { 97 } },
359 };
360 static cmdp_token tokens_BAR_FONT[1] = {
361 { "string", "font", __CALL, { 98 } },
362 };
363 static cmdp_token tokens_BAR_MODE[3] = {
364 { "'dock", "mode", __CALL, { 99 } },
365 { "'hide", "mode", __CALL, { 100 } },
366 { "'invisible", "mode", __CALL, { 101 } },
367 };
368 static cmdp_token tokens_CRITERIA[13] = {
369 { "'class", "ctype", CRITERION, { 0 } },
370 { "'instance", "ctype", CRITERION, { 0 } },
371 { "'window_role", "ctype", CRITERION, { 0 } },
372 { "'con_id", "ctype", CRITERION, { 0 } },
373 { "'id", "ctype", CRITERION, { 0 } },
374 { "'window_type", "ctype", CRITERION, { 0 } },
375 { "'con_mark", "ctype", CRITERION, { 0 } },
376 { "'title", "ctype", CRITERION, { 0 } },
377 { "'urgent", "ctype", CRITERION, { 0 } },
378 { "'workspace", "ctype", CRITERION, { 0 } },
379 { "'tiling", "ctype", __CALL, { 102 } },
380 { "'floating", "ctype", __CALL, { 103 } },
381 { "']", "", __CALL, { 104 } },
382 };
383 static cmdp_token tokens_MODENAME[2] = {
384 { "'--pango_markup", "pango_markup", MODENAME, { 0 } },
385 { "word", "modename", __CALL, { 105 } },
386 };
387 static cmdp_token tokens_NO_FOCUS[1] = {
388 { "'[", "", __CALL, { 106 } },
389 };
390 static cmdp_token tokens_BINDING[20] = {
391 { "'--release", "release", BINDING, { 0 } },
392 { "'--border", "border", BINDING, { 0 } },
393 { "'--whole-window", "whole_window", BINDING, { 0 } },
394 { "'--exclude-titlebar", "exclude_titlebar", BINDING, { 0 } },
395 { "'Mod1", "modifiers", BINDING, { 0 } },
396 { "'Mod2", "modifiers", BINDING, { 0 } },
397 { "'Mod3", "modifiers", BINDING, { 0 } },
398 { "'Mod4", "modifiers", BINDING, { 0 } },
399 { "'Mod5", "modifiers", BINDING, { 0 } },
400 { "'Shift", "modifiers", BINDING, { 0 } },
401 { "'Control", "modifiers", BINDING, { 0 } },
402 { "'Ctrl", "modifiers", BINDING, { 0 } },
403 { "'Mode_switch", "modifiers", BINDING, { 0 } },
404 { "'Group1", "modifiers", BINDING, { 0 } },
405 { "'Group2", "modifiers", BINDING, { 0 } },
406 { "'Group3", "modifiers", BINDING, { 0 } },
407 { "'Group4", "modifiers", BINDING, { 0 } },
408 { "'$mod", "modifiers", BINDING, { 0 } },
409 { "'+", "", BINDING, { 0 } },
410 { "word", "key", BINDCOMMAND, { 0 } },
411 };
412 static cmdp_token tokens_INITIAL[54] = {
413 { "end", "", INITIAL, { 0 } },
414 { "error", "", INITIAL, { 0 } },
415 { "'#", "", IGNORE_LINE, { 0 } },
416 { "'set ", "", IGNORE_LINE, { 0 } },
417 { "'set ", "", IGNORE_LINE, { 0 } },
418 { "'set_from_resource", "", IGNORE_LINE, { 0 } },
419 { "'bindsym", "bindtype", BINDING, { 0 } },
420 { "'bindcode", "bindtype", BINDING, { 0 } },
421 { "'bind", "bindtype", BINDING, { 0 } },
422 { "'bar", "", BARBRACE, { 0 } },
423 { "'font", "", FONT, { 0 } },
424 { "'mode", "", MODENAME, { 0 } },
425 { "'floating_minimum_size", "", FLOATING_MINIMUM_SIZE_WIDTH, { 0 } },
426 { "'floating_maximum_size", "", FLOATING_MAXIMUM_SIZE_WIDTH, { 0 } },
427 { "'floating_modifier", "", FLOATING_MODIFIER, { 0 } },
428 { "'default_orientation", "", DEFAULT_ORIENTATION, { 0 } },
429 { "'workspace_layout", "", WORKSPACE_LAYOUT, { 0 } },
430 { "'default_border", "windowtype", DEFAULT_BORDER, { 0 } },
431 { "'new_window", "windowtype", DEFAULT_BORDER, { 0 } },
432 { "'default_floating_border", "windowtype", DEFAULT_BORDER, { 0 } },
433 { "'new_float", "windowtype", DEFAULT_BORDER, { 0 } },
434 { "'hide_edge_borders", "", HIDE_EDGE_BORDERS, { 0 } },
435 { "'for_window", "", FOR_WINDOW, { 0 } },
436 { "'assign", "", ASSIGN, { 0 } },
437 { "'no_focus", "", NO_FOCUS, { 0 } },
438 { "'focus_follows_mouse", "", FOCUS_FOLLOWS_MOUSE, { 0 } },
439 { "'mouse_warping", "", MOUSE_WARPING, { 0 } },
440 { "'focus_wrapping", "", FOCUS_WRAPPING, { 0 } },
441 { "'force_focus_wrapping", "", FORCE_FOCUS_WRAPPING, { 0 } },
442 { "'force_xinerama", "", FORCE_XINERAMA, { 0 } },
443 { "'force-xinerama", "", FORCE_XINERAMA, { 0 } },
444 { "'disable_randr15", "", DISABLE_RANDR15, { 0 } },
445 { "'disable-randr15", "", DISABLE_RANDR15, { 0 } },
446 { "'workspace_auto_back_and_forth", "", WORKSPACE_BACK_AND_FORTH, { 0 } },
447 { "'fake_outputs", "", FAKE_OUTPUTS, { 0 } },
448 { "'fake-outputs", "", FAKE_OUTPUTS, { 0 } },
449 { "'force_display_urgency_hint", "", FORCE_DISPLAY_URGENCY_HINT, { 0 } },
450 { "'focus_on_window_activation", "", FOCUS_ON_WINDOW_ACTIVATION, { 0 } },
451 { "'title_align", "", TITLE_ALIGN, { 0 } },
452 { "'show_marks", "", SHOW_MARKS, { 0 } },
453 { "'workspace", "", WORKSPACE, { 0 } },
454 { "'ipc_socket", "", IPC_SOCKET, { 0 } },
455 { "'ipc-socket", "", IPC_SOCKET, { 0 } },
456 { "'ipc_kill_timeout", "", IPC_KILL_TIMEOUT, { 0 } },
457 { "'restart_state", "", RESTART_STATE, { 0 } },
458 { "'popup_during_fullscreen", "", POPUP_DURING_FULLSCREEN, { 0 } },
459 { "'exec_always", "exectype", EXEC, { 0 } },
460 { "'exec", "exectype", EXEC, { 0 } },
461 { "'client.background", "colorclass", COLOR_SINGLE, { 0 } },
462 { "'client.focused_inactive", "colorclass", COLOR_BORDER, { 0 } },
463 { "'client.focused", "colorclass", COLOR_BORDER, { 0 } },
464 { "'client.unfocused", "colorclass", COLOR_BORDER, { 0 } },
465 { "'client.urgent", "colorclass", COLOR_BORDER, { 0 } },
466 { "'client.placeholder", "colorclass", COLOR_BORDER, { 0 } },
467 };
468 static cmdp_token tokens_ASSIGN[1] = {
469 { "'[", "", __CALL, { 107 } },
470 };
471 static cmdp_token tokens_BAR_ID[1] = {
472 { "word", "bar_id", __CALL, { 108 } },
473 };
474 static cmdp_token tokens_EXEC[2] = {
475 { "'--no-startup-id", "no_startup_id", EXEC, { 0 } },
476 { "string", "command", __CALL, { 109 } },
477 };
478 static cmdp_token tokens_FONT[1] = {
479 { "string", "font", __CALL, { 110 } },
480 };
481 static cmdp_token tokens_MODE[8] = {
482 { "end", "", MODE, { 0 } },
483 { "error", "", MODE, { 0 } },
484 { "'#", "", MODE_IGNORE_LINE, { 0 } },
485 { "'set", "", MODE_IGNORE_LINE, { 0 } },
486 { "'bindsym", "bindtype", MODE_BINDING, { 0 } },
487 { "'bindcode", "bindtype", MODE_BINDING, { 0 } },
488 { "'bind", "bindtype", MODE_BINDING, { 0 } },
489 { "'}", "", INITIAL, { 0 } },
490 };
491 static cmdp_token tokens_BAR[27] = {
492 { "end", "", BAR, { 0 } },
493 { "error", "", BAR, { 0 } },
494 { "'#", "", BAR_IGNORE_LINE, { 0 } },
495 { "'set", "", BAR_IGNORE_LINE, { 0 } },
496 { "'i3bar_command", "", BAR_BAR_COMMAND, { 0 } },
497 { "'status_command", "", BAR_STATUS_COMMAND, { 0 } },
498 { "'socket_path", "", BAR_SOCKET_PATH, { 0 } },
499 { "'mode", "", BAR_MODE, { 0 } },
500 { "'hidden_state", "", BAR_HIDDEN_STATE, { 0 } },
501 { "'id", "", BAR_ID, { 0 } },
502 { "'modifier", "", BAR_MODIFIER, { 0 } },
503 { "'wheel_up_cmd", "", BAR_WHEEL_UP_CMD, { 0 } },
504 { "'wheel_down_cmd", "", BAR_WHEEL_DOWN_CMD, { 0 } },
505 { "'bindsym", "", BAR_BINDSYM, { 0 } },
506 { "'position", "", BAR_POSITION, { 0 } },
507 { "'output", "", BAR_OUTPUT, { 0 } },
508 { "'tray_output", "", BAR_TRAY_OUTPUT, { 0 } },
509 { "'tray_padding", "", BAR_TRAY_PADDING, { 0 } },
510 { "'font", "", BAR_FONT, { 0 } },
511 { "'separator_symbol", "", BAR_SEPARATOR_SYMBOL, { 0 } },
512 { "'binding_mode_indicator", "", BAR_BINDING_MODE_INDICATOR, { 0 } },
513 { "'workspace_buttons", "", BAR_WORKSPACE_BUTTONS, { 0 } },
514 { "'strip_workspace_numbers", "", BAR_STRIP_WORKSPACE_NUMBERS, { 0 } },
515 { "'strip_workspace_name", "", BAR_STRIP_WORKSPACE_NAME, { 0 } },
516 { "'verbose", "", BAR_VERBOSE, { 0 } },
517 { "'colors", "", BAR_COLORS_BRACE, { 0 } },
518 { "'}", "", __CALL, { 111 } },
519 };
520 static cmdp_token_ptr tokens[95] = {
521 { tokens_FORCE_DISPLAY_URGENCY_HINT_MS, 2 },
522 { tokens_FLOATING_MAXIMUM_SIZE_HEIGHT, 1 },
523 { tokens_FLOATING_MINIMUM_SIZE_HEIGHT, 1 },
524 { tokens_BAR_STRIP_WORKSPACE_NUMBERS, 1 },
525 { tokens_FLOATING_MAXIMUM_SIZE_WIDTH, 1 },
526 { tokens_FLOATING_MINIMUM_SIZE_WIDTH, 1 },
527 { tokens_BAR_BINDING_MODE_INDICATOR, 1 },
528 { tokens_FOCUS_ON_WINDOW_ACTIVATION, 1 },
529 { tokens_FORCE_DISPLAY_URGENCY_HINT, 1 },
530 { tokens_BAR_STRIP_WORKSPACE_NAME, 1 },
531 { tokens_DEFAULT_BORDER_PIXELS_PX, 2 },
532 { tokens_WORKSPACE_BACK_AND_FORTH, 1 },
533 { tokens_ASSIGN_WORKSPACE_NUMBER, 1 },
534 { tokens_FLOATING_MAXIMUM_SIZE_X, 1 },
535 { tokens_FLOATING_MINIMUM_SIZE_X, 1 },
536 { tokens_POPUP_DURING_FULLSCREEN, 3 },
537 { tokens_BAR_COLORS_IGNORE_LINE, 1 },
538 { tokens_BAR_COLORS_BACKGROUND, 1 },
539 { tokens_BAR_WORKSPACE_BUTTONS, 1 },
540 { tokens_DEFAULT_BORDER_PIXELS, 2 },
541 { tokens_BAR_SEPARATOR_SYMBOL, 1 },
542 { tokens_FORCE_FOCUS_WRAPPING, 1 },
543 { tokens_WORKSPACE_OUTPUT_STR, 1 },
544 { tokens_BAR_BINDSYM_COMMAND, 2 },
545 { tokens_BAR_TRAY_PADDING_PX, 2 },
546 { tokens_DEFAULT_ORIENTATION, 3 },
547 { tokens_FOCUS_FOLLOWS_MOUSE, 1 },
548 { tokens_BAR_STATUS_COMMAND, 1 },
549 { tokens_BAR_WHEEL_DOWN_CMD, 1 },
550 { tokens_COLOR_CHILD_BORDER, 2 },
551 { tokens_FOR_WINDOW_COMMAND, 1 },
552 { tokens_BAR_COLORS_BORDER, 1 },
553 { tokens_BAR_COLORS_SINGLE, 1 },
554 { tokens_FLOATING_MODIFIER, 10 },
555 { tokens_HIDE_EDGE_BORDERS, 11 },
556 { tokens_ASSIGN_WORKSPACE, 5 },
557 { tokens_BAR_COLORS_BRACE, 2 },
558 { tokens_BAR_HIDDEN_STATE, 2 },
559 { tokens_BAR_TRAY_PADDING, 1 },
560 { tokens_BAR_WHEEL_UP_CMD, 1 },
561 { tokens_COLOR_BACKGROUND, 1 },
562 { tokens_IPC_KILL_TIMEOUT, 1 },
563 { tokens_MODE_BINDCOMMAND, 5 },
564 { tokens_MODE_IGNORE_LINE, 1 },
565 { tokens_WORKSPACE_LAYOUT, 4 },
566 { tokens_WORKSPACE_OUTPUT, 1 },
567 { tokens_BAR_BAR_COMMAND, 1 },
568 { tokens_BAR_COLORS_TEXT, 2 },
569 { tokens_BAR_IGNORE_LINE, 1 },
570 { tokens_BAR_SOCKET_PATH, 1 },
571 { tokens_BAR_TRAY_OUTPUT, 1 },
572 { tokens_COLOR_INDICATOR, 2 },
573 { tokens_DISABLE_RANDR15, 1 },
574 { tokens_DEFAULT_BORDER, 4 },
575 { tokens_FOCUS_WRAPPING, 13 },
576 { tokens_FORCE_XINERAMA, 1 },
577 { tokens_ASSIGN_OUTPUT, 1 },
578 { tokens_CRITERION_STR, 1 },
579 { tokens_MOUSE_WARPING, 2 },
580 { tokens_RESTART_STATE, 1 },
581 { tokens_BAR_MODIFIER, 12 },
582 { tokens_BAR_POSITION, 2 },
583 { tokens_COLOR_BORDER, 1 },
584 { tokens_COLOR_SINGLE, 1 },
585 { tokens_FAKE_OUTPUTS, 1 },
586 { tokens_MODE_BINDING, 20 },
587 { tokens_NO_FOCUS_END, 1 },
588 { tokens_BAR_BINDSYM, 2 },
589 { tokens_BAR_VERBOSE, 1 },
590 { tokens_BINDCOMMAND, 5 },
591 { tokens_IGNORE_LINE, 1 },
592 { tokens_TITLE_ALIGN, 3 },
593 { tokens_BAR_COLORS, 15 },
594 { tokens_BAR_OUTPUT, 1 },
595 { tokens_COLOR_TEXT, 1 },
596 { tokens_FOR_WINDOW, 1 },
597 { tokens_IPC_SOCKET, 1 },
598 { tokens_SHOW_MARKS, 1 },
599 { tokens_CRITERION, 1 },
600 { tokens_MODEBRACE, 2 },
601 { tokens_WORKSPACE, 1 },
602 { tokens_BARBRACE, 2 },
603 { tokens_BAR_FONT, 1 },
604 { tokens_BAR_MODE, 3 },
605 { tokens_CRITERIA, 13 },
606 { tokens_MODENAME, 2 },
607 { tokens_NO_FOCUS, 1 },
608 { tokens_BINDING, 20 },
609 { tokens_INITIAL, 54 },
610 { tokens_ASSIGN, 1 },
611 { tokens_BAR_ID, 1 },
612 { tokens_EXEC, 2 },
613 { tokens_FONT, 1 },
614 { tokens_MODE, 8 },
615 { tokens_BAR, 27 },
616 };
0 # vim:ts=2:sw=2:expandtab
1 #
2 # i3 - an improved dynamic tiling window manager
3 # © 2009 Michael Stapelberg and contributors (see also: LICENSE)
4 #
5 # parser-specs/commands.spec: Specification file for generate-command-parser.pl
6 # which will generate the appropriate header files for our C parser.
7 #
8 # Use :source highlighting.vim in vim to get syntax highlighting
9 # for this file.
10
11 state INITIAL:
12 # We have an end token here for all the commands which just call some
13 # function without using an explicit 'end' token.
14 end ->
15 '[' -> call cmd_criteria_init(); CRITERIA
16 'move' -> MOVE
17 'exec' -> EXEC
18 'exit' -> call cmd_exit()
19 'restart' -> call cmd_restart()
20 'reload' -> call cmd_reload()
21 'shmlog' -> SHMLOG
22 'debuglog' -> DEBUGLOG
23 'border' -> BORDER
24 'layout' -> LAYOUT
25 'append_layout' -> APPEND_LAYOUT
26 'workspace' -> WORKSPACE
27 'focus' -> FOCUS
28 'kill' -> KILL
29 'open' -> call cmd_open()
30 'fullscreen' -> FULLSCREEN
31 'sticky' -> STICKY
32 'split' -> SPLIT
33 'floating' -> FLOATING
34 'mark' -> MARK
35 'unmark' -> UNMARK
36 'resize' -> RESIZE
37 'rename' -> RENAME
38 'nop' -> NOP
39 'scratchpad' -> SCRATCHPAD
40 'swap' -> SWAP
41 'title_format' -> TITLE_FORMAT
42 'mode' -> MODE
43 'bar' -> BAR
44
45 state CRITERIA:
46 ctype = 'class' -> CRITERION
47 ctype = 'instance' -> CRITERION
48 ctype = 'window_role' -> CRITERION
49 ctype = 'con_id' -> CRITERION
50 ctype = 'id' -> CRITERION
51 ctype = 'window_type' -> CRITERION
52 ctype = 'con_mark' -> CRITERION
53 ctype = 'title' -> CRITERION
54 ctype = 'urgent' -> CRITERION
55 ctype = 'workspace' -> CRITERION
56 ctype = 'tiling', 'floating'
57 -> call cmd_criteria_add($ctype, NULL); CRITERIA
58 ']' -> call cmd_criteria_match_windows(); INITIAL
59
60 state CRITERION:
61 '=' -> CRITERION_STR
62
63 state CRITERION_STR:
64 cvalue = word
65 -> call cmd_criteria_add($ctype, $cvalue); CRITERIA
66
67 # exec [--no-startup-id] <command>
68 state EXEC:
69 nosn = '--no-startup-id'
70 ->
71 command = string
72 -> call cmd_exec($nosn, $command)
73
74 # shmlog <size>|toggle|on|off
75 state SHMLOG:
76 # argument may be a number
77 argument = string
78 -> call cmd_shmlog($argument)
79
80 # debuglog toggle|on|off
81 state DEBUGLOG:
82 argument = 'toggle', 'on', 'off'
83 -> call cmd_debuglog($argument)
84
85 # border normal|pixel [<n>]
86 # border none|1pixel|toggle
87 state BORDER:
88 border_style = 'normal', 'pixel', 'toggle'
89 -> BORDER_WIDTH
90 border_style = 'none'
91 -> call cmd_border($border_style, 0)
92 '1pixel'
93 -> call cmd_border("pixel", 1)
94
95 state BORDER_WIDTH:
96 end
97 -> call cmd_border($border_style, -1)
98 border_width = number
99 -> call cmd_border($border_style, &border_width)
100
101 # layout default|stacked|stacking|tabbed|splitv|splith
102 # layout toggle [split|all]
103 state LAYOUT:
104 layout_mode = 'default', 'stacked', 'stacking', 'tabbed', 'splitv', 'splith'
105 -> call cmd_layout($layout_mode)
106 'toggle'
107 -> LAYOUT_TOGGLE
108
109 # layout toggle [split|all]
110 state LAYOUT_TOGGLE:
111 end
112 -> call cmd_layout_toggle($toggle_mode)
113 toggle_mode = string
114 -> call cmd_layout_toggle($toggle_mode)
115
116 # append_layout <path>
117 state APPEND_LAYOUT:
118 path = string -> call cmd_append_layout($path)
119
120 # workspace next|prev|next_on_output|prev_on_output
121 # workspace back_and_forth
122 # workspace [--no-auto-back-and-forth] <name>
123 # workspace [--no-auto-back-and-forth] number <number>
124 state WORKSPACE:
125 no_auto_back_and_forth = '--no-auto-back-and-forth'
126 ->
127 direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
128 -> call cmd_workspace($direction)
129 'back_and_forth'
130 -> call cmd_workspace_back_and_forth()
131 'number'
132 -> WORKSPACE_NUMBER
133 workspace = string
134 -> call cmd_workspace_name($workspace, $no_auto_back_and_forth)
135
136 state WORKSPACE_NUMBER:
137 workspace = string
138 -> call cmd_workspace_number($workspace, $no_auto_back_and_forth)
139
140 # focus left|right|up|down
141 # focus output <output>
142 # focus tiling|floating|mode_toggle
143 # focus parent|child
144 # focus
145 state FOCUS:
146 direction = 'left', 'right', 'up', 'down'
147 -> call cmd_focus_direction($direction)
148 'output'
149 -> FOCUS_OUTPUT
150 window_mode = 'tiling', 'floating', 'mode_toggle'
151 -> call cmd_focus_window_mode($window_mode)
152 level = 'parent', 'child'
153 -> call cmd_focus_level($level)
154 end
155 -> call cmd_focus()
156
157 state FOCUS_OUTPUT:
158 output = string
159 -> call cmd_focus_output($output)
160
161 # kill [window|client]
162 state KILL:
163 kill_mode = 'window', 'client'
164 -> call cmd_kill($kill_mode)
165 end
166 -> call cmd_kill($kill_mode)
167
168 # fullscreen enable|toggle [global]
169 # fullscreen disable
170 # fullscreen [global]
171 state FULLSCREEN:
172 action = 'disable'
173 -> call cmd_fullscreen($action, "output")
174 action = 'enable', 'toggle'
175 -> FULLSCREEN_MODE
176 action = ''
177 -> FULLSCREEN_COMPAT
178
179 state FULLSCREEN_MODE:
180 mode = 'global'
181 -> call cmd_fullscreen($action, $mode)
182 end
183 -> call cmd_fullscreen($action, "output")
184
185 state FULLSCREEN_COMPAT:
186 mode = 'global'
187 -> call cmd_fullscreen("toggle", $mode)
188 end
189 -> call cmd_fullscreen("toggle", "output")
190
191 # sticky enable|disable|toggle
192 state STICKY:
193 action = 'enable', 'disable', 'toggle'
194 -> call cmd_sticky($action)
195
196 # split v|h|t|vertical|horizontal|toggle
197 state SPLIT:
198 direction = 'horizontal', 'vertical', 'toggle', 'v', 'h', 't'
199 -> call cmd_split($direction)
200
201 # floating enable|disable|toggle
202 state FLOATING:
203 floating = 'enable', 'disable', 'toggle'
204 -> call cmd_floating($floating)
205
206 # mark [--add|--replace] [--toggle] <mark>
207 state MARK:
208 mode = '--add', '--replace'
209 ->
210 toggle = '--toggle'
211 ->
212 mark = string
213 -> call cmd_mark($mark, $mode, $toggle)
214
215 # unmark [mark]
216 state UNMARK:
217 end
218 -> call cmd_unmark($mark)
219 mark = string
220 -> call cmd_unmark($mark)
221
222 # resize
223 state RESIZE:
224 way = 'grow', 'shrink'
225 -> RESIZE_DIRECTION
226 set = 'set'
227 -> RESIZE_SET
228
229 state RESIZE_DIRECTION:
230 direction = 'up', 'down', 'left', 'right', 'width', 'height'
231 -> RESIZE_PX
232
233 state RESIZE_PX:
234 resize_px = number
235 -> RESIZE_TILING
236 end
237 -> call cmd_resize($way, $direction, 10, 10)
238
239 state RESIZE_TILING:
240 'px'
241 ->
242 'or'
243 -> RESIZE_TILING_OR
244 end
245 -> call cmd_resize($way, $direction, &resize_px, 0)
246
247 state RESIZE_TILING_OR:
248 resize_ppt = number
249 -> RESIZE_TILING_FINAL
250
251 state RESIZE_TILING_FINAL:
252 'ppt', end
253 -> call cmd_resize($way, $direction, &resize_px, &resize_ppt)
254
255 state RESIZE_SET:
256 'height'
257 -> RESIZE_HEIGHT_GET_NUMBER
258 'width'
259 ->
260 width = number
261 -> RESIZE_WIDTH
262
263 state RESIZE_WIDTH:
264 mode_width = 'px', 'ppt'
265 ->
266 end
267 -> call cmd_resize_set(&width, $mode_width, 0, 0)
268 'height'
269 -> RESIZE_HEIGHT_GET_NUMBER
270 height = number
271 -> RESIZE_HEIGHT
272
273 state RESIZE_HEIGHT_GET_NUMBER:
274 height = number
275 -> RESIZE_HEIGHT
276
277 state RESIZE_HEIGHT:
278 mode_height = 'px', 'ppt'
279 ->
280 end
281 -> call cmd_resize_set(&width, $mode_width, &height, $mode_height)
282
283 # rename workspace <name> to <name>
284 # rename workspace to <name>
285 state RENAME:
286 'workspace'
287 -> RENAME_WORKSPACE
288
289 state RENAME_WORKSPACE:
290 'to'
291 -> RENAME_WORKSPACE_LIKELY_TO
292 old_name = word
293 -> RENAME_WORKSPACE_TO
294
295 state RENAME_WORKSPACE_LIKELY_TO:
296 'to '
297 -> RENAME_WORKSPACE_LIKELY_TO_NEW_NAME
298 new_name = word
299 -> call cmd_rename_workspace(NULL, $new_name)
300
301 state RENAME_WORKSPACE_LIKELY_TO_NEW_NAME:
302 new_name = string
303 -> call cmd_rename_workspace("to", $new_name)
304 end
305 -> call cmd_rename_workspace(NULL, "to")
306
307 state RENAME_WORKSPACE_TO:
308 'to'
309 -> RENAME_WORKSPACE_TO_NEW_NAME
310
311 state RENAME_WORKSPACE_TO_NEW_NAME:
312 new_name = string
313 -> call cmd_rename_workspace($old_name, $new_name)
314
315 # move <direction> [<pixels> [px]]
316 # move [window|container] [to] workspace [<str>|next|prev|next_on_output|prev_on_output|current]
317 # move [window|container] [to] output <str>
318 # move [window|container] [to] mark <str>
319 # move [window|container] [to] scratchpad
320 # move workspace to [output] <str>
321 # move scratchpad
322 # move [window|container] [to] [absolute] position [ [<pixels> [px] <pixels> [px]] | center ]
323 # move [window|container] [to] position mouse|cursor|pointer
324 state MOVE:
325 'window'
326 ->
327 'container'
328 ->
329 'to'
330 ->
331 no_auto_back_and_forth = '--no-auto-back-and-forth'
332 ->
333 'workspace'
334 -> MOVE_WORKSPACE
335 'output'
336 -> MOVE_TO_OUTPUT
337 'mark'
338 -> MOVE_TO_MARK
339 'scratchpad'
340 -> call cmd_move_scratchpad()
341 direction = 'left', 'right', 'up', 'down'
342 -> MOVE_DIRECTION
343 method = 'position'
344 -> MOVE_TO_POSITION
345 method = 'absolute'
346 -> MOVE_TO_ABSOLUTE_POSITION
347
348 state MOVE_DIRECTION:
349 pixels = number
350 -> MOVE_DIRECTION_PX
351 end
352 -> call cmd_move_direction($direction, 10)
353
354 state MOVE_DIRECTION_PX:
355 'px'
356 -> call cmd_move_direction($direction, &pixels)
357 end
358 -> call cmd_move_direction($direction, &pixels)
359
360 state MOVE_WORKSPACE:
361 'to '
362 -> MOVE_WORKSPACE_TO_OUTPUT
363 workspace = 'next_on_output', 'prev_on_output', 'next', 'prev', 'current'
364 -> call cmd_move_con_to_workspace($workspace)
365 'back_and_forth'
366 -> call cmd_move_con_to_workspace_back_and_forth()
367 'number'
368 -> MOVE_WORKSPACE_NUMBER
369 workspace = string
370 -> call cmd_move_con_to_workspace_name($workspace, $no_auto_back_and_forth)
371
372 state MOVE_WORKSPACE_NUMBER:
373 number = string
374 -> call cmd_move_con_to_workspace_number($number, $no_auto_back_and_forth)
375
376 state MOVE_TO_OUTPUT:
377 output = string
378 -> call cmd_move_con_to_output($output)
379
380 state MOVE_TO_MARK:
381 mark = string
382 -> call cmd_move_con_to_mark($mark)
383
384 state MOVE_WORKSPACE_TO_OUTPUT:
385 'output'
386 ->
387 output = string
388 -> call cmd_move_workspace_to_output($output)
389
390 state MOVE_TO_ABSOLUTE_POSITION:
391 'position'
392 -> MOVE_TO_POSITION
393
394 state MOVE_TO_POSITION:
395 'center'
396 -> call cmd_move_window_to_center($method)
397 'mouse', 'cursor', 'pointer'
398 -> call cmd_move_window_to_mouse()
399 coord_x = number
400 -> MOVE_TO_POSITION_X
401
402 state MOVE_TO_POSITION_X:
403 'px'
404 ->
405 coord_y = number
406 -> MOVE_TO_POSITION_Y
407
408 state MOVE_TO_POSITION_Y:
409 'px', end
410 -> call cmd_move_window_to_position(&coord_x, &coord_y)
411
412 # mode <string>
413 state MODE:
414 mode = string
415 -> call cmd_mode($mode)
416
417 state NOP:
418 comment = string
419 -> call cmd_nop($comment)
420 end
421 -> call cmd_nop(NULL)
422
423 state SCRATCHPAD:
424 'show'
425 -> call cmd_scratchpad_show()
426
427 # swap [container] [with] id <window>
428 # swap [container] [with] con_id <con_id>
429 # swap [container] [with] mark <mark>
430 state SWAP:
431 'container'
432 ->
433 'with'
434 ->
435 mode = 'id', 'con_id', 'mark'
436 -> SWAP_ARGUMENT
437
438 state SWAP_ARGUMENT:
439 arg = string
440 -> call cmd_swap($mode, $arg)
441
442 state TITLE_FORMAT:
443 format = string
444 -> call cmd_title_format($format)
445
446 # bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]
447 state BAR:
448 bar_type = 'hidden_state'
449 -> BAR_HIDDEN_STATE
450 bar_type = 'mode'
451 -> BAR_MODE
452
453 state BAR_HIDDEN_STATE:
454 bar_value = 'hide', 'show', 'toggle'
455 -> BAR_W_ID
456
457 state BAR_MODE:
458 bar_value = 'dock', 'hide', 'invisible', 'toggle'
459 -> BAR_W_ID
460
461 state BAR_W_ID:
462 bar_id = word
463 ->
464 end
465 -> call cmd_bar($bar_type, $bar_value, $bar_id)
0 # vim:ts=2:sw=2:expandtab
1 #
2 # i3 - an improved dynamic tiling window manager
3 # © 2009 Michael Stapelberg and contributors (see also: LICENSE)
4 #
5 # parser-specs/config.spec: Specification file for generate-command-parser.pl
6 # which will generate the appropriate header files for our C parser.
7 #
8 # Use :source highlighting.vim in vim to get syntax highlighting
9 # for this file.
10
11 # TODO: should we implement an include statement for the criteria part so we DRY?
12
13 state INITIAL:
14 # We have an end token here for all the commands which just call some
15 # function without using an explicit 'end' token.
16 end ->
17 error ->
18 '#' -> IGNORE_LINE
19 'set ' -> IGNORE_LINE
20 'set ' -> IGNORE_LINE
21 'set_from_resource' -> IGNORE_LINE
22 bindtype = 'bindsym', 'bindcode', 'bind' -> BINDING
23 'bar' -> BARBRACE
24 'font' -> FONT
25 'mode' -> MODENAME
26 'floating_minimum_size' -> FLOATING_MINIMUM_SIZE_WIDTH
27 'floating_maximum_size' -> FLOATING_MAXIMUM_SIZE_WIDTH
28 'floating_modifier' -> FLOATING_MODIFIER
29 'default_orientation' -> DEFAULT_ORIENTATION
30 'workspace_layout' -> WORKSPACE_LAYOUT
31 windowtype = 'default_border', 'new_window', 'default_floating_border', 'new_float'
32 -> DEFAULT_BORDER
33 'hide_edge_borders' -> HIDE_EDGE_BORDERS
34 'for_window' -> FOR_WINDOW
35 'assign' -> ASSIGN
36 'no_focus' -> NO_FOCUS
37 'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE
38 'mouse_warping' -> MOUSE_WARPING
39 'focus_wrapping' -> FOCUS_WRAPPING
40 'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING
41 'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA
42 'disable_randr15', 'disable-randr15' -> DISABLE_RANDR15
43 'workspace_auto_back_and_forth' -> WORKSPACE_BACK_AND_FORTH
44 'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS
45 'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT
46 'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION
47 'title_align' -> TITLE_ALIGN
48 'show_marks' -> SHOW_MARKS
49 'workspace' -> WORKSPACE
50 'ipc_socket', 'ipc-socket' -> IPC_SOCKET
51 'ipc_kill_timeout' -> IPC_KILL_TIMEOUT
52 'restart_state' -> RESTART_STATE
53 'popup_during_fullscreen' -> POPUP_DURING_FULLSCREEN
54 exectype = 'exec_always', 'exec' -> EXEC
55 colorclass = 'client.background'
56 -> COLOR_SINGLE
57 colorclass = 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
58 -> COLOR_BORDER
59
60 # We ignore comments and 'set' lines (variables).
61 state IGNORE_LINE:
62 line
63 -> INITIAL
64
65 # floating_minimum_size <width> x <height>
66 state FLOATING_MINIMUM_SIZE_WIDTH:
67 width = number
68 -> FLOATING_MINIMUM_SIZE_X
69
70 state FLOATING_MINIMUM_SIZE_X:
71 'x'
72 -> FLOATING_MINIMUM_SIZE_HEIGHT
73
74 state FLOATING_MINIMUM_SIZE_HEIGHT:
75 height = number
76 -> call cfg_floating_minimum_size(&width, &height)
77
78 # floating_maximum_size <width> x <height>
79 state FLOATING_MAXIMUM_SIZE_WIDTH:
80 width = number
81 -> FLOATING_MAXIMUM_SIZE_X
82
83 state FLOATING_MAXIMUM_SIZE_X:
84 'x'
85 -> FLOATING_MAXIMUM_SIZE_HEIGHT
86
87 state FLOATING_MAXIMUM_SIZE_HEIGHT:
88 height = number
89 -> call cfg_floating_maximum_size(&width, &height)
90
91 # floating_modifier <modifier>
92 state FLOATING_MODIFIER:
93 modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl'
94 ->
95 '+'
96 ->
97 end
98 -> call cfg_floating_modifier($modifiers)
99
100 # default_orientation <horizontal|vertical|auto>
101 state DEFAULT_ORIENTATION:
102 orientation = 'horizontal', 'vertical', 'auto'
103 -> call cfg_default_orientation($orientation)
104
105 # workspace_layout <default|stacking|tabbed>
106 state WORKSPACE_LAYOUT:
107 layout = 'default', 'stacking', 'stacked', 'tabbed'
108 -> call cfg_workspace_layout($layout)
109
110 # <default_border|new_window> <normal|1pixel|none>
111 # <default_floating_border|new_float> <normal|1pixel|none>
112 state DEFAULT_BORDER:
113 border = 'normal', 'pixel'
114 -> DEFAULT_BORDER_PIXELS
115 border = '1pixel', 'none'
116 -> call cfg_default_border($windowtype, $border, -1)
117
118 state DEFAULT_BORDER_PIXELS:
119 end
120 -> call cfg_default_border($windowtype, $border, 2)
121 width = number
122 -> DEFAULT_BORDER_PIXELS_PX
123
124 state DEFAULT_BORDER_PIXELS_PX:
125 'px'
126 ->
127 end
128 -> call cfg_default_border($windowtype, $border, &width)
129
130 # hide_edge_borders <none|vertical|horizontal|both|smart>
131 # also hide_edge_borders <bool> for compatibility
132 state HIDE_EDGE_BORDERS:
133 hide_borders = 'none', 'vertical', 'horizontal', 'both', 'smart'
134 -> call cfg_hide_edge_borders($hide_borders)
135 hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active'
136 -> call cfg_hide_edge_borders($hide_borders)
137
138 # for_window <criteria> command
139 state FOR_WINDOW:
140 '['
141 -> call cfg_criteria_init(FOR_WINDOW_COMMAND); CRITERIA
142
143 state FOR_WINDOW_COMMAND:
144 command = string
145 -> call cfg_for_window($command)
146
147 # assign <criteria> [→] [workspace | output] <name>
148 state ASSIGN:
149 '['
150 -> call cfg_criteria_init(ASSIGN_WORKSPACE); CRITERIA
151
152 state ASSIGN_WORKSPACE:
153 '→'
154 ->
155 'output'
156 -> ASSIGN_OUTPUT
157 'workspace'
158 ->
159 'number'
160 -> ASSIGN_WORKSPACE_NUMBER
161 workspace = string
162 -> call cfg_assign($workspace, 0)
163
164 state ASSIGN_OUTPUT:
165 output = string
166 -> call cfg_assign_output($output)
167
168 state ASSIGN_WORKSPACE_NUMBER:
169 number = string
170 -> call cfg_assign($number, 1)
171
172 # no_focus <criteria>
173 state NO_FOCUS:
174 '['
175 -> call cfg_criteria_init(NO_FOCUS_END); CRITERIA
176
177 state NO_FOCUS_END:
178 end
179 -> call cfg_no_focus()
180
181 # Criteria: Used by for_window and assign.
182 state CRITERIA:
183 ctype = 'class' -> CRITERION
184 ctype = 'instance' -> CRITERION
185 ctype = 'window_role' -> CRITERION
186 ctype = 'con_id' -> CRITERION
187 ctype = 'id' -> CRITERION
188 ctype = 'window_type' -> CRITERION
189 ctype = 'con_mark' -> CRITERION
190 ctype = 'title' -> CRITERION
191 ctype = 'urgent' -> CRITERION
192 ctype = 'workspace' -> CRITERION
193 ctype = 'tiling', 'floating'
194 -> call cfg_criteria_add($ctype, NULL); CRITERIA
195 ']'
196 -> call cfg_criteria_pop_state()
197
198 state CRITERION:
199 '=' -> CRITERION_STR
200
201 state CRITERION_STR:
202 cvalue = word
203 -> call cfg_criteria_add($ctype, $cvalue); CRITERIA
204
205 # focus_follows_mouse bool
206 state FOCUS_FOLLOWS_MOUSE:
207 value = word
208 -> call cfg_focus_follows_mouse($value)
209
210 # mouse_warping warping_t
211 state MOUSE_WARPING:
212 value = 'none', 'output'
213 -> call cfg_mouse_warping($value)
214
215 # focus_wrapping
216 state FOCUS_WRAPPING:
217 value = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive', 'force'
218 -> call cfg_focus_wrapping($value)
219
220 # force_focus_wrapping
221 state FORCE_FOCUS_WRAPPING:
222 value = word
223 -> call cfg_force_focus_wrapping($value)
224
225 # force_xinerama
226 state FORCE_XINERAMA:
227 value = word
228 -> call cfg_force_xinerama($value)
229
230 # disable_randr15
231 state DISABLE_RANDR15:
232 value = word
233 -> call cfg_disable_randr15($value)
234
235 # workspace_back_and_forth
236 state WORKSPACE_BACK_AND_FORTH:
237 value = word
238 -> call cfg_workspace_back_and_forth($value)
239
240
241 # fake_outputs (for testcases)
242 state FAKE_OUTPUTS:
243 outputs = string
244 -> call cfg_fake_outputs($outputs)
245
246 # force_display_urgency_hint <timeout> ms
247 state FORCE_DISPLAY_URGENCY_HINT:
248 duration_ms = number
249 -> FORCE_DISPLAY_URGENCY_HINT_MS
250
251 # title_align [left|center|right]
252 state TITLE_ALIGN:
253 alignment = 'left', 'center', 'right'
254 -> call cfg_title_align($alignment)
255
256 # show_marks
257 state SHOW_MARKS:
258 value = word
259 -> call cfg_show_marks($value)
260
261 state FORCE_DISPLAY_URGENCY_HINT_MS:
262 'ms'
263 ->
264 end
265 -> call cfg_force_display_urgency_hint(&duration_ms)
266
267 # focus_on_window_activation <smart|urgent|focus|none>
268 state FOCUS_ON_WINDOW_ACTIVATION:
269 mode = word
270 -> call cfg_focus_on_window_activation($mode)
271
272 # workspace <workspace> output <output>
273 state WORKSPACE:
274 workspace = word
275 -> WORKSPACE_OUTPUT
276
277 state WORKSPACE_OUTPUT:
278 'output'
279 -> WORKSPACE_OUTPUT_STR
280
281 state WORKSPACE_OUTPUT_STR:
282 output = string
283 -> call cfg_workspace($workspace, $output)
284
285 # ipc-socket <path>
286 state IPC_SOCKET:
287 path = string
288 -> call cfg_ipc_socket($path)
289
290 # ipc_kill_timeout
291 state IPC_KILL_TIMEOUT:
292 timeout = number
293 -> call cfg_ipc_kill_timeout(&timeout)
294
295 # restart_state <path> (for testcases)
296 state RESTART_STATE:
297 path = string
298 -> call cfg_restart_state($path)
299
300 # popup_during_fullscreen
301 state POPUP_DURING_FULLSCREEN:
302 value = 'ignore', 'leave_fullscreen', 'smart'
303 -> call cfg_popup_during_fullscreen($value)
304
305 # client.background <hexcolor>
306 state COLOR_SINGLE:
307 color = word
308 -> call cfg_color_single($colorclass, $color)
309
310 # colorclass border background text indicator
311 state COLOR_BORDER:
312 border = word
313 -> COLOR_BACKGROUND
314
315 state COLOR_BACKGROUND:
316 background = word
317 -> COLOR_TEXT
318
319 state COLOR_TEXT:
320 text = word
321 -> COLOR_INDICATOR
322
323 state COLOR_INDICATOR:
324 indicator = word
325 -> COLOR_CHILD_BORDER
326 end
327 -> call cfg_color($colorclass, $border, $background, $text, NULL, NULL)
328
329 state COLOR_CHILD_BORDER:
330 child_border = word
331 -> call cfg_color($colorclass, $border, $background, $text, $indicator, $child_border)
332 end
333 -> call cfg_color($colorclass, $border, $background, $text, $indicator, NULL)
334
335 # <exec|exec_always> [--no-startup-id] command
336 state EXEC:
337 no_startup_id = '--no-startup-id'
338 ->
339 command = string
340 -> call cfg_exec($exectype, $no_startup_id, $command)
341
342 # font font
343 state FONT:
344 font = string
345 -> call cfg_font($font)
346
347 # bindsym/bindcode
348 state BINDING:
349 release = '--release'
350 ->
351 border = '--border'
352 ->
353 whole_window = '--whole-window'
354 ->
355 exclude_titlebar = '--exclude-titlebar'
356 ->
357 modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', 'Group1', 'Group2', 'Group3', 'Group4', '$mod'
358 ->
359 '+'
360 ->
361 key = word
362 -> BINDCOMMAND
363
364 state BINDCOMMAND:
365 release = '--release'
366 ->
367 border = '--border'
368 ->
369 whole_window = '--whole-window'
370 ->
371 exclude_titlebar = '--exclude-titlebar'
372 ->
373 command = string
374 -> call cfg_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $exclude_titlebar, $command)
375
376 ################################################################################
377 # Mode configuration
378 ################################################################################
379
380 state MODENAME:
381 pango_markup = '--pango_markup'
382 ->
383 modename = word
384 -> call cfg_enter_mode($pango_markup, $modename); MODEBRACE
385
386 state MODEBRACE:
387 end
388 ->
389 '{'
390 -> MODE
391
392 state MODE:
393 end ->
394 error ->
395 '#' -> MODE_IGNORE_LINE
396 'set' -> MODE_IGNORE_LINE
397 bindtype = 'bindsym', 'bindcode', 'bind'
398 -> MODE_BINDING
399 '}'
400 -> INITIAL
401
402 # We ignore comments and 'set' lines (variables).
403 state MODE_IGNORE_LINE:
404 line
405 -> MODE
406
407 state MODE_BINDING:
408 release = '--release'
409 ->
410 border = '--border'
411 ->
412 whole_window = '--whole-window'
413 ->
414 exclude_titlebar = '--exclude-titlebar'
415 ->
416 modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', 'Group1', 'Group2', 'Group3', 'Group4', '$mod'
417 ->
418 '+'
419 ->
420 key = word
421 -> MODE_BINDCOMMAND
422
423 state MODE_BINDCOMMAND:
424 release = '--release'
425 ->
426 border = '--border'
427 ->
428 whole_window = '--whole-window'
429 ->
430 exclude_titlebar = '--exclude-titlebar'
431 ->
432 command = string
433 -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $exclude_titlebar, $command); MODE
434
435 ################################################################################
436 # Bar configuration (i3bar)
437 ################################################################################
438
439 state BARBRACE:
440 end
441 ->
442 '{'
443 -> call cfg_bar_start(); BAR
444
445 state BAR:
446 end ->
447 error ->
448 '#' -> BAR_IGNORE_LINE
449 'set' -> BAR_IGNORE_LINE
450 'i3bar_command' -> BAR_BAR_COMMAND
451 'status_command' -> BAR_STATUS_COMMAND
452 'socket_path' -> BAR_SOCKET_PATH
453 'mode' -> BAR_MODE
454 'hidden_state' -> BAR_HIDDEN_STATE
455 'id' -> BAR_ID
456 'modifier' -> BAR_MODIFIER
457 'wheel_up_cmd' -> BAR_WHEEL_UP_CMD
458 'wheel_down_cmd' -> BAR_WHEEL_DOWN_CMD
459 'bindsym' -> BAR_BINDSYM
460 'position' -> BAR_POSITION
461 'output' -> BAR_OUTPUT
462 'tray_output' -> BAR_TRAY_OUTPUT
463 'tray_padding' -> BAR_TRAY_PADDING
464 'font' -> BAR_FONT
465 'separator_symbol' -> BAR_SEPARATOR_SYMBOL
466 'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
467 'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
468 'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS
469 'strip_workspace_name' -> BAR_STRIP_WORKSPACE_NAME
470 'verbose' -> BAR_VERBOSE
471 'colors' -> BAR_COLORS_BRACE
472 '}'
473 -> call cfg_bar_finish(); INITIAL
474
475 # We ignore comments and 'set' lines (variables).
476 state BAR_IGNORE_LINE:
477 line
478 -> BAR
479
480 state BAR_BAR_COMMAND:
481 command = string
482 -> call cfg_bar_i3bar_command($command); BAR
483
484 state BAR_STATUS_COMMAND:
485 command = string
486 -> call cfg_bar_status_command($command); BAR
487
488 state BAR_SOCKET_PATH:
489 path = string
490 -> call cfg_bar_socket_path($path); BAR
491
492 state BAR_MODE:
493 mode = 'dock', 'hide', 'invisible'
494 -> call cfg_bar_mode($mode); BAR
495
496 state BAR_HIDDEN_STATE:
497 hidden_state = 'hide', 'show'
498 -> call cfg_bar_hidden_state($hidden_state); BAR
499
500 state BAR_ID:
501 bar_id = word
502 -> call cfg_bar_id($bar_id); BAR
503
504 state BAR_MODIFIER:
505 'off', 'none'
506 -> call cfg_bar_modifier(NULL); BAR
507 modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl'
508 ->
509 '+'
510 ->
511 end
512 -> call cfg_bar_modifier($modifiers); BAR
513
514 state BAR_WHEEL_UP_CMD:
515 command = string
516 -> call cfg_bar_wheel_up_cmd($command); BAR
517
518 state BAR_WHEEL_DOWN_CMD:
519 command = string
520 -> call cfg_bar_wheel_down_cmd($command); BAR
521
522 state BAR_BINDSYM:
523 release = '--release'
524 ->
525 button = word
526 -> BAR_BINDSYM_COMMAND
527
528 state BAR_BINDSYM_COMMAND:
529 release = '--release'
530 ->
531 command = string
532 -> call cfg_bar_bindsym($button, $release, $command); BAR
533
534 state BAR_POSITION:
535 position = 'top', 'bottom'
536 -> call cfg_bar_position($position); BAR
537
538 state BAR_OUTPUT:
539 output = string
540 -> call cfg_bar_output($output); BAR
541
542 state BAR_TRAY_OUTPUT:
543 output = word
544 -> call cfg_bar_tray_output($output); BAR
545
546 state BAR_TRAY_PADDING:
547 padding_px = number
548 -> BAR_TRAY_PADDING_PX
549
550 state BAR_TRAY_PADDING_PX:
551 'px'
552 ->
553 end
554 -> call cfg_bar_tray_padding(&padding_px); BAR
555
556 state BAR_FONT:
557 font = string
558 -> call cfg_bar_font($font); BAR
559
560 state BAR_SEPARATOR_SYMBOL:
561 separator = string
562 -> call cfg_bar_separator_symbol($separator); BAR
563
564 state BAR_BINDING_MODE_INDICATOR:
565 value = word
566 -> call cfg_bar_binding_mode_indicator($value); BAR
567
568 state BAR_WORKSPACE_BUTTONS:
569 value = word
570 -> call cfg_bar_workspace_buttons($value); BAR
571
572 state BAR_STRIP_WORKSPACE_NUMBERS:
573 value = word
574 -> call cfg_bar_strip_workspace_numbers($value); BAR
575
576 state BAR_STRIP_WORKSPACE_NAME:
577 value = word
578 -> call cfg_bar_strip_workspace_name($value); BAR
579
580 state BAR_VERBOSE:
581 value = word
582 -> call cfg_bar_verbose($value); BAR
583
584 state BAR_COLORS_BRACE:
585 end
586 ->
587 '{'
588 -> BAR_COLORS
589
590 state BAR_COLORS:
591 end ->
592 '#' -> BAR_COLORS_IGNORE_LINE
593 'set' -> BAR_COLORS_IGNORE_LINE
594 colorclass = 'background', 'statusline', 'separator', 'focused_background', 'focused_statusline', 'focused_separator'
595 -> BAR_COLORS_SINGLE
596 colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace', 'binding_mode'
597 -> BAR_COLORS_BORDER
598 '}'
599 -> BAR
600
601 # We ignore comments and 'set' lines (variables).
602 state BAR_COLORS_IGNORE_LINE:
603 line
604 -> BAR_COLORS
605
606 state BAR_COLORS_SINGLE:
607 color = word
608 -> call cfg_bar_color_single($colorclass, $color); BAR_COLORS
609
610 state BAR_COLORS_BORDER:
611 border = word
612 -> BAR_COLORS_BACKGROUND
613
614 state BAR_COLORS_BACKGROUND:
615 background = word
616 -> BAR_COLORS_TEXT
617
618 state BAR_COLORS_TEXT:
619 end
620 -> call cfg_bar_color($colorclass, $border, $background, NULL); BAR_COLORS
621 text = word
622 -> call cfg_bar_color($colorclass, $border, $background, $text); BAR_COLORS
0 set filetype=i3cmd
1 syntax case match
2 syntax clear
3
4 syntax keyword i3specStatement state call
5 highlight link i3specStatement Statement
6
7 syntax match i3specComment /#.*/
8 highlight link i3specComment Comment
9
10 syntax region i3specLiteral start=/'/ end=/'/
11 syntax keyword i3specToken string word number end
12 highlight link i3specLiteral String
13 highlight link i3specToken String
14
15 syntax match i3specState /[A-Z_]\{3,}/
16 highlight link i3specState PreProc
17
18 syntax match i3specSpecial /[->]/
19 highlight link i3specSpecial Special
0 # Doxyfile 1.5.6
1 #
2 # You can use this file with doxygen to create a pseudo-documentation
3 # automatically from source. doxygen-comments are not used very extensively
4 # in i3, mostly for the reason that it clutters the source code and has no
5 # real use (doxygen’s output looks really ugly).
6 #
7 # So, if you want to use it, here you go. This is however not a supported
8 # document, and I recommend you have a look at the docs/ folder or at
9 # https://i3wm.org/ for more, real documentation.
10 #
11
12 #---------------------------------------------------------------------------
13 # Project related configuration options
14 #---------------------------------------------------------------------------
15
16 DOXYFILE_ENCODING = UTF-8
17 PROJECT_NAME = i3
18 PROJECT_NUMBER =
19 OUTPUT_DIRECTORY = pseudo-doc
20 OUTPUT_LANGUAGE = English
21 BRIEF_MEMBER_DESC = YES
22 REPEAT_BRIEF = YES
23 ALWAYS_DETAILED_SEC = YES
24 FULL_PATH_NAMES = YES
25 SHORT_NAMES = YES
26 JAVADOC_AUTOBRIEF = YES
27 TAB_SIZE = 8
28 OPTIMIZE_OUTPUT_FOR_C = YES
29
30 #---------------------------------------------------------------------------
31 # Build related configuration options
32 #---------------------------------------------------------------------------
33
34 EXTRACT_ALL = YES
35 EXTRACT_PRIVATE = NO
36 EXTRACT_STATIC = YES
37 EXTRACT_LOCAL_CLASSES = YES
38 CASE_SENSE_NAMES = YES
39 SHOW_INCLUDE_FILES = YES
40 SORT_MEMBER_DOCS = YES
41 SORT_BRIEF_DOCS = NO
42 SORT_GROUP_NAMES = NO
43 SORT_BY_SCOPE_NAME = NO
44 SHOW_USED_FILES = YES
45 SHOW_FILES = YES
46
47 #---------------------------------------------------------------------------
48 # configuration options related to warning and progress messages
49 #---------------------------------------------------------------------------
50
51 QUIET = NO
52 WARNINGS = YES
53 WARN_IF_UNDOCUMENTED = YES
54 WARN_IF_DOC_ERROR = YES
55 WARN_NO_PARAMDOC = NO
56 WARN_FORMAT = "$file:$line: $text"
57 WARN_LOGFILE =
58
59 #---------------------------------------------------------------------------
60 # configuration options related to the input files
61 #---------------------------------------------------------------------------
62
63 INPUT = src include
64 INPUT_ENCODING = UTF-8
65 RECURSIVE = NO
66
67 #---------------------------------------------------------------------------
68 # configuration options related to source browsing
69 #---------------------------------------------------------------------------
70
71 SOURCE_BROWSER = YES
72 INLINE_SOURCES = NO
73
74 STRIP_CODE_COMMENTS = YES
75
76 REFERENCED_BY_RELATION = YES
77 REFERENCES_RELATION = YES
78 REFERENCES_LINK_SOURCE = YES
79
80 #---------------------------------------------------------------------------
81 # configuration options related to the alphabetical class index
82 #---------------------------------------------------------------------------
83
84 ALPHABETICAL_INDEX = NO
85 COLS_IN_ALPHA_INDEX = 5
86
87 #---------------------------------------------------------------------------
88 # configuration options related to the HTML output
89 #---------------------------------------------------------------------------
90
91 GENERATE_HTML = YES
92 HTML_OUTPUT = html
93 HTML_FILE_EXTENSION = .html
94 HTML_ALIGN_MEMBERS = YES
95 HTML_DYNAMIC_SECTIONS = NO
96 FORMULA_FONTSIZE = 10
97
98 GENERATE_LATEX = NO
99 GENERATE_RTF = NO
100 GENERATE_MAN = NO
101 GENERATE_XML = NO
102 GENERATE_AUTOGEN_DEF = NO
103 GENERATE_PERLMOD = NO
104
105 ENABLE_PREPROCESSING = YES
106 MACRO_EXPANSION = NO
107 EXPAND_ONLY_PREDEF = NO
108 SEARCH_INCLUDES = YES
109 SKIP_FUNCTION_MACROS = YES
110
111 #---------------------------------------------------------------------------
112 # Configuration::additions related to external references
113 #---------------------------------------------------------------------------
114
115 PERL_PATH = /usr/bin/perl
116
117 #---------------------------------------------------------------------------
118 # Configuration options related to the dot tool
119 #---------------------------------------------------------------------------
120
121 CLASS_DIAGRAMS = YES
122 HAVE_DOT = YES
123 DOT_FONTNAME = FreeSans
124 DOT_FONTPATH =
125 CLASS_GRAPH = YES
126 COLLABORATION_GRAPH = YES
127 GROUP_GRAPHS = YES
128 UML_LOOK = NO
129 INCLUDE_GRAPH = YES
130 INCLUDED_BY_GRAPH = YES
131 CALL_GRAPH = YES
132 CALLER_GRAPH = NO
133 GRAPHICAL_HIERARCHY = YES
134 DIRECTORY_GRAPH = YES
135 DOT_IMAGE_FORMAT = png
136 DOT_GRAPH_MAX_NODES = 50
137 MAX_DOT_GRAPH_DEPTH = 0
138 DOT_TRANSPARENT = YES
139 DOT_MULTI_TARGETS = NO
140 GENERATE_LEGEND = YES
141 DOT_CLEANUP = YES
142
143 #---------------------------------------------------------------------------
144 # Configuration::additions related to the search engine
145 #---------------------------------------------------------------------------
146
147 SEARCHENGINE = NO
0 [Desktop Entry]
1 Type=Application
2 Name=i3
3 NoDisplay=true
4 GenericName=A dynamic tiling window manager
5 Comment=improved dynamic tiling window manager
6 Exec=i3
7 X-GNOME-WMName=i3
8 X-GNOME-Autostart-Phase=WindowManager
9 X-GNOME-Provides=windowmanager
10 X-GNOME-Autostart-Notify=false
0 [Desktop Entry]
1 Name=i3 (with debug log)
2 Comment=improved dynamic tiling window manager
3 Exec=i3-with-shmlog
4 Type=Application
5 Keywords=tiling;wm;windowmanager;window;manager;
0 [Desktop Entry]
1 Name=i3
2 Comment=improved dynamic tiling window manager
3 Exec=i3
4 TryExec=i3
5 Type=Application
6 X-LightDM-DesktopName=i3
7 DesktopNames=i3
8 Keywords=tiling;wm;windowmanager;window;manager;
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * assignments.c: Assignments for specific windows (for_window).
7 *
8 */
9 #include "all.h"
10
11 /*
12 * Checks the list of assignments for the given window and runs all matching
13 * ones (unless they have already been run for this specific window).
14 *
15 */
16 void run_assignments(i3Window *window) {
17 DLOG("Checking if any assignments match this window\n");
18
19 bool needs_tree_render = false;
20
21 /* Check if any assignments match */
22 Assignment *current;
23 TAILQ_FOREACH(current, &assignments, assignments) {
24 if (current->type != A_COMMAND || !match_matches_window(&(current->match), window))
25 continue;
26
27 bool skip = false;
28 for (uint32_t c = 0; c < window->nr_assignments; c++) {
29 if (window->ran_assignments[c] != current)
30 continue;
31
32 DLOG("This assignment already ran for the given window, not executing it again.\n");
33 skip = true;
34 break;
35 }
36
37 if (skip)
38 continue;
39
40 /* Store that we ran this assignment to not execute it again. We have
41 * to do this before running the actual command to prevent infinite
42 * loops. */
43 window->nr_assignments++;
44 window->ran_assignments = srealloc(window->ran_assignments, sizeof(Assignment *) * window->nr_assignments);
45 window->ran_assignments[window->nr_assignments - 1] = current;
46
47 DLOG("matching assignment, execute command %s\n", current->dest.command);
48 char *full_command;
49 sasprintf(&full_command, "[id=\"%d\"] %s", window->id, current->dest.command);
50 CommandResult *result = parse_command(full_command, NULL);
51 free(full_command);
52
53 if (result->needs_tree_render)
54 needs_tree_render = true;
55
56 command_result_free(result);
57 }
58
59 /* If any of the commands required re-rendering, we will do that now. */
60 if (needs_tree_render)
61 tree_render();
62 }
63
64 /*
65 * Returns the first matching assignment for the given window.
66 *
67 */
68 Assignment *assignment_for(i3Window *window, int type) {
69 Assignment *assignment;
70
71 TAILQ_FOREACH(assignment, &assignments, assignments) {
72 if ((type != A_ANY && (assignment->type & type) == 0) ||
73 !match_matches_window(&(assignment->match), window))
74 continue;
75 DLOG("got a matching assignment\n");
76 return assignment;
77 }
78
79 return NULL;
80 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * bindings.c: Functions for configuring, finding and, running bindings.
7 */
8 #include "all.h"
9
10 #include <xkbcommon/xkbcommon.h>
11 #include <xkbcommon/xkbcommon-x11.h>
12
13 static struct xkb_context *xkb_context;
14 static struct xkb_keymap *xkb_keymap;
15
16 pid_t command_error_nagbar_pid = -1;
17
18 /*
19 * The name of the default mode.
20 *
21 */
22 const char *DEFAULT_BINDING_MODE = "default";
23
24 /*
25 * Returns the mode specified by `name` or creates a new mode and adds it to
26 * the list of modes.
27 *
28 */
29 static struct Mode *mode_from_name(const char *name, bool pango_markup) {
30 struct Mode *mode;
31
32 /* Try to find the mode in the list of modes and return it */
33 SLIST_FOREACH(mode, &modes, modes) {
34 if (strcmp(mode->name, name) == 0) {
35 return mode;
36 }
37 }
38
39 /* If the mode was not found, create a new one */
40 mode = scalloc(1, sizeof(struct Mode));
41 mode->name = sstrdup(name);
42 mode->pango_markup = pango_markup;
43 mode->bindings = scalloc(1, sizeof(struct bindings_head));
44 TAILQ_INIT(mode->bindings);
45 SLIST_INSERT_HEAD(&modes, mode, modes);
46
47 return mode;
48 }
49
50 /*
51 * Adds a binding from config parameters given as strings and returns a
52 * pointer to the binding structure. Returns NULL if the input code could not
53 * be parsed.
54 *
55 */
56 Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
57 const char *release, const char *border, const char *whole_window,
58 const char *exclude_titlebar, const char *command, const char *modename,
59 bool pango_markup) {
60 Binding *new_binding = scalloc(1, sizeof(Binding));
61 DLOG("Binding %p bindtype %s, modifiers %s, input code %s, release %s\n", new_binding, bindtype, modifiers, input_code, release);
62 new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
63 new_binding->border = (border != NULL);
64 new_binding->whole_window = (whole_window != NULL);
65 new_binding->exclude_titlebar = (exclude_titlebar != NULL);
66 if (strcmp(bindtype, "bindsym") == 0) {
67 new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
68 ? B_MOUSE
69 : B_KEYBOARD);
70
71 new_binding->symbol = sstrdup(input_code);
72 } else {
73 long keycode;
74 if (!parse_long(input_code, &keycode, 10)) {
75 ELOG("Could not parse \"%s\" as an input code, ignoring this binding.\n", input_code);
76 FREE(new_binding);
77 return NULL;
78 }
79
80 new_binding->keycode = keycode;
81 new_binding->input_type = B_KEYBOARD;
82 }
83 new_binding->command = sstrdup(command);
84 new_binding->event_state_mask = event_state_from_str(modifiers);
85 int group_bits_set = 0;
86 if ((new_binding->event_state_mask >> 16) & I3_XKB_GROUP_MASK_1)
87 group_bits_set++;
88 if ((new_binding->event_state_mask >> 16) & I3_XKB_GROUP_MASK_2)
89 group_bits_set++;
90 if ((new_binding->event_state_mask >> 16) & I3_XKB_GROUP_MASK_3)
91 group_bits_set++;
92 if ((new_binding->event_state_mask >> 16) & I3_XKB_GROUP_MASK_4)
93 group_bits_set++;
94 if (group_bits_set > 1)
95 ELOG("Keybinding has more than one Group specified, but your X server is always in precisely one group. The keybinding can never trigger.\n");
96
97 struct Mode *mode = mode_from_name(modename, pango_markup);
98 TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings);
99
100 TAILQ_INIT(&(new_binding->keycodes_head));
101
102 return new_binding;
103 }
104
105 static bool binding_in_current_group(const Binding *bind) {
106 /* If no bits are set, the binding should be installed in every group. */
107 if ((bind->event_state_mask >> 16) == I3_XKB_GROUP_MASK_ANY)
108 return true;
109 switch (xkb_current_group) {
110 case XCB_XKB_GROUP_1:
111 return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_1);
112 case XCB_XKB_GROUP_2:
113 return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_2);
114 case XCB_XKB_GROUP_3:
115 return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_3);
116 case XCB_XKB_GROUP_4:
117 return ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_4);
118 default:
119 ELOG("BUG: xkb_current_group (= %d) outside of [XCB_XKB_GROUP_1..XCB_XKB_GROUP_4]\n", xkb_current_group);
120 return false;
121 }
122 }
123
124 static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
125 /* Grab the key in all combinations */
126 #define GRAB_KEY(modifier) \
127 do { \
128 xcb_grab_key(conn, 0, root, modifier, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
129 } while (0)
130 const int mods = (bind->event_state_mask & 0xFFFF);
131 DLOG("Binding %p Grabbing keycode %d with event state mask 0x%x (mods 0x%x)\n",
132 bind, keycode, bind->event_state_mask, mods);
133 GRAB_KEY(mods);
134 /* Also bind the key with active NumLock */
135 GRAB_KEY(mods | xcb_numlock_mask);
136 /* Also bind the key with active CapsLock */
137 GRAB_KEY(mods | XCB_MOD_MASK_LOCK);
138 /* Also bind the key with active NumLock+CapsLock */
139 GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
140 }
141
142 /*
143 * Grab the bound keys (tell X to send us keypress events for those keycodes)
144 *
145 */
146 void grab_all_keys(xcb_connection_t *conn) {
147 Binding *bind;
148 TAILQ_FOREACH(bind, bindings, bindings) {
149 if (bind->input_type != B_KEYBOARD)
150 continue;
151
152 if (!binding_in_current_group(bind))
153 continue;
154
155 /* The easy case: the user specified a keycode directly. */
156 if (bind->keycode > 0) {
157 grab_keycode_for_binding(conn, bind, bind->keycode);
158 continue;
159 }
160
161 struct Binding_Keycode *binding_keycode;
162 TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
163 const int keycode = binding_keycode->keycode;
164 const int mods = (binding_keycode->modifiers & 0xFFFF);
165 DLOG("Binding %p Grabbing keycode %d with mods %d\n", bind, keycode, mods);
166 xcb_grab_key(conn, 0, root, mods, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
167 }
168 }
169 }
170
171 /*
172 * Release the button grabs on all managed windows and regrab them,
173 * reevaluating which buttons need to be grabbed.
174 *
175 */
176 void regrab_all_buttons(xcb_connection_t *conn) {
177 int *buttons = bindings_get_buttons_to_grab();
178 xcb_grab_server(conn);
179
180 Con *con;
181 TAILQ_FOREACH(con, &all_cons, all_cons) {
182 if (con->window == NULL)
183 continue;
184
185 xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, con->window->id, XCB_BUTTON_MASK_ANY);
186 xcb_grab_buttons(conn, con->window->id, buttons);
187 }
188
189 FREE(buttons);
190 xcb_ungrab_server(conn);
191 }
192
193 /*
194 * Returns a pointer to the Binding with the specified modifiers and
195 * keycode or NULL if no such binding exists.
196 *
197 */
198 static Binding *get_binding(i3_event_state_mask_t state_filtered, bool is_release, uint16_t input_code, input_type_t input_type) {
199 Binding *bind;
200 Binding *result = NULL;
201
202 if (!is_release) {
203 /* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS
204 * bindings back to B_UPON_KEYRELEASE */
205 TAILQ_FOREACH(bind, bindings, bindings) {
206 if (bind->input_type != input_type)
207 continue;
208 if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
209 bind->release = B_UPON_KEYRELEASE;
210 }
211 }
212
213 const uint32_t xkb_group_state = (state_filtered & 0xFFFF0000);
214 const uint32_t modifiers_state = (state_filtered & 0x0000FFFF);
215 TAILQ_FOREACH(bind, bindings, bindings) {
216 if (bind->input_type != input_type) {
217 continue;
218 }
219
220 const uint32_t xkb_group_mask = (bind->event_state_mask & 0xFFFF0000);
221 const bool groups_match = ((xkb_group_state & xkb_group_mask) == xkb_group_mask);
222 if (!groups_match) {
223 DLOG("skipping binding %p because XKB groups do not match\n", bind);
224 continue;
225 }
226
227 /* For keyboard bindings where a symbol was specified by the user, we
228 * need to look in the array of translated keycodes for the event’s
229 * keycode */
230 bool found_keycode = false;
231 if (input_type == B_KEYBOARD && bind->symbol != NULL) {
232 xcb_keycode_t input_keycode = (xcb_keycode_t)input_code;
233 struct Binding_Keycode *binding_keycode;
234 TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
235 const uint32_t modifiers_mask = (binding_keycode->modifiers & 0x0000FFFF);
236 const bool mods_match = (modifiers_mask == modifiers_state);
237 DLOG("binding_keycode->modifiers = %d, modifiers_mask = %d, modifiers_state = %d, mods_match = %s\n",
238 binding_keycode->modifiers, modifiers_mask, modifiers_state, (mods_match ? "yes" : "no"));
239 if (binding_keycode->keycode == input_keycode &&
240 (mods_match || (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS && is_release))) {
241 found_keycode = true;
242 break;
243 }
244 }
245 } else {
246 /* This case is easier: The user specified a keycode */
247 if (bind->keycode != input_code) {
248 continue;
249 }
250
251 struct Binding_Keycode *binding_keycode;
252 TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
253 const uint32_t modifiers_mask = (binding_keycode->modifiers & 0x0000FFFF);
254 const bool mods_match = (modifiers_mask == modifiers_state);
255 DLOG("binding_keycode->modifiers = %d, modifiers_mask = %d, modifiers_state = %d, mods_match = %s\n",
256 binding_keycode->modifiers, modifiers_mask, modifiers_state, (mods_match ? "yes" : "no"));
257 if (mods_match || (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS && is_release)) {
258 found_keycode = true;
259 break;
260 }
261 }
262 }
263 if (!found_keycode) {
264 continue;
265 }
266
267 /* If this binding is a release binding, it matches the key which the
268 * user pressed. We therefore mark it as B_UPON_KEYRELEASE_IGNORE_MODS
269 * for later, so that the user can release the modifiers before the
270 * actual key or button and the release event will still be matched. */
271 if (bind->release == B_UPON_KEYRELEASE && !is_release) {
272 bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
273 DLOG("marked bind %p as B_UPON_KEYRELEASE_IGNORE_MODS\n", bind);
274 if (result) {
275 break;
276 }
277 continue;
278 }
279
280 /* Check if the binding is for a press or a release event */
281 if ((bind->release == B_UPON_KEYPRESS && is_release)) {
282 continue;
283 }
284
285 if (is_release) {
286 return bind;
287 } else if (!result) {
288 /* Continue looping to mark needed B_UPON_KEYRELEASE_IGNORE_MODS. */
289 result = bind;
290 }
291 }
292
293 return result;
294 }
295
296 /*
297 * Returns a pointer to the Binding that matches the given xcb button or key
298 * event or NULL if no such binding exists.
299 *
300 */
301 Binding *get_binding_from_xcb_event(xcb_generic_event_t *event) {
302 const bool is_release = (event->response_type == XCB_KEY_RELEASE ||
303 event->response_type == XCB_BUTTON_RELEASE);
304
305 const input_type_t input_type = ((event->response_type == XCB_BUTTON_RELEASE ||
306 event->response_type == XCB_BUTTON_PRESS)
307 ? B_MOUSE
308 : B_KEYBOARD);
309
310 const uint16_t event_state = ((xcb_key_press_event_t *)event)->state;
311 const uint16_t event_detail = ((xcb_key_press_event_t *)event)->detail;
312
313 /* Remove the CapsLock bit */
314 i3_event_state_mask_t state_filtered = event_state & ~XCB_MOD_MASK_LOCK;
315 DLOG("(removed capslock, state = 0x%x)\n", state_filtered);
316 /* Transform the keyboard_group from bit 13 and bit 14 into an
317 * i3_xkb_group_mask_t, so that get_binding() can just bitwise AND the
318 * configured bindings against |state_filtered|.
319 *
320 * These bits are only set because we set the XKB client flags
321 * XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE and
322 * XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED. See also doc/kbproto
323 * section 2.2.2:
324 * https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Computing_A_State_Field_from_an_XKB_State */
325 switch ((event_state & 0x6000) >> 13) {
326 case XCB_XKB_GROUP_1:
327 state_filtered |= (I3_XKB_GROUP_MASK_1 << 16);
328 break;
329 case XCB_XKB_GROUP_2:
330 state_filtered |= (I3_XKB_GROUP_MASK_2 << 16);
331 break;
332 case XCB_XKB_GROUP_3:
333 state_filtered |= (I3_XKB_GROUP_MASK_3 << 16);
334 break;
335 case XCB_XKB_GROUP_4:
336 state_filtered |= (I3_XKB_GROUP_MASK_4 << 16);
337 break;
338 }
339 state_filtered &= ~0x6000;
340 DLOG("(transformed keyboard group, state = 0x%x)\n", state_filtered);
341 return get_binding(state_filtered, is_release, event_detail, input_type);
342 }
343
344 struct resolve {
345 /* The binding which we are resolving. */
346 Binding *bind;
347
348 /* |bind|’s keysym (translated to xkb_keysym_t), e.g. XKB_KEY_R. */
349 xkb_keysym_t keysym;
350
351 /* The xkb state built from the user-provided modifiers and group. */
352 struct xkb_state *xkb_state;
353
354 /* Like |xkb_state|, just without the shift modifier, if shift was specified. */
355 struct xkb_state *xkb_state_no_shift;
356
357 /* Like |xkb_state|, but with NumLock. */
358 struct xkb_state *xkb_state_numlock;
359
360 /* Like |xkb_state|, but with NumLock, just without the shift modifier, if shift was specified. */
361 struct xkb_state *xkb_state_numlock_no_shift;
362 };
363
364 #define ADD_TRANSLATED_KEY(code, mods) \
365 do { \
366 struct Binding_Keycode *binding_keycode = smalloc(sizeof(struct Binding_Keycode)); \
367 binding_keycode->modifiers = (mods); \
368 binding_keycode->keycode = (code); \
369 TAILQ_INSERT_TAIL(&(bind->keycodes_head), binding_keycode, keycodes); \
370 } while (0)
371
372 /*
373 * add_keycode_if_matches is called for each keycode in the keymap and will add
374 * the keycode to |data->bind| if the keycode can result in the keysym
375 * |data->resolving|.
376 *
377 */
378 static void add_keycode_if_matches(struct xkb_keymap *keymap, xkb_keycode_t key, void *data) {
379 const struct resolve *resolving = data;
380 struct xkb_state *numlock_state = resolving->xkb_state_numlock;
381 xkb_keysym_t sym = xkb_state_key_get_one_sym(resolving->xkb_state, key);
382 if (sym != resolving->keysym) {
383 /* Check if Shift was specified, and try resolving the symbol without
384 * shift, so that “bindsym $mod+Shift+a nop” actually works. */
385 const xkb_layout_index_t layout = xkb_state_key_get_layout(resolving->xkb_state, key);
386 if (layout == XKB_LAYOUT_INVALID)
387 return;
388 if (xkb_state_key_get_level(resolving->xkb_state, key, layout) > 1)
389 return;
390 /* Skip the Shift fallback for keypad keys, otherwise one cannot bind
391 * KP_1 independent of KP_End. */
392 if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_Equal)
393 return;
394 numlock_state = resolving->xkb_state_numlock_no_shift;
395 sym = xkb_state_key_get_one_sym(resolving->xkb_state_no_shift, key);
396 if (sym != resolving->keysym)
397 return;
398 }
399 Binding *bind = resolving->bind;
400
401 ADD_TRANSLATED_KEY(key, bind->event_state_mask);
402
403 /* Also bind the key with active CapsLock */
404 ADD_TRANSLATED_KEY(key, bind->event_state_mask | XCB_MOD_MASK_LOCK);
405
406 /* If this binding is not explicitly for NumLock, check whether we need to
407 * add a fallback. */
408 if ((bind->event_state_mask & xcb_numlock_mask) != xcb_numlock_mask) {
409 /* Check whether the keycode results in the same keysym when NumLock is
410 * active. If so, grab the key with NumLock as well, so that users don’t
411 * need to duplicate every key binding with an additional Mod2 specified.
412 */
413 xkb_keysym_t sym_numlock = xkb_state_key_get_one_sym(numlock_state, key);
414 if (sym_numlock == resolving->keysym) {
415 /* Also bind the key with active NumLock */
416 ADD_TRANSLATED_KEY(key, bind->event_state_mask | xcb_numlock_mask);
417
418 /* Also bind the key with active NumLock+CapsLock */
419 ADD_TRANSLATED_KEY(key, bind->event_state_mask | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
420 } else {
421 DLOG("Skipping automatic numlock fallback, key %d resolves to 0x%x with numlock\n",
422 key, sym_numlock);
423 }
424 }
425 }
426
427 /*
428 * Translates keysymbols to keycodes for all bindings which use keysyms.
429 *
430 */
431 void translate_keysyms(void) {
432 struct xkb_state *dummy_state = NULL;
433 struct xkb_state *dummy_state_no_shift = NULL;
434 struct xkb_state *dummy_state_numlock = NULL;
435 struct xkb_state *dummy_state_numlock_no_shift = NULL;
436 bool has_errors = false;
437
438 if ((dummy_state = xkb_state_new(xkb_keymap)) == NULL ||
439 (dummy_state_no_shift = xkb_state_new(xkb_keymap)) == NULL ||
440 (dummy_state_numlock = xkb_state_new(xkb_keymap)) == NULL ||
441 (dummy_state_numlock_no_shift = xkb_state_new(xkb_keymap)) == NULL) {
442 ELOG("Could not create XKB state, cannot translate keysyms.\n");
443 goto out;
444 }
445
446 Binding *bind;
447 TAILQ_FOREACH(bind, bindings, bindings) {
448 if (bind->input_type == B_MOUSE) {
449 long button;
450 if (!parse_long(bind->symbol + (sizeof("button") - 1), &button, 10)) {
451 ELOG("Could not translate string to button: \"%s\"\n", bind->symbol);
452 }
453
454 xcb_keycode_t key = button;
455 bind->keycode = key;
456 DLOG("Binding Mouse button, Keycode = %d\n", key);
457 }
458
459 xkb_layout_index_t group = XCB_XKB_GROUP_1;
460 if ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_2)
461 group = XCB_XKB_GROUP_2;
462 else if ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_3)
463 group = XCB_XKB_GROUP_3;
464 else if ((bind->event_state_mask >> 16) & I3_XKB_GROUP_MASK_4)
465 group = XCB_XKB_GROUP_4;
466
467 DLOG("Binding %p group = %d, event_state_mask = %d, &2 = %s, &3 = %s, &4 = %s\n",
468 bind,
469 group,
470 bind->event_state_mask,
471 (bind->event_state_mask & I3_XKB_GROUP_MASK_2) ? "yes" : "no",
472 (bind->event_state_mask & I3_XKB_GROUP_MASK_3) ? "yes" : "no",
473 (bind->event_state_mask & I3_XKB_GROUP_MASK_4) ? "yes" : "no");
474 (void)xkb_state_update_mask(
475 dummy_state,
476 (bind->event_state_mask & 0x1FFF) /* xkb_mod_mask_t base_mods, */,
477 0 /* xkb_mod_mask_t latched_mods, */,
478 0 /* xkb_mod_mask_t locked_mods, */,
479 0 /* xkb_layout_index_t base_group, */,
480 0 /* xkb_layout_index_t latched_group, */,
481 group /* xkb_layout_index_t locked_group, */);
482
483 (void)xkb_state_update_mask(
484 dummy_state_no_shift,
485 (bind->event_state_mask & 0x1FFF) ^ XCB_KEY_BUT_MASK_SHIFT /* xkb_mod_mask_t base_mods, */,
486 0 /* xkb_mod_mask_t latched_mods, */,
487 0 /* xkb_mod_mask_t locked_mods, */,
488 0 /* xkb_layout_index_t base_group, */,
489 0 /* xkb_layout_index_t latched_group, */,
490 group /* xkb_layout_index_t locked_group, */);
491
492 (void)xkb_state_update_mask(
493 dummy_state_numlock,
494 (bind->event_state_mask & 0x1FFF) | xcb_numlock_mask /* xkb_mod_mask_t base_mods, */,
495 0 /* xkb_mod_mask_t latched_mods, */,
496 0 /* xkb_mod_mask_t locked_mods, */,
497 0 /* xkb_layout_index_t base_group, */,
498 0 /* xkb_layout_index_t latched_group, */,
499 group /* xkb_layout_index_t locked_group, */);
500
501 (void)xkb_state_update_mask(
502 dummy_state_numlock_no_shift,
503 ((bind->event_state_mask & 0x1FFF) | xcb_numlock_mask) ^ XCB_KEY_BUT_MASK_SHIFT /* xkb_mod_mask_t base_mods, */,
504 0 /* xkb_mod_mask_t latched_mods, */,
505 0 /* xkb_mod_mask_t locked_mods, */,
506 0 /* xkb_layout_index_t base_group, */,
507 0 /* xkb_layout_index_t latched_group, */,
508 group /* xkb_layout_index_t locked_group, */);
509
510 if (bind->keycode > 0) {
511 /* We need to specify modifiers for the keycode binding (numlock
512 * fallback). */
513 while (!TAILQ_EMPTY(&(bind->keycodes_head))) {
514 struct Binding_Keycode *first = TAILQ_FIRST(&(bind->keycodes_head));
515 TAILQ_REMOVE(&(bind->keycodes_head), first, keycodes);
516 FREE(first);
517 }
518
519 ADD_TRANSLATED_KEY(bind->keycode, bind->event_state_mask);
520
521 /* Also bind the key with active CapsLock */
522 ADD_TRANSLATED_KEY(bind->keycode, bind->event_state_mask | XCB_MOD_MASK_LOCK);
523
524 /* If this binding is not explicitly for NumLock, check whether we need to
525 * add a fallback. */
526 if ((bind->event_state_mask & xcb_numlock_mask) != xcb_numlock_mask) {
527 /* Check whether the keycode results in the same keysym when NumLock is
528 * active. If so, grab the key with NumLock as well, so that users don’t
529 * need to duplicate every key binding with an additional Mod2 specified.
530 */
531 xkb_keysym_t sym = xkb_state_key_get_one_sym(dummy_state, bind->keycode);
532 xkb_keysym_t sym_numlock = xkb_state_key_get_one_sym(dummy_state_numlock, bind->keycode);
533 if (sym == sym_numlock) {
534 /* Also bind the key with active NumLock */
535 ADD_TRANSLATED_KEY(bind->keycode, bind->event_state_mask | xcb_numlock_mask);
536
537 /* Also bind the key with active NumLock+CapsLock */
538 ADD_TRANSLATED_KEY(bind->keycode, bind->event_state_mask | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
539 } else {
540 DLOG("Skipping automatic numlock fallback, key %d resolves to 0x%x with numlock\n",
541 bind->keycode, sym_numlock);
542 }
543 }
544
545 continue;
546 }
547
548 /* We need to translate the symbol to a keycode */
549 const xkb_keysym_t keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
550 if (keysym == XKB_KEY_NoSymbol) {
551 ELOG("Could not translate string to key symbol: \"%s\"\n",
552 bind->symbol);
553 continue;
554 }
555
556 struct resolve resolving = {
557 .bind = bind,
558 .keysym = keysym,
559 .xkb_state = dummy_state,
560 .xkb_state_no_shift = dummy_state_no_shift,
561 .xkb_state_numlock = dummy_state_numlock,
562 .xkb_state_numlock_no_shift = dummy_state_numlock_no_shift,
563 };
564 while (!TAILQ_EMPTY(&(bind->keycodes_head))) {
565 struct Binding_Keycode *first = TAILQ_FIRST(&(bind->keycodes_head));
566 TAILQ_REMOVE(&(bind->keycodes_head), first, keycodes);
567 FREE(first);
568 }
569 xkb_keymap_key_for_each(xkb_keymap, add_keycode_if_matches, &resolving);
570 char *keycodes = sstrdup("");
571 int num_keycodes = 0;
572 struct Binding_Keycode *binding_keycode;
573 TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
574 char *tmp;
575 sasprintf(&tmp, "%s %d", keycodes, binding_keycode->keycode);
576 free(keycodes);
577 keycodes = tmp;
578 num_keycodes++;
579
580 /* check for duplicate bindings */
581 Binding *check;
582 TAILQ_FOREACH(check, bindings, bindings) {
583 if (check == bind)
584 continue;
585 if (check->symbol != NULL)
586 continue;
587 if (check->keycode != binding_keycode->keycode ||
588 check->event_state_mask != binding_keycode->modifiers ||
589 check->release != bind->release)
590 continue;
591 has_errors = true;
592 ELOG("Duplicate keybinding in config file:\n keysym = %s, keycode = %d, state_mask = 0x%x\n", bind->symbol, check->keycode, bind->event_state_mask);
593 }
594 }
595 DLOG("state=0x%x, cfg=\"%s\", sym=0x%x → keycodes%s (%d)\n",
596 bind->event_state_mask, bind->symbol, keysym, keycodes, num_keycodes);
597 free(keycodes);
598 }
599
600 out:
601 xkb_state_unref(dummy_state);
602 xkb_state_unref(dummy_state_no_shift);
603 xkb_state_unref(dummy_state_numlock);
604 xkb_state_unref(dummy_state_numlock_no_shift);
605
606 if (has_errors) {
607 start_config_error_nagbar(current_configpath, true);
608 }
609 }
610
611 #undef ADD_TRANSLATED_KEY
612
613 /*
614 * Switches the key bindings to the given mode, if the mode exists
615 *
616 */
617 void switch_mode(const char *new_mode) {
618 struct Mode *mode;
619
620 DLOG("Switching to mode %s\n", new_mode);
621
622 SLIST_FOREACH(mode, &modes, modes) {
623 if (strcasecmp(mode->name, new_mode) != 0)
624 continue;
625
626 ungrab_all_keys(conn);
627 bindings = mode->bindings;
628 translate_keysyms();
629 grab_all_keys(conn);
630
631 /* Reset all B_UPON_KEYRELEASE_IGNORE_MODS bindings to avoid possibly
632 * activating one of them. */
633 Binding *bind;
634 TAILQ_FOREACH(bind, bindings, bindings) {
635 if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
636 bind->release = B_UPON_KEYRELEASE;
637 }
638
639 char *event_msg;
640 sasprintf(&event_msg, "{\"change\":\"%s\", \"pango_markup\":%s}",
641 mode->name, (mode->pango_markup ? "true" : "false"));
642
643 ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg);
644 FREE(event_msg);
645
646 return;
647 }
648
649 ELOG("Mode not found\n");
650 }
651
652 static int reorder_binding_cmp(const void *a, const void *b) {
653 Binding *first = *((Binding **)a);
654 Binding *second = *((Binding **)b);
655 if (first->event_state_mask < second->event_state_mask) {
656 return 1;
657 } else if (first->event_state_mask == second->event_state_mask) {
658 return 0;
659 } else {
660 return -1;
661 }
662 }
663
664 static void reorder_bindings_of_mode(struct Mode *mode) {
665 /* Copy the bindings into an array, so that we can use qsort(3). */
666 int n = 0;
667 Binding *current;
668 TAILQ_FOREACH(current, mode->bindings, bindings) {
669 n++;
670 }
671 Binding **tmp = scalloc(n, sizeof(Binding *));
672 n = 0;
673 TAILQ_FOREACH(current, mode->bindings, bindings) {
674 tmp[n++] = current;
675 }
676
677 qsort(tmp, n, sizeof(Binding *), reorder_binding_cmp);
678
679 struct bindings_head *reordered = scalloc(1, sizeof(struct bindings_head));
680 TAILQ_INIT(reordered);
681 for (int i = 0; i < n; i++) {
682 current = tmp[i];
683 TAILQ_REMOVE(mode->bindings, current, bindings);
684 TAILQ_INSERT_TAIL(reordered, current, bindings);
685 }
686 free(tmp);
687 assert(TAILQ_EMPTY(mode->bindings));
688 /* Free the old bindings_head, which is now empty. */
689 free(mode->bindings);
690 mode->bindings = reordered;
691 }
692
693 /*
694 * Reorders bindings by event_state_mask descendingly so that get_binding()
695 * correctly matches more specific bindings before more generic bindings. Take
696 * the following binding configuration as an example:
697 *
698 * bindsym n nop lower-case n pressed
699 * bindsym Shift+n nop upper-case n pressed
700 *
701 * Without reordering, the first binding’s event_state_mask of 0x0 would match
702 * the actual event_stat_mask of 0x1 and hence trigger instead of the second
703 * keybinding.
704 *
705 */
706 void reorder_bindings(void) {
707 struct Mode *mode;
708 SLIST_FOREACH(mode, &modes, modes) {
709 const bool current_mode = (mode->bindings == bindings);
710 reorder_bindings_of_mode(mode);
711 if (current_mode)
712 bindings = mode->bindings;
713 }
714 }
715
716 /*
717 * Checks for duplicate key bindings (the same keycode or keysym is configured
718 * more than once). If a duplicate binding is found, a message is printed to
719 * stderr and the has_errors variable is set to true, which will start
720 * i3-nagbar.
721 *
722 */
723 void check_for_duplicate_bindings(struct context *context) {
724 Binding *bind, *current;
725 TAILQ_FOREACH(current, bindings, bindings) {
726 TAILQ_FOREACH(bind, bindings, bindings) {
727 /* Abort when we reach the current keybinding, only check the
728 * bindings before */
729 if (bind == current)
730 break;
731
732 /* Check if the input types are different */
733 if (bind->input_type != current->input_type)
734 continue;
735
736 /* Check if one is using keysym while the other is using bindsym.
737 * If so, skip. */
738 if ((bind->symbol == NULL && current->symbol != NULL) ||
739 (bind->symbol != NULL && current->symbol == NULL))
740 continue;
741
742 /* If bind is NULL, current has to be NULL, too (see above).
743 * If the keycodes differ, it can't be a duplicate. */
744 if (bind->symbol != NULL &&
745 strcasecmp(bind->symbol, current->symbol) != 0)
746 continue;
747
748 /* Check if the keycodes or modifiers are different. If so, they
749 * can't be duplicate */
750 if (bind->keycode != current->keycode ||
751 bind->event_state_mask != current->event_state_mask ||
752 bind->release != current->release)
753 continue;
754
755 context->has_errors = true;
756 if (current->keycode != 0) {
757 ELOG("Duplicate keybinding in config file:\n state mask 0x%x with keycode %d, command \"%s\"\n",
758 current->event_state_mask, current->keycode, current->command);
759 } else {
760 ELOG("Duplicate keybinding in config file:\n state mask 0x%x with keysym %s, command \"%s\"\n",
761 current->event_state_mask, current->symbol, current->command);
762 }
763 }
764 }
765 }
766
767 /*
768 * Creates a dynamically allocated copy of bind.
769 */
770 static Binding *binding_copy(Binding *bind) {
771 Binding *ret = smalloc(sizeof(Binding));
772 *ret = *bind;
773 if (bind->symbol != NULL)
774 ret->symbol = sstrdup(bind->symbol);
775 if (bind->command != NULL)
776 ret->command = sstrdup(bind->command);
777 TAILQ_INIT(&(ret->keycodes_head));
778 struct Binding_Keycode *binding_keycode;
779 TAILQ_FOREACH(binding_keycode, &(bind->keycodes_head), keycodes) {
780 struct Binding_Keycode *ret_binding_keycode = smalloc(sizeof(struct Binding_Keycode));
781 *ret_binding_keycode = *binding_keycode;
782 TAILQ_INSERT_TAIL(&(ret->keycodes_head), ret_binding_keycode, keycodes);
783 }
784
785 return ret;
786 }
787
788 /*
789 * Frees the binding. If bind is null, it simply returns.
790 */
791 void binding_free(Binding *bind) {
792 if (bind == NULL) {
793 return;
794 }
795
796 while (!TAILQ_EMPTY(&(bind->keycodes_head))) {
797 struct Binding_Keycode *first = TAILQ_FIRST(&(bind->keycodes_head));
798 TAILQ_REMOVE(&(bind->keycodes_head), first, keycodes);
799 FREE(first);
800 }
801
802 FREE(bind->symbol);
803 FREE(bind->command);
804 FREE(bind);
805 }
806
807 /*
808 * Runs the given binding and handles parse errors. If con is passed, it will
809 * execute the command binding with that container selected by criteria.
810 * Returns a CommandResult for running the binding's command. Free with
811 * command_result_free().
812 *
813 */
814 CommandResult *run_binding(Binding *bind, Con *con) {
815 char *command;
816
817 /* We need to copy the binding and command since “reload” may be part of
818 * the command, and then the memory that bind points to may not contain the
819 * same data anymore. */
820 if (con == NULL)
821 command = sstrdup(bind->command);
822 else
823 sasprintf(&command, "[con_id=\"%p\"] %s", con, bind->command);
824
825 Binding *bind_cp = binding_copy(bind);
826 CommandResult *result = parse_command(command, NULL);
827 free(command);
828
829 if (result->needs_tree_render)
830 tree_render();
831
832 if (result->parse_error) {
833 char *pageraction;
834 sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename);
835 char *argv[] = {
836 NULL, /* will be replaced by the executable path */
837 "-f",
838 config.font.pattern,
839 "-t",
840 "error",
841 "-m",
842 "The configured command for this shortcut could not be run successfully.",
843 "-b",
844 "show errors",
845 pageraction,
846 NULL};
847 start_nagbar(&command_error_nagbar_pid, argv);
848 free(pageraction);
849 }
850
851 ipc_send_binding_event("run", bind_cp);
852 binding_free(bind_cp);
853
854 return result;
855 }
856
857 static int fill_rmlvo_from_root(struct xkb_rule_names *xkb_names) {
858 xcb_intern_atom_reply_t *atom_reply;
859 size_t content_max_words = 256;
860
861 atom_reply = xcb_intern_atom_reply(
862 conn, xcb_intern_atom(conn, 0, strlen("_XKB_RULES_NAMES"), "_XKB_RULES_NAMES"), NULL);
863 if (atom_reply == NULL)
864 return -1;
865
866 xcb_get_property_cookie_t prop_cookie;
867 xcb_get_property_reply_t *prop_reply;
868 prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
869 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
870 prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
871 if (prop_reply == NULL) {
872 free(atom_reply);
873 return -1;
874 }
875 if (xcb_get_property_value_length(prop_reply) > 0 && prop_reply->bytes_after > 0) {
876 /* We received an incomplete value. Ask again but with a properly
877 * adjusted size. */
878 content_max_words += ceil(prop_reply->bytes_after / 4.0);
879 /* Repeat the request, with adjusted size */
880 free(prop_reply);
881 prop_cookie = xcb_get_property_unchecked(conn, false, root, atom_reply->atom,
882 XCB_GET_PROPERTY_TYPE_ANY, 0, content_max_words);
883 prop_reply = xcb_get_property_reply(conn, prop_cookie, NULL);
884 if (prop_reply == NULL) {
885 free(atom_reply);
886 return -1;
887 }
888 }
889 if (xcb_get_property_value_length(prop_reply) == 0) {
890 free(atom_reply);
891 free(prop_reply);
892 return -1;
893 }
894
895 const char *walk = (const char *)xcb_get_property_value(prop_reply);
896 int remaining = xcb_get_property_value_length(prop_reply);
897 for (int i = 0; i < 5 && remaining > 0; i++) {
898 const int len = strnlen(walk, remaining);
899 switch (i) {
900 case 0:
901 sasprintf((char **)&(xkb_names->rules), "%.*s", len, walk);
902 break;
903 case 1:
904 sasprintf((char **)&(xkb_names->model), "%.*s", len, walk);
905 break;
906 case 2:
907 sasprintf((char **)&(xkb_names->layout), "%.*s", len, walk);
908 break;
909 case 3:
910 sasprintf((char **)&(xkb_names->variant), "%.*s", len, walk);
911 break;
912 case 4:
913 sasprintf((char **)&(xkb_names->options), "%.*s", len, walk);
914 break;
915 }
916 DLOG("component %d of _XKB_RULES_NAMES is \"%.*s\"\n", i, len, walk);
917 walk += (len + 1);
918 remaining -= (len + 1);
919 }
920
921 free(atom_reply);
922 free(prop_reply);
923 return 0;
924 }
925
926 /*
927 * Loads the XKB keymap from the X11 server and feeds it to xkbcommon.
928 *
929 */
930 bool load_keymap(void) {
931 if (xkb_context == NULL) {
932 if ((xkb_context = xkb_context_new(0)) == NULL) {
933 ELOG("Could not create xkbcommon context\n");
934 return false;
935 }
936 }
937
938 struct xkb_keymap *new_keymap = NULL;
939 int32_t device_id;
940 if (xkb_supported && (device_id = xkb_x11_get_core_keyboard_device_id(conn)) > -1) {
941 if ((new_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) {
942 ELOG("xkb_x11_keymap_new_from_device failed\n");
943 return false;
944 }
945 } else {
946 /* Likely there is no XKB support on this server, possibly because it
947 * is a VNC server. */
948 LOG("No XKB / core keyboard device? Assembling keymap from local RMLVO.\n");
949 struct xkb_rule_names names = {
950 .rules = NULL,
951 .model = NULL,
952 .layout = NULL,
953 .variant = NULL,
954 .options = NULL};
955 if (fill_rmlvo_from_root(&names) == -1) {
956 ELOG("Could not get _XKB_RULES_NAMES atom from root window, falling back to defaults.\n");
957 /* Using NULL for the fields of xkb_rule_names. */
958 }
959 new_keymap = xkb_keymap_new_from_names(xkb_context, &names, 0);
960 free((char *)names.rules);
961 free((char *)names.model);
962 free((char *)names.layout);
963 free((char *)names.variant);
964 free((char *)names.options);
965 if (new_keymap == NULL) {
966 ELOG("xkb_keymap_new_from_names failed\n");
967 return false;
968 }
969 }
970 xkb_keymap_unref(xkb_keymap);
971 xkb_keymap = new_keymap;
972
973 return true;
974 }
975
976 /*
977 * Returns a list of buttons that should be grabbed on a window.
978 * This list will always contain 1–3, all higher buttons will only be returned
979 * if there is a whole-window binding for it on some window in the current
980 * config.
981 * The list is terminated by a 0.
982 */
983 int *bindings_get_buttons_to_grab(void) {
984 /* Let's make the reasonable assumption that there's no more than 25
985 * buttons. */
986 int num_max = 25;
987
988 int buffer[num_max];
989 int num = 0;
990
991 /* We always return buttons 1 through 3. */
992 buffer[num++] = 1;
993 buffer[num++] = 2;
994 buffer[num++] = 3;
995
996 Binding *bind;
997 TAILQ_FOREACH(bind, bindings, bindings) {
998 if (num + 1 == num_max)
999 break;
1000
1001 /* We are only interested in whole window mouse bindings. */
1002 if (bind->input_type != B_MOUSE || !bind->whole_window)
1003 continue;
1004
1005 long button;
1006 if (!parse_long(bind->symbol + (sizeof("button") - 1), &button, 10)) {
1007 ELOG("Could not parse button number, skipping this binding. Please report this bug in i3.\n");
1008 continue;
1009 }
1010
1011 /* Avoid duplicates. */
1012 for (int i = 0; i < num; i++) {
1013 if (buffer[i] == button)
1014 continue;
1015 }
1016
1017 buffer[num++] = button;
1018 }
1019 buffer[num++] = 0;
1020
1021 int *buttons = scalloc(num, sizeof(int));
1022 memcpy(buttons, buffer, num * sizeof(int));
1023
1024 return buttons;
1025 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * click.c: Button press (mouse click) events.
7 *
8 */
9 #include "all.h"
10
11 #include <time.h>
12 #include <math.h>
13
14 #include <xcb/xcb_icccm.h>
15
16 #include <X11/XKBlib.h>
17
18 typedef enum { CLICK_BORDER = 0,
19 CLICK_DECORATION = 1,
20 CLICK_INSIDE = 2 } click_destination_t;
21
22 /*
23 * Finds the correct pair of first/second cons between the resize will take
24 * place according to the passed border position (top, left, right, bottom),
25 * then calls resize_graphical_handler().
26 *
27 */
28 static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press_event_t *event) {
29 DLOG("border = %d, con = %p\n", border, con);
30 Con *second = NULL;
31 Con *first = con;
32 direction_t search_direction;
33 switch (border) {
34 case BORDER_LEFT:
35 search_direction = D_LEFT;
36 break;
37 case BORDER_RIGHT:
38 search_direction = D_RIGHT;
39 break;
40 case BORDER_TOP:
41 search_direction = D_UP;
42 break;
43 case BORDER_BOTTOM:
44 search_direction = D_DOWN;
45 break;
46 }
47
48 bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
49 if (!res) {
50 LOG("No second container in this direction found.\n");
51 return false;
52 }
53
54 assert(first != second);
55 assert(first->parent == second->parent);
56
57 /* The first container should always be in front of the second container */
58 if (search_direction == D_UP || search_direction == D_LEFT) {
59 Con *tmp = first;
60 first = second;
61 second = tmp;
62 }
63
64 const orientation_t orientation = ((border == BORDER_LEFT || border == BORDER_RIGHT) ? HORIZ : VERT);
65
66 resize_graphical_handler(first, second, orientation, event);
67
68 DLOG("After resize handler, rendering\n");
69 tree_render();
70 return true;
71 }
72
73 /*
74 * Called when the user clicks using the floating_modifier, but the client is in
75 * tiling layout.
76 *
77 * Returns false if it does not do anything (that is, the click should be sent
78 * to the client).
79 *
80 */
81 static bool floating_mod_on_tiled_client(Con *con, xcb_button_press_event_t *event) {
82 /* The client is in tiling layout. We can still initiate a resize with the
83 * right mouse button, by chosing the border which is the most near one to
84 * the position of the mouse pointer */
85 int to_right = con->rect.width - event->event_x,
86 to_left = event->event_x,
87 to_top = event->event_y,
88 to_bottom = con->rect.height - event->event_y;
89
90 DLOG("click was %d px to the right, %d px to the left, %d px to top, %d px to bottom\n",
91 to_right, to_left, to_top, to_bottom);
92
93 if (to_right < to_left &&
94 to_right < to_top &&
95 to_right < to_bottom)
96 return tiling_resize_for_border(con, BORDER_RIGHT, event);
97
98 if (to_left < to_right &&
99 to_left < to_top &&
100 to_left < to_bottom)
101 return tiling_resize_for_border(con, BORDER_LEFT, event);
102
103 if (to_top < to_right &&
104 to_top < to_left &&
105 to_top < to_bottom)
106 return tiling_resize_for_border(con, BORDER_TOP, event);
107
108 if (to_bottom < to_right &&
109 to_bottom < to_left &&
110 to_bottom < to_top)
111 return tiling_resize_for_border(con, BORDER_BOTTOM, event);
112
113 return false;
114 }
115
116 /*
117 * Finds out which border was clicked on and calls tiling_resize_for_border().
118 *
119 */
120 static bool tiling_resize(Con *con, xcb_button_press_event_t *event, const click_destination_t dest) {
121 /* check if this was a click on the window border (and on which one) */
122 Rect bsr = con_border_style_rect(con);
123 DLOG("BORDER x = %d, y = %d for con %p, window 0x%08x\n",
124 event->event_x, event->event_y, con, event->event);
125 DLOG("checks for right >= %d\n", con->window_rect.x + con->window_rect.width);
126 if (dest == CLICK_DECORATION) {
127 /* The user clicked on a window decoration. We ignore the following case:
128 * The container is a h-split, tabbed or stacked container with > 1
129 * window. Decorations will end up next to each other and the user
130 * expects to switch to a window by clicking on its decoration. */
131
132 /* Since the container might either be the child *or* already a split
133 * container (in the case of a nested split container), we need to make
134 * sure that we are dealing with the split container here. */
135 Con *check_con = con;
136 if (con_is_leaf(check_con) && check_con->parent->type == CT_CON)
137 check_con = check_con->parent;
138
139 if ((check_con->layout == L_STACKED ||
140 check_con->layout == L_TABBED ||
141 con_orientation(check_con) == HORIZ) &&
142 con_num_children(check_con) > 1) {
143 DLOG("Not handling this resize, this container has > 1 child.\n");
144 return false;
145 }
146 return tiling_resize_for_border(con, BORDER_TOP, event);
147 }
148
149 if (event->event_x >= 0 && event->event_x <= (int32_t)bsr.x &&
150 event->event_y >= (int32_t)bsr.y && event->event_y <= (int32_t)(con->rect.height + bsr.height))
151 return tiling_resize_for_border(con, BORDER_LEFT, event);
152
153 if (event->event_x >= (int32_t)(con->window_rect.x + con->window_rect.width) &&
154 event->event_y >= (int32_t)bsr.y && event->event_y <= (int32_t)(con->rect.height + bsr.height))
155 return tiling_resize_for_border(con, BORDER_RIGHT, event);
156
157 if (event->event_y >= (int32_t)(con->window_rect.y + con->window_rect.height))
158 return tiling_resize_for_border(con, BORDER_BOTTOM, event);
159
160 return false;
161 }
162
163 /*
164 * Being called by handle_button_press, this function calls the appropriate
165 * functions for resizing/dragging.
166 *
167 */
168 static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod_pressed, const click_destination_t dest) {
169 DLOG("--> click properties: mod = %d, destination = %d\n", mod_pressed, dest);
170 DLOG("--> OUTCOME = %p\n", con);
171 DLOG("type = %d, name = %s\n", con->type, con->name);
172
173 /* don’t handle dockarea cons, they must not be focused */
174 if (con->parent->type == CT_DOCKAREA)
175 goto done;
176
177 const bool is_left_or_right_click = (event->detail == XCB_BUTTON_CLICK_LEFT ||
178 event->detail == XCB_BUTTON_CLICK_RIGHT);
179
180 /* if the user has bound an action to this click, it should override the
181 * default behavior. */
182 if (dest == CLICK_DECORATION || dest == CLICK_INSIDE || dest == CLICK_BORDER) {
183 Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
184
185 if (bind != NULL && ((dest == CLICK_DECORATION && !bind->exclude_titlebar) ||
186 (dest == CLICK_INSIDE && bind->whole_window) ||
187 (dest == CLICK_BORDER && bind->border))) {
188 CommandResult *result = run_binding(bind, con);
189
190 /* ASYNC_POINTER eats the event */
191 xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
192 xcb_flush(conn);
193
194 command_result_free(result);
195 return 0;
196 }
197 }
198
199 /* There is no default behavior for button release events so we are done. */
200 if (event->response_type == XCB_BUTTON_RELEASE) {
201 goto done;
202 }
203
204 /* Any click in a workspace should focus that workspace. If the
205 * workspace is on another output we need to do a workspace_show in
206 * order for i3bar (and others) to notice the change in workspace. */
207 Con *ws = con_get_workspace(con);
208 Con *focused_workspace = con_get_workspace(focused);
209
210 if (!ws) {
211 ws = TAILQ_FIRST(&(output_get_content(con_get_output(con))->focus_head));
212 if (!ws)
213 goto done;
214 }
215
216 if (ws != focused_workspace)
217 workspace_show(ws);
218
219 /* get the floating con */
220 Con *floatingcon = con_inside_floating(con);
221 const bool proportional = (event->state & XCB_KEY_BUT_MASK_SHIFT) == XCB_KEY_BUT_MASK_SHIFT;
222 const bool in_stacked = (con->parent->layout == L_STACKED || con->parent->layout == L_TABBED);
223
224 /* 1: see if the user scrolled on the decoration of a stacked/tabbed con */
225 if (in_stacked &&
226 dest == CLICK_DECORATION &&
227 (event->detail == XCB_BUTTON_SCROLL_UP ||
228 event->detail == XCB_BUTTON_SCROLL_DOWN ||
229 event->detail == XCB_BUTTON_SCROLL_LEFT ||
230 event->detail == XCB_BUTTON_SCROLL_RIGHT)) {
231 DLOG("Scrolling on a window decoration\n");
232 orientation_t orientation = con_orientation(con->parent);
233 /* Use the focused child of the tabbed / stacked container, not the
234 * container the user scrolled on. */
235 Con *focused = con->parent;
236 focused = TAILQ_FIRST(&(focused->focus_head));
237 con_activate(con_descend_focused(focused));
238 /* To prevent scrolling from going outside the container (see ticket
239 * #557), we first check if scrolling is possible at all. */
240 bool scroll_prev_possible = (TAILQ_PREV(focused, nodes_head, nodes) != NULL);
241 bool scroll_next_possible = (TAILQ_NEXT(focused, nodes) != NULL);
242 if ((event->detail == XCB_BUTTON_SCROLL_UP || event->detail == XCB_BUTTON_SCROLL_LEFT) && scroll_prev_possible) {
243 tree_next('p', orientation);
244 } else if ((event->detail == XCB_BUTTON_SCROLL_DOWN || event->detail == XCB_BUTTON_SCROLL_RIGHT) && scroll_next_possible) {
245 tree_next('n', orientation);
246 }
247
248 goto done;
249 }
250
251 /* 2: focus this con. */
252 con_activate(con);
253
254 /* 3: For floating containers, we also want to raise them on click.
255 * We will skip handling events on floating cons in fullscreen mode */
256 Con *fs = con_get_fullscreen_covering_ws(ws);
257 if (floatingcon != NULL && fs != con) {
258 /* 4: floating_modifier plus left mouse button drags */
259 if (mod_pressed && event->detail == XCB_BUTTON_CLICK_LEFT) {
260 floating_drag_window(floatingcon, event);
261 return 1;
262 }
263
264 /* 5: resize (floating) if this was a (left or right) click on the
265 * left/right/bottom border, or a right click on the decoration.
266 * also try resizing (tiling) if it was a click on the top */
267 if (mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) {
268 DLOG("floating resize due to floatingmodifier\n");
269 floating_resize_window(floatingcon, proportional, event);
270 return 1;
271 }
272
273 if (!in_stacked && dest == CLICK_DECORATION &&
274 is_left_or_right_click) {
275 /* try tiling resize, but continue if it doesn’t work */
276 DLOG("tiling resize with fallback\n");
277 if (tiling_resize(con, event, dest))
278 goto done;
279 }
280
281 if (dest == CLICK_DECORATION && event->detail == XCB_BUTTON_CLICK_RIGHT) {
282 DLOG("floating resize due to decoration right click\n");
283 floating_resize_window(floatingcon, proportional, event);
284 return 1;
285 }
286
287 if (dest == CLICK_BORDER && is_left_or_right_click) {
288 DLOG("floating resize due to border click\n");
289 floating_resize_window(floatingcon, proportional, event);
290 return 1;
291 }
292
293 /* 6: dragging, if this was a click on a decoration (which did not lead
294 * to a resize) */
295 if (!in_stacked && dest == CLICK_DECORATION &&
296 (event->detail == XCB_BUTTON_CLICK_LEFT)) {
297 floating_drag_window(floatingcon, event);
298 return 1;
299 }
300
301 goto done;
302 }
303
304 if (in_stacked) {
305 /* for stacked/tabbed cons, the resizing applies to the parent
306 * container */
307 con = con->parent;
308 }
309
310 /* 7: floating modifier pressed, initiate a resize */
311 if (dest == CLICK_INSIDE && mod_pressed && event->detail == XCB_BUTTON_CLICK_RIGHT) {
312 if (floating_mod_on_tiled_client(con, event))
313 return 1;
314 }
315 /* 8: otherwise, check for border/decoration clicks and resize */
316 else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) &&
317 is_left_or_right_click) {
318 DLOG("Trying to resize (tiling)\n");
319 tiling_resize(con, event, dest);
320 }
321
322 done:
323 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
324 xcb_flush(conn);
325 tree_render();
326
327 return 0;
328 }
329
330 /*
331 * The button press X callback. This function determines whether the floating
332 * modifier is pressed and where the user clicked (decoration, border, inside
333 * the window).
334 *
335 * Then, route_click is called on the appropriate con.
336 *
337 */
338 int handle_button_press(xcb_button_press_event_t *event) {
339 Con *con;
340 DLOG("Button %d (state %d) %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
341 event->detail, event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"),
342 event->event, event->child, event->event_x, event->event_y, event->root_x,
343 event->root_y);
344
345 last_timestamp = event->time;
346
347 const uint32_t mod = (config.floating_modifier & 0xFFFF);
348 const bool mod_pressed = (mod != 0 && (event->state & mod) == mod);
349 DLOG("floating_mod = %d, detail = %d\n", mod_pressed, event->detail);
350 if ((con = con_by_window_id(event->event)))
351 return route_click(con, event, mod_pressed, CLICK_INSIDE);
352
353 if (!(con = con_by_frame_id(event->event))) {
354 /* Run bindings on the root window as well, see #2097. We only run it
355 * if --whole-window was set as that's the equivalent for a normal
356 * window. */
357 if (event->event == root) {
358 Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
359 if (bind != NULL && bind->whole_window) {
360 CommandResult *result = run_binding(bind, NULL);
361 command_result_free(result);
362 }
363 }
364
365 /* If the root window is clicked, find the relevant output from the
366 * click coordinates and focus the output's active workspace. */
367 if (event->event == root && event->response_type == XCB_BUTTON_PRESS) {
368 Con *output, *ws;
369 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
370 if (con_is_internal(output) ||
371 !rect_contains(output->rect, event->event_x, event->event_y))
372 continue;
373
374 ws = TAILQ_FIRST(&(output_get_content(output)->focus_head));
375 if (ws != con_get_workspace(focused)) {
376 workspace_show(ws);
377 tree_render();
378 }
379 return 1;
380 }
381 return 0;
382 }
383
384 ELOG("Clicked into unknown window?!\n");
385 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
386 xcb_flush(conn);
387 return 0;
388 }
389
390 /* Check if the click was on the decoration of a child */
391 Con *child;
392 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
393 if (!rect_contains(child->deco_rect, event->event_x, event->event_y))
394 continue;
395
396 return route_click(child, event, mod_pressed, CLICK_DECORATION);
397 }
398
399 if (event->child != XCB_NONE) {
400 DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n");
401 return route_click(con, event, mod_pressed, CLICK_INSIDE);
402 }
403
404 return route_click(con, event, mod_pressed, CLICK_BORDER);
405 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * commands.c: all command functions (see commands_parser.c)
7 *
8 */
9 #include "all.h"
10
11 #include <stdint.h>
12 #include <float.h>
13 #include <stdarg.h>
14
15 #include "shmlog.h"
16
17 // Macros to make the YAJL API a bit easier to use.
18 #define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_##x(cmd_output->json_gen, ##__VA_ARGS__) : 0)
19 #define ystr(str) (cmd_output->json_gen != NULL ? yajl_gen_string(cmd_output->json_gen, (unsigned char *)str, strlen(str)) : 0)
20 #define ysuccess(success) \
21 do { \
22 if (cmd_output->json_gen != NULL) { \
23 y(map_open); \
24 ystr("success"); \
25 y(bool, success); \
26 y(map_close); \
27 } \
28 } while (0)
29 #define yerror(format, ...) \
30 do { \
31 if (cmd_output->json_gen != NULL) { \
32 char *message; \
33 sasprintf(&message, format, ##__VA_ARGS__); \
34 y(map_open); \
35 ystr("success"); \
36 y(bool, false); \
37 ystr("error"); \
38 ystr(message); \
39 y(map_close); \
40 free(message); \
41 } \
42 } while (0)
43
44 /** If an error occurred during parsing of the criteria, we want to exit instead
45 * of relying on fallback behavior. See #2091. */
46 #define HANDLE_INVALID_MATCH \
47 do { \
48 if (current_match->error != NULL) { \
49 yerror("Invalid match: %s", current_match->error); \
50 return; \
51 } \
52 } while (0)
53
54 /** When the command did not include match criteria (!), we use the currently
55 * focused container. Do not confuse this case with a command which included
56 * criteria but which did not match any windows. This macro has to be called in
57 * every command.
58 */
59 #define HANDLE_EMPTY_MATCH \
60 do { \
61 HANDLE_INVALID_MATCH; \
62 \
63 if (match_is_empty(current_match)) { \
64 while (!TAILQ_EMPTY(&owindows)) { \
65 owindow *ow = TAILQ_FIRST(&owindows); \
66 TAILQ_REMOVE(&owindows, ow, owindows); \
67 free(ow); \
68 } \
69 owindow *ow = smalloc(sizeof(owindow)); \
70 ow->con = focused; \
71 TAILQ_INIT(&owindows); \
72 TAILQ_INSERT_TAIL(&owindows, ow, owindows); \
73 } \
74 } while (0)
75
76 /*
77 * Checks whether we switched to a new workspace and returns false in that case,
78 * signaling that further workspace switching should be done by the calling function
79 * If not, calls workspace_back_and_forth() if workspace_auto_back_and_forth is set
80 * and return true, signaling that no further workspace switching should occur in the calling function.
81 *
82 */
83 static bool maybe_back_and_forth(struct CommandResultIR *cmd_output, const char *name) {
84 Con *ws = con_get_workspace(focused);
85
86 /* If we switched to a different workspace, do nothing */
87 if (strcmp(ws->name, name) != 0)
88 return false;
89
90 DLOG("This workspace is already focused.\n");
91 if (config.workspace_auto_back_and_forth) {
92 workspace_back_and_forth();
93 cmd_output->needs_tree_render = true;
94 }
95 return true;
96 }
97
98 /*
99 * Return the passed workspace unless it is the current one and auto back and
100 * forth is enabled, in which case the back_and_forth workspace is returned.
101 */
102 static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
103 Con *current, *baf;
104
105 if (!config.workspace_auto_back_and_forth)
106 return workspace;
107
108 current = con_get_workspace(focused);
109
110 if (current == workspace) {
111 baf = workspace_back_and_forth_get();
112 if (baf != NULL) {
113 DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
114 return baf;
115 }
116 }
117
118 return workspace;
119 }
120
121 /*******************************************************************************
122 * Criteria functions.
123 ******************************************************************************/
124
125 /*
126 * Helper data structure for an operation window (window on which the operation
127 * will be performed). Used to build the TAILQ owindows.
128 *
129 */
130 typedef struct owindow {
131 Con *con;
132
133 TAILQ_ENTRY(owindow)
134 owindows;
135 } owindow;
136
137 typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
138
139 static owindows_head owindows;
140
141 /*
142 * Initializes the specified 'Match' data structure and the initial state of
143 * commands.c for matching target windows of a command.
144 *
145 */
146 void cmd_criteria_init(I3_CMD) {
147 Con *con;
148 owindow *ow;
149
150 DLOG("Initializing criteria, current_match = %p\n", current_match);
151 match_free(current_match);
152 match_init(current_match);
153 while (!TAILQ_EMPTY(&owindows)) {
154 ow = TAILQ_FIRST(&owindows);
155 TAILQ_REMOVE(&owindows, ow, owindows);
156 free(ow);
157 }
158 TAILQ_INIT(&owindows);
159 /* copy all_cons */
160 TAILQ_FOREACH(con, &all_cons, all_cons) {
161 ow = smalloc(sizeof(owindow));
162 ow->con = con;
163 TAILQ_INSERT_TAIL(&owindows, ow, owindows);
164 }
165 }
166
167 /*
168 * A match specification just finished (the closing square bracket was found),
169 * so we filter the list of owindows.
170 *
171 */
172 void cmd_criteria_match_windows(I3_CMD) {
173 owindow *next, *current;
174
175 DLOG("match specification finished, matching...\n");
176 /* copy the old list head to iterate through it and start with a fresh
177 * list which will contain only matching windows */
178 struct owindows_head old = owindows;
179 TAILQ_INIT(&owindows);
180 for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
181 /* make a copy of the next pointer and advance the pointer to the
182 * next element as we are going to invalidate the element’s
183 * next/prev pointers by calling TAILQ_INSERT_TAIL later */
184 current = next;
185 next = TAILQ_NEXT(next, owindows);
186
187 DLOG("checking if con %p / %s matches\n", current->con, current->con->name);
188
189 /* We use this flag to prevent matching on window-less containers if
190 * only window-specific criteria were specified. */
191 bool accept_match = false;
192
193 if (current_match->con_id != NULL) {
194 accept_match = true;
195
196 if (current_match->con_id == current->con) {
197 DLOG("con_id matched.\n");
198 } else {
199 DLOG("con_id does not match.\n");
200 FREE(current);
201 continue;
202 }
203 }
204
205 if (current_match->mark != NULL && !TAILQ_EMPTY(&(current->con->marks_head))) {
206 accept_match = true;
207 bool matched_by_mark = false;
208
209 mark_t *mark;
210 TAILQ_FOREACH(mark, &(current->con->marks_head), marks) {
211 if (!regex_matches(current_match->mark, mark->name))
212 continue;
213
214 DLOG("match by mark\n");
215 matched_by_mark = true;
216 break;
217 }
218
219 if (!matched_by_mark) {
220 DLOG("mark does not match.\n");
221 FREE(current);
222 continue;
223 }
224 }
225
226 if (current->con->window != NULL) {
227 if (match_matches_window(current_match, current->con->window)) {
228 DLOG("matches window!\n");
229 accept_match = true;
230 } else {
231 DLOG("doesn't match\n");
232 FREE(current);
233 continue;
234 }
235 }
236
237 if (accept_match) {
238 TAILQ_INSERT_TAIL(&owindows, current, owindows);
239 } else {
240 FREE(current);
241 continue;
242 }
243 }
244
245 TAILQ_FOREACH(current, &owindows, owindows) {
246 DLOG("matching: %p / %s\n", current->con, current->con->name);
247 }
248 }
249
250 /*
251 * Interprets a ctype=cvalue pair and adds it to the current match
252 * specification.
253 *
254 */
255 void cmd_criteria_add(I3_CMD, const char *ctype, const char *cvalue) {
256 match_parse_property(current_match, ctype, cvalue);
257 }
258
259 static void move_matches_to_workspace(Con *ws) {
260 owindow *current;
261 TAILQ_FOREACH(current, &owindows, owindows) {
262 DLOG("matching: %p / %s\n", current->con, current->con->name);
263 con_move_to_workspace(current->con, ws, true, false, false);
264 }
265 }
266
267 #define CHECK_MOVE_CON_TO_WORKSPACE \
268 do { \
269 HANDLE_EMPTY_MATCH; \
270 if (TAILQ_EMPTY(&owindows)) { \
271 yerror("Nothing to move: specified criteria don't match any window"); \
272 return; \
273 } else { \
274 bool found = false; \
275 owindow *current = TAILQ_FIRST(&owindows); \
276 while (current) { \
277 owindow *next = TAILQ_NEXT(current, owindows); \
278 \
279 if (current->con->type == CT_WORKSPACE && !con_has_children(current->con)) { \
280 TAILQ_REMOVE(&owindows, current, owindows); \
281 } else { \
282 found = true; \
283 } \
284 \
285 current = next; \
286 } \
287 if (!found) { \
288 yerror("Nothing to move: workspace empty"); \
289 return; \
290 } \
291 } \
292 } while (0)
293
294 /*
295 * Implementation of 'move [window|container] [to] workspace
296 * next|prev|next_on_output|prev_on_output|current'.
297 *
298 */
299 void cmd_move_con_to_workspace(I3_CMD, const char *which) {
300 DLOG("which=%s\n", which);
301
302 CHECK_MOVE_CON_TO_WORKSPACE;
303
304 /* get the workspace */
305 Con *ws;
306 if (strcmp(which, "next") == 0)
307 ws = workspace_next();
308 else if (strcmp(which, "prev") == 0)
309 ws = workspace_prev();
310 else if (strcmp(which, "next_on_output") == 0)
311 ws = workspace_next_on_output();
312 else if (strcmp(which, "prev_on_output") == 0)
313 ws = workspace_prev_on_output();
314 else if (strcmp(which, "current") == 0)
315 ws = con_get_workspace(focused);
316 else {
317 yerror("BUG: called with which=%s", which);
318 return;
319 }
320
321 move_matches_to_workspace(ws);
322
323 cmd_output->needs_tree_render = true;
324 // XXX: default reply for now, make this a better reply
325 ysuccess(true);
326 }
327
328 /*
329 * Implementation of 'move [window|container] [to] workspace back_and_forth'.
330 *
331 */
332 void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
333 Con *ws = workspace_back_and_forth_get();
334 if (ws == NULL) {
335 yerror("No workspace was previously active.");
336 return;
337 }
338
339 HANDLE_EMPTY_MATCH;
340
341 move_matches_to_workspace(ws);
342
343 cmd_output->needs_tree_render = true;
344 // XXX: default reply for now, make this a better reply
345 ysuccess(true);
346 }
347
348 /*
349 * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace <name>'.
350 *
351 */
352 void cmd_move_con_to_workspace_name(I3_CMD, const char *name, const char *no_auto_back_and_forth) {
353 if (strncasecmp(name, "__", strlen("__")) == 0) {
354 yerror("You cannot move containers to i3-internal workspaces (\"%s\").", name);
355 return;
356 }
357
358 CHECK_MOVE_CON_TO_WORKSPACE;
359
360 LOG("should move window to workspace %s\n", name);
361 /* get the workspace */
362 Con *ws = workspace_get(name, NULL);
363
364 if (no_auto_back_and_forth == NULL) {
365 ws = maybe_auto_back_and_forth_workspace(ws);
366 }
367
368 move_matches_to_workspace(ws);
369
370 cmd_output->needs_tree_render = true;
371 // XXX: default reply for now, make this a better reply
372 ysuccess(true);
373 }
374
375 /*
376 * Implementation of 'move [--no-auto-back-and-forth] [window|container] [to] workspace number <name>'.
377 *
378 */
379 void cmd_move_con_to_workspace_number(I3_CMD, const char *which, const char *no_auto_back_and_forth) {
380 CHECK_MOVE_CON_TO_WORKSPACE;
381
382 LOG("should move window to workspace %s\n", which);
383
384 long parsed_num = ws_name_to_number(which);
385 if (parsed_num == -1) {
386 LOG("Could not parse initial part of \"%s\" as a number.\n", which);
387 yerror("Could not parse number \"%s\"", which);
388 return;
389 }
390
391 Con *ws = get_existing_workspace_by_num(parsed_num);
392 if (!ws) {
393 ws = workspace_get(which, NULL);
394 }
395
396 if (no_auto_back_and_forth == NULL) {
397 ws = maybe_auto_back_and_forth_workspace(ws);
398 }
399
400 move_matches_to_workspace(ws);
401
402 cmd_output->needs_tree_render = true;
403 // XXX: default reply for now, make this a better reply
404 ysuccess(true);
405 }
406
407 /*
408 * Convert a string direction ("left", "right", etc.) to a direction_t. Assumes
409 * valid direction string.
410 */
411 static direction_t parse_direction(const char *str) {
412 if (strcmp(str, "left") == 0) {
413 return D_LEFT;
414 } else if (strcmp(str, "right") == 0) {
415 return D_RIGHT;
416 } else if (strcmp(str, "up") == 0) {
417 return D_UP;
418 } else if (strcmp(str, "down") == 0) {
419 return D_DOWN;
420 } else {
421 ELOG("Invalid direction. This is a parser bug.\n");
422 assert(false);
423 }
424 }
425
426 static void cmd_resize_floating(I3_CMD, const char *way, const char *direction_str, Con *floating_con, int px) {
427 Rect old_rect = floating_con->rect;
428 Con *focused_con = con_descend_focused(floating_con);
429
430 direction_t direction;
431 if (strcmp(direction_str, "height") == 0) {
432 direction = D_DOWN;
433 } else if (strcmp(direction_str, "width") == 0) {
434 direction = D_RIGHT;
435 } else {
436 direction = parse_direction(direction_str);
437 }
438 orientation_t orientation = orientation_from_direction(direction);
439
440 /* ensure that resize will take place even if pixel increment is smaller than
441 * height increment or width increment.
442 * fixes #1011 */
443 const i3Window *window = focused_con->window;
444 if (window != NULL) {
445 if (orientation == VERT) {
446 if (px < 0) {
447 px = (-px < window->height_increment) ? -window->height_increment : px;
448 } else {
449 px = (px < window->height_increment) ? window->height_increment : px;
450 }
451 } else {
452 if (px < 0) {
453 px = (-px < window->width_increment) ? -window->width_increment : px;
454 } else {
455 px = (px < window->width_increment) ? window->width_increment : px;
456 }
457 }
458 }
459
460 if (orientation == VERT) {
461 floating_con->rect.height += px;
462 } else {
463 floating_con->rect.width += px;
464 }
465 floating_check_size(floating_con);
466
467 /* Did we actually resize anything or did the size constraints prevent us?
468 * If we could not resize, exit now to not move the window. */
469 if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0) {
470 return;
471 }
472
473 if (direction == D_UP) {
474 floating_con->rect.y -= (floating_con->rect.height - old_rect.height);
475 } else if (direction == D_LEFT) {
476 floating_con->rect.x -= (floating_con->rect.width - old_rect.width);
477 }
478
479 /* If this is a scratchpad window, don't auto center it from now on. */
480 if (floating_con->scratchpad_state == SCRATCHPAD_FRESH) {
481 floating_con->scratchpad_state = SCRATCHPAD_CHANGED;
482 }
483 }
484
485 static bool cmd_resize_tiling_direction(I3_CMD, Con *current, const char *direction, int px, int ppt) {
486 Con *second = NULL;
487 Con *first = current;
488 direction_t search_direction = parse_direction(direction);
489
490 bool res = resize_find_tiling_participants(&first, &second, search_direction, false);
491 if (!res) {
492 yerror("No second container found in this direction.");
493 return false;
494 }
495
496 if (ppt) {
497 /* For backwards compatibility, 'X px or Y ppt' means that ppt is
498 * preferred. */
499 px = 0;
500 }
501 return resize_neighboring_cons(first, second, px, ppt);
502 }
503
504 static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, const char *direction, int px, double ppt) {
505 LOG("width/height resize\n");
506
507 /* get the appropriate current container (skip stacked/tabbed cons) */
508 Con *dummy = NULL;
509 direction_t search_direction = (strcmp(direction, "width") == 0 ? D_LEFT : D_DOWN);
510 bool search_result = resize_find_tiling_participants(&current, &dummy, search_direction, true);
511 if (search_result == false) {
512 yerror("Failed to find appropriate tiling containers for resize operation");
513 return false;
514 }
515
516 /* get the default percentage */
517 int children = con_num_children(current->parent);
518 LOG("ins. %d children\n", children);
519 double percentage = 1.0 / children;
520 LOG("default percentage = %f\n", percentage);
521
522 /* Ensure all the other children have a percentage set. */
523 Con *child;
524 TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
525 LOG("child->percent = %f (child %p)\n", child->percent, child);
526 if (child->percent == 0.0)
527 child->percent = percentage;
528 }
529
530 double new_current_percent;
531 double subtract_percent;
532 if (ppt != 0.0) {
533 new_current_percent = current->percent + ppt;
534 } else {
535 new_current_percent = px_resize_to_percent(current, px);
536 ppt = new_current_percent - current->percent;
537 }
538 subtract_percent = ppt / (children - 1);
539 if (ppt < 0.0 && new_current_percent < percent_for_1px(current)) {
540 yerror("Not resizing, container would end with less than 1px");
541 return false;
542 }
543
544 LOG("new_current_percent = %f\n", new_current_percent);
545 LOG("subtract_percent = %f\n", subtract_percent);
546 /* Ensure that the new percentages are positive. */
547 if (subtract_percent >= 0.0) {
548 TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
549 if (child == current) {
550 continue;
551 }
552 if (child->percent - subtract_percent < percent_for_1px(child)) {
553 yerror("Not resizing, already at minimum size (child %p would end up with a size of %.f", child, child->percent - subtract_percent);
554 return false;
555 }
556 }
557 }
558
559 current->percent = new_current_percent;
560 LOG("current->percent after = %f\n", current->percent);
561
562 TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
563 if (child == current)
564 continue;
565 child->percent -= subtract_percent;
566 LOG("child->percent after (%p) = %f\n", child, child->percent);
567 }
568
569 return true;
570 }
571
572 /*
573 * Implementation of 'resize grow|shrink <direction> [<px> px] [or <ppt> ppt]'.
574 *
575 */
576 void cmd_resize(I3_CMD, const char *way, const char *direction, long resize_px, long resize_ppt) {
577 DLOG("resizing in way %s, direction %s, px %ld or ppt %ld\n", way, direction, resize_px, resize_ppt);
578 if (strcmp(way, "shrink") == 0) {
579 resize_px *= -1;
580 resize_ppt *= -1;
581 }
582
583 HANDLE_EMPTY_MATCH;
584
585 owindow *current;
586 TAILQ_FOREACH(current, &owindows, owindows) {
587 /* Don't handle dock windows (issue #1201) */
588 if (current->con->window && current->con->window->dock) {
589 DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
590 continue;
591 }
592
593 Con *floating_con;
594 if ((floating_con = con_inside_floating(current->con))) {
595 cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, resize_px);
596 } else {
597 if (strcmp(direction, "width") == 0 ||
598 strcmp(direction, "height") == 0) {
599 const double ppt = (double)resize_ppt / 100.0;
600 if (!cmd_resize_tiling_width_height(current_match, cmd_output,
601 current->con, direction,
602 resize_px, ppt))
603 return;
604 } else {
605 if (!cmd_resize_tiling_direction(current_match, cmd_output,
606 current->con, direction,
607 resize_px, resize_ppt))
608 return;
609 }
610 }
611 }
612
613 cmd_output->needs_tree_render = true;
614 // XXX: default reply for now, make this a better reply
615 ysuccess(true);
616 }
617
618 static bool resize_set_tiling(I3_CMD, Con *target, orientation_t resize_orientation, bool is_ppt, long target_size) {
619 direction_t search_direction;
620 char *mode;
621 if (resize_orientation == HORIZ) {
622 search_direction = D_LEFT;
623 mode = "width";
624 } else {
625 search_direction = D_DOWN;
626 mode = "height";
627 }
628
629 /* Get the appropriate current container (skip stacked/tabbed cons) */
630 Con *dummy;
631 resize_find_tiling_participants(&target, &dummy, search_direction, true);
632
633 /* Calculate new size for the target container */
634 double ppt = 0.0;
635 int px = 0;
636 if (is_ppt) {
637 ppt = (double)target_size / 100.0 - target->percent;
638 } else {
639 px = target_size - (resize_orientation == HORIZ ? target->rect.width : target->rect.height);
640 }
641
642 /* Perform resizing and report failure if not possible */
643 return cmd_resize_tiling_width_height(current_match, cmd_output,
644 target, mode, px, ppt);
645 }
646
647 /*
648 * Implementation of 'resize set <width> [px | ppt] <height> [px | ppt]'.
649 *
650 */
651 void cmd_resize_set(I3_CMD, long cwidth, const char *mode_width, long cheight, const char *mode_height) {
652 DLOG("resizing to %ld %s x %ld %s\n", cwidth, mode_width, cheight, mode_height);
653 if (cwidth < 0 || cheight < 0) {
654 ELOG("Resize failed: dimensions cannot be negative (was %ld %s x %ld %s)\n", cwidth, mode_width, cheight, mode_height);
655 return;
656 }
657
658 HANDLE_EMPTY_MATCH;
659
660 owindow *current;
661 bool success = true;
662 TAILQ_FOREACH(current, &owindows, owindows) {
663 Con *floating_con;
664 if ((floating_con = con_inside_floating(current->con))) {
665 Con *output = con_get_output(floating_con);
666 if (cwidth == 0) {
667 cwidth = floating_con->rect.width;
668 } else if (mode_width && strcmp(mode_width, "ppt") == 0) {
669 cwidth = output->rect.width * ((double)cwidth / 100.0);
670 }
671 if (cheight == 0) {
672 cheight = floating_con->rect.height;
673 } else if (mode_height && strcmp(mode_height, "ppt") == 0) {
674 cheight = output->rect.height * ((double)cheight / 100.0);
675 }
676 floating_resize(floating_con, cwidth, cheight);
677 } else {
678 if (current->con->window && current->con->window->dock) {
679 DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
680 continue;
681 }
682
683 if (cwidth > 0) {
684 bool is_ppt = mode_width && strcmp(mode_width, "ppt") == 0;
685 success &= resize_set_tiling(current_match, cmd_output, current->con,
686 HORIZ, is_ppt, cwidth);
687 }
688 if (cheight > 0) {
689 bool is_ppt = mode_height && strcmp(mode_height, "ppt") == 0;
690 success &= resize_set_tiling(current_match, cmd_output, current->con,
691 VERT, is_ppt, cheight);
692 }
693 }
694 }
695
696 cmd_output->needs_tree_render = true;
697 ysuccess(success);
698 }
699
700 static int border_width_from_style(border_style_t border_style, long border_width, Con *con) {
701 if (border_style == BS_NONE) {
702 return 0;
703 }
704 if (border_width >= 0) {
705 return logical_px(border_width);
706 }
707
708 const bool is_floating = con_inside_floating(con) != NULL;
709 /* Load the configured defaults. */
710 if (is_floating && border_style == config.default_floating_border) {
711 return config.default_floating_border_width;
712 } else if (!is_floating && border_style == config.default_border) {
713 return config.default_border_width;
714 } else {
715 /* Use some hardcoded values. */
716 return logical_px(border_style == BS_NORMAL ? 2 : 1);
717 }
718 }
719
720 /*
721 * Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
722 *
723 */
724 void cmd_border(I3_CMD, const char *border_style_str, long border_width) {
725 DLOG("border style should be changed to %s with border width %ld\n", border_style_str, border_width);
726 owindow *current;
727
728 HANDLE_EMPTY_MATCH;
729
730 TAILQ_FOREACH(current, &owindows, owindows) {
731 DLOG("matching: %p / %s\n", current->con, current->con->name);
732
733 border_style_t border_style;
734 if (strcmp(border_style_str, "toggle") == 0) {
735 border_style = (current->con->border_style + 1) % 3;
736 } else if (strcmp(border_style_str, "normal") == 0) {
737 border_style = BS_NORMAL;
738 } else if (strcmp(border_style_str, "pixel") == 0) {
739 border_style = BS_PIXEL;
740 } else if (strcmp(border_style_str, "none") == 0) {
741 border_style = BS_NONE;
742 } else {
743 yerror("BUG: called with border_style=%s", border_style_str);
744 return;
745 }
746
747 const int con_border_width = border_width_from_style(border_style, border_width, current->con);
748 con_set_border_style(current->con, border_style, con_border_width);
749 }
750
751 cmd_output->needs_tree_render = true;
752 ysuccess(true);
753 }
754
755 /*
756 * Implementation of 'nop <comment>'.
757 *
758 */
759 void cmd_nop(I3_CMD, const char *comment) {
760 LOG("-------------------------------------------------\n");
761 LOG(" NOP: %s\n", comment);
762 LOG("-------------------------------------------------\n");
763 ysuccess(true);
764 }
765
766 /*
767 * Implementation of 'append_layout <path>'.
768 *
769 */
770 void cmd_append_layout(I3_CMD, const char *cpath) {
771 LOG("Appending layout \"%s\"\n", cpath);
772
773 /* Make sure we allow paths like '~/.i3/layout.json' */
774 char *path = resolve_tilde(cpath);
775
776 char *buf = NULL;
777 ssize_t len;
778 if ((len = slurp(path, &buf)) < 0) {
779 /* slurp already logged an error. */
780 goto out;
781 }
782
783 if (!json_validate(buf, len)) {
784 ELOG("Could not parse \"%s\" as JSON, not loading.\n", path);
785 yerror("Could not parse \"%s\" as JSON.", path);
786 goto out;
787 }
788
789 json_content_t content = json_determine_content(buf, len);
790 LOG("JSON content = %d\n", content);
791 if (content == JSON_CONTENT_UNKNOWN) {
792 ELOG("Could not determine the contents of \"%s\", not loading.\n", path);
793 yerror("Could not determine the contents of \"%s\".", path);
794 goto out;
795 }
796
797 Con *parent = focused;
798 if (content == JSON_CONTENT_WORKSPACE) {
799 parent = output_get_content(con_get_output(parent));
800 } else {
801 /* We need to append the layout to a split container, since a leaf
802 * container must not have any children (by definition).
803 * Note that we explicitly check for workspaces, since they are okay for
804 * this purpose, but con_accepts_window() returns false for workspaces. */
805 while (parent->type != CT_WORKSPACE && !con_accepts_window(parent))
806 parent = parent->parent;
807 }
808 DLOG("Appending to parent=%p instead of focused=%p\n", parent, focused);
809 char *errormsg = NULL;
810 tree_append_json(parent, buf, len, &errormsg);
811 if (errormsg != NULL) {
812 yerror(errormsg);
813 free(errormsg);
814 /* Note that we continue executing since tree_append_json() has
815 * side-effects — user-provided layouts can be partly valid, partly
816 * invalid, leading to half of the placeholder containers being
817 * created. */
818 } else {
819 ysuccess(true);
820 }
821
822 // XXX: This is a bit of a kludge. Theoretically, render_con(parent,
823 // false); should be enough, but when sending 'workspace 4; append_layout
824 // /tmp/foo.json', the needs_tree_render == true of the workspace command
825 // is not executed yet and will be batched with append_layout’s
826 // needs_tree_render after the parser finished. We should check if that is
827 // necessary at all.
828 render_con(croot, false);
829
830 restore_open_placeholder_windows(parent);
831
832 if (content == JSON_CONTENT_WORKSPACE)
833 ipc_send_workspace_event("restored", parent, NULL);
834
835 cmd_output->needs_tree_render = true;
836 out:
837 free(path);
838 free(buf);
839 }
840
841 /*
842 * Implementation of 'workspace next|prev|next_on_output|prev_on_output'.
843 *
844 */
845 void cmd_workspace(I3_CMD, const char *which) {
846 Con *ws;
847
848 DLOG("which=%s\n", which);
849
850 if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
851 yerror("Cannot switch workspace while in global fullscreen");
852 return;
853 }
854
855 if (strcmp(which, "next") == 0)
856 ws = workspace_next();
857 else if (strcmp(which, "prev") == 0)
858 ws = workspace_prev();
859 else if (strcmp(which, "next_on_output") == 0)
860 ws = workspace_next_on_output();
861 else if (strcmp(which, "prev_on_output") == 0)
862 ws = workspace_prev_on_output();
863 else {
864 yerror("BUG: called with which=%s", which);
865 return;
866 }
867
868 workspace_show(ws);
869
870 cmd_output->needs_tree_render = true;
871 // XXX: default reply for now, make this a better reply
872 ysuccess(true);
873 }
874
875 /*
876 * Implementation of 'workspace [--no-auto-back-and-forth] number <name>'
877 *
878 */
879 void cmd_workspace_number(I3_CMD, const char *which, const char *_no_auto_back_and_forth) {
880 const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
881
882 if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
883 yerror("Cannot switch workspace while in global fullscreen");
884 return;
885 }
886
887 long parsed_num = ws_name_to_number(which);
888 if (parsed_num == -1) {
889 yerror("Could not parse initial part of \"%s\" as a number.", which);
890 return;
891 }
892
893 Con *workspace = get_existing_workspace_by_num(parsed_num);
894 if (!workspace) {
895 LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
896 ysuccess(true);
897 workspace_show_by_name(which);
898 cmd_output->needs_tree_render = true;
899 return;
900 }
901 if (!no_auto_back_and_forth && maybe_back_and_forth(cmd_output, workspace->name)) {
902 ysuccess(true);
903 return;
904 }
905 workspace_show(workspace);
906
907 cmd_output->needs_tree_render = true;
908 // XXX: default reply for now, make this a better reply
909 ysuccess(true);
910 }
911
912 /*
913 * Implementation of 'workspace back_and_forth'.
914 *
915 */
916 void cmd_workspace_back_and_forth(I3_CMD) {
917 if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
918 yerror("Cannot switch workspace while in global fullscreen");
919 return;
920 }
921
922 workspace_back_and_forth();
923
924 cmd_output->needs_tree_render = true;
925 // XXX: default reply for now, make this a better reply
926 ysuccess(true);
927 }
928
929 /*
930 * Implementation of 'workspace [--no-auto-back-and-forth] <name>'
931 *
932 */
933 void cmd_workspace_name(I3_CMD, const char *name, const char *_no_auto_back_and_forth) {
934 const bool no_auto_back_and_forth = (_no_auto_back_and_forth != NULL);
935
936 if (strncasecmp(name, "__", strlen("__")) == 0) {
937 yerror("You cannot switch to the i3-internal workspaces (\"%s\").", name);
938 return;
939 }
940
941 if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
942 yerror("Cannot switch workspace while in global fullscreen");
943 return;
944 }
945
946 DLOG("should switch to workspace %s\n", name);
947 if (!no_auto_back_and_forth && maybe_back_and_forth(cmd_output, name)) {
948 ysuccess(true);
949 return;
950 }
951 workspace_show_by_name(name);
952
953 cmd_output->needs_tree_render = true;
954 // XXX: default reply for now, make this a better reply
955 ysuccess(true);
956 }
957
958 /*
959 * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
960 *
961 */
962 void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle) {
963 HANDLE_EMPTY_MATCH;
964
965 owindow *current = TAILQ_FIRST(&owindows);
966 if (current == NULL) {
967 yerror("Given criteria don't match a window");
968 return;
969 }
970
971 /* Marks must be unique, i.e., no two windows must have the same mark. */
972 if (current != TAILQ_LAST(&owindows, owindows_head)) {
973 yerror("A mark must not be put onto more than one window");
974 return;
975 }
976
977 DLOG("matching: %p / %s\n", current->con, current->con->name);
978
979 mark_mode_t mark_mode = (mode == NULL || strcmp(mode, "--replace") == 0) ? MM_REPLACE : MM_ADD;
980 if (toggle != NULL) {
981 con_mark_toggle(current->con, mark, mark_mode);
982 } else {
983 con_mark(current->con, mark, mark_mode);
984 }
985
986 cmd_output->needs_tree_render = true;
987 // XXX: default reply for now, make this a better reply
988 ysuccess(true);
989 }
990
991 /*
992 * Implementation of 'unmark [mark]'
993 *
994 */
995 void cmd_unmark(I3_CMD, const char *mark) {
996 if (match_is_empty(current_match)) {
997 con_unmark(NULL, mark);
998 } else {
999 owindow *current;
1000 TAILQ_FOREACH(current, &owindows, owindows) {
1001 con_unmark(current->con, mark);
1002 }
1003 }
1004
1005 cmd_output->needs_tree_render = true;
1006 // XXX: default reply for now, make this a better reply
1007 ysuccess(true);
1008 }
1009
1010 /*
1011 * Implementation of 'mode <string>'.
1012 *
1013 */
1014 void cmd_mode(I3_CMD, const char *mode) {
1015 DLOG("mode=%s\n", mode);
1016 switch_mode(mode);
1017
1018 // XXX: default reply for now, make this a better reply
1019 ysuccess(true);
1020 }
1021
1022 /*
1023 * Implementation of 'move [window|container] [to] output <str>'.
1024 *
1025 */
1026 void cmd_move_con_to_output(I3_CMD, const char *name) {
1027 DLOG("Should move window to output \"%s\".\n", name);
1028 HANDLE_EMPTY_MATCH;
1029
1030 owindow *current;
1031 bool had_error = false;
1032 TAILQ_FOREACH(current, &owindows, owindows) {
1033 DLOG("matching: %p / %s\n", current->con, current->con->name);
1034
1035 had_error |= !con_move_to_output_name(current->con, name, true);
1036 }
1037
1038 cmd_output->needs_tree_render = true;
1039 ysuccess(!had_error);
1040 }
1041
1042 /*
1043 * Implementation of 'move [container|window] [to] mark <str>'.
1044 *
1045 */
1046 void cmd_move_con_to_mark(I3_CMD, const char *mark) {
1047 DLOG("moving window to mark \"%s\"\n", mark);
1048
1049 HANDLE_EMPTY_MATCH;
1050
1051 bool result = true;
1052 owindow *current;
1053 TAILQ_FOREACH(current, &owindows, owindows) {
1054 DLOG("moving matched window %p / %s to mark \"%s\"\n", current->con, current->con->name, mark);
1055 result &= con_move_to_mark(current->con, mark);
1056 }
1057
1058 cmd_output->needs_tree_render = true;
1059 ysuccess(result);
1060 }
1061
1062 /*
1063 * Implementation of 'floating enable|disable|toggle'
1064 *
1065 */
1066 void cmd_floating(I3_CMD, const char *floating_mode) {
1067 owindow *current;
1068
1069 DLOG("floating_mode=%s\n", floating_mode);
1070
1071 HANDLE_EMPTY_MATCH;
1072
1073 TAILQ_FOREACH(current, &owindows, owindows) {
1074 DLOG("matching: %p / %s\n", current->con, current->con->name);
1075 if (strcmp(floating_mode, "toggle") == 0) {
1076 DLOG("should toggle mode\n");
1077 toggle_floating_mode(current->con, false);
1078 } else {
1079 DLOG("should switch mode to %s\n", floating_mode);
1080 if (strcmp(floating_mode, "enable") == 0) {
1081 floating_enable(current->con, false);
1082 } else {
1083 floating_disable(current->con, false);
1084 }
1085 }
1086 }
1087
1088 cmd_output->needs_tree_render = true;
1089 // XXX: default reply for now, make this a better reply
1090 ysuccess(true);
1091 }
1092
1093 /*
1094 * Implementation of 'move workspace to [output] <str>'.
1095 *
1096 */
1097 void cmd_move_workspace_to_output(I3_CMD, const char *name) {
1098 DLOG("should move workspace to output %s\n", name);
1099
1100 HANDLE_EMPTY_MATCH;
1101
1102 owindow *current;
1103 TAILQ_FOREACH(current, &owindows, owindows) {
1104 Con *ws = con_get_workspace(current->con);
1105 if (con_is_internal(ws)) {
1106 continue;
1107 }
1108
1109 Output *current_output = get_output_for_con(ws);
1110 if (current_output == NULL) {
1111 yerror("Cannot get current output. This is a bug in i3.");
1112 return;
1113 }
1114
1115 Output *target_output = get_output_from_string(current_output, name);
1116 if (!target_output) {
1117 yerror("Could not get output from string \"%s\"", name);
1118 return;
1119 }
1120
1121 bool success = workspace_move_to_output(ws, target_output);
1122 if (!success) {
1123 yerror("Failed to move workspace to output.");
1124 return;
1125 }
1126 }
1127
1128 cmd_output->needs_tree_render = true;
1129 ysuccess(true);
1130 }
1131
1132 /*
1133 * Implementation of 'split v|h|t|vertical|horizontal|toggle'.
1134 *
1135 */
1136 void cmd_split(I3_CMD, const char *direction) {
1137 HANDLE_EMPTY_MATCH;
1138
1139 owindow *current;
1140 LOG("splitting in direction %c\n", direction[0]);
1141 TAILQ_FOREACH(current, &owindows, owindows) {
1142 if (con_is_docked(current->con)) {
1143 ELOG("Cannot split a docked container, skipping.\n");
1144 continue;
1145 }
1146
1147 DLOG("matching: %p / %s\n", current->con, current->con->name);
1148 if (direction[0] == 't') {
1149 layout_t current_layout;
1150 if (current->con->type == CT_WORKSPACE) {
1151 current_layout = current->con->layout;
1152 } else {
1153 current_layout = current->con->parent->layout;
1154 }
1155 /* toggling split orientation */
1156 if (current_layout == L_SPLITH) {
1157 tree_split(current->con, VERT);
1158 } else {
1159 tree_split(current->con, HORIZ);
1160 }
1161 } else {
1162 tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
1163 }
1164 }
1165
1166 cmd_output->needs_tree_render = true;
1167 // XXX: default reply for now, make this a better reply
1168 ysuccess(true);
1169 }
1170
1171 /*
1172 * Implementation of 'kill [window|client]'.
1173 *
1174 */
1175 void cmd_kill(I3_CMD, const char *kill_mode_str) {
1176 if (kill_mode_str == NULL)
1177 kill_mode_str = "window";
1178
1179 DLOG("kill_mode=%s\n", kill_mode_str);
1180
1181 int kill_mode;
1182 if (strcmp(kill_mode_str, "window") == 0)
1183 kill_mode = KILL_WINDOW;
1184 else if (strcmp(kill_mode_str, "client") == 0)
1185 kill_mode = KILL_CLIENT;
1186 else {
1187 yerror("BUG: called with kill_mode=%s", kill_mode_str);
1188 return;
1189 }
1190
1191 HANDLE_EMPTY_MATCH;
1192
1193 owindow *current;
1194 TAILQ_FOREACH(current, &owindows, owindows) {
1195 con_close(current->con, kill_mode);
1196 }
1197
1198 cmd_output->needs_tree_render = true;
1199 // XXX: default reply for now, make this a better reply
1200 ysuccess(true);
1201 }
1202
1203 /*
1204 * Implementation of 'exec [--no-startup-id] <command>'.
1205 *
1206 */
1207 void cmd_exec(I3_CMD, const char *nosn, const char *command) {
1208 bool no_startup_id = (nosn != NULL);
1209
1210 DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id);
1211 start_application(command, no_startup_id);
1212
1213 ysuccess(true);
1214 }
1215
1216 /*
1217 * Implementation of 'focus left|right|up|down'.
1218 *
1219 */
1220 void cmd_focus_direction(I3_CMD, const char *direction) {
1221 switch (parse_direction(direction)) {
1222 case D_LEFT:
1223 tree_next('p', HORIZ);
1224 break;
1225 case D_RIGHT:
1226 tree_next('n', HORIZ);
1227 break;
1228 case D_UP:
1229 tree_next('p', VERT);
1230 break;
1231 case D_DOWN:
1232 tree_next('n', VERT);
1233 break;
1234 }
1235
1236 cmd_output->needs_tree_render = true;
1237 // XXX: default reply for now, make this a better reply
1238 ysuccess(true);
1239 }
1240
1241 /*
1242 * Focus a container and disable any other fullscreen container not permitting the focus.
1243 *
1244 */
1245 static void cmd_focus_force_focus(Con *con) {
1246 /* Disable fullscreen container in workspace with container to be focused. */
1247 Con *ws = con_get_workspace(con);
1248 Con *fullscreen_on_ws = con_get_fullscreen_covering_ws(ws);
1249 if (fullscreen_on_ws && fullscreen_on_ws != con && !con_has_parent(con, fullscreen_on_ws)) {
1250 con_disable_fullscreen(fullscreen_on_ws);
1251 }
1252 con_activate(con);
1253 }
1254
1255 /*
1256 * Implementation of 'focus tiling|floating|mode_toggle'.
1257 *
1258 */
1259 void cmd_focus_window_mode(I3_CMD, const char *window_mode) {
1260 DLOG("window_mode = %s\n", window_mode);
1261
1262 bool to_floating = false;
1263 if (strcmp(window_mode, "mode_toggle") == 0) {
1264 to_floating = !con_inside_floating(focused);
1265 } else if (strcmp(window_mode, "floating") == 0) {
1266 to_floating = true;
1267 } else if (strcmp(window_mode, "tiling") == 0) {
1268 to_floating = false;
1269 }
1270
1271 Con *ws = con_get_workspace(focused);
1272 Con *current;
1273 bool success = false;
1274 TAILQ_FOREACH(current, &(ws->focus_head), focused) {
1275 if ((to_floating && current->type != CT_FLOATING_CON) ||
1276 (!to_floating && current->type == CT_FLOATING_CON))
1277 continue;
1278
1279 cmd_focus_force_focus(con_descend_focused(current));
1280 success = true;
1281 break;
1282 }
1283
1284 if (success) {
1285 cmd_output->needs_tree_render = true;
1286 ysuccess(true);
1287 } else {
1288 yerror("Failed to find a %s container in workspace.", to_floating ? "floating" : "tiling");
1289 }
1290 }
1291
1292 /*
1293 * Implementation of 'focus parent|child'.
1294 *
1295 */
1296 void cmd_focus_level(I3_CMD, const char *level) {
1297 DLOG("level = %s\n", level);
1298 bool success = false;
1299
1300 /* Focusing the parent can only be allowed if the newly
1301 * focused container won't escape the fullscreen container. */
1302 if (strcmp(level, "parent") == 0) {
1303 if (focused && focused->parent) {
1304 if (con_fullscreen_permits_focusing(focused->parent))
1305 success = level_up();
1306 else
1307 ELOG("'focus parent': Currently in fullscreen, not going up\n");
1308 }
1309 }
1310
1311 /* Focusing a child should always be allowed. */
1312 else
1313 success = level_down();
1314
1315 cmd_output->needs_tree_render = success;
1316 // XXX: default reply for now, make this a better reply
1317 ysuccess(success);
1318 }
1319
1320 /*
1321 * Implementation of 'focus'.
1322 *
1323 */
1324 void cmd_focus(I3_CMD) {
1325 DLOG("current_match = %p\n", current_match);
1326
1327 if (match_is_empty(current_match)) {
1328 ELOG("You have to specify which window/container should be focused.\n");
1329 ELOG("Example: [class=\"urxvt\" title=\"irssi\"] focus\n");
1330
1331 yerror("You have to specify which window/container should be focused");
1332
1333 return;
1334 }
1335
1336 Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
1337 int count = 0;
1338 owindow *current;
1339 TAILQ_FOREACH(current, &owindows, owindows) {
1340 Con *ws = con_get_workspace(current->con);
1341 /* If no workspace could be found, this was a dock window.
1342 * Just skip it, you cannot focus dock windows. */
1343 if (!ws)
1344 continue;
1345
1346 /* In case this is a scratchpad window, call scratchpad_show(). */
1347 if (ws == __i3_scratch) {
1348 scratchpad_show(current->con);
1349 count++;
1350 /* While for the normal focus case we can change focus multiple
1351 * times and only a single window ends up focused, we could show
1352 * multiple scratchpad windows. So, rather break here. */
1353 break;
1354 }
1355
1356 /* If the container is not on the current workspace,
1357 * workspace_show() will switch to a different workspace and (if
1358 * enabled) trigger a mouse pointer warp to the currently focused
1359 * container (!) on the target workspace.
1360 *
1361 * Therefore, before calling workspace_show(), we make sure that
1362 * 'current' will be focused on the workspace. However, we cannot
1363 * just con_focus(current) because then the pointer will not be
1364 * warped at all (the code thinks we are already there).
1365 *
1366 * So we focus 'current' to make it the currently focused window of
1367 * the target workspace, then revert focus. */
1368 Con *currently_focused = focused;
1369 cmd_focus_force_focus(current->con);
1370 con_activate(currently_focused);
1371
1372 /* Now switch to the workspace, then focus */
1373 workspace_show(ws);
1374 LOG("focusing %p / %s\n", current->con, current->con->name);
1375 con_activate(current->con);
1376 count++;
1377 }
1378
1379 if (count > 1)
1380 LOG("WARNING: Your criteria for the focus command matches %d containers, "
1381 "while only exactly one container can be focused at a time.\n",
1382 count);
1383
1384 cmd_output->needs_tree_render = true;
1385 ysuccess(count > 0);
1386 }
1387
1388 /*
1389 * Implementation of 'fullscreen enable|toggle [global]' and
1390 * 'fullscreen disable'
1391 *
1392 */
1393 void cmd_fullscreen(I3_CMD, const char *action, const char *fullscreen_mode) {
1394 fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
1395 DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
1396 owindow *current;
1397
1398 HANDLE_EMPTY_MATCH;
1399
1400 TAILQ_FOREACH(current, &owindows, owindows) {
1401 DLOG("matching: %p / %s\n", current->con, current->con->name);
1402 if (strcmp(action, "toggle") == 0) {
1403 con_toggle_fullscreen(current->con, mode);
1404 } else if (strcmp(action, "enable") == 0) {
1405 con_enable_fullscreen(current->con, mode);
1406 } else if (strcmp(action, "disable") == 0) {
1407 con_disable_fullscreen(current->con);
1408 }
1409 }
1410
1411 cmd_output->needs_tree_render = true;
1412 // XXX: default reply for now, make this a better reply
1413 ysuccess(true);
1414 }
1415
1416 /*
1417 * Implementation of 'sticky enable|disable|toggle'.
1418 *
1419 */
1420 void cmd_sticky(I3_CMD, const char *action) {
1421 DLOG("%s sticky on window\n", action);
1422 HANDLE_EMPTY_MATCH;
1423
1424 owindow *current;
1425 TAILQ_FOREACH(current, &owindows, owindows) {
1426 if (current->con->window == NULL) {
1427 ELOG("only containers holding a window can be made sticky, skipping con = %p\n", current->con);
1428 continue;
1429 }
1430 DLOG("setting sticky for container = %p / %s\n", current->con, current->con->name);
1431
1432 bool sticky = false;
1433 if (strcmp(action, "enable") == 0)
1434 sticky = true;
1435 else if (strcmp(action, "disable") == 0)
1436 sticky = false;
1437 else if (strcmp(action, "toggle") == 0)
1438 sticky = !current->con->sticky;
1439
1440 current->con->sticky = sticky;
1441 ewmh_update_sticky(current->con->window->id, sticky);
1442 }
1443
1444 /* A window we made sticky might not be on a visible workspace right now, so we need to make
1445 * sure it gets pushed to the front now. */
1446 output_push_sticky_windows(focused);
1447
1448 ewmh_update_wm_desktop();
1449
1450 cmd_output->needs_tree_render = true;
1451 ysuccess(true);
1452 }
1453
1454 /*
1455 * Implementation of 'move <direction> [<pixels> [px]]'.
1456 *
1457 */
1458 void cmd_move_direction(I3_CMD, const char *direction_str, long move_px) {
1459 owindow *current;
1460 HANDLE_EMPTY_MATCH;
1461
1462 Con *initially_focused = focused;
1463 direction_t direction = parse_direction(direction_str);
1464
1465 TAILQ_FOREACH(current, &owindows, owindows) {
1466 DLOG("moving in direction %s, px %ld\n", direction_str, move_px);
1467 if (con_is_floating(current->con)) {
1468 DLOG("floating move with %ld pixels\n", move_px);
1469 Rect newrect = current->con->parent->rect;
1470
1471 switch (direction) {
1472 case D_LEFT:
1473 newrect.x -= move_px;
1474 break;
1475 case D_RIGHT:
1476 newrect.x += move_px;
1477 break;
1478 case D_UP:
1479 newrect.y -= move_px;
1480 break;
1481 case D_DOWN:
1482 newrect.y += move_px;
1483 break;
1484 }
1485
1486 floating_reposition(current->con->parent, newrect);
1487 } else {
1488 tree_move(current->con, direction);
1489 cmd_output->needs_tree_render = true;
1490 }
1491 }
1492
1493 /* the move command should not disturb focus */
1494 if (focused != initially_focused)
1495 con_activate(initially_focused);
1496
1497 // XXX: default reply for now, make this a better reply
1498 ysuccess(true);
1499 }
1500
1501 /*
1502 * Implementation of 'layout default|stacked|stacking|tabbed|splitv|splith'.
1503 *
1504 */
1505 void cmd_layout(I3_CMD, const char *layout_str) {
1506 HANDLE_EMPTY_MATCH;
1507
1508 layout_t layout;
1509 if (!layout_from_name(layout_str, &layout)) {
1510 ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
1511 return;
1512 }
1513
1514 DLOG("changing layout to %s (%d)\n", layout_str, layout);
1515
1516 owindow *current;
1517 TAILQ_FOREACH(current, &owindows, owindows) {
1518 if (con_is_docked(current->con)) {
1519 ELOG("cannot change layout of a docked container, skipping it.\n");
1520 continue;
1521 }
1522
1523 DLOG("matching: %p / %s\n", current->con, current->con->name);
1524 con_set_layout(current->con, layout);
1525 }
1526
1527 cmd_output->needs_tree_render = true;
1528 // XXX: default reply for now, make this a better reply
1529 ysuccess(true);
1530 }
1531
1532 /*
1533 * Implementation of 'layout toggle [all|split]'.
1534 *
1535 */
1536 void cmd_layout_toggle(I3_CMD, const char *toggle_mode) {
1537 owindow *current;
1538
1539 if (toggle_mode == NULL)
1540 toggle_mode = "default";
1541
1542 DLOG("toggling layout (mode = %s)\n", toggle_mode);
1543
1544 /* check if the match is empty, not if the result is empty */
1545 if (match_is_empty(current_match))
1546 con_toggle_layout(focused, toggle_mode);
1547 else {
1548 TAILQ_FOREACH(current, &owindows, owindows) {
1549 DLOG("matching: %p / %s\n", current->con, current->con->name);
1550 con_toggle_layout(current->con, toggle_mode);
1551 }
1552 }
1553
1554 cmd_output->needs_tree_render = true;
1555 // XXX: default reply for now, make this a better reply
1556 ysuccess(true);
1557 }
1558
1559 /*
1560 * Implementation of 'exit'.
1561 *
1562 */
1563 void cmd_exit(I3_CMD) {
1564 LOG("Exiting due to user command.\n");
1565 exit(0);
1566
1567 /* unreached */
1568 }
1569
1570 /*
1571 * Implementation of 'reload'.
1572 *
1573 */
1574 void cmd_reload(I3_CMD) {
1575 LOG("reloading\n");
1576 kill_nagbar(&config_error_nagbar_pid, false);
1577 kill_nagbar(&command_error_nagbar_pid, false);
1578 load_configuration(conn, NULL, true);
1579 x_set_i3_atoms();
1580 /* Send an IPC event just in case the ws names have changed */
1581 ipc_send_workspace_event("reload", NULL, NULL);
1582 /* Send an update event for the barconfig just in case it has changed */
1583 update_barconfig();
1584
1585 // XXX: default reply for now, make this a better reply
1586 ysuccess(true);
1587 }
1588
1589 /*
1590 * Implementation of 'restart'.
1591 *
1592 */
1593 void cmd_restart(I3_CMD) {
1594 LOG("restarting i3\n");
1595 ipc_shutdown(SHUTDOWN_REASON_RESTART);
1596 unlink(config.ipc_socket_path);
1597 /* We need to call this manually since atexit handlers don’t get called
1598 * when exec()ing */
1599 purge_zerobyte_logfile();
1600 i3_restart(false);
1601
1602 // XXX: default reply for now, make this a better reply
1603 ysuccess(true);
1604 }
1605
1606 /*
1607 * Implementation of 'open'.
1608 *
1609 */
1610 void cmd_open(I3_CMD) {
1611 LOG("opening new container\n");
1612 Con *con = tree_open_con(NULL, NULL);
1613 con->layout = L_SPLITH;
1614 con_activate(con);
1615
1616 y(map_open);
1617 ystr("success");
1618 y(bool, true);
1619 ystr("id");
1620 y(integer, (uintptr_t)con);
1621 y(map_close);
1622
1623 cmd_output->needs_tree_render = true;
1624 }
1625
1626 /*
1627 * Implementation of 'focus output <output>'.
1628 *
1629 */
1630 void cmd_focus_output(I3_CMD, const char *name) {
1631 owindow *current;
1632
1633 DLOG("name = %s\n", name);
1634
1635 HANDLE_EMPTY_MATCH;
1636
1637 /* get the output */
1638 Output *current_output = NULL;
1639 Output *output;
1640
1641 TAILQ_FOREACH(current, &owindows, owindows)
1642 current_output = get_output_for_con(current->con);
1643 assert(current_output != NULL);
1644
1645 output = get_output_from_string(current_output, name);
1646
1647 if (!output) {
1648 yerror("No such output found.");
1649 return;
1650 }
1651
1652 /* get visible workspace on output */
1653 Con *ws = NULL;
1654 GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1655 if (!ws) {
1656 yerror("BUG: No workspace found on output.");
1657 return;
1658 }
1659
1660 workspace_show(ws);
1661
1662 cmd_output->needs_tree_render = true;
1663 // XXX: default reply for now, make this a better reply
1664 ysuccess(true);
1665 }
1666
1667 /*
1668 * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
1669 *
1670 */
1671 void cmd_move_window_to_position(I3_CMD, long x, long y) {
1672 bool has_error = false;
1673
1674 owindow *current;
1675 HANDLE_EMPTY_MATCH;
1676
1677 TAILQ_FOREACH(current, &owindows, owindows) {
1678 if (!con_is_floating(current->con)) {
1679 ELOG("Cannot change position. The window/container is not floating\n");
1680
1681 if (!has_error) {
1682 yerror("Cannot change position of a window/container because it is not floating.");
1683 has_error = true;
1684 }
1685
1686 continue;
1687 }
1688
1689 Rect newrect = current->con->parent->rect;
1690
1691 DLOG("moving to position %ld %ld\n", x, y);
1692 newrect.x = x;
1693 newrect.y = y;
1694
1695 if (!floating_reposition(current->con->parent, newrect)) {
1696 yerror("Cannot move window/container out of bounds.");
1697 has_error = true;
1698 }
1699 }
1700
1701 if (!has_error)
1702 ysuccess(true);
1703 }
1704
1705 /*
1706 * Implementation of 'move [window|container] [to] [absolute] position center
1707 *
1708 */
1709 void cmd_move_window_to_center(I3_CMD, const char *method) {
1710 bool has_error = false;
1711 HANDLE_EMPTY_MATCH;
1712
1713 owindow *current;
1714 TAILQ_FOREACH(current, &owindows, owindows) {
1715 Con *floating_con = con_inside_floating(current->con);
1716 if (floating_con == NULL) {
1717 ELOG("con %p / %s is not floating, cannot move it to the center.\n",
1718 current->con, current->con->name);
1719
1720 if (!has_error) {
1721 yerror("Cannot change position of a window/container because it is not floating.");
1722 has_error = true;
1723 }
1724
1725 continue;
1726 }
1727
1728 if (strcmp(method, "absolute") == 0) {
1729 DLOG("moving to absolute center\n");
1730 floating_center(floating_con, croot->rect);
1731
1732 floating_maybe_reassign_ws(floating_con);
1733 cmd_output->needs_tree_render = true;
1734 }
1735
1736 if (strcmp(method, "position") == 0) {
1737 DLOG("moving to center\n");
1738 floating_center(floating_con, con_get_workspace(floating_con)->rect);
1739
1740 cmd_output->needs_tree_render = true;
1741 }
1742 }
1743
1744 // XXX: default reply for now, make this a better reply
1745 if (!has_error)
1746 ysuccess(true);
1747 }
1748
1749 /*
1750 * Implementation of 'move [window|container] [to] position mouse'
1751 *
1752 */
1753 void cmd_move_window_to_mouse(I3_CMD) {
1754 HANDLE_EMPTY_MATCH;
1755
1756 owindow *current;
1757 TAILQ_FOREACH(current, &owindows, owindows) {
1758 Con *floating_con = con_inside_floating(current->con);
1759 if (floating_con == NULL) {
1760 DLOG("con %p / %s is not floating, cannot move it to the mouse position.\n",
1761 current->con, current->con->name);
1762 continue;
1763 }
1764
1765 DLOG("moving floating container %p / %s to cursor position\n", floating_con, floating_con->name);
1766 floating_move_to_pointer(floating_con);
1767 }
1768
1769 cmd_output->needs_tree_render = true;
1770 ysuccess(true);
1771 }
1772
1773 /*
1774 * Implementation of 'move scratchpad'.
1775 *
1776 */
1777 void cmd_move_scratchpad(I3_CMD) {
1778 DLOG("should move window to scratchpad\n");
1779 owindow *current;
1780
1781 HANDLE_EMPTY_MATCH;
1782
1783 TAILQ_FOREACH(current, &owindows, owindows) {
1784 DLOG("matching: %p / %s\n", current->con, current->con->name);
1785 scratchpad_move(current->con);
1786 }
1787
1788 cmd_output->needs_tree_render = true;
1789 // XXX: default reply for now, make this a better reply
1790 ysuccess(true);
1791 }
1792
1793 /*
1794 * Implementation of 'scratchpad show'.
1795 *
1796 */
1797 void cmd_scratchpad_show(I3_CMD) {
1798 DLOG("should show scratchpad window\n");
1799 owindow *current;
1800 bool result = false;
1801
1802 if (match_is_empty(current_match)) {
1803 result = scratchpad_show(NULL);
1804 } else {
1805 TAILQ_FOREACH(current, &owindows, owindows) {
1806 DLOG("matching: %p / %s\n", current->con, current->con->name);
1807 result |= scratchpad_show(current->con);
1808 }
1809 }
1810
1811 cmd_output->needs_tree_render = true;
1812
1813 ysuccess(result);
1814 }
1815
1816 /*
1817 * Implementation of 'swap [container] [with] id|con_id|mark <arg>'.
1818 *
1819 */
1820 void cmd_swap(I3_CMD, const char *mode, const char *arg) {
1821 HANDLE_EMPTY_MATCH;
1822
1823 owindow *match = TAILQ_FIRST(&owindows);
1824 if (match == NULL) {
1825 yerror("No match found for swapping.");
1826 return;
1827 }
1828 if (match->con == NULL) {
1829 yerror("Match %p has no container.", match);
1830 return;
1831 }
1832
1833 Con *con;
1834 if (strcmp(mode, "id") == 0) {
1835 long target;
1836 if (!parse_long(arg, &target, 0)) {
1837 yerror("Failed to parse %s into a window id.", arg);
1838 return;
1839 }
1840
1841 con = con_by_window_id(target);
1842 } else if (strcmp(mode, "con_id") == 0) {
1843 long target;
1844 if (!parse_long(arg, &target, 0)) {
1845 yerror("Failed to parse %s into a container id.", arg);
1846 return;
1847 }
1848
1849 con = con_by_con_id(target);
1850 } else if (strcmp(mode, "mark") == 0) {
1851 con = con_by_mark(arg);
1852 } else {
1853 yerror("Unhandled swap mode \"%s\". This is a bug.", mode);
1854 return;
1855 }
1856
1857 if (con == NULL) {
1858 yerror("Could not find container for %s = %s", mode, arg);
1859 return;
1860 }
1861
1862 if (match != TAILQ_LAST(&owindows, owindows_head)) {
1863 LOG("More than one container matched the swap command, only using the first one.");
1864 }
1865
1866 DLOG("Swapping %p with %p.\n", match->con, con);
1867 bool result = con_swap(match->con, con);
1868
1869 cmd_output->needs_tree_render = true;
1870 // XXX: default reply for now, make this a better reply
1871 ysuccess(result);
1872 }
1873
1874 /*
1875 * Implementation of 'title_format <format>'
1876 *
1877 */
1878 void cmd_title_format(I3_CMD, const char *format) {
1879 DLOG("setting title_format to \"%s\"\n", format);
1880 HANDLE_EMPTY_MATCH;
1881
1882 owindow *current;
1883 TAILQ_FOREACH(current, &owindows, owindows) {
1884 DLOG("setting title_format for %p / %s\n", current->con, current->con->name);
1885 FREE(current->con->title_format);
1886
1887 /* If we only display the title without anything else, we can skip the parsing step,
1888 * so we remove the title format altogether. */
1889 if (strcasecmp(format, "%title") != 0) {
1890 current->con->title_format = sstrdup(format);
1891
1892 if (current->con->window != NULL) {
1893 i3String *formatted_title = con_parse_title_format(current->con);
1894 ewmh_update_visible_name(current->con->window->id, i3string_as_utf8(formatted_title));
1895 I3STRING_FREE(formatted_title);
1896 }
1897 } else {
1898 if (current->con->window != NULL) {
1899 /* We can remove _NET_WM_VISIBLE_NAME since we don't display a custom title. */
1900 ewmh_update_visible_name(current->con->window->id, NULL);
1901 }
1902 }
1903
1904 if (current->con->window != NULL) {
1905 /* Make sure the window title is redrawn immediately. */
1906 current->con->window->name_x_changed = true;
1907 } else {
1908 /* For windowless containers we also need to force the redrawing. */
1909 FREE(current->con->deco_render_params);
1910 }
1911 }
1912
1913 cmd_output->needs_tree_render = true;
1914 ysuccess(true);
1915 }
1916
1917 /*
1918 * Implementation of 'rename workspace [<name>] to <name>'
1919 *
1920 */
1921 void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
1922 if (strncasecmp(new_name, "__", strlen("__")) == 0) {
1923 yerror("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name);
1924 return;
1925 }
1926 if (old_name) {
1927 LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
1928 } else {
1929 LOG("Renaming current workspace to \"%s\"\n", new_name);
1930 }
1931
1932 Con *workspace;
1933 if (old_name) {
1934 workspace = get_existing_workspace_by_name(old_name);
1935 } else {
1936 workspace = con_get_workspace(focused);
1937 old_name = workspace->name;
1938 }
1939
1940 if (!workspace) {
1941 yerror("Old workspace \"%s\" not found", old_name);
1942 return;
1943 }
1944
1945 Con *check_dest = get_existing_workspace_by_name(new_name);
1946
1947 /* If check_dest == workspace, the user might be changing the case of the
1948 * workspace, or it might just be a no-op. */
1949 if (check_dest != NULL && check_dest != workspace) {
1950 yerror("New workspace \"%s\" already exists", new_name);
1951 return;
1952 }
1953
1954 /* Change the name and try to parse it as a number. */
1955 /* old_name might refer to workspace->name, so copy it before free()ing */
1956 char *old_name_copy = sstrdup(old_name);
1957 FREE(workspace->name);
1958 workspace->name = sstrdup(new_name);
1959
1960 workspace->num = ws_name_to_number(new_name);
1961 LOG("num = %d\n", workspace->num);
1962
1963 /* By re-attaching, the sort order will be correct afterwards. */
1964 Con *previously_focused = focused;
1965 Con *previously_focused_content = focused->type == CT_WORKSPACE ? focused->parent : NULL;
1966 Con *parent = workspace->parent;
1967 con_detach(workspace);
1968 con_attach(workspace, parent, false);
1969 ipc_send_workspace_event("rename", workspace, NULL);
1970
1971 /* Move the workspace to the correct output if it has an assignment */
1972 struct Workspace_Assignment *assignment = NULL;
1973 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
1974 if (assignment->output == NULL)
1975 continue;
1976 if (strcmp(assignment->name, workspace->name) != 0 && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) {
1977 continue;
1978 }
1979
1980 Output *target_output = get_output_by_name(assignment->output, true);
1981 if (!target_output) {
1982 LOG("Could not get output named \"%s\"\n", assignment->output);
1983 continue;
1984 }
1985 if (!output_triggers_assignment(target_output, assignment)) {
1986 continue;
1987 }
1988 workspace_move_to_output(workspace, target_output);
1989
1990 break;
1991 }
1992
1993 bool can_restore_focus = previously_focused != NULL;
1994 /* NB: If previously_focused is a workspace we can't work directly with it
1995 * since it might have been cleaned up by workspace_show() already,
1996 * depending on the focus order/number of other workspaces on the output.
1997 * Instead, we loop through the available workspaces and only focus
1998 * previously_focused if we still find it. */
1999 if (previously_focused_content) {
2000 Con *workspace = NULL;
2001 GREP_FIRST(workspace, previously_focused_content, child == previously_focused);
2002 can_restore_focus &= (workspace != NULL);
2003 }
2004
2005 if (can_restore_focus) {
2006 /* Restore the previous focus since con_attach messes with the focus. */
2007 workspace_show(con_get_workspace(previously_focused));
2008 con_focus(previously_focused);
2009 }
2010
2011 cmd_output->needs_tree_render = true;
2012 ysuccess(true);
2013
2014 ewmh_update_desktop_names();
2015 ewmh_update_desktop_viewport();
2016 ewmh_update_current_desktop();
2017
2018 startup_sequence_rename_workspace(old_name_copy, new_name);
2019 free(old_name_copy);
2020 }
2021
2022 /*
2023 * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
2024 *
2025 */
2026 static bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
2027 int mode = M_DOCK;
2028 bool toggle = false;
2029 if (strcmp(bar_mode, "dock") == 0)
2030 mode = M_DOCK;
2031 else if (strcmp(bar_mode, "hide") == 0)
2032 mode = M_HIDE;
2033 else if (strcmp(bar_mode, "invisible") == 0)
2034 mode = M_INVISIBLE;
2035 else if (strcmp(bar_mode, "toggle") == 0)
2036 toggle = true;
2037 else {
2038 ELOG("Unknown bar mode \"%s\", this is a mismatch between code and parser spec.\n", bar_mode);
2039 return false;
2040 }
2041
2042 bool changed_sth = false;
2043 Barconfig *current = NULL;
2044 TAILQ_FOREACH(current, &barconfigs, configs) {
2045 if (bar_id && strcmp(current->id, bar_id) != 0)
2046 continue;
2047
2048 if (toggle)
2049 mode = (current->mode + 1) % 2;
2050
2051 DLOG("Changing bar mode of bar_id '%s' to '%s (%d)'\n", current->id, bar_mode, mode);
2052 current->mode = mode;
2053 changed_sth = true;
2054
2055 if (bar_id)
2056 break;
2057 }
2058
2059 if (bar_id && !changed_sth) {
2060 DLOG("Changing bar mode of bar_id %s failed, bar_id not found.\n", bar_id);
2061 return false;
2062 }
2063
2064 return true;
2065 }
2066
2067 /*
2068 * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
2069 *
2070 */
2071 static bool cmd_bar_hidden_state(const char *bar_hidden_state, const char *bar_id) {
2072 int hidden_state = S_SHOW;
2073 bool toggle = false;
2074 if (strcmp(bar_hidden_state, "hide") == 0)
2075 hidden_state = S_HIDE;
2076 else if (strcmp(bar_hidden_state, "show") == 0)
2077 hidden_state = S_SHOW;
2078 else if (strcmp(bar_hidden_state, "toggle") == 0)
2079 toggle = true;
2080 else {
2081 ELOG("Unknown bar state \"%s\", this is a mismatch between code and parser spec.\n", bar_hidden_state);
2082 return false;
2083 }
2084
2085 bool changed_sth = false;
2086 Barconfig *current = NULL;
2087 TAILQ_FOREACH(current, &barconfigs, configs) {
2088 if (bar_id && strcmp(current->id, bar_id) != 0)
2089 continue;
2090
2091 if (toggle)
2092 hidden_state = (current->hidden_state + 1) % 2;
2093
2094 DLOG("Changing bar hidden_state of bar_id '%s' to '%s (%d)'\n", current->id, bar_hidden_state, hidden_state);
2095 current->hidden_state = hidden_state;
2096 changed_sth = true;
2097
2098 if (bar_id)
2099 break;
2100 }
2101
2102 if (bar_id && !changed_sth) {
2103 DLOG("Changing bar hidden_state of bar_id %s failed, bar_id not found.\n", bar_id);
2104 return false;
2105 }
2106
2107 return true;
2108 }
2109
2110 /*
2111 * Implementation of 'bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]'
2112 *
2113 */
2114 void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id) {
2115 bool ret;
2116 if (strcmp(bar_type, "mode") == 0)
2117 ret = cmd_bar_mode(bar_value, bar_id);
2118 else if (strcmp(bar_type, "hidden_state") == 0)
2119 ret = cmd_bar_hidden_state(bar_value, bar_id);
2120 else {
2121 ELOG("Unknown bar option type \"%s\", this is a mismatch between code and parser spec.\n", bar_type);
2122 ret = false;
2123 }
2124
2125 ysuccess(ret);
2126 if (!ret)
2127 return;
2128
2129 update_barconfig();
2130 }
2131
2132 /*
2133 * Implementation of 'shmlog <size>|toggle|on|off'
2134 *
2135 */
2136 void cmd_shmlog(I3_CMD, const char *argument) {
2137 if (!strcmp(argument, "toggle"))
2138 /* Toggle shm log, if size is not 0. If it is 0, set it to default. */
2139 shmlog_size = shmlog_size ? -shmlog_size : default_shmlog_size;
2140 else if (!strcmp(argument, "on"))
2141 shmlog_size = default_shmlog_size;
2142 else if (!strcmp(argument, "off"))
2143 shmlog_size = 0;
2144 else {
2145 long new_size = 0;
2146 if (!parse_long(argument, &new_size, 0)) {
2147 yerror("Failed to parse %s into a shmlog size.", argument);
2148 return;
2149 }
2150 /* If shm logging now, restart logging with the new size. */
2151 if (shmlog_size > 0) {
2152 shmlog_size = 0;
2153 LOG("Restarting shm logging...\n");
2154 init_logging();
2155 }
2156 shmlog_size = (int)new_size;
2157 }
2158 LOG("%s shm logging\n", shmlog_size > 0 ? "Enabling" : "Disabling");
2159 init_logging();
2160 update_shmlog_atom();
2161 ysuccess(true);
2162 }
2163
2164 /*
2165 * Implementation of 'debuglog toggle|on|off'
2166 *
2167 */
2168 void cmd_debuglog(I3_CMD, const char *argument) {
2169 bool logging = get_debug_logging();
2170 if (!strcmp(argument, "toggle")) {
2171 LOG("%s debug logging\n", logging ? "Disabling" : "Enabling");
2172 set_debug_logging(!logging);
2173 } else if (!strcmp(argument, "on") && !logging) {
2174 LOG("Enabling debug logging\n");
2175 set_debug_logging(true);
2176 } else if (!strcmp(argument, "off") && logging) {
2177 LOG("Disabling debug logging\n");
2178 set_debug_logging(false);
2179 }
2180 // XXX: default reply for now, make this a better reply
2181 ysuccess(true);
2182 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * commands_parser.c: hand-written parser to parse commands (commands are what
7 * you bind on keys and what you can send to i3 using the IPC interface, like
8 * 'move left' or 'workspace 4').
9 *
10 * We use a hand-written parser instead of lex/yacc because our commands are
11 * easy for humans, not for computers. Thus, it’s quite hard to specify a
12 * context-free grammar for the commands. A PEG grammar would be easier, but
13 * there’s downsides to every PEG parser generator I have come across so far.
14 *
15 * This parser is basically a state machine which looks for literals or strings
16 * and can push either on a stack. After identifying a literal or string, it
17 * will either transition to the current state, to a different state, or call a
18 * function (like cmd_move()).
19 *
20 * Special care has been taken that error messages are useful and the code is
21 * well testable (when compiled with -DTEST_PARSER it will output to stdout
22 * instead of actually calling any function).
23 *
24 */
25 #include "all.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33
34 // Macros to make the YAJL API a bit easier to use.
35 #define y(x, ...) (command_output.json_gen != NULL ? yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) : 0)
36 #define ystr(str) (command_output.json_gen != NULL ? yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) : 0)
37
38 /*******************************************************************************
39 * The data structures used for parsing. Essentially the current state and a
40 * list of tokens for that state.
41 *
42 * The GENERATED_* files are generated by generate-commands-parser.pl with the
43 * input parser-specs/commands.spec.
44 ******************************************************************************/
45
46 #include "GENERATED_command_enums.h"
47
48 typedef struct token {
49 char *name;
50 char *identifier;
51 /* This might be __CALL */
52 cmdp_state next_state;
53 union {
54 uint16_t call_identifier;
55 } extra;
56 } cmdp_token;
57
58 typedef struct tokenptr {
59 cmdp_token *array;
60 int n;
61 } cmdp_token_ptr;
62
63 #include "GENERATED_command_tokens.h"
64
65 /*******************************************************************************
66 * The (small) stack where identified literals are stored during the parsing
67 * of a single command (like $workspace).
68 ******************************************************************************/
69
70 struct stack_entry {
71 /* Just a pointer, not dynamically allocated. */
72 const char *identifier;
73 enum {
74 STACK_STR = 0,
75 STACK_LONG = 1,
76 } type;
77 union {
78 char *str;
79 long num;
80 } val;
81 };
82
83 /* 10 entries should be enough for everybody. */
84 static struct stack_entry stack[10];
85
86 /*
87 * Pushes a string (identified by 'identifier') on the stack. We simply use a
88 * single array, since the number of entries we have to store is very small.
89 *
90 */
91 static void push_string(const char *identifier, char *str) {
92 for (int c = 0; c < 10; c++) {
93 if (stack[c].identifier != NULL)
94 continue;
95 /* Found a free slot, let’s store it here. */
96 stack[c].identifier = identifier;
97 stack[c].val.str = str;
98 stack[c].type = STACK_STR;
99 return;
100 }
101
102 /* When we arrive here, the stack is full. This should not happen and
103 * means there’s either a bug in this parser or the specification
104 * contains a command with more than 10 identified tokens. */
105 fprintf(stderr, "BUG: commands_parser stack full. This means either a bug "
106 "in the code, or a new command which contains more than "
107 "10 identified tokens.\n");
108 exit(1);
109 }
110
111 // TODO move to a common util
112 static void push_long(const char *identifier, long num) {
113 for (int c = 0; c < 10; c++) {
114 if (stack[c].identifier != NULL) {
115 continue;
116 }
117
118 stack[c].identifier = identifier;
119 stack[c].val.num = num;
120 stack[c].type = STACK_LONG;
121 return;
122 }
123
124 /* When we arrive here, the stack is full. This should not happen and
125 * means there’s either a bug in this parser or the specification
126 * contains a command with more than 10 identified tokens. */
127 fprintf(stderr, "BUG: commands_parser stack full. This means either a bug "
128 "in the code, or a new command which contains more than "
129 "10 identified tokens.\n");
130 exit(1);
131 }
132
133 // TODO move to a common util
134 static const char *get_string(const char *identifier) {
135 for (int c = 0; c < 10; c++) {
136 if (stack[c].identifier == NULL)
137 break;
138 if (strcmp(identifier, stack[c].identifier) == 0)
139 return stack[c].val.str;
140 }
141 return NULL;
142 }
143
144 // TODO move to a common util
145 static long get_long(const char *identifier) {
146 for (int c = 0; c < 10; c++) {
147 if (stack[c].identifier == NULL)
148 break;
149 if (strcmp(identifier, stack[c].identifier) == 0)
150 return stack[c].val.num;
151 }
152
153 return 0;
154 }
155
156 // TODO move to a common util
157 static void clear_stack(void) {
158 for (int c = 0; c < 10; c++) {
159 if (stack[c].type == STACK_STR)
160 free(stack[c].val.str);
161 stack[c].identifier = NULL;
162 stack[c].val.str = NULL;
163 stack[c].val.num = 0;
164 }
165 }
166
167 /*******************************************************************************
168 * The parser itself.
169 ******************************************************************************/
170
171 static cmdp_state state;
172 #ifndef TEST_PARSER
173 static Match current_match;
174 #endif
175 static struct CommandResultIR subcommand_output;
176 static struct CommandResultIR command_output;
177
178 #include "GENERATED_command_call.h"
179
180 static void next_state(const cmdp_token *token) {
181 if (token->next_state == __CALL) {
182 subcommand_output.json_gen = command_output.json_gen;
183 subcommand_output.needs_tree_render = false;
184 GENERATED_call(token->extra.call_identifier, &subcommand_output);
185 state = subcommand_output.next_state;
186 /* If any subcommand requires a tree_render(), we need to make the
187 * whole parser result request a tree_render(). */
188 if (subcommand_output.needs_tree_render)
189 command_output.needs_tree_render = true;
190 clear_stack();
191 return;
192 }
193
194 state = token->next_state;
195 if (state == INITIAL) {
196 clear_stack();
197 }
198 }
199
200 /*
201 * Parses a string (or word, if as_word is true). Extracted out of
202 * parse_command so that it can be used in src/workspace.c for interpreting
203 * workspace commands.
204 *
205 */
206 char *parse_string(const char **walk, bool as_word) {
207 const char *beginning = *walk;
208 /* Handle quoted strings (or words). */
209 if (**walk == '"') {
210 beginning++;
211 (*walk)++;
212 for (; **walk != '\0' && **walk != '"'; (*walk)++)
213 if (**walk == '\\' && *(*walk + 1) != '\0')
214 (*walk)++;
215 } else {
216 if (!as_word) {
217 /* For a string (starting with 's'), the delimiters are
218 * comma (,) and semicolon (;) which introduce a new
219 * operation or command, respectively. Also, newlines
220 * end a command. */
221 while (**walk != ';' && **walk != ',' &&
222 **walk != '\0' && **walk != '\r' &&
223 **walk != '\n')
224 (*walk)++;
225 } else {
226 /* For a word, the delimiters are white space (' ' or
227 * '\t'), closing square bracket (]), comma (,) and
228 * semicolon (;). */
229 while (**walk != ' ' && **walk != '\t' &&
230 **walk != ']' && **walk != ',' &&
231 **walk != ';' && **walk != '\r' &&
232 **walk != '\n' && **walk != '\0')
233 (*walk)++;
234 }
235 }
236 if (*walk == beginning)
237 return NULL;
238
239 char *str = scalloc(*walk - beginning + 1, 1);
240 /* We copy manually to handle escaping of characters. */
241 int inpos, outpos;
242 for (inpos = 0, outpos = 0;
243 inpos < (*walk - beginning);
244 inpos++, outpos++) {
245 /* We only handle escaped double quotes and backslashes to not break
246 * backwards compatibility with people using \w in regular expressions
247 * etc. */
248 if (beginning[inpos] == '\\' && (beginning[inpos + 1] == '"' || beginning[inpos + 1] == '\\'))
249 inpos++;
250 str[outpos] = beginning[inpos];
251 }
252
253 return str;
254 }
255
256 /*
257 * Parses and executes the given command. If a caller-allocated yajl_gen is
258 * passed, a json reply will be generated in the format specified by the ipc
259 * protocol. Pass NULL if no json reply is required.
260 *
261 * Free the returned CommandResult with command_result_free().
262 */
263 CommandResult *parse_command(const char *input, yajl_gen gen) {
264 DLOG("COMMAND: *%s*\n", input);
265 state = INITIAL;
266 CommandResult *result = scalloc(1, sizeof(CommandResult));
267
268 /* A YAJL JSON generator used for formatting replies. */
269 command_output.json_gen = gen;
270
271 y(array_open);
272 command_output.needs_tree_render = false;
273
274 const char *walk = input;
275 const size_t len = strlen(input);
276 int c;
277 const cmdp_token *token;
278 bool token_handled;
279
280 // TODO: make this testable
281 #ifndef TEST_PARSER
282 cmd_criteria_init(&current_match, &subcommand_output);
283 #endif
284
285 /* The "<=" operator is intentional: We also handle the terminating 0-byte
286 * explicitly by looking for an 'end' token. */
287 while ((size_t)(walk - input) <= len) {
288 /* skip whitespace and newlines before every token */
289 while ((*walk == ' ' || *walk == '\t' ||
290 *walk == '\r' || *walk == '\n') &&
291 *walk != '\0')
292 walk++;
293
294 cmdp_token_ptr *ptr = &(tokens[state]);
295 token_handled = false;
296 for (c = 0; c < ptr->n; c++) {
297 token = &(ptr->array[c]);
298
299 /* A literal. */
300 if (token->name[0] == '\'') {
301 if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
302 if (token->identifier != NULL)
303 push_string(token->identifier, sstrdup(token->name + 1));
304 walk += strlen(token->name) - 1;
305 next_state(token);
306 token_handled = true;
307 break;
308 }
309 continue;
310 }
311
312 if (strcmp(token->name, "number") == 0) {
313 /* Handle numbers. We only accept decimal numbers for now. */
314 char *end = NULL;
315 errno = 0;
316 long int num = strtol(walk, &end, 10);
317 if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
318 (errno != 0 && num == 0))
319 continue;
320
321 /* No valid numbers found */
322 if (end == walk)
323 continue;
324
325 if (token->identifier != NULL)
326 push_long(token->identifier, num);
327
328 /* Set walk to the first non-number character */
329 walk = end;
330 next_state(token);
331 token_handled = true;
332 break;
333 }
334
335 if (strcmp(token->name, "string") == 0 ||
336 strcmp(token->name, "word") == 0) {
337 char *str = parse_string(&walk, (token->name[0] != 's'));
338 if (str != NULL) {
339 if (token->identifier)
340 push_string(token->identifier, str);
341 /* If we are at the end of a quoted string, skip the ending
342 * double quote. */
343 if (*walk == '"')
344 walk++;
345 next_state(token);
346 token_handled = true;
347 break;
348 }
349 }
350
351 if (strcmp(token->name, "end") == 0) {
352 if (*walk == '\0' || *walk == ',' || *walk == ';') {
353 next_state(token);
354 token_handled = true;
355 /* To make sure we start with an appropriate matching
356 * datastructure for commands which do *not* specify any
357 * criteria, we re-initialize the criteria system after
358 * every command. */
359 // TODO: make this testable
360 #ifndef TEST_PARSER
361 if (*walk == '\0' || *walk == ';')
362 cmd_criteria_init(&current_match, &subcommand_output);
363 #endif
364 walk++;
365 break;
366 }
367 }
368 }
369
370 if (!token_handled) {
371 /* Figure out how much memory we will need to fill in the names of
372 * all tokens afterwards. */
373 int tokenlen = 0;
374 for (c = 0; c < ptr->n; c++)
375 tokenlen += strlen(ptr->array[c].name) + strlen("'', ");
376
377 /* Build up a decent error message. We include the problem, the
378 * full input, and underline the position where the parser
379 * currently is. */
380 char *errormessage;
381 char *possible_tokens = smalloc(tokenlen + 1);
382 char *tokenwalk = possible_tokens;
383 for (c = 0; c < ptr->n; c++) {
384 token = &(ptr->array[c]);
385 if (token->name[0] == '\'') {
386 /* A literal is copied to the error message enclosed with
387 * single quotes. */
388 *tokenwalk++ = '\'';
389 strcpy(tokenwalk, token->name + 1);
390 tokenwalk += strlen(token->name + 1);
391 *tokenwalk++ = '\'';
392 } else {
393 /* Any other token is copied to the error message enclosed
394 * with angle brackets. */
395 *tokenwalk++ = '<';
396 strcpy(tokenwalk, token->name);
397 tokenwalk += strlen(token->name);
398 *tokenwalk++ = '>';
399 }
400 if (c < (ptr->n - 1)) {
401 *tokenwalk++ = ',';
402 *tokenwalk++ = ' ';
403 }
404 }
405 *tokenwalk = '\0';
406 sasprintf(&errormessage, "Expected one of these tokens: %s",
407 possible_tokens);
408 free(possible_tokens);
409
410 /* Contains the same amount of characters as 'input' has, but with
411 * the unparseable part highlighted using ^ characters. */
412 char *position = smalloc(len + 1);
413 for (const char *copywalk = input; *copywalk != '\0'; copywalk++)
414 position[(copywalk - input)] = (copywalk >= walk ? '^' : ' ');
415 position[len] = '\0';
416
417 ELOG("%s\n", errormessage);
418 ELOG("Your command: %s\n", input);
419 ELOG(" %s\n", position);
420
421 result->parse_error = true;
422 result->error_message = errormessage;
423
424 /* Format this error message as a JSON reply. */
425 y(map_open);
426 ystr("success");
427 y(bool, false);
428 /* We set parse_error to true to distinguish this from other
429 * errors. i3-nagbar is spawned upon keypresses only for parser
430 * errors. */
431 ystr("parse_error");
432 y(bool, true);
433 ystr("error");
434 ystr(errormessage);
435 ystr("input");
436 ystr(input);
437 ystr("errorposition");
438 ystr(position);
439 y(map_close);
440
441 free(position);
442 clear_stack();
443 break;
444 }
445 }
446
447 y(array_close);
448
449 result->needs_tree_render = command_output.needs_tree_render;
450 return result;
451 }
452
453 /*
454 * Frees a CommandResult
455 */
456 void command_result_free(CommandResult *result) {
457 if (result == NULL)
458 return;
459
460 FREE(result->error_message);
461 FREE(result);
462 }
463
464 /*******************************************************************************
465 * Code for building the stand-alone binary test.commands_parser which is used
466 * by t/187-commands-parser.t.
467 ******************************************************************************/
468
469 #ifdef TEST_PARSER
470
471 /*
472 * Logs the given message to stdout while prefixing the current time to it,
473 * but only if debug logging was activated.
474 * This is to be called by DLOG() which includes filename/linenumber
475 *
476 */
477 void debuglog(char *fmt, ...) {
478 va_list args;
479
480 va_start(args, fmt);
481 fprintf(stdout, "# ");
482 vfprintf(stdout, fmt, args);
483 va_end(args);
484 }
485
486 void errorlog(char *fmt, ...) {
487 va_list args;
488
489 va_start(args, fmt);
490 vfprintf(stderr, fmt, args);
491 va_end(args);
492 }
493
494 int main(int argc, char *argv[]) {
495 if (argc < 2) {
496 fprintf(stderr, "Syntax: %s <command>\n", argv[0]);
497 return 1;
498 }
499 yajl_gen gen = yajl_gen_alloc(NULL);
500
501 CommandResult *result = parse_command(argv[1], gen);
502
503 command_result_free(result);
504
505 yajl_gen_free(gen);
506 }
507 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * con.c: Functions which deal with containers directly (creating containers,
7 * searching containers, getting specific properties from containers,
8 * …).
9 *
10 */
11 #include "all.h"
12
13 #include "yajl_utils.h"
14
15 static void con_on_remove_child(Con *con);
16
17 /*
18 * force parent split containers to be redrawn
19 *
20 */
21 void con_force_split_parents_redraw(Con *con) {
22 Con *parent = con;
23
24 while (parent != NULL && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
25 if (!con_is_leaf(parent)) {
26 FREE(parent->deco_render_params);
27 }
28
29 parent = parent->parent;
30 }
31 }
32
33 /*
34 * Create a new container (and attach it to the given parent, if not NULL).
35 * This function only initializes the data structures.
36 *
37 */
38 Con *con_new_skeleton(Con *parent, i3Window *window) {
39 Con *new = scalloc(1, sizeof(Con));
40 new->on_remove_child = con_on_remove_child;
41 TAILQ_INSERT_TAIL(&all_cons, new, all_cons);
42 new->type = CT_CON;
43 new->window = window;
44 new->border_style = config.default_border;
45 new->current_border_width = -1;
46 if (window) {
47 new->depth = window->depth;
48 new->window->aspect_ratio = 0.0;
49 } else {
50 new->depth = root_depth;
51 }
52 DLOG("opening window\n");
53
54 TAILQ_INIT(&(new->floating_head));
55 TAILQ_INIT(&(new->nodes_head));
56 TAILQ_INIT(&(new->focus_head));
57 TAILQ_INIT(&(new->swallow_head));
58 TAILQ_INIT(&(new->marks_head));
59
60 if (parent != NULL)
61 con_attach(new, parent, false);
62
63 return new;
64 }
65
66 /* A wrapper for con_new_skeleton, to retain the old con_new behaviour
67 *
68 */
69 Con *con_new(Con *parent, i3Window *window) {
70 Con *new = con_new_skeleton(parent, window);
71 x_con_init(new);
72 return new;
73 }
74
75 /*
76 * Frees the specified container.
77 *
78 */
79 void con_free(Con *con) {
80 free(con->name);
81 FREE(con->deco_render_params);
82 TAILQ_REMOVE(&all_cons, con, all_cons);
83 while (!TAILQ_EMPTY(&(con->swallow_head))) {
84 Match *match = TAILQ_FIRST(&(con->swallow_head));
85 TAILQ_REMOVE(&(con->swallow_head), match, matches);
86 match_free(match);
87 free(match);
88 }
89 while (!TAILQ_EMPTY(&(con->marks_head))) {
90 mark_t *mark = TAILQ_FIRST(&(con->marks_head));
91 TAILQ_REMOVE(&(con->marks_head), mark, marks);
92 FREE(mark->name);
93 FREE(mark);
94 }
95 free(con);
96 DLOG("con %p freed\n", con);
97 }
98
99 static void _con_attach(Con *con, Con *parent, Con *previous, bool ignore_focus) {
100 con->parent = parent;
101 Con *loop;
102 Con *current = previous;
103 struct nodes_head *nodes_head = &(parent->nodes_head);
104 struct focus_head *focus_head = &(parent->focus_head);
105
106 /* Workspaces are handled differently: they need to be inserted at the
107 * right position. */
108 if (con->type == CT_WORKSPACE) {
109 DLOG("it's a workspace. num = %d\n", con->num);
110 if (con->num == -1 || TAILQ_EMPTY(nodes_head)) {
111 TAILQ_INSERT_TAIL(nodes_head, con, nodes);
112 } else {
113 current = TAILQ_FIRST(nodes_head);
114 if (con->num < current->num) {
115 /* we need to insert the container at the beginning */
116 TAILQ_INSERT_HEAD(nodes_head, con, nodes);
117 } else {
118 while (current->num != -1 && con->num > current->num) {
119 current = TAILQ_NEXT(current, nodes);
120 if (current == TAILQ_END(nodes_head)) {
121 current = NULL;
122 break;
123 }
124 }
125 /* we need to insert con after current, if current is not NULL */
126 if (current)
127 TAILQ_INSERT_BEFORE(current, con, nodes);
128 else
129 TAILQ_INSERT_TAIL(nodes_head, con, nodes);
130 }
131 }
132 goto add_to_focus_head;
133 }
134
135 if (con->type == CT_FLOATING_CON) {
136 DLOG("Inserting into floating containers\n");
137 TAILQ_INSERT_TAIL(&(parent->floating_head), con, floating_windows);
138 } else {
139 if (!ignore_focus) {
140 /* Get the first tiling container in focus stack */
141 TAILQ_FOREACH(loop, &(parent->focus_head), focused) {
142 if (loop->type == CT_FLOATING_CON)
143 continue;
144 current = loop;
145 break;
146 }
147 }
148
149 /* When the container is not a split container (but contains a window)
150 * and is attached to a workspace, we check if the user configured a
151 * workspace_layout. This is done in workspace_attach_to, which will
152 * provide us with the container to which we should attach (either the
153 * workspace or a new split container with the configured
154 * workspace_layout).
155 */
156 if (con->window != NULL &&
157 parent->type == CT_WORKSPACE &&
158 parent->workspace_layout != L_DEFAULT) {
159 DLOG("Parent is a workspace. Applying default layout...\n");
160 Con *target = workspace_attach_to(parent);
161
162 /* Attach the original con to this new split con instead */
163 nodes_head = &(target->nodes_head);
164 focus_head = &(target->focus_head);
165 con->parent = target;
166 current = NULL;
167
168 DLOG("done\n");
169 }
170
171 /* Insert the container after the tiling container, if found.
172 * When adding to a CT_OUTPUT, just append one after another. */
173 if (current != NULL && parent->type != CT_OUTPUT) {
174 DLOG("Inserting con = %p after con %p\n", con, current);
175 TAILQ_INSERT_AFTER(nodes_head, current, con, nodes);
176 } else
177 TAILQ_INSERT_TAIL(nodes_head, con, nodes);
178 }
179
180 add_to_focus_head:
181 /* We insert to the TAIL because con_focus() will correct this.
182 * This way, we have the option to insert Cons without having
183 * to focus them. */
184 TAILQ_INSERT_TAIL(focus_head, con, focused);
185 con_force_split_parents_redraw(con);
186 }
187
188 /*
189 * Attaches the given container to the given parent. This happens when moving
190 * a container or when inserting a new container at a specific place in the
191 * tree.
192 *
193 * ignore_focus is to just insert the Con at the end (useful when creating a
194 * new split container *around* some containers, that is, detaching and
195 * attaching them in order without wanting to mess with the focus in between).
196 *
197 */
198 void con_attach(Con *con, Con *parent, bool ignore_focus) {
199 _con_attach(con, parent, NULL, ignore_focus);
200 }
201
202 /*
203 * Detaches the given container from its current parent
204 *
205 */
206 void con_detach(Con *con) {
207 con_force_split_parents_redraw(con);
208 if (con->type == CT_FLOATING_CON) {
209 TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows);
210 TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
211 } else {
212 TAILQ_REMOVE(&(con->parent->nodes_head), con, nodes);
213 TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
214 }
215 }
216
217 /*
218 * Sets input focus to the given container. Will be updated in X11 in the next
219 * run of x_push_changes().
220 *
221 */
222 void con_focus(Con *con) {
223 assert(con != NULL);
224 DLOG("con_focus = %p\n", con);
225
226 /* 1: set focused-pointer to the new con */
227 /* 2: exchange the position of the container in focus stack of the parent all the way up */
228 TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
229 TAILQ_INSERT_HEAD(&(con->parent->focus_head), con, focused);
230 if (con->parent->parent != NULL)
231 con_focus(con->parent);
232
233 focused = con;
234 /* We can't blindly reset non-leaf containers since they might have
235 * other urgent children. Therefore we only reset leafs and propagate
236 * the changes upwards via con_update_parents_urgency() which does proper
237 * checks before resetting the urgency.
238 */
239 if (con->urgent && con_is_leaf(con)) {
240 con_set_urgency(con, false);
241 con_update_parents_urgency(con);
242 workspace_update_urgent_flag(con_get_workspace(con));
243 ipc_send_window_event("urgent", con);
244 }
245 }
246
247 /*
248 * Raise container to the top if it is floating or inside some floating
249 * container.
250 *
251 */
252 static void con_raise(Con *con) {
253 Con *floating = con_inside_floating(con);
254 if (floating) {
255 floating_raise_con(floating);
256 }
257 }
258
259 /*
260 * Sets input focus to the given container and raises it to the top.
261 *
262 */
263 void con_activate(Con *con) {
264 con_focus(con);
265 con_raise(con);
266 }
267
268 /*
269 * Closes the given container.
270 *
271 */
272 void con_close(Con *con, kill_window_t kill_window) {
273 assert(con != NULL);
274 DLOG("Closing con = %p.\n", con);
275
276 /* We never close output or root containers. */
277 if (con->type == CT_OUTPUT || con->type == CT_ROOT) {
278 DLOG("con = %p is of type %d, not closing anything.\n", con, con->type);
279 return;
280 }
281
282 if (con->type == CT_WORKSPACE) {
283 DLOG("con = %p is a workspace, closing all children instead.\n", con);
284 Con *child, *nextchild;
285 for (child = TAILQ_FIRST(&(con->focus_head)); child;) {
286 nextchild = TAILQ_NEXT(child, focused);
287 DLOG("killing child = %p.\n", child);
288 tree_close_internal(child, kill_window, false);
289 child = nextchild;
290 }
291
292 return;
293 }
294
295 tree_close_internal(con, kill_window, false);
296 }
297
298 /*
299 * Returns true when this node is a leaf node (has no children)
300 *
301 */
302 bool con_is_leaf(Con *con) {
303 return TAILQ_EMPTY(&(con->nodes_head));
304 }
305
306 /*
307 * Returns true when this con is a leaf node with a managed X11 window (e.g.,
308 * excluding dock containers)
309 */
310 bool con_has_managed_window(Con *con) {
311 return (con != NULL && con->window != NULL && con->window->id != XCB_WINDOW_NONE && con_get_workspace(con) != NULL);
312 }
313
314 /*
315 * Returns true if this node has regular or floating children.
316 *
317 */
318 bool con_has_children(Con *con) {
319 return (!con_is_leaf(con) || !TAILQ_EMPTY(&(con->floating_head)));
320 }
321
322 /*
323 * Returns true if a container should be considered split.
324 *
325 */
326 bool con_is_split(Con *con) {
327 if (con_is_leaf(con))
328 return false;
329
330 switch (con->layout) {
331 case L_DOCKAREA:
332 case L_OUTPUT:
333 return false;
334
335 default:
336 return true;
337 }
338 }
339
340 /*
341 * This will only return true for containers which have some parent with
342 * a tabbed / stacked parent of which they are not the currently focused child.
343 *
344 */
345 bool con_is_hidden(Con *con) {
346 Con *current = con;
347
348 /* ascend to the workspace level and memorize the highest-up container
349 * which is stacked or tabbed. */
350 while (current != NULL && current->type != CT_WORKSPACE) {
351 Con *parent = current->parent;
352 if (parent != NULL && (parent->layout == L_TABBED || parent->layout == L_STACKED)) {
353 if (TAILQ_FIRST(&(parent->focus_head)) != current)
354 return true;
355 }
356
357 current = parent;
358 }
359
360 return false;
361 }
362
363 /*
364 * Returns whether the container or any of its children is sticky.
365 *
366 */
367 bool con_is_sticky(Con *con) {
368 if (con->sticky)
369 return true;
370
371 Con *child;
372 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
373 if (con_is_sticky(child))
374 return true;
375 }
376
377 return false;
378 }
379
380 /*
381 * Returns true if this node accepts a window (if the node swallows windows,
382 * it might already have swallowed enough and cannot hold any more).
383 *
384 */
385 bool con_accepts_window(Con *con) {
386 /* 1: workspaces never accept direct windows */
387 if (con->type == CT_WORKSPACE)
388 return false;
389
390 if (con_is_split(con)) {
391 DLOG("container %p does not accept windows, it is a split container.\n", con);
392 return false;
393 }
394
395 /* TODO: if this is a swallowing container, we need to check its max_clients */
396 return (con->window == NULL);
397 }
398
399 /*
400 * Gets the output container (first container with CT_OUTPUT in hierarchy) this
401 * node is on.
402 *
403 */
404 Con *con_get_output(Con *con) {
405 Con *result = con;
406 while (result != NULL && result->type != CT_OUTPUT)
407 result = result->parent;
408 /* We must be able to get an output because focus can never be set higher
409 * in the tree (root node cannot be focused). */
410 assert(result != NULL);
411 return result;
412 }
413
414 /*
415 * Gets the workspace container this node is on.
416 *
417 */
418 Con *con_get_workspace(Con *con) {
419 Con *result = con;
420 while (result != NULL && result->type != CT_WORKSPACE)
421 result = result->parent;
422 return result;
423 }
424
425 /*
426 * Searches parents of the given 'con' until it reaches one with the specified
427 * 'orientation'. Aborts when it comes across a floating_con.
428 *
429 */
430 Con *con_parent_with_orientation(Con *con, orientation_t orientation) {
431 DLOG("Searching for parent of Con %p with orientation %d\n", con, orientation);
432 Con *parent = con->parent;
433 if (parent->type == CT_FLOATING_CON)
434 return NULL;
435 while (con_orientation(parent) != orientation) {
436 DLOG("Need to go one level further up\n");
437 parent = parent->parent;
438 /* Abort when we reach a floating con, or an output con */
439 if (parent &&
440 (parent->type == CT_FLOATING_CON ||
441 parent->type == CT_OUTPUT ||
442 (parent->parent && parent->parent->type == CT_OUTPUT)))
443 parent = NULL;
444 if (parent == NULL)
445 break;
446 }
447 DLOG("Result: %p\n", parent);
448 return parent;
449 }
450
451 /*
452 * helper data structure for the breadth-first-search in
453 * con_get_fullscreen_con()
454 *
455 */
456 struct bfs_entry {
457 Con *con;
458
459 TAILQ_ENTRY(bfs_entry)
460 entries;
461 };
462
463 /*
464 * Returns the first fullscreen node below this node.
465 *
466 */
467 Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) {
468 Con *current, *child;
469
470 /* TODO: is breadth-first-search really appropriate? (check as soon as
471 * fullscreen levels and fullscreen for containers is implemented) */
472 TAILQ_HEAD(bfs_head, bfs_entry)
473 bfs_head = TAILQ_HEAD_INITIALIZER(bfs_head);
474
475 struct bfs_entry *entry = smalloc(sizeof(struct bfs_entry));
476 entry->con = con;
477 TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
478
479 while (!TAILQ_EMPTY(&bfs_head)) {
480 entry = TAILQ_FIRST(&bfs_head);
481 current = entry->con;
482 if (current != con && current->fullscreen_mode == fullscreen_mode) {
483 /* empty the queue */
484 while (!TAILQ_EMPTY(&bfs_head)) {
485 entry = TAILQ_FIRST(&bfs_head);
486 TAILQ_REMOVE(&bfs_head, entry, entries);
487 free(entry);
488 }
489 return current;
490 }
491
492 TAILQ_REMOVE(&bfs_head, entry, entries);
493 free(entry);
494
495 TAILQ_FOREACH(child, &(current->nodes_head), nodes) {
496 entry = smalloc(sizeof(struct bfs_entry));
497 entry->con = child;
498 TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
499 }
500
501 TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
502 entry = smalloc(sizeof(struct bfs_entry));
503 entry->con = child;
504 TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
505 }
506 }
507
508 return NULL;
509 }
510
511 /*
512 * Returns the fullscreen node that covers the given workspace if it exists.
513 * This is either a CF_GLOBAL fullscreen container anywhere or a CF_OUTPUT
514 * fullscreen container in the workspace.
515 *
516 */
517 Con *con_get_fullscreen_covering_ws(Con *ws) {
518 if (!ws) {
519 return NULL;
520 }
521 Con *fs = con_get_fullscreen_con(croot, CF_GLOBAL);
522 if (!fs) {
523 return con_get_fullscreen_con(ws, CF_OUTPUT);
524 }
525 return fs;
526 }
527
528 /*
529 * Returns true if the container is internal, such as __i3_scratch
530 *
531 */
532 bool con_is_internal(Con *con) {
533 return (con->name[0] == '_' && con->name[1] == '_');
534 }
535
536 /*
537 * Returns true if the node is floating.
538 *
539 */
540 bool con_is_floating(Con *con) {
541 assert(con != NULL);
542 DLOG("checking if con %p is floating\n", con);
543 return (con->floating >= FLOATING_AUTO_ON);
544 }
545
546 /*
547 * Returns true if the container is a docked container.
548 *
549 */
550 bool con_is_docked(Con *con) {
551 if (con->parent == NULL)
552 return false;
553
554 if (con->parent->type == CT_DOCKAREA)
555 return true;
556
557 return con_is_docked(con->parent);
558 }
559
560 /*
561 * Checks if the given container is either floating or inside some floating
562 * container. It returns the FLOATING_CON container.
563 *
564 */
565 Con *con_inside_floating(Con *con) {
566 assert(con != NULL);
567 if (con->type == CT_FLOATING_CON)
568 return con;
569
570 if (con->floating >= FLOATING_AUTO_ON)
571 return con->parent;
572
573 if (con->type == CT_WORKSPACE || con->type == CT_OUTPUT)
574 return NULL;
575
576 return con_inside_floating(con->parent);
577 }
578
579 /*
580 * Checks if the given container is inside a focused container.
581 *
582 */
583 bool con_inside_focused(Con *con) {
584 if (con == focused)
585 return true;
586 if (!con->parent)
587 return false;
588 return con_inside_focused(con->parent);
589 }
590
591 /*
592 * Checks if the container has the given parent as an actual parent.
593 *
594 */
595 bool con_has_parent(Con *con, Con *parent) {
596 Con *current = con->parent;
597 if (current == NULL) {
598 return false;
599 }
600
601 if (current == parent) {
602 return true;
603 }
604
605 return con_has_parent(current, parent);
606 }
607
608 /*
609 * Returns the container with the given client window ID or NULL if no such
610 * container exists.
611 *
612 */
613 Con *con_by_window_id(xcb_window_t window) {
614 Con *con;
615 TAILQ_FOREACH(con, &all_cons, all_cons)
616 if (con->window != NULL && con->window->id == window)
617 return con;
618 return NULL;
619 }
620
621 /*
622 * Returns the container with the given container ID or NULL if no such
623 * container exists.
624 *
625 */
626 Con *con_by_con_id(long target) {
627 Con *con;
628 TAILQ_FOREACH(con, &all_cons, all_cons) {
629 if (con == (Con *)target) {
630 return con;
631 }
632 }
633
634 return NULL;
635 }
636
637 /*
638 * Returns true if the given container (still) exists.
639 * This can be used, e.g., to make sure a container hasn't been closed in the meantime.
640 *
641 */
642 bool con_exists(Con *con) {
643 return con_by_con_id((long)con) != NULL;
644 }
645
646 /*
647 * Returns the container with the given frame ID or NULL if no such container
648 * exists.
649 *
650 */
651 Con *con_by_frame_id(xcb_window_t frame) {
652 Con *con;
653 TAILQ_FOREACH(con, &all_cons, all_cons)
654 if (con->frame.id == frame)
655 return con;
656 return NULL;
657 }
658
659 /*
660 * Returns the container with the given mark or NULL if no such container
661 * exists.
662 *
663 */
664 Con *con_by_mark(const char *mark) {
665 Con *con;
666 TAILQ_FOREACH(con, &all_cons, all_cons) {
667 if (con_has_mark(con, mark))
668 return con;
669 }
670
671 return NULL;
672 }
673
674 /*
675 * Returns true if and only if the given containers holds the mark.
676 *
677 */
678 bool con_has_mark(Con *con, const char *mark) {
679 mark_t *current;
680 TAILQ_FOREACH(current, &(con->marks_head), marks) {
681 if (strcmp(current->name, mark) == 0)
682 return true;
683 }
684
685 return false;
686 }
687
688 /*
689 * Toggles the mark on a container.
690 * If the container already has this mark, the mark is removed.
691 * Otherwise, the mark is assigned to the container.
692 *
693 */
694 void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode) {
695 assert(con != NULL);
696 DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con);
697
698 if (con_has_mark(con, mark)) {
699 con_unmark(con, mark);
700 } else {
701 con_mark(con, mark, mode);
702 }
703 }
704
705 /*
706 * Assigns a mark to the container.
707 *
708 */
709 void con_mark(Con *con, const char *mark, mark_mode_t mode) {
710 assert(con != NULL);
711 DLOG("Setting mark \"%s\" on con = %p.\n", mark, con);
712
713 con_unmark(NULL, mark);
714 if (mode == MM_REPLACE) {
715 DLOG("Removing all existing marks on con = %p.\n", con);
716
717 mark_t *current;
718 while (!TAILQ_EMPTY(&(con->marks_head))) {
719 current = TAILQ_FIRST(&(con->marks_head));
720 con_unmark(con, current->name);
721 }
722 }
723
724 mark_t *new = scalloc(1, sizeof(mark_t));
725 new->name = sstrdup(mark);
726 TAILQ_INSERT_TAIL(&(con->marks_head), new, marks);
727 ipc_send_window_event("mark", con);
728
729 con->mark_changed = true;
730 }
731
732 /*
733 * Removes marks from containers.
734 * If con is NULL, all containers are considered.
735 * If name is NULL, this removes all existing marks.
736 * Otherwise, it will only remove the given mark (if it is present).
737 *
738 */
739 void con_unmark(Con *con, const char *name) {
740 Con *current;
741 if (name == NULL) {
742 DLOG("Unmarking all containers.\n");
743 TAILQ_FOREACH(current, &all_cons, all_cons) {
744 if (con != NULL && current != con)
745 continue;
746
747 if (TAILQ_EMPTY(&(current->marks_head)))
748 continue;
749
750 mark_t *mark;
751 while (!TAILQ_EMPTY(&(current->marks_head))) {
752 mark = TAILQ_FIRST(&(current->marks_head));
753 FREE(mark->name);
754 TAILQ_REMOVE(&(current->marks_head), mark, marks);
755 FREE(mark);
756
757 ipc_send_window_event("mark", current);
758 }
759
760 current->mark_changed = true;
761 }
762 } else {
763 DLOG("Removing mark \"%s\".\n", name);
764 current = (con == NULL) ? con_by_mark(name) : con;
765 if (current == NULL) {
766 DLOG("No container found with this mark, so there is nothing to do.\n");
767 return;
768 }
769
770 DLOG("Found mark on con = %p. Removing it now.\n", current);
771 current->mark_changed = true;
772
773 mark_t *mark;
774 TAILQ_FOREACH(mark, &(current->marks_head), marks) {
775 if (strcmp(mark->name, name) != 0)
776 continue;
777
778 FREE(mark->name);
779 TAILQ_REMOVE(&(current->marks_head), mark, marks);
780 FREE(mark);
781
782 ipc_send_window_event("mark", current);
783 break;
784 }
785 }
786 }
787
788 /*
789 * Returns the first container below 'con' which wants to swallow this window
790 * TODO: priority
791 *
792 */
793 Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
794 Con *child;
795 Match *match;
796 //DLOG("searching con for window %p starting at con %p\n", window, con);
797 //DLOG("class == %s\n", window->class_class);
798
799 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
800 TAILQ_FOREACH(match, &(child->swallow_head), matches) {
801 if (!match_matches_window(match, window))
802 continue;
803 if (store_match != NULL)
804 *store_match = match;
805 return child;
806 }
807 Con *result = con_for_window(child, window, store_match);
808 if (result != NULL)
809 return result;
810 }
811
812 TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
813 TAILQ_FOREACH(match, &(child->swallow_head), matches) {
814 if (!match_matches_window(match, window))
815 continue;
816 if (store_match != NULL)
817 *store_match = match;
818 return child;
819 }
820 Con *result = con_for_window(child, window, store_match);
821 if (result != NULL)
822 return result;
823 }
824
825 return NULL;
826 }
827
828 static int num_focus_heads(Con *con) {
829 int focus_heads = 0;
830
831 Con *current;
832 TAILQ_FOREACH(current, &(con->focus_head), focused) {
833 focus_heads++;
834 }
835
836 return focus_heads;
837 }
838
839 /*
840 * Iterate over the container's focus stack and return an array with the
841 * containers inside it, ordered from higher focus order to lowest.
842 *
843 */
844 Con **get_focus_order(Con *con) {
845 const int focus_heads = num_focus_heads(con);
846 Con **focus_order = smalloc(focus_heads * sizeof(Con *));
847 Con *current;
848 int idx = 0;
849 TAILQ_FOREACH(current, &(con->focus_head), focused) {
850 assert(idx < focus_heads);
851 focus_order[idx++] = current;
852 }
853
854 return focus_order;
855 }
856
857 /*
858 * Clear the container's focus stack and re-add it using the provided container
859 * array. The function doesn't check if the provided array contains the same
860 * containers with the previous focus stack but will not add floating containers
861 * in the new focus stack if container is not a workspace.
862 *
863 */
864 void set_focus_order(Con *con, Con **focus_order) {
865 int focus_heads = 0;
866 while (!TAILQ_EMPTY(&(con->focus_head))) {
867 Con *current = TAILQ_FIRST(&(con->focus_head));
868
869 TAILQ_REMOVE(&(con->focus_head), current, focused);
870 focus_heads++;
871 }
872
873 for (int idx = 0; idx < focus_heads; idx++) {
874 /* Useful when encapsulating a workspace. */
875 if (con->type != CT_WORKSPACE && con_inside_floating(focus_order[idx])) {
876 focus_heads++;
877 continue;
878 }
879
880 TAILQ_INSERT_TAIL(&(con->focus_head), focus_order[idx], focused);
881 }
882 }
883
884 /*
885 * Returns the number of children of this container.
886 *
887 */
888 int con_num_children(Con *con) {
889 Con *child;
890 int children = 0;
891
892 TAILQ_FOREACH(child, &(con->nodes_head), nodes)
893 children++;
894
895 return children;
896 }
897
898 /*
899 * Returns the number of visible non-floating children of this container.
900 * For example, if the container contains a hsplit which has two children,
901 * this will return 2 instead of 1.
902 */
903 int con_num_visible_children(Con *con) {
904 if (con == NULL)
905 return 0;
906
907 int children = 0;
908 Con *current = NULL;
909 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
910 /* Visible leaf nodes are a child. */
911 if (!con_is_hidden(current) && con_is_leaf(current))
912 children++;
913 /* All other containers need to be recursed. */
914 else
915 children += con_num_visible_children(current);
916 }
917
918 return children;
919 }
920
921 /*
922 * Count the number of windows (i.e., leaf containers).
923 *
924 */
925 int con_num_windows(Con *con) {
926 if (con == NULL)
927 return 0;
928
929 if (con_has_managed_window(con))
930 return 1;
931
932 int num = 0;
933 Con *current = NULL;
934 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
935 num += con_num_windows(current);
936 }
937
938 TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
939 num += con_num_windows(current);
940 }
941
942 return num;
943 }
944
945 /*
946 * Updates the percent attribute of the children of the given container. This
947 * function needs to be called when a window is added or removed from a
948 * container.
949 *
950 */
951 void con_fix_percent(Con *con) {
952 Con *child;
953 int children = con_num_children(con);
954
955 // calculate how much we have distributed and how many containers
956 // with a percentage set we have
957 double total = 0.0;
958 int children_with_percent = 0;
959 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
960 if (child->percent > 0.0) {
961 total += child->percent;
962 ++children_with_percent;
963 }
964 }
965
966 // if there were children without a percentage set, set to a value that
967 // will make those children proportional to all others
968 if (children_with_percent != children) {
969 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
970 if (child->percent <= 0.0) {
971 if (children_with_percent == 0) {
972 total += (child->percent = 1.0);
973 } else {
974 total += (child->percent = total / children_with_percent);
975 }
976 }
977 }
978 }
979
980 // if we got a zero, just distribute the space equally, otherwise
981 // distribute according to the proportions we got
982 if (total == 0.0) {
983 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
984 child->percent = 1.0 / children;
985 }
986 } else if (total != 1.0) {
987 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
988 child->percent /= total;
989 }
990 }
991 }
992
993 /*
994 * Toggles fullscreen mode for the given container. If there already is a
995 * fullscreen container on this workspace, fullscreen will be disabled and then
996 * enabled for the container the user wants to have in fullscreen mode.
997 *
998 */
999 void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
1000 if (con->type == CT_WORKSPACE) {
1001 DLOG("You cannot make a workspace fullscreen.\n");
1002 return;
1003 }
1004
1005 DLOG("toggling fullscreen for %p / %s\n", con, con->name);
1006
1007 if (con->fullscreen_mode == CF_NONE)
1008 con_enable_fullscreen(con, fullscreen_mode);
1009 else
1010 con_disable_fullscreen(con);
1011 }
1012
1013 /*
1014 * Sets the specified fullscreen mode for the given container, sends the
1015 * “fullscreen_mode” event and changes the XCB fullscreen property of the
1016 * container’s window, if any.
1017 *
1018 */
1019 static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode) {
1020 con->fullscreen_mode = fullscreen_mode;
1021
1022 DLOG("mode now: %d\n", con->fullscreen_mode);
1023
1024 /* Send an ipc window "fullscreen_mode" event */
1025 ipc_send_window_event("fullscreen_mode", con);
1026
1027 /* update _NET_WM_STATE if this container has a window */
1028 /* TODO: when a window is assigned to a container which is already
1029 * fullscreened, this state needs to be pushed to the client, too */
1030 if (con->window == NULL)
1031 return;
1032
1033 if (con->fullscreen_mode != CF_NONE) {
1034 DLOG("Setting _NET_WM_STATE_FULLSCREEN for con = %p / window = %d.\n", con, con->window->id);
1035 xcb_add_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_FULLSCREEN);
1036 } else {
1037 DLOG("Removing _NET_WM_STATE_FULLSCREEN for con = %p / window = %d.\n", con, con->window->id);
1038 xcb_remove_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_FULLSCREEN);
1039 }
1040 }
1041
1042 /*
1043 * Enables fullscreen mode for the given container, if necessary.
1044 *
1045 * If the container’s mode is already CF_OUTPUT or CF_GLOBAL, the container is
1046 * kept fullscreen but its mode is set to CF_GLOBAL and CF_OUTPUT,
1047 * respectively.
1048 *
1049 * Other fullscreen containers will be disabled first, if they hide the new
1050 * one.
1051 *
1052 */
1053 void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) {
1054 if (con->type == CT_WORKSPACE) {
1055 DLOG("You cannot make a workspace fullscreen.\n");
1056 return;
1057 }
1058
1059 assert(fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT);
1060
1061 if (fullscreen_mode == CF_GLOBAL)
1062 DLOG("enabling global fullscreen for %p / %s\n", con, con->name);
1063 else
1064 DLOG("enabling fullscreen for %p / %s\n", con, con->name);
1065
1066 if (con->fullscreen_mode == fullscreen_mode) {
1067 DLOG("fullscreen already enabled for %p / %s\n", con, con->name);
1068 return;
1069 }
1070
1071 Con *con_ws = con_get_workspace(con);
1072
1073 /* Disable any fullscreen container that would conflict the new one. */
1074 Con *fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
1075 if (fullscreen == NULL)
1076 fullscreen = con_get_fullscreen_con(con_ws, CF_OUTPUT);
1077 if (fullscreen != NULL)
1078 con_disable_fullscreen(fullscreen);
1079
1080 /* Set focus to new fullscreen container. Unless in global fullscreen mode
1081 * and on another workspace restore focus afterwards.
1082 * Switch to the container’s workspace if mode is global. */
1083 Con *cur_ws = con_get_workspace(focused);
1084 Con *old_focused = focused;
1085 if (fullscreen_mode == CF_GLOBAL && cur_ws != con_ws)
1086 workspace_show(con_ws);
1087 con_activate(con);
1088 if (fullscreen_mode != CF_GLOBAL && cur_ws != con_ws)
1089 con_activate(old_focused);
1090
1091 con_set_fullscreen_mode(con, fullscreen_mode);
1092 }
1093
1094 /*
1095 * Disables fullscreen mode for the given container regardless of the mode, if
1096 * necessary.
1097 *
1098 */
1099 void con_disable_fullscreen(Con *con) {
1100 if (con->type == CT_WORKSPACE) {
1101 DLOG("You cannot make a workspace fullscreen.\n");
1102 return;
1103 }
1104
1105 DLOG("disabling fullscreen for %p / %s\n", con, con->name);
1106
1107 if (con->fullscreen_mode == CF_NONE) {
1108 DLOG("fullscreen already disabled for %p / %s\n", con, con->name);
1109 return;
1110 }
1111
1112 con_set_fullscreen_mode(con, CF_NONE);
1113 }
1114
1115 static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fix_coordinates, bool dont_warp, bool ignore_focus, bool fix_percentage) {
1116 Con *orig_target = target;
1117
1118 /* Prevent moving if this would violate the fullscreen focus restrictions. */
1119 Con *target_ws = con_get_workspace(target);
1120 if (!ignore_focus && !con_fullscreen_permits_focusing(target_ws)) {
1121 LOG("Cannot move out of a fullscreen container.\n");
1122 return false;
1123 }
1124
1125 if (con_is_floating(con)) {
1126 DLOG("Container is floating, using parent instead.\n");
1127 con = con->parent;
1128 }
1129
1130 Con *source_ws = con_get_workspace(con);
1131
1132 if (con->type == CT_WORKSPACE) {
1133 /* Re-parent all of the old workspace's floating windows. */
1134 Con *child;
1135 while (!TAILQ_EMPTY(&(source_ws->floating_head))) {
1136 child = TAILQ_FIRST(&(source_ws->floating_head));
1137 con_move_to_workspace(child, target_ws, true, true, false);
1138 }
1139
1140 /* If there are no non-floating children, ignore the workspace. */
1141 if (con_is_leaf(con))
1142 return false;
1143
1144 con = workspace_encapsulate(con);
1145 if (con == NULL) {
1146 ELOG("Workspace failed to move its contents into a container!\n");
1147 return false;
1148 }
1149 }
1150
1151 /* Save the urgency state so that we can restore it. */
1152 bool urgent = con->urgent;
1153
1154 /* Save the current workspace. So we can call workspace_show() by the end
1155 * of this function. */
1156 Con *current_ws = con_get_workspace(focused);
1157
1158 Con *source_output = con_get_output(con),
1159 *dest_output = con_get_output(target_ws);
1160
1161 /* 1: save the container which is going to be focused after the current
1162 * container is moved away */
1163 Con *focus_next = NULL;
1164 if (!ignore_focus && source_ws == current_ws) {
1165 focus_next = con_descend_focused(source_ws);
1166 if (focus_next == con || con_has_parent(focus_next, con)) {
1167 focus_next = con_next_focused(con);
1168 }
1169 }
1170
1171 /* 2: we go up one level, but only when target is a normal container */
1172 if (target->type != CT_WORKSPACE) {
1173 DLOG("target originally = %p / %s / type %d\n", target, target->name, target->type);
1174 target = target->parent;
1175 }
1176
1177 /* 3: if the original target is the direct child of a floating container, we
1178 * can't move con next to it - floating containers have only one child - so
1179 * we get the workspace instead. */
1180 if (target->type == CT_FLOATING_CON) {
1181 DLOG("floatingcon, going up even further\n");
1182 orig_target = target;
1183 target = target->parent;
1184 }
1185
1186 if (con->type == CT_FLOATING_CON) {
1187 Con *ws = con_get_workspace(target);
1188 DLOG("This is a floating window, using workspace %p / %s\n", ws, ws->name);
1189 target = ws;
1190 }
1191
1192 if (source_output != dest_output) {
1193 /* Take the relative coordinates of the current output, then add them
1194 * to the coordinate space of the correct output */
1195 if (fix_coordinates && con->type == CT_FLOATING_CON) {
1196 floating_fix_coordinates(con, &(source_output->rect), &(dest_output->rect));
1197 } else
1198 DLOG("Not fixing coordinates, fix_coordinates flag = %d\n", fix_coordinates);
1199 }
1200
1201 /* If moving a fullscreen container and the destination already has a
1202 * fullscreen window on it, un-fullscreen the target's fullscreen con. */
1203 Con *fullscreen = con_get_fullscreen_con(target_ws, CF_OUTPUT);
1204 if (con->fullscreen_mode != CF_NONE && fullscreen != NULL) {
1205 con_toggle_fullscreen(fullscreen, CF_OUTPUT);
1206 fullscreen = NULL;
1207 }
1208
1209 DLOG("Re-attaching container to %p / %s\n", target, target->name);
1210 /* 4: re-attach the con to the parent of this focused container */
1211 Con *parent = con->parent;
1212 con_detach(con);
1213 _con_attach(con, target, behind_focused ? NULL : orig_target, !behind_focused);
1214
1215 /* 5: fix the percentages */
1216 if (fix_percentage) {
1217 con_fix_percent(parent);
1218 con->percent = 0.0;
1219 con_fix_percent(target);
1220 }
1221
1222 /* 6: focus the con on the target workspace, but only within that
1223 * workspace, that is, don’t move focus away if the target workspace is
1224 * invisible.
1225 * We don’t focus the con for i3 pseudo workspaces like __i3_scratch and
1226 * we don’t focus when there is a fullscreen con on that workspace. We
1227 * also don't do it if the caller requested to ignore focus. */
1228 if (!ignore_focus && !con_is_internal(target_ws) && !fullscreen) {
1229 /* We need to save the focused workspace on the output in case the
1230 * new workspace is hidden and it's necessary to immediately switch
1231 * back to the originally-focused workspace. */
1232 Con *old_focus_ws = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
1233 Con *old_focus = focused;
1234 con_activate(con_descend_focused(con));
1235
1236 if (old_focus_ws == current_ws && old_focus->type != CT_WORKSPACE) {
1237 /* Restore focus to the currently focused container. */
1238 con_activate(old_focus);
1239 } else if (con_get_workspace(focused) != old_focus_ws) {
1240 /* Restore focus if the output's focused workspace has changed. */
1241 con_focus(con_descend_focused(old_focus_ws));
1242 }
1243 }
1244
1245 /* 7: when moving to another workspace, we leave the focus on the current
1246 * workspace. (see also #809) */
1247 if (!ignore_focus) {
1248 workspace_show(current_ws);
1249 if (dont_warp) {
1250 DLOG("x_set_warp_to(NULL) because dont_warp is set\n");
1251 x_set_warp_to(NULL);
1252 }
1253 }
1254
1255 /* Set focus only if con was on current workspace before moving.
1256 * Otherwise we would give focus to some window on different workspace. */
1257 if (focus_next)
1258 con_activate(con_descend_focused(focus_next));
1259
1260 /* 8. If anything within the container is associated with a startup sequence,
1261 * delete it so child windows won't be created on the old workspace. */
1262 struct Startup_Sequence *sequence;
1263 xcb_get_property_cookie_t cookie;
1264 xcb_get_property_reply_t *startup_id_reply;
1265
1266 if (!con_is_leaf(con)) {
1267 Con *child;
1268 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
1269 if (!child->window)
1270 continue;
1271
1272 cookie = xcb_get_property(conn, false, child->window->id,
1273 A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
1274 startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
1275
1276 sequence = startup_sequence_get(child->window, startup_id_reply, true);
1277 if (sequence != NULL)
1278 startup_sequence_delete(sequence);
1279 }
1280 }
1281
1282 if (con->window) {
1283 cookie = xcb_get_property(conn, false, con->window->id,
1284 A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
1285 startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
1286
1287 sequence = startup_sequence_get(con->window, startup_id_reply, true);
1288 if (sequence != NULL)
1289 startup_sequence_delete(sequence);
1290 }
1291
1292 /* 9. If the container was marked urgent, move the urgency hint. */
1293 if (urgent) {
1294 workspace_update_urgent_flag(source_ws);
1295 con_set_urgency(con, true);
1296 }
1297
1298 /* Ensure the container will be redrawn. */
1299 FREE(con->deco_render_params);
1300
1301 CALL(parent, on_remove_child);
1302
1303 ipc_send_window_event("move", con);
1304 ewmh_update_wm_desktop();
1305 return true;
1306 }
1307
1308 /*
1309 * Moves the given container to the given mark.
1310 *
1311 */
1312 bool con_move_to_mark(Con *con, const char *mark) {
1313 Con *target = con_by_mark(mark);
1314 if (target == NULL) {
1315 DLOG("found no container with mark \"%s\"\n", mark);
1316 return false;
1317 }
1318
1319 /* For floating target containers, we just send the window to the same workspace. */
1320 if (con_is_floating(target)) {
1321 DLOG("target container is floating, moving container to target's workspace.\n");
1322 con_move_to_workspace(con, con_get_workspace(target), true, false, false);
1323 return true;
1324 }
1325
1326 if (target->type == CT_WORKSPACE) {
1327 DLOG("target container is a workspace, simply moving the container there.\n");
1328 con_move_to_workspace(con, target, true, false, false);
1329 return true;
1330 }
1331
1332 /* For split containers, we use the currently focused container within it.
1333 * This allows setting marks on, e.g., tabbed containers which will move
1334 * con to a new tab behind the focused tab. */
1335 if (con_is_split(target)) {
1336 DLOG("target is a split container, descending to the currently focused child.\n");
1337 target = TAILQ_FIRST(&(target->focus_head));
1338 }
1339
1340 if (con == target || con_has_parent(target, con)) {
1341 DLOG("cannot move the container to or inside itself, aborting.\n");
1342 return false;
1343 }
1344
1345 return _con_move_to_con(con, target, false, true, false, false, true);
1346 }
1347
1348 /*
1349 * Moves the given container to the currently focused container on the given
1350 * workspace.
1351 *
1352 * The fix_coordinates flag will translate the current coordinates (offset from
1353 * the monitor position basically) to appropriate coordinates on the
1354 * destination workspace.
1355 * Not enabling this behaviour comes in handy when this function gets called by
1356 * floating_maybe_reassign_ws, which will only "move" a floating window when it
1357 * *already* changed its coordinates to a different output.
1358 *
1359 * The dont_warp flag disables pointer warping and will be set when this
1360 * function is called while dragging a floating window.
1361 *
1362 * If ignore_focus is set, the container will be moved without modifying focus
1363 * at all.
1364 *
1365 * TODO: is there a better place for this function?
1366 *
1367 */
1368 void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp, bool ignore_focus) {
1369 assert(workspace->type == CT_WORKSPACE);
1370
1371 Con *source_ws = con_get_workspace(con);
1372 if (workspace == source_ws) {
1373 DLOG("Not moving, already there\n");
1374 return;
1375 }
1376
1377 Con *target = con_descend_focused(workspace);
1378 _con_move_to_con(con, target, true, fix_coordinates, dont_warp, ignore_focus, true);
1379 }
1380
1381 /*
1382 * Moves the given container to the currently focused container on the
1383 * visible workspace on the given output.
1384 *
1385 */
1386 void con_move_to_output(Con *con, Output *output, bool fix_coordinates) {
1387 Con *ws = NULL;
1388 GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
1389 assert(ws != NULL);
1390 DLOG("Moving con %p to output %s\n", con, output_primary_name(output));
1391 con_move_to_workspace(con, ws, fix_coordinates, false, false);
1392 }
1393
1394 /*
1395 * Moves the given container to the currently focused container on the
1396 * visible workspace on the output specified by the given name.
1397 * The current output for the container is used to resolve relative names
1398 * such as left, right, up, down.
1399 *
1400 */
1401 bool con_move_to_output_name(Con *con, const char *name, bool fix_coordinates) {
1402 Output *current_output = get_output_for_con(con);
1403 assert(current_output != NULL);
1404
1405 Output *output = get_output_from_string(current_output, name);
1406 if (output == NULL) {
1407 ELOG("Could not find output \"%s\"\n", name);
1408 return false;
1409 }
1410
1411 con_move_to_output(con, output, fix_coordinates);
1412 return true;
1413 }
1414
1415 /*
1416 * Returns the orientation of the given container (for stacked containers,
1417 * vertical orientation is used regardless of the actual orientation of the
1418 * container).
1419 *
1420 */
1421 orientation_t con_orientation(Con *con) {
1422 switch (con->layout) {
1423 case L_SPLITV:
1424 /* stacking containers behave like they are in vertical orientation */
1425 case L_STACKED:
1426 return VERT;
1427
1428 case L_SPLITH:
1429 /* tabbed containers behave like they are in vertical orientation */
1430 case L_TABBED:
1431 return HORIZ;
1432
1433 case L_DEFAULT:
1434 ELOG("Someone called con_orientation() on a con with L_DEFAULT, this is a bug in the code.\n");
1435 assert(false);
1436
1437 case L_DOCKAREA:
1438 case L_OUTPUT:
1439 ELOG("con_orientation() called on dockarea/output (%d) container %p\n", con->layout, con);
1440 assert(false);
1441 }
1442 /* should not be reached */
1443 assert(false);
1444 }
1445
1446 /*
1447 * Returns the container which will be focused next when the given container
1448 * is not available anymore. Called in tree_close_internal and con_move_to_workspace
1449 * to properly restore focus.
1450 *
1451 */
1452 Con *con_next_focused(Con *con) {
1453 /* dock clients cannot be focused, so we focus the workspace instead */
1454 if (con->parent->type == CT_DOCKAREA) {
1455 DLOG("selecting workspace for dock client\n");
1456 return con_descend_focused(output_get_content(con->parent->parent));
1457 }
1458 if (con_is_floating(con)) {
1459 con = con->parent;
1460 }
1461
1462 /* if 'con' is not the first entry in the focus stack, use the first one as
1463 * it’s currently focused already */
1464 Con *next = TAILQ_FIRST(&(con->parent->focus_head));
1465 if (next != con) {
1466 DLOG("Using first entry %p\n", next);
1467 } else {
1468 /* try to focus the next container on the same level as this one or fall
1469 * back to its parent */
1470 if (!(next = TAILQ_NEXT(con, focused))) {
1471 next = con->parent;
1472 }
1473 }
1474
1475 /* now go down the focus stack as far as
1476 * possible, excluding the current container */
1477 while (!TAILQ_EMPTY(&(next->focus_head)) && TAILQ_FIRST(&(next->focus_head)) != con) {
1478 next = TAILQ_FIRST(&(next->focus_head));
1479 }
1480
1481 if (con->type == CT_FLOATING_CON && next != con->parent) {
1482 next = con_descend_focused(next);
1483 }
1484
1485 return next;
1486 }
1487
1488 /*
1489 * Get the next/previous container in the specified orientation. This may
1490 * travel up until it finds a container with suitable orientation.
1491 *
1492 */
1493 Con *con_get_next(Con *con, char way, orientation_t orientation) {
1494 DLOG("con_get_next(way=%c, orientation=%d)\n", way, orientation);
1495 /* 1: get the first parent with the same orientation */
1496 Con *cur = con;
1497 while (con_orientation(cur->parent) != orientation) {
1498 DLOG("need to go one level further up\n");
1499 if (cur->parent->type == CT_WORKSPACE) {
1500 LOG("that's a workspace, we can't go further up\n");
1501 return NULL;
1502 }
1503 cur = cur->parent;
1504 }
1505
1506 /* 2: chose next (or previous) */
1507 Con *next;
1508 if (way == 'n') {
1509 next = TAILQ_NEXT(cur, nodes);
1510 /* if we are at the end of the list, we need to wrap */
1511 if (next == TAILQ_END(&(parent->nodes_head)))
1512 return NULL;
1513 } else {
1514 next = TAILQ_PREV(cur, nodes_head, nodes);
1515 /* if we are at the end of the list, we need to wrap */
1516 if (next == TAILQ_END(&(cur->nodes_head)))
1517 return NULL;
1518 }
1519 DLOG("next = %p\n", next);
1520
1521 return next;
1522 }
1523
1524 /*
1525 * Returns the focused con inside this client, descending the tree as far as
1526 * possible. This comes in handy when attaching a con to a workspace at the
1527 * currently focused position, for example.
1528 *
1529 */
1530 Con *con_descend_focused(Con *con) {
1531 Con *next = con;
1532 while (next != focused && !TAILQ_EMPTY(&(next->focus_head)))
1533 next = TAILQ_FIRST(&(next->focus_head));
1534 return next;
1535 }
1536
1537 /*
1538 * Returns the focused con inside this client, descending the tree as far as
1539 * possible. This comes in handy when attaching a con to a workspace at the
1540 * currently focused position, for example.
1541 *
1542 * Works like con_descend_focused but considers only tiling cons.
1543 *
1544 */
1545 Con *con_descend_tiling_focused(Con *con) {
1546 Con *next = con;
1547 Con *before;
1548 Con *child;
1549 if (next == focused)
1550 return next;
1551 do {
1552 before = next;
1553 TAILQ_FOREACH(child, &(next->focus_head), focused) {
1554 if (child->type == CT_FLOATING_CON)
1555 continue;
1556
1557 next = child;
1558 break;
1559 }
1560 } while (before != next && next != focused);
1561 return next;
1562 }
1563
1564 /*
1565 * Returns the leftmost, rightmost, etc. container in sub-tree. For example, if
1566 * direction is D_LEFT, then we return the rightmost container and if direction
1567 * is D_RIGHT, we return the leftmost container. This is because if we are
1568 * moving D_LEFT, and thus want the rightmost container.
1569 *
1570 */
1571 Con *con_descend_direction(Con *con, direction_t direction) {
1572 Con *most = NULL;
1573 Con *current;
1574 int orientation = con_orientation(con);
1575 DLOG("con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
1576 if (direction == D_LEFT || direction == D_RIGHT) {
1577 if (orientation == HORIZ) {
1578 /* If the direction is horizontal, we can use either the first
1579 * (D_RIGHT) or the last con (D_LEFT) */
1580 if (direction == D_RIGHT)
1581 most = TAILQ_FIRST(&(con->nodes_head));
1582 else
1583 most = TAILQ_LAST(&(con->nodes_head), nodes_head);
1584 } else if (orientation == VERT) {
1585 /* Wrong orientation. We use the last focused con. Within that con,
1586 * we recurse to chose the left/right con or at least the last
1587 * focused one. */
1588 TAILQ_FOREACH(current, &(con->focus_head), focused) {
1589 if (current->type != CT_FLOATING_CON) {
1590 most = current;
1591 break;
1592 }
1593 }
1594 } else {
1595 /* If the con has no orientation set, it’s not a split container
1596 * but a container with a client window, so stop recursing */
1597 return con;
1598 }
1599 }
1600
1601 if (direction == D_UP || direction == D_DOWN) {
1602 if (orientation == VERT) {
1603 /* If the direction is vertical, we can use either the first
1604 * (D_DOWN) or the last con (D_UP) */
1605 if (direction == D_UP)
1606 most = TAILQ_LAST(&(con->nodes_head), nodes_head);
1607 else
1608 most = TAILQ_FIRST(&(con->nodes_head));
1609 } else if (orientation == HORIZ) {
1610 /* Wrong orientation. We use the last focused con. Within that con,
1611 * we recurse to chose the top/bottom con or at least the last
1612 * focused one. */
1613 TAILQ_FOREACH(current, &(con->focus_head), focused) {
1614 if (current->type != CT_FLOATING_CON) {
1615 most = current;
1616 break;
1617 }
1618 }
1619 } else {
1620 /* If the con has no orientation set, it’s not a split container
1621 * but a container with a client window, so stop recursing */
1622 return con;
1623 }
1624 }
1625
1626 if (!most)
1627 return con;
1628 return con_descend_direction(most, direction);
1629 }
1630
1631 /*
1632 * Returns a "relative" Rect which contains the amount of pixels that need to
1633 * be added to the original Rect to get the final position (obviously the
1634 * amount of pixels for normal, 1pixel and borderless are different).
1635 *
1636 */
1637 Rect con_border_style_rect(Con *con) {
1638 if (config.hide_edge_borders == HEBM_SMART && con_num_visible_children(con_get_workspace(con)) <= 1) {
1639 if (!con_is_floating(con)) {
1640 return (Rect){0, 0, 0, 0};
1641 }
1642 }
1643
1644 adjacent_t borders_to_hide = ADJ_NONE;
1645 int border_width = con->current_border_width;
1646 DLOG("The border width for con is set to: %d\n", con->current_border_width);
1647 Rect result;
1648 if (con->current_border_width < 0) {
1649 if (con_is_floating(con)) {
1650 border_width = config.default_floating_border_width;
1651 } else {
1652 border_width = config.default_border_width;
1653 }
1654 }
1655 DLOG("Effective border width is set to: %d\n", border_width);
1656 /* Shortcut to avoid calling con_adjacent_borders() on dock containers. */
1657 int border_style = con_border_style(con);
1658 if (border_style == BS_NONE)
1659 return (Rect){0, 0, 0, 0};
1660 if (border_style == BS_NORMAL) {
1661 result = (Rect){border_width, 0, -(2 * border_width), -(border_width)};
1662 } else {
1663 result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
1664 }
1665
1666 borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
1667 if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
1668 result.x -= border_width;
1669 result.width += border_width;
1670 }
1671 if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
1672 result.width += border_width;
1673 }
1674 if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE && (border_style != BS_NORMAL)) {
1675 result.y -= border_width;
1676 result.height += border_width;
1677 }
1678 if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
1679 result.height += border_width;
1680 }
1681 return result;
1682 }
1683
1684 /*
1685 * Returns adjacent borders of the window. We need this if hide_edge_borders is
1686 * enabled.
1687 */
1688 adjacent_t con_adjacent_borders(Con *con) {
1689 adjacent_t result = ADJ_NONE;
1690 /* Floating windows are never adjacent to any other window, so
1691 don’t hide their border(s). This prevents bug #998. */
1692 if (con_is_floating(con))
1693 return result;
1694
1695 Con *workspace = con_get_workspace(con);
1696 if (con->rect.x == workspace->rect.x)
1697 result |= ADJ_LEFT_SCREEN_EDGE;
1698 if (con->rect.x + con->rect.width == workspace->rect.x + workspace->rect.width)
1699 result |= ADJ_RIGHT_SCREEN_EDGE;
1700 if (con->rect.y == workspace->rect.y)
1701 result |= ADJ_UPPER_SCREEN_EDGE;
1702 if (con->rect.y + con->rect.height == workspace->rect.y + workspace->rect.height)
1703 result |= ADJ_LOWER_SCREEN_EDGE;
1704 return result;
1705 }
1706
1707 /*
1708 * Use this function to get a container’s border style. This is important
1709 * because when inside a stack, the border style is always BS_NORMAL.
1710 * For tabbed mode, the same applies, with one exception: when the container is
1711 * borderless and the only element in the tabbed container, the border is not
1712 * rendered.
1713 *
1714 * For children of a CT_DOCKAREA, the border style is always none.
1715 *
1716 */
1717 int con_border_style(Con *con) {
1718 if (con->fullscreen_mode == CF_OUTPUT || con->fullscreen_mode == CF_GLOBAL) {
1719 DLOG("this one is fullscreen! overriding BS_NONE\n");
1720 return BS_NONE;
1721 }
1722
1723 if (con->parent->layout == L_STACKED)
1724 return (con_num_children(con->parent) == 1 ? con->border_style : BS_NORMAL);
1725
1726 if (con->parent->layout == L_TABBED && con->border_style != BS_NORMAL)
1727 return (con_num_children(con->parent) == 1 ? con->border_style : BS_NORMAL);
1728
1729 if (con->parent->type == CT_DOCKAREA)
1730 return BS_NONE;
1731
1732 return con->border_style;
1733 }
1734
1735 /*
1736 * Sets the given border style on con, correctly keeping the position/size of a
1737 * floating window.
1738 *
1739 */
1740 void con_set_border_style(Con *con, int border_style, int border_width) {
1741 /* Handle the simple case: non-floating containerns */
1742 if (!con_is_floating(con)) {
1743 con->border_style = border_style;
1744 con->current_border_width = border_width;
1745 return;
1746 }
1747
1748 /* For floating containers, we want to keep the position/size of the
1749 * *window* itself. We first add the border pixels to con->rect to make
1750 * con->rect represent the absolute position of the window (same for
1751 * parent). Then, we change the border style and subtract the new border
1752 * pixels. For the parent, we do the same also for the decoration. */
1753 DLOG("This is a floating container\n");
1754
1755 Con *parent = con->parent;
1756 Rect bsr = con_border_style_rect(con);
1757 int deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
1758
1759 con->rect = rect_add(con->rect, bsr);
1760 parent->rect = rect_add(parent->rect, bsr);
1761 parent->rect.y += deco_height;
1762 parent->rect.height -= deco_height;
1763
1764 /* Change the border style, get new border/decoration values. */
1765 con->border_style = border_style;
1766 con->current_border_width = border_width;
1767 bsr = con_border_style_rect(con);
1768 deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
1769
1770 con->rect = rect_sub(con->rect, bsr);
1771 parent->rect = rect_sub(parent->rect, bsr);
1772 parent->rect.y -= deco_height;
1773 parent->rect.height += deco_height;
1774 }
1775
1776 /*
1777 * This function changes the layout of a given container. Use it to handle
1778 * special cases like changing a whole workspace to stacked/tabbed (creates a
1779 * new split container before).
1780 *
1781 */
1782 void con_set_layout(Con *con, layout_t layout) {
1783 DLOG("con_set_layout(%p, %d), con->type = %d\n",
1784 con, layout, con->type);
1785
1786 /* Users can focus workspaces, but not any higher in the hierarchy.
1787 * Focus on the workspace is a special case, since in every other case, the
1788 * user means "change the layout of the parent split container". */
1789 if (con->type != CT_WORKSPACE)
1790 con = con->parent;
1791
1792 /* We fill in last_split_layout when switching to a different layout
1793 * since there are many places in the code that don’t use
1794 * con_set_layout(). */
1795 if (con->layout == L_SPLITH || con->layout == L_SPLITV)
1796 con->last_split_layout = con->layout;
1797
1798 /* When the container type is CT_WORKSPACE, the user wants to change the
1799 * whole workspace into stacked/tabbed mode. To do this and still allow
1800 * intuitive operations (like level-up and then opening a new window), we
1801 * need to create a new split container. */
1802 if (con->type == CT_WORKSPACE) {
1803 if (con_num_children(con) == 0) {
1804 layout_t ws_layout = (layout == L_STACKED || layout == L_TABBED) ? layout : L_DEFAULT;
1805 DLOG("Setting workspace_layout to %d\n", ws_layout);
1806 con->workspace_layout = ws_layout;
1807 DLOG("Setting layout to %d\n", layout);
1808 con->layout = layout;
1809 } else if (layout == L_STACKED || layout == L_TABBED || layout == L_SPLITV || layout == L_SPLITH) {
1810 DLOG("Creating new split container\n");
1811 /* 1: create a new split container */
1812 Con *new = con_new(NULL, NULL);
1813 new->parent = con;
1814
1815 /* 2: Set the requested layout on the split container and mark it as
1816 * split. */
1817 new->layout = layout;
1818 new->last_split_layout = con->last_split_layout;
1819
1820 /* 3: move the existing cons of this workspace below the new con */
1821 Con **focus_order = get_focus_order(con);
1822
1823 DLOG("Moving cons\n");
1824 Con *child;
1825 while (!TAILQ_EMPTY(&(con->nodes_head))) {
1826 child = TAILQ_FIRST(&(con->nodes_head));
1827 con_detach(child);
1828 con_attach(child, new, true);
1829 }
1830
1831 set_focus_order(new, focus_order);
1832 free(focus_order);
1833
1834 /* 4: attach the new split container to the workspace */
1835 DLOG("Attaching new split to ws\n");
1836 con_attach(new, con, false);
1837
1838 tree_flatten(croot);
1839 }
1840 con_force_split_parents_redraw(con);
1841 return;
1842 }
1843
1844 if (layout == L_DEFAULT) {
1845 /* Special case: the layout formerly known as "default" (in combination
1846 * with an orientation). Since we switched to splith/splitv layouts,
1847 * using the "default" layout (which "only" should happen when using
1848 * legacy configs) is using the last split layout (either splith or
1849 * splitv) in order to still do the same thing. */
1850 con->layout = con->last_split_layout;
1851 /* In case last_split_layout was not initialized… */
1852 if (con->layout == L_DEFAULT)
1853 con->layout = L_SPLITH;
1854 } else {
1855 con->layout = layout;
1856 }
1857 con_force_split_parents_redraw(con);
1858 }
1859
1860 /*
1861 * This function toggles the layout of a given container. toggle_mode can be
1862 * either 'default' (toggle only between stacked/tabbed/last_split_layout),
1863 * 'split' (toggle only between splitv/splith) or 'all' (toggle between all
1864 * layouts).
1865 *
1866 */
1867 void con_toggle_layout(Con *con, const char *toggle_mode) {
1868 Con *parent = con;
1869 /* Users can focus workspaces, but not any higher in the hierarchy.
1870 * Focus on the workspace is a special case, since in every other case, the
1871 * user means "change the layout of the parent split container". */
1872 if (con->type != CT_WORKSPACE)
1873 parent = con->parent;
1874 DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent);
1875
1876 const char delim[] = " ";
1877
1878 if (strcasecmp(toggle_mode, "split") == 0 || strstr(toggle_mode, delim)) {
1879 /* L_DEFAULT is used as a placeholder value to distinguish if
1880 * the first layout has already been saved. (it can never be L_DEFAULT) */
1881 layout_t new_layout = L_DEFAULT;
1882 bool current_layout_found = false;
1883 char *tm_dup = sstrdup(toggle_mode);
1884 char *cur_tok = strtok(tm_dup, delim);
1885
1886 for (layout_t layout; cur_tok != NULL; cur_tok = strtok(NULL, delim)) {
1887 if (strcasecmp(cur_tok, "split") == 0) {
1888 /* Toggle between splits. When the current layout is not a split
1889 * layout, we just switch back to last_split_layout. Otherwise, we
1890 * change to the opposite split layout. */
1891 if (parent->layout != L_SPLITH && parent->layout != L_SPLITV) {
1892 layout = parent->last_split_layout;
1893 /* In case last_split_layout was not initialized… */
1894 if (layout == L_DEFAULT) {
1895 layout = L_SPLITH;
1896 }
1897 } else {
1898 layout = (parent->layout == L_SPLITH) ? L_SPLITV : L_SPLITH;
1899 }
1900 } else {
1901 bool success = layout_from_name(cur_tok, &layout);
1902 if (!success || layout == L_DEFAULT) {
1903 ELOG("The token '%s' was not recognized and has been skipped.\n", cur_tok);
1904 continue;
1905 }
1906 }
1907
1908 /* If none of the specified layouts match the current,
1909 * fall back to the first layout in the list */
1910 if (new_layout == L_DEFAULT) {
1911 new_layout = layout;
1912 }
1913
1914 /* We found the active layout in the last iteration, so
1915 * now let's activate the current layout (next in list) */
1916 if (current_layout_found) {
1917 new_layout = layout;
1918 break;
1919 }
1920
1921 if (parent->layout == layout) {
1922 current_layout_found = true;
1923 }
1924 }
1925 free(tm_dup);
1926
1927 if (new_layout != L_DEFAULT) {
1928 con_set_layout(con, new_layout);
1929 }
1930 } else if (strcasecmp(toggle_mode, "all") == 0 || strcasecmp(toggle_mode, "default") == 0) {
1931 if (parent->layout == L_STACKED)
1932 con_set_layout(con, L_TABBED);
1933 else if (parent->layout == L_TABBED) {
1934 if (strcasecmp(toggle_mode, "all") == 0)
1935 con_set_layout(con, L_SPLITH);
1936 else
1937 con_set_layout(con, parent->last_split_layout);
1938 } else if (parent->layout == L_SPLITH || parent->layout == L_SPLITV) {
1939 if (strcasecmp(toggle_mode, "all") == 0) {
1940 /* When toggling through all modes, we toggle between
1941 * splith/splitv, whereas normally we just directly jump to
1942 * stacked. */
1943 if (parent->layout == L_SPLITH)
1944 con_set_layout(con, L_SPLITV);
1945 else
1946 con_set_layout(con, L_STACKED);
1947 } else {
1948 con_set_layout(con, L_STACKED);
1949 }
1950 }
1951 }
1952 }
1953
1954 /*
1955 * Callback which will be called when removing a child from the given con.
1956 * Kills the container if it is empty and replaces it with the child if there
1957 * is exactly one child.
1958 *
1959 */
1960 static void con_on_remove_child(Con *con) {
1961 DLOG("on_remove_child\n");
1962
1963 /* Every container 'above' (in the hierarchy) the workspace content should
1964 * not be closed when the last child was removed */
1965 if (con->type == CT_OUTPUT ||
1966 con->type == CT_ROOT ||
1967 con->type == CT_DOCKAREA ||
1968 (con->parent != NULL && con->parent->type == CT_OUTPUT)) {
1969 DLOG("not handling, type = %d, name = %s\n", con->type, con->name);
1970 return;
1971 }
1972
1973 /* For workspaces, close them only if they're not visible anymore */
1974 if (con->type == CT_WORKSPACE) {
1975 if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
1976 LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
1977 yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
1978 tree_close_internal(con, DONT_KILL_WINDOW, false);
1979
1980 const unsigned char *payload;
1981 ylength length;
1982 y(get_buf, &payload, &length);
1983 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
1984
1985 y(free);
1986 }
1987 return;
1988 }
1989
1990 con_force_split_parents_redraw(con);
1991 con->urgent = con_has_urgent_child(con);
1992 con_update_parents_urgency(con);
1993
1994 /* TODO: check if this container would swallow any other client and
1995 * don’t close it automatically. */
1996 int children = con_num_children(con);
1997 if (children == 0) {
1998 DLOG("Container empty, closing\n");
1999 tree_close_internal(con, DONT_KILL_WINDOW, false);
2000 return;
2001 }
2002 }
2003
2004 /*
2005 * Determines the minimum size of the given con by looking at its children (for
2006 * split/stacked/tabbed cons). Will be called when resizing floating cons
2007 *
2008 */
2009 Rect con_minimum_size(Con *con) {
2010 DLOG("Determining minimum size for con %p\n", con);
2011
2012 if (con_is_leaf(con)) {
2013 DLOG("leaf node, returning 75x50\n");
2014 return (Rect){0, 0, 75, 50};
2015 }
2016
2017 if (con->type == CT_FLOATING_CON) {
2018 DLOG("floating con\n");
2019 Con *child = TAILQ_FIRST(&(con->nodes_head));
2020 return con_minimum_size(child);
2021 }
2022
2023 if (con->layout == L_STACKED || con->layout == L_TABBED) {
2024 uint32_t max_width = 0, max_height = 0, deco_height = 0;
2025 Con *child;
2026 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
2027 Rect min = con_minimum_size(child);
2028 deco_height += child->deco_rect.height;
2029 max_width = max(max_width, min.width);
2030 max_height = max(max_height, min.height);
2031 }
2032 DLOG("stacked/tabbed now, returning %d x %d + deco_rect = %d\n",
2033 max_width, max_height, deco_height);
2034 return (Rect){0, 0, max_width, max_height + deco_height};
2035 }
2036
2037 /* For horizontal/vertical split containers we sum up the width (h-split)
2038 * or height (v-split) and use the maximum of the height (h-split) or width
2039 * (v-split) as minimum size. */
2040 if (con_is_split(con)) {
2041 uint32_t width = 0, height = 0;
2042 Con *child;
2043 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
2044 Rect min = con_minimum_size(child);
2045 if (con->layout == L_SPLITH) {
2046 width += min.width;
2047 height = max(height, min.height);
2048 } else {
2049 height += min.height;
2050 width = max(width, min.width);
2051 }
2052 }
2053 DLOG("split container, returning width = %d x height = %d\n", width, height);
2054 return (Rect){0, 0, width, height};
2055 }
2056
2057 ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
2058 con->type, con->layout, con_is_split(con));
2059 assert(false);
2060 }
2061
2062 /*
2063 * Returns true if changing the focus to con would be allowed considering
2064 * the fullscreen focus constraints. Specifically, if a fullscreen container or
2065 * any of its descendants is focused, this function returns true if and only if
2066 * focusing con would mean that focus would still be visible on screen, i.e.,
2067 * the newly focused container would not be obscured by a fullscreen container.
2068 *
2069 * In the simplest case, if a fullscreen container or any of its descendants is
2070 * fullscreen, this functions returns true if con is the fullscreen container
2071 * itself or any of its descendants, as this means focus wouldn't escape the
2072 * boundaries of the fullscreen container.
2073 *
2074 * In case the fullscreen container is of type CF_OUTPUT, this function returns
2075 * true if con is on a different workspace, as focus wouldn't be obscured by
2076 * the fullscreen container that is constrained to a different workspace.
2077 *
2078 * Note that this same logic can be applied to moving containers. If a
2079 * container can be focused under the fullscreen focus constraints, it can also
2080 * become a parent or sibling to the currently focused container.
2081 *
2082 */
2083 bool con_fullscreen_permits_focusing(Con *con) {
2084 /* No focus, no problem. */
2085 if (!focused)
2086 return true;
2087
2088 /* Find the first fullscreen ascendent. */
2089 Con *fs = focused;
2090 while (fs && fs->fullscreen_mode == CF_NONE)
2091 fs = fs->parent;
2092
2093 /* fs must be non-NULL since the workspace con doesn’t have CF_NONE and
2094 * there always has to be a workspace con in the hierarchy. */
2095 assert(fs != NULL);
2096 /* The most common case is we hit the workspace level. In this
2097 * situation, changing focus is also harmless. */
2098 assert(fs->fullscreen_mode != CF_NONE);
2099 if (fs->type == CT_WORKSPACE)
2100 return true;
2101
2102 /* Allow it if the container itself is the fullscreen container. */
2103 if (con == fs)
2104 return true;
2105
2106 /* If fullscreen is per-output, the focus being in a different workspace is
2107 * sufficient to guarantee that change won't leave fullscreen in bad shape. */
2108 if (fs->fullscreen_mode == CF_OUTPUT &&
2109 con_get_workspace(con) != con_get_workspace(fs)) {
2110 return true;
2111 }
2112
2113 /* Allow it only if the container to be focused is contained within the
2114 * current fullscreen container. */
2115 return con_has_parent(con, fs);
2116 }
2117
2118 /*
2119 *
2120 * Checks if the given container has an urgent child.
2121 *
2122 */
2123 bool con_has_urgent_child(Con *con) {
2124 Con *child;
2125
2126 if (con_is_leaf(con))
2127 return con->urgent;
2128
2129 /* We are not interested in floating windows since they can only be
2130 * attached to a workspace → nodes_head instead of focus_head */
2131 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
2132 if (con_has_urgent_child(child))
2133 return true;
2134 }
2135
2136 return false;
2137 }
2138
2139 /*
2140 * Make all parent containers urgent if con is urgent or clear the urgent flag
2141 * of all parent containers if there are no more urgent children left.
2142 *
2143 */
2144 void con_update_parents_urgency(Con *con) {
2145 Con *parent = con->parent;
2146
2147 /* Urgency hints should not be set on any container higher up in the
2148 * hierarchy than the workspace level. Unfortunately, since the content
2149 * container has type == CT_CON, that’s not easy to verify in the loop
2150 * below, so we need another condition to catch that case: */
2151 if (con->type == CT_WORKSPACE)
2152 return;
2153
2154 bool new_urgency_value = con->urgent;
2155 while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
2156 if (new_urgency_value) {
2157 parent->urgent = true;
2158 } else {
2159 /* We can only reset the urgency when the parent
2160 * has no other urgent children */
2161 if (!con_has_urgent_child(parent))
2162 parent->urgent = false;
2163 }
2164 parent = parent->parent;
2165 }
2166 }
2167
2168 /*
2169 * Set urgency flag to the container, all the parent containers and the workspace.
2170 *
2171 */
2172 void con_set_urgency(Con *con, bool urgent) {
2173 if (urgent && focused == con) {
2174 DLOG("Ignoring urgency flag for current client\n");
2175 return;
2176 }
2177
2178 const bool old_urgent = con->urgent;
2179
2180 if (con->urgency_timer == NULL) {
2181 con->urgent = urgent;
2182 } else
2183 DLOG("Discarding urgency WM_HINT because timer is running\n");
2184
2185 //CLIENT_LOG(con);
2186 if (con->window) {
2187 if (con->urgent) {
2188 gettimeofday(&con->window->urgent, NULL);
2189 } else {
2190 con->window->urgent.tv_sec = 0;
2191 con->window->urgent.tv_usec = 0;
2192 }
2193 }
2194
2195 con_update_parents_urgency(con);
2196
2197 Con *ws;
2198 /* Set the urgency flag on the workspace, if a workspace could be found
2199 * (for dock clients, that is not the case). */
2200 if ((ws = con_get_workspace(con)) != NULL)
2201 workspace_update_urgent_flag(ws);
2202
2203 if (con->urgent != old_urgent) {
2204 LOG("Urgency flag changed to %d\n", con->urgent);
2205 ipc_send_window_event("urgent", con);
2206 }
2207 }
2208
2209 /*
2210 * Create a string representing the subtree under con.
2211 *
2212 */
2213 char *con_get_tree_representation(Con *con) {
2214 /* this code works as follows:
2215 * 1) create a string with the layout type (D/V/H/T/S) and an opening bracket
2216 * 2) append the tree representation of the children to the string
2217 * 3) add closing bracket
2218 *
2219 * The recursion ends when we hit a leaf, in which case we return the
2220 * class_instance of the contained window.
2221 */
2222
2223 /* end of recursion */
2224 if (con_is_leaf(con)) {
2225 if (!con->window)
2226 return sstrdup("nowin");
2227
2228 if (!con->window->class_instance)
2229 return sstrdup("noinstance");
2230
2231 return sstrdup(con->window->class_instance);
2232 }
2233
2234 char *buf;
2235 /* 1) add the Layout type to buf */
2236 if (con->layout == L_DEFAULT)
2237 buf = sstrdup("D[");
2238 else if (con->layout == L_SPLITV)
2239 buf = sstrdup("V[");
2240 else if (con->layout == L_SPLITH)
2241 buf = sstrdup("H[");
2242 else if (con->layout == L_TABBED)
2243 buf = sstrdup("T[");
2244 else if (con->layout == L_STACKED)
2245 buf = sstrdup("S[");
2246 else {
2247 ELOG("BUG: Code not updated to account for new layout type\n");
2248 assert(false);
2249 }
2250
2251 /* 2) append representation of children */
2252 Con *child;
2253 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
2254 char *child_txt = con_get_tree_representation(child);
2255
2256 char *tmp_buf;
2257 sasprintf(&tmp_buf, "%s%s%s", buf,
2258 (TAILQ_FIRST(&(con->nodes_head)) == child ? "" : " "), child_txt);
2259 free(buf);
2260 buf = tmp_buf;
2261 free(child_txt);
2262 }
2263
2264 /* 3) close the brackets */
2265 char *complete_buf;
2266 sasprintf(&complete_buf, "%s]", buf);
2267 free(buf);
2268
2269 return complete_buf;
2270 }
2271
2272 /*
2273 * Returns the container's title considering the current title format.
2274 *
2275 */
2276 i3String *con_parse_title_format(Con *con) {
2277 assert(con->title_format != NULL);
2278
2279 i3Window *win = con->window;
2280
2281 /* We need to ensure that we only escape the window title if pango
2282 * is used by the current font. */
2283 const bool pango_markup = font_is_pango();
2284
2285 char *title;
2286 char *class;
2287 char *instance;
2288 if (win == NULL) {
2289 title = pango_escape_markup(con_get_tree_representation(con));
2290 class = sstrdup("i3-frame");
2291 instance = sstrdup("i3-frame");
2292 } else {
2293 title = pango_escape_markup(sstrdup((win->name == NULL) ? "" : i3string_as_utf8(win->name)));
2294 class = pango_escape_markup(sstrdup((win->class_class == NULL) ? "" : win->class_class));
2295 instance = pango_escape_markup(sstrdup((win->class_instance == NULL) ? "" : win->class_instance));
2296 }
2297
2298 placeholder_t placeholders[] = {
2299 {.name = "%title", .value = title},
2300 {.name = "%class", .value = class},
2301 {.name = "%instance", .value = instance}};
2302 const size_t num = sizeof(placeholders) / sizeof(placeholder_t);
2303
2304 char *formatted_str = format_placeholders(con->title_format, &placeholders[0], num);
2305 i3String *formatted = i3string_from_utf8(formatted_str);
2306 i3string_set_markup(formatted, pango_markup);
2307 FREE(formatted_str);
2308
2309 for (size_t i = 0; i < num; i++) {
2310 FREE(placeholders[i].value);
2311 }
2312
2313 return formatted;
2314 }
2315
2316 /*
2317 * Swaps the two containers.
2318 *
2319 */
2320 bool con_swap(Con *first, Con *second) {
2321 assert(first != NULL);
2322 assert(second != NULL);
2323 DLOG("Swapping containers %p / %p\n", first, second);
2324
2325 if (first->type != CT_CON) {
2326 ELOG("Only regular containers can be swapped, but found con = %p with type = %d.\n", first, first->type);
2327 return false;
2328 }
2329
2330 if (second->type != CT_CON) {
2331 ELOG("Only regular containers can be swapped, but found con = %p with type = %d.\n", second, second->type);
2332 return false;
2333 }
2334
2335 if (con_is_floating(first) || con_is_floating(second)) {
2336 ELOG("Floating windows cannot be swapped.\n");
2337 return false;
2338 }
2339
2340 if (first == second) {
2341 DLOG("Swapping container %p with itself, nothing to do.\n", first);
2342 return false;
2343 }
2344
2345 if (con_has_parent(first, second) || con_has_parent(second, first)) {
2346 ELOG("Cannot swap containers %p and %p because they are in a parent-child relationship.\n", first, second);
2347 return false;
2348 }
2349
2350 Con *old_focus = focused;
2351
2352 Con *first_ws = con_get_workspace(first);
2353 Con *second_ws = con_get_workspace(second);
2354 Con *current_ws = con_get_workspace(old_focus);
2355 const bool focused_within_first = (first == old_focus || con_has_parent(old_focus, first));
2356 const bool focused_within_second = (second == old_focus || con_has_parent(old_focus, second));
2357 fullscreen_mode_t first_fullscreen_mode = first->fullscreen_mode;
2358 fullscreen_mode_t second_fullscreen_mode = second->fullscreen_mode;
2359
2360 if (first_fullscreen_mode != CF_NONE) {
2361 con_disable_fullscreen(first);
2362 }
2363 if (second_fullscreen_mode != CF_NONE) {
2364 con_disable_fullscreen(second);
2365 }
2366
2367 double first_percent = first->percent;
2368 double second_percent = second->percent;
2369
2370 /* De- and reattaching the containers will insert them at the tail of the
2371 * focus_heads. We will need to fix this. But we need to make sure first
2372 * and second don't get in each other's way if they share the same parent,
2373 * so we select the closest previous focus_head that isn't involved. */
2374 Con *first_prev_focus_head = first;
2375 while (first_prev_focus_head == first || first_prev_focus_head == second) {
2376 first_prev_focus_head = TAILQ_PREV(first_prev_focus_head, focus_head, focused);
2377 }
2378
2379 Con *second_prev_focus_head = second;
2380 while (second_prev_focus_head == second || second_prev_focus_head == first) {
2381 second_prev_focus_head = TAILQ_PREV(second_prev_focus_head, focus_head, focused);
2382 }
2383
2384 /* We use a fake container to mark the spot of where the second container needs to go. */
2385 Con *fake = con_new(NULL, NULL);
2386 fake->layout = L_SPLITH;
2387 _con_attach(fake, first->parent, first, true);
2388
2389 bool result = true;
2390 /* Swap the containers. We set the ignore_focus flag here because after the
2391 * container is attached, the focus order is not yet correct and would
2392 * result in wrong windows being focused. */
2393
2394 /* Move first to second. */
2395 result &= _con_move_to_con(first, second, false, false, false, true, false);
2396 /* If swapping the containers didn't work we don't need to mess with the focus. */
2397 if (!result) {
2398 goto swap_end;
2399 }
2400
2401 /* If we moved the container holding the focused window to another
2402 * workspace we need to ensure the visible workspace has the focused
2403 * container.
2404 * We don't need to check this for the second container because we've only
2405 * moved the first one at this point.*/
2406 if (first_ws != second_ws && focused_within_first) {
2407 con_activate(con_descend_focused(current_ws));
2408 }
2409
2410 /* Move second to where first has been originally. */
2411 result &= _con_move_to_con(second, fake, false, false, false, true, false);
2412 if (!result) {
2413 goto swap_end;
2414 }
2415
2416 /* Swapping will have inserted the containers at the tail of their parents'
2417 * focus head. We fix this now by putting them in the position of the focus
2418 * head the container they swapped with was in. */
2419 TAILQ_REMOVE(&(first->parent->focus_head), first, focused);
2420 TAILQ_REMOVE(&(second->parent->focus_head), second, focused);
2421
2422 if (second_prev_focus_head == NULL) {
2423 TAILQ_INSERT_HEAD(&(first->parent->focus_head), first, focused);
2424 } else {
2425 TAILQ_INSERT_AFTER(&(first->parent->focus_head), second_prev_focus_head, first, focused);
2426 }
2427
2428 if (first_prev_focus_head == NULL) {
2429 TAILQ_INSERT_HEAD(&(second->parent->focus_head), second, focused);
2430 } else {
2431 TAILQ_INSERT_AFTER(&(second->parent->focus_head), first_prev_focus_head, second, focused);
2432 }
2433
2434 /* If the focus was within any of the swapped containers, do the following:
2435 * - If swapping took place within a workspace, ensure the previously
2436 * focused container stays focused.
2437 * - Otherwise, focus the container that has been swapped in.
2438 *
2439 * To understand why fixing the focus_head previously wasn't enough,
2440 * consider the scenario
2441 * H[ V[ A X ] V[ Y B ] ]
2442 * with B being focused, but X being the focus_head within its parent. If
2443 * we swap A and B now, fixing the focus_head would focus X, but since B
2444 * was the focused container before it should stay focused.
2445 */
2446 if (focused_within_first) {
2447 if (first_ws == second_ws) {
2448 con_activate(old_focus);
2449 } else {
2450 con_activate(con_descend_focused(second));
2451 }
2452 } else if (focused_within_second) {
2453 if (first_ws == second_ws) {
2454 con_activate(old_focus);
2455 } else {
2456 con_activate(con_descend_focused(first));
2457 }
2458 }
2459
2460 /* We need to copy each other's percentages to ensure that the geometry
2461 * doesn't change during the swap. This needs to happen _before_ we close
2462 * the fake container as closing the tree will recalculate percentages. */
2463 first->percent = second_percent;
2464 second->percent = first_percent;
2465 fake->percent = 0.0;
2466
2467 SWAP(first_fullscreen_mode, second_fullscreen_mode, fullscreen_mode_t);
2468
2469 swap_end:
2470 /* The two windows exchange their original fullscreen status */
2471 if (first_fullscreen_mode != CF_NONE) {
2472 con_enable_fullscreen(first, first_fullscreen_mode);
2473 }
2474 if (second_fullscreen_mode != CF_NONE) {
2475 con_enable_fullscreen(second, second_fullscreen_mode);
2476 }
2477
2478 /* We don't actually need this since percentages-wise we haven't changed
2479 * anything, but we'll better be safe than sorry and just make sure as we'd
2480 * otherwise crash i3. */
2481 con_fix_percent(first->parent);
2482 con_fix_percent(second->parent);
2483
2484 /* We can get rid of the fake container again now. */
2485 con_close(fake, DONT_KILL_WINDOW);
2486
2487 con_force_split_parents_redraw(first);
2488 con_force_split_parents_redraw(second);
2489
2490 return result;
2491 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * config.c: Configuration file (calling the parser (src/config_parser.c) with
7 * the correct path, switching key bindings mode).
8 *
9 */
10 #include "all.h"
11
12 #include <xkbcommon/xkbcommon.h>
13
14 char *current_configpath = NULL;
15 char *current_config = NULL;
16 Config config;
17 struct modes_head modes;
18 struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
19
20 /*
21 * Ungrabs all keys, to be called before re-grabbing the keys because of a
22 * mapping_notify event or a configuration file reload
23 *
24 */
25 void ungrab_all_keys(xcb_connection_t *conn) {
26 DLOG("Ungrabbing all keys\n");
27 xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
28 }
29
30 /*
31 * Sends the current bar configuration as an event to all barconfig_update listeners.
32 *
33 */
34 void update_barconfig(void) {
35 Barconfig *current;
36 TAILQ_FOREACH(current, &barconfigs, configs) {
37 ipc_send_barconfig_update_event(current);
38 }
39 }
40
41 /*
42 * Finds the configuration file to use (either the one specified by
43 * override_configpath), the user’s one or the system default) and calls
44 * parse_file().
45 *
46 */
47 bool parse_configuration(const char *override_configpath, bool use_nagbar) {
48 char *path = get_config_path(override_configpath, true);
49 if (path == NULL) {
50 die("Unable to find the configuration file (looked at "
51 "$XDG_CONFIG_HOME/i3/config, ~/.i3/config, $XDG_CONFIG_DIRS/i3/config "
52 "and " SYSCONFDIR "/i3/config)");
53 }
54
55 LOG("Parsing configfile %s\n", path);
56 FREE(current_configpath);
57 current_configpath = path;
58
59 /* initialize default bindings if we're just validating the config file */
60 if (!use_nagbar && bindings == NULL) {
61 bindings = scalloc(1, sizeof(struct bindings_head));
62 TAILQ_INIT(bindings);
63 }
64
65 return parse_file(path, use_nagbar);
66 }
67
68 /*
69 * (Re-)loads the configuration file (sets useful defaults before).
70 *
71 */
72 void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload) {
73 if (reload) {
74 /* If we are currently in a binding mode, we first revert to the
75 * default since we have no guarantee that the current mode will even
76 * still exist after parsing the config again. See #2228. */
77 switch_mode("default");
78
79 /* First ungrab the keys */
80 ungrab_all_keys(conn);
81
82 struct Mode *mode;
83 while (!SLIST_EMPTY(&modes)) {
84 mode = SLIST_FIRST(&modes);
85 FREE(mode->name);
86
87 /* Clear the old binding list */
88 while (!TAILQ_EMPTY(mode->bindings)) {
89 Binding *bind = TAILQ_FIRST(mode->bindings);
90 TAILQ_REMOVE(mode->bindings, bind, bindings);
91 binding_free(bind);
92 }
93 FREE(mode->bindings);
94
95 SLIST_REMOVE(&modes, mode, Mode, modes);
96 FREE(mode);
97 }
98
99 while (!TAILQ_EMPTY(&assignments)) {
100 struct Assignment *assign = TAILQ_FIRST(&assignments);
101 if (assign->type == A_TO_WORKSPACE || assign->type == A_TO_WORKSPACE_NUMBER)
102 FREE(assign->dest.workspace);
103 else if (assign->type == A_COMMAND)
104 FREE(assign->dest.command);
105 else if (assign->type == A_TO_OUTPUT)
106 FREE(assign->dest.output);
107 match_free(&(assign->match));
108 TAILQ_REMOVE(&assignments, assign, assignments);
109 FREE(assign);
110 }
111
112 while (!TAILQ_EMPTY(&ws_assignments)) {
113 struct Workspace_Assignment *assign = TAILQ_FIRST(&ws_assignments);
114 FREE(assign->name);
115 FREE(assign->output);
116 TAILQ_REMOVE(&ws_assignments, assign, ws_assignments);
117 FREE(assign);
118 }
119
120 /* Clear bar configs */
121 Barconfig *barconfig;
122 while (!TAILQ_EMPTY(&barconfigs)) {
123 barconfig = TAILQ_FIRST(&barconfigs);
124 FREE(barconfig->id);
125 for (int c = 0; c < barconfig->num_outputs; c++)
126 free(barconfig->outputs[c]);
127
128 while (!TAILQ_EMPTY(&(barconfig->bar_bindings))) {
129 struct Barbinding *binding = TAILQ_FIRST(&(barconfig->bar_bindings));
130 FREE(binding->command);
131 TAILQ_REMOVE(&(barconfig->bar_bindings), binding, bindings);
132 FREE(binding);
133 }
134
135 while (!TAILQ_EMPTY(&(barconfig->tray_outputs))) {
136 struct tray_output_t *tray_output = TAILQ_FIRST(&(barconfig->tray_outputs));
137 FREE(tray_output->output);
138 TAILQ_REMOVE(&(barconfig->tray_outputs), tray_output, tray_outputs);
139 FREE(tray_output);
140 }
141
142 FREE(barconfig->outputs);
143 FREE(barconfig->socket_path);
144 FREE(barconfig->status_command);
145 FREE(barconfig->i3bar_command);
146 FREE(barconfig->font);
147 FREE(barconfig->colors.background);
148 FREE(barconfig->colors.statusline);
149 FREE(barconfig->colors.separator);
150 FREE(barconfig->colors.focused_background);
151 FREE(barconfig->colors.focused_statusline);
152 FREE(barconfig->colors.focused_separator);
153 FREE(barconfig->colors.focused_workspace_border);
154 FREE(barconfig->colors.focused_workspace_bg);
155 FREE(barconfig->colors.focused_workspace_text);
156 FREE(barconfig->colors.active_workspace_border);
157 FREE(barconfig->colors.active_workspace_bg);
158 FREE(barconfig->colors.active_workspace_text);
159 FREE(barconfig->colors.inactive_workspace_border);
160 FREE(barconfig->colors.inactive_workspace_bg);
161 FREE(barconfig->colors.inactive_workspace_text);
162 FREE(barconfig->colors.urgent_workspace_border);
163 FREE(barconfig->colors.urgent_workspace_bg);
164 FREE(barconfig->colors.urgent_workspace_text);
165 FREE(barconfig->colors.binding_mode_border);
166 FREE(barconfig->colors.binding_mode_bg);
167 FREE(barconfig->colors.binding_mode_text);
168 TAILQ_REMOVE(&barconfigs, barconfig, configs);
169 FREE(barconfig);
170 }
171
172 Con *con;
173 TAILQ_FOREACH(con, &all_cons, all_cons) {
174 /* Assignments changed, previously ran assignments are invalid. */
175 if (con->window) {
176 con->window->nr_assignments = 0;
177 FREE(con->window->ran_assignments);
178 }
179 /* Invalidate pixmap caches in case font or colors changed. */
180 FREE(con->deco_render_params);
181 }
182
183 /* Get rid of the current font */
184 free_font();
185
186 free(config.ipc_socket_path);
187 free(config.restart_state_path);
188 free(config.fake_outputs);
189 }
190
191 SLIST_INIT(&modes);
192
193 struct Mode *default_mode = scalloc(1, sizeof(struct Mode));
194 default_mode->name = sstrdup("default");
195 default_mode->bindings = scalloc(1, sizeof(struct bindings_head));
196 TAILQ_INIT(default_mode->bindings);
197 SLIST_INSERT_HEAD(&modes, default_mode, modes);
198
199 bindings = default_mode->bindings;
200
201 /* Clear the old config or initialize the data structure */
202 memset(&config, 0, sizeof(config));
203
204 /* Initialize default colors */
205 #define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \
206 do { \
207 x.border = draw_util_hex_to_color(cborder); \
208 x.background = draw_util_hex_to_color(cbackground); \
209 x.text = draw_util_hex_to_color(ctext); \
210 x.indicator = draw_util_hex_to_color(cindicator); \
211 x.child_border = draw_util_hex_to_color(cbackground); \
212 } while (0)
213
214 config.client.background = draw_util_hex_to_color("#000000");
215 INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4");
216 INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50");
217 INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e");
218 INIT_COLOR(config.client.urgent, "#2f343a", "#900000", "#ffffff", "#900000");
219
220 /* border and indicator color are ignored for placeholder contents */
221 INIT_COLOR(config.client.placeholder, "#000000", "#0c0c0c", "#ffffff", "#000000");
222
223 /* the last argument (indicator color) is ignored for bar colors */
224 INIT_COLOR(config.bar.focused, "#4c7899", "#285577", "#ffffff", "#000000");
225 INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000");
226 INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000");
227
228 config.show_marks = true;
229
230 config.default_border = BS_NORMAL;
231 config.default_floating_border = BS_NORMAL;
232 config.default_border_width = logical_px(2);
233 config.default_floating_border_width = logical_px(2);
234 /* Set default_orientation to NO_ORIENTATION for auto orientation. */
235 config.default_orientation = NO_ORIENTATION;
236
237 /* Set default urgency reset delay to 500ms */
238 if (config.workspace_urgency_timer == 0)
239 config.workspace_urgency_timer = 0.5;
240
241 config.focus_wrapping = FOCUS_WRAPPING_ON;
242
243 parse_configuration(override_configpath, true);
244
245 if (reload) {
246 translate_keysyms();
247 grab_all_keys(conn);
248 regrab_all_buttons(conn);
249 }
250
251 if (config.font.type == FONT_TYPE_NONE) {
252 ELOG("You did not specify required configuration option \"font\"\n");
253 config.font = load_font("fixed", true);
254 set_font(&config.font);
255 }
256
257 /* Redraw the currently visible decorations on reload, so that
258 * the possibly new drawing parameters changed. */
259 if (reload) {
260 x_deco_recurse(croot);
261 xcb_flush(conn);
262 }
263 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * config_directives.c: all config storing functions (see config_parser.c)
7 *
8 */
9 #include "all.h"
10
11 #include <float.h>
12 #include <stdarg.h>
13
14 /*******************************************************************************
15 * Criteria functions.
16 ******************************************************************************/
17
18 static int criteria_next_state;
19
20 /*
21 * Initializes the specified 'Match' data structure and the initial state of
22 * commands.c for matching target windows of a command.
23 *
24 */
25 CFGFUN(criteria_init, int _state) {
26 criteria_next_state = _state;
27
28 DLOG("Initializing criteria, current_match = %p, state = %d\n", current_match, _state);
29 match_free(current_match);
30 match_init(current_match);
31 }
32
33 CFGFUN(criteria_pop_state) {
34 result->next_state = criteria_next_state;
35 }
36
37 /*
38 * Interprets a ctype=cvalue pair and adds it to the current match
39 * specification.
40 *
41 */
42 CFGFUN(criteria_add, const char *ctype, const char *cvalue) {
43 match_parse_property(current_match, ctype, cvalue);
44 }
45
46 /*******************************************************************************
47 * Utility functions
48 ******************************************************************************/
49
50 static bool eval_boolstr(const char *str) {
51 return (strcasecmp(str, "1") == 0 ||
52 strcasecmp(str, "yes") == 0 ||
53 strcasecmp(str, "true") == 0 ||
54 strcasecmp(str, "on") == 0 ||
55 strcasecmp(str, "enable") == 0 ||
56 strcasecmp(str, "active") == 0);
57 }
58
59 /*
60 * A utility function to convert a string containing the group and modifiers to
61 * the corresponding bit mask.
62 */
63 i3_event_state_mask_t event_state_from_str(const char *str) {
64 /* It might be better to use strtok() here, but the simpler strstr() should
65 * do for now. */
66 i3_event_state_mask_t result = 0;
67 if (str == NULL)
68 return result;
69 if (strstr(str, "Mod1") != NULL)
70 result |= XCB_KEY_BUT_MASK_MOD_1;
71 if (strstr(str, "Mod2") != NULL)
72 result |= XCB_KEY_BUT_MASK_MOD_2;
73 if (strstr(str, "Mod3") != NULL)
74 result |= XCB_KEY_BUT_MASK_MOD_3;
75 if (strstr(str, "Mod4") != NULL)
76 result |= XCB_KEY_BUT_MASK_MOD_4;
77 if (strstr(str, "Mod5") != NULL)
78 result |= XCB_KEY_BUT_MASK_MOD_5;
79 if (strstr(str, "Control") != NULL ||
80 strstr(str, "Ctrl") != NULL)
81 result |= XCB_KEY_BUT_MASK_CONTROL;
82 if (strstr(str, "Shift") != NULL)
83 result |= XCB_KEY_BUT_MASK_SHIFT;
84
85 if (strstr(str, "Group1") != NULL)
86 result |= (I3_XKB_GROUP_MASK_1 << 16);
87 if (strstr(str, "Group2") != NULL ||
88 strstr(str, "Mode_switch") != NULL)
89 result |= (I3_XKB_GROUP_MASK_2 << 16);
90 if (strstr(str, "Group3") != NULL)
91 result |= (I3_XKB_GROUP_MASK_3 << 16);
92 if (strstr(str, "Group4") != NULL)
93 result |= (I3_XKB_GROUP_MASK_4 << 16);
94 return result;
95 }
96
97 static char *font_pattern;
98
99 CFGFUN(font, const char *font) {
100 config.font = load_font(font, true);
101 set_font(&config.font);
102
103 /* Save the font pattern for using it as bar font later on */
104 FREE(font_pattern);
105 font_pattern = sstrdup(font);
106 }
107
108 CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command) {
109 configure_binding(bindtype, modifiers, key, release, border, whole_window, exclude_titlebar, command, DEFAULT_BINDING_MODE, false);
110 }
111
112 /*******************************************************************************
113 * Mode handling
114 ******************************************************************************/
115
116 static char *current_mode;
117 static bool current_mode_pango_markup;
118
119 CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command) {
120 configure_binding(bindtype, modifiers, key, release, border, whole_window, exclude_titlebar, command, current_mode, current_mode_pango_markup);
121 }
122
123 CFGFUN(enter_mode, const char *pango_markup, const char *modename) {
124 if (strcasecmp(modename, DEFAULT_BINDING_MODE) == 0) {
125 ELOG("You cannot use the name %s for your mode\n", DEFAULT_BINDING_MODE);
126 return;
127 }
128
129 struct Mode *mode;
130 SLIST_FOREACH(mode, &modes, modes) {
131 if (strcmp(mode->name, modename) == 0) {
132 ELOG("The binding mode with name \"%s\" is defined at least twice.\n", modename);
133 }
134 }
135
136 DLOG("\t now in mode %s\n", modename);
137 FREE(current_mode);
138 current_mode = sstrdup(modename);
139 current_mode_pango_markup = (pango_markup != NULL);
140 }
141
142 CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command) {
143 struct Autostart *new = smalloc(sizeof(struct Autostart));
144 new->command = sstrdup(command);
145 new->no_startup_id = (no_startup_id != NULL);
146 if (strcmp(exectype, "exec") == 0) {
147 TAILQ_INSERT_TAIL(&autostarts, new, autostarts);
148 } else {
149 TAILQ_INSERT_TAIL(&autostarts_always, new, autostarts_always);
150 }
151 }
152
153 CFGFUN(for_window, const char *command) {
154 if (match_is_empty(current_match)) {
155 ELOG("Match is empty, ignoring this for_window statement\n");
156 return;
157 }
158 DLOG("\t should execute command %s for the criteria mentioned above\n", command);
159 Assignment *assignment = scalloc(1, sizeof(Assignment));
160 assignment->type = A_COMMAND;
161 match_copy(&(assignment->match), current_match);
162 assignment->dest.command = sstrdup(command);
163 TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
164 }
165
166 CFGFUN(floating_minimum_size, const long width, const long height) {
167 config.floating_minimum_width = width;
168 config.floating_minimum_height = height;
169 }
170
171 CFGFUN(floating_maximum_size, const long width, const long height) {
172 config.floating_maximum_width = width;
173 config.floating_maximum_height = height;
174 }
175
176 CFGFUN(floating_modifier, const char *modifiers) {
177 config.floating_modifier = event_state_from_str(modifiers);
178 }
179
180 CFGFUN(default_orientation, const char *orientation) {
181 if (strcmp(orientation, "horizontal") == 0)
182 config.default_orientation = HORIZ;
183 else if (strcmp(orientation, "vertical") == 0)
184 config.default_orientation = VERT;
185 else
186 config.default_orientation = NO_ORIENTATION;
187 }
188
189 CFGFUN(workspace_layout, const char *layout) {
190 if (strcmp(layout, "default") == 0)
191 config.default_layout = L_DEFAULT;
192 else if (strcmp(layout, "stacking") == 0 ||
193 strcmp(layout, "stacked") == 0)
194 config.default_layout = L_STACKED;
195 else
196 config.default_layout = L_TABBED;
197 }
198
199 CFGFUN(default_border, const char *windowtype, const char *border, const long width) {
200 int border_style;
201 int border_width;
202
203 if (strcmp(border, "1pixel") == 0) {
204 border_style = BS_PIXEL;
205 border_width = 1;
206 } else if (strcmp(border, "none") == 0) {
207 border_style = BS_NONE;
208 border_width = 0;
209 } else if (strcmp(border, "pixel") == 0) {
210 border_style = BS_PIXEL;
211 border_width = width;
212 } else {
213 border_style = BS_NORMAL;
214 border_width = width;
215 }
216
217 if ((strcmp(windowtype, "default_border") == 0) ||
218 (strcmp(windowtype, "new_window") == 0)) {
219 DLOG("default tiled border style = %d and border width = %d (%d physical px)\n",
220 border_style, border_width, logical_px(border_width));
221 config.default_border = border_style;
222 config.default_border_width = logical_px(border_width);
223 } else {
224 DLOG("default floating border style = %d and border width = %d (%d physical px)\n",
225 border_style, border_width, logical_px(border_width));
226 config.default_floating_border = border_style;
227 config.default_floating_border_width = logical_px(border_width);
228 }
229 }
230
231 CFGFUN(hide_edge_borders, const char *borders) {
232 if (strcmp(borders, "smart") == 0)
233 config.hide_edge_borders = HEBM_SMART;
234 else if (strcmp(borders, "vertical") == 0)
235 config.hide_edge_borders = HEBM_VERTICAL;
236 else if (strcmp(borders, "horizontal") == 0)
237 config.hide_edge_borders = HEBM_HORIZONTAL;
238 else if (strcmp(borders, "both") == 0)
239 config.hide_edge_borders = HEBM_BOTH;
240 else if (strcmp(borders, "none") == 0)
241 config.hide_edge_borders = HEBM_NONE;
242 else if (eval_boolstr(borders))
243 config.hide_edge_borders = HEBM_VERTICAL;
244 else
245 config.hide_edge_borders = HEBM_NONE;
246 }
247
248 CFGFUN(focus_follows_mouse, const char *value) {
249 config.disable_focus_follows_mouse = !eval_boolstr(value);
250 }
251
252 CFGFUN(mouse_warping, const char *value) {
253 if (strcmp(value, "none") == 0)
254 config.mouse_warping = POINTER_WARPING_NONE;
255 else if (strcmp(value, "output") == 0)
256 config.mouse_warping = POINTER_WARPING_OUTPUT;
257 }
258
259 CFGFUN(force_xinerama, const char *value) {
260 config.force_xinerama = eval_boolstr(value);
261 }
262
263 CFGFUN(disable_randr15, const char *value) {
264 config.disable_randr15 = eval_boolstr(value);
265 }
266
267 CFGFUN(focus_wrapping, const char *value) {
268 if (strcmp(value, "force") == 0) {
269 config.focus_wrapping = FOCUS_WRAPPING_FORCE;
270 } else if (eval_boolstr(value)) {
271 config.focus_wrapping = FOCUS_WRAPPING_ON;
272 } else {
273 config.focus_wrapping = FOCUS_WRAPPING_OFF;
274 }
275 }
276
277 CFGFUN(force_focus_wrapping, const char *value) {
278 /* Legacy syntax. */
279 if (eval_boolstr(value)) {
280 config.focus_wrapping = FOCUS_WRAPPING_FORCE;
281 } else {
282 /* For "force_focus_wrapping off", don't enable or disable
283 * focus wrapping, just ensure it's not forced. */
284 if (config.focus_wrapping == FOCUS_WRAPPING_FORCE) {
285 config.focus_wrapping = FOCUS_WRAPPING_ON;
286 }
287 }
288 }
289
290 CFGFUN(workspace_back_and_forth, const char *value) {
291 config.workspace_auto_back_and_forth = eval_boolstr(value);
292 }
293
294 CFGFUN(fake_outputs, const char *outputs) {
295 free(config.fake_outputs);
296 config.fake_outputs = sstrdup(outputs);
297 }
298
299 CFGFUN(force_display_urgency_hint, const long duration_ms) {
300 config.workspace_urgency_timer = duration_ms / 1000.0;
301 }
302
303 CFGFUN(focus_on_window_activation, const char *mode) {
304 if (strcmp(mode, "smart") == 0)
305 config.focus_on_window_activation = FOWA_SMART;
306 else if (strcmp(mode, "urgent") == 0)
307 config.focus_on_window_activation = FOWA_URGENT;
308 else if (strcmp(mode, "focus") == 0)
309 config.focus_on_window_activation = FOWA_FOCUS;
310 else if (strcmp(mode, "none") == 0)
311 config.focus_on_window_activation = FOWA_NONE;
312 else {
313 ELOG("Unknown focus_on_window_activation mode \"%s\", ignoring it.\n", mode);
314 return;
315 }
316
317 DLOG("Set new focus_on_window_activation mode = %i.\n", config.focus_on_window_activation);
318 }
319
320 CFGFUN(title_align, const char *alignment) {
321 if (strcmp(alignment, "left") == 0) {
322 config.title_align = ALIGN_LEFT;
323 } else if (strcmp(alignment, "center") == 0) {
324 config.title_align = ALIGN_CENTER;
325 } else if (strcmp(alignment, "right") == 0) {
326 config.title_align = ALIGN_RIGHT;
327 } else {
328 assert(false);
329 }
330 }
331
332 CFGFUN(show_marks, const char *value) {
333 config.show_marks = eval_boolstr(value);
334 }
335
336 CFGFUN(workspace, const char *workspace, const char *outputs) {
337 DLOG("Assigning workspace \"%s\" to outputs \"%s\"\n", workspace, outputs);
338 /* Check for earlier assignments of the same workspace so that we
339 * don’t have assignments of a single workspace to different
340 * outputs */
341 struct Workspace_Assignment *assignment;
342 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
343 if (strcasecmp(assignment->name, workspace) == 0) {
344 ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
345 workspace);
346 return;
347 }
348 }
349
350 char *buf = sstrdup(outputs);
351 char *output = strtok(buf, " ");
352 while (output != NULL) {
353 assignment = scalloc(1, sizeof(struct Workspace_Assignment));
354 assignment->name = sstrdup(workspace);
355 assignment->output = sstrdup(output);
356 TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments);
357 output = strtok(NULL, " ");
358 }
359 free(buf);
360 }
361
362 CFGFUN(ipc_socket, const char *path) {
363 free(config.ipc_socket_path);
364 config.ipc_socket_path = sstrdup(path);
365 }
366
367 CFGFUN(restart_state, const char *path) {
368 free(config.restart_state_path);
369 config.restart_state_path = sstrdup(path);
370 }
371
372 CFGFUN(popup_during_fullscreen, const char *value) {
373 if (strcmp(value, "ignore") == 0) {
374 config.popup_during_fullscreen = PDF_IGNORE;
375 } else if (strcmp(value, "leave_fullscreen") == 0) {
376 config.popup_during_fullscreen = PDF_LEAVE_FULLSCREEN;
377 } else {
378 config.popup_during_fullscreen = PDF_SMART;
379 }
380 }
381
382 CFGFUN(color_single, const char *colorclass, const char *color) {
383 /* used for client.background only currently */
384 config.client.background = draw_util_hex_to_color(color);
385 }
386
387 CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border) {
388 #define APPLY_COLORS(classname) \
389 do { \
390 if (strcmp(colorclass, "client." #classname) == 0) { \
391 config.client.classname.border = draw_util_hex_to_color(border); \
392 config.client.classname.background = draw_util_hex_to_color(background); \
393 config.client.classname.text = draw_util_hex_to_color(text); \
394 if (indicator != NULL) { \
395 config.client.classname.indicator = draw_util_hex_to_color(indicator); \
396 } \
397 if (child_border != NULL) { \
398 config.client.classname.child_border = draw_util_hex_to_color(child_border); \
399 } else { \
400 config.client.classname.child_border = config.client.classname.background; \
401 } \
402 } \
403 } while (0)
404
405 APPLY_COLORS(focused_inactive);
406 APPLY_COLORS(focused);
407 APPLY_COLORS(unfocused);
408 APPLY_COLORS(urgent);
409 APPLY_COLORS(placeholder);
410
411 #undef APPLY_COLORS
412 }
413
414 CFGFUN(assign_output, const char *output) {
415 if (match_is_empty(current_match)) {
416 ELOG("Match is empty, ignoring this assignment\n");
417 return;
418 }
419
420 if (current_match->window_mode != WM_ANY) {
421 ELOG("Assignments using window mode (floating/tiling) is not supported\n");
422 return;
423 }
424
425 DLOG("New assignment, using above criteria, to output \"%s\".\n", output);
426 Assignment *assignment = scalloc(1, sizeof(Assignment));
427 match_copy(&(assignment->match), current_match);
428 assignment->type = A_TO_OUTPUT;
429 assignment->dest.output = sstrdup(output);
430 TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
431 }
432
433 CFGFUN(assign, const char *workspace, bool is_number) {
434 if (match_is_empty(current_match)) {
435 ELOG("Match is empty, ignoring this assignment\n");
436 return;
437 }
438
439 if (current_match->window_mode != WM_ANY) {
440 ELOG("Assignments using window mode (floating/tiling) is not supported\n");
441 return;
442 }
443
444 if (is_number && ws_name_to_number(workspace) == -1) {
445 ELOG("Could not parse initial part of \"%s\" as a number.\n", workspace);
446 return;
447 }
448
449 DLOG("New assignment, using above criteria, to workspace \"%s\".\n", workspace);
450 Assignment *assignment = scalloc(1, sizeof(Assignment));
451 match_copy(&(assignment->match), current_match);
452 assignment->type = is_number ? A_TO_WORKSPACE_NUMBER : A_TO_WORKSPACE;
453 assignment->dest.workspace = sstrdup(workspace);
454 TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
455 }
456
457 CFGFUN(no_focus) {
458 if (match_is_empty(current_match)) {
459 ELOG("Match is empty, ignoring this assignment\n");
460 return;
461 }
462
463 DLOG("New assignment, using above criteria, to ignore focus on manage.\n");
464 Assignment *assignment = scalloc(1, sizeof(Assignment));
465 match_copy(&(assignment->match), current_match);
466 assignment->type = A_NO_FOCUS;
467 TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
468 }
469
470 CFGFUN(ipc_kill_timeout, const long timeout_ms) {
471 ipc_set_kill_timeout(timeout_ms / 1000.0);
472 }
473
474 /*******************************************************************************
475 * Bar configuration (i3bar)
476 ******************************************************************************/
477
478 static Barconfig *current_bar;
479
480 CFGFUN(bar_font, const char *font) {
481 FREE(current_bar->font);
482 current_bar->font = sstrdup(font);
483 }
484
485 CFGFUN(bar_separator_symbol, const char *separator) {
486 FREE(current_bar->separator_symbol);
487 current_bar->separator_symbol = sstrdup(separator);
488 }
489
490 CFGFUN(bar_mode, const char *mode) {
491 current_bar->mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE));
492 }
493
494 CFGFUN(bar_hidden_state, const char *hidden_state) {
495 current_bar->hidden_state = (strcmp(hidden_state, "hide") == 0 ? S_HIDE : S_SHOW);
496 }
497
498 CFGFUN(bar_id, const char *bar_id) {
499 current_bar->id = sstrdup(bar_id);
500 }
501
502 CFGFUN(bar_output, const char *output) {
503 int new_outputs = current_bar->num_outputs + 1;
504 current_bar->outputs = srealloc(current_bar->outputs, sizeof(char *) * new_outputs);
505 current_bar->outputs[current_bar->num_outputs] = sstrdup(output);
506 current_bar->num_outputs = new_outputs;
507 }
508
509 CFGFUN(bar_verbose, const char *verbose) {
510 current_bar->verbose = eval_boolstr(verbose);
511 }
512
513 CFGFUN(bar_modifier, const char *modifiers) {
514 current_bar->modifier = modifiers ? event_state_from_str(modifiers) : XCB_NONE;
515 }
516
517 static void bar_configure_binding(const char *button, const char *release, const char *command) {
518 if (strncasecmp(button, "button", strlen("button")) != 0) {
519 ELOG("Bindings for a bar can only be mouse bindings, not \"%s\", ignoring.\n", button);
520 return;
521 }
522
523 int input_code = atoi(button + strlen("button"));
524 if (input_code < 1) {
525 ELOG("Button \"%s\" does not seem to be in format 'buttonX'.\n", button);
526 return;
527 }
528 const bool release_bool = release != NULL;
529
530 struct Barbinding *current;
531 TAILQ_FOREACH(current, &(current_bar->bar_bindings), bindings) {
532 if (current->input_code == input_code && current->release == release_bool) {
533 ELOG("command for button %s was already specified, ignoring.\n", button);
534 return;
535 }
536 }
537
538 struct Barbinding *new_binding = scalloc(1, sizeof(struct Barbinding));
539 new_binding->release = release_bool;
540 new_binding->input_code = input_code;
541 new_binding->command = sstrdup(command);
542 TAILQ_INSERT_TAIL(&(current_bar->bar_bindings), new_binding, bindings);
543 }
544
545 CFGFUN(bar_wheel_up_cmd, const char *command) {
546 ELOG("'wheel_up_cmd' is deprecated. Please us 'bindsym button4 %s' instead.\n", command);
547 bar_configure_binding("button4", NULL, command);
548 }
549
550 CFGFUN(bar_wheel_down_cmd, const char *command) {
551 ELOG("'wheel_down_cmd' is deprecated. Please us 'bindsym button5 %s' instead.\n", command);
552 bar_configure_binding("button5", NULL, command);
553 }
554
555 CFGFUN(bar_bindsym, const char *button, const char *release, const char *command) {
556 bar_configure_binding(button, release, command);
557 }
558
559 CFGFUN(bar_position, const char *position) {
560 current_bar->position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM);
561 }
562
563 CFGFUN(bar_i3bar_command, const char *i3bar_command) {
564 FREE(current_bar->i3bar_command);
565 current_bar->i3bar_command = sstrdup(i3bar_command);
566 }
567
568 CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text) {
569 #define APPLY_COLORS(classname) \
570 do { \
571 if (strcmp(colorclass, #classname) == 0) { \
572 if (text != NULL) { \
573 /* New syntax: border, background, text */ \
574 current_bar->colors.classname##_border = sstrdup(border); \
575 current_bar->colors.classname##_bg = sstrdup(background); \
576 current_bar->colors.classname##_text = sstrdup(text); \
577 } else { \
578 /* Old syntax: text, background */ \
579 current_bar->colors.classname##_bg = sstrdup(background); \
580 current_bar->colors.classname##_text = sstrdup(border); \
581 } \
582 } \
583 } while (0)
584
585 APPLY_COLORS(focused_workspace);
586 APPLY_COLORS(active_workspace);
587 APPLY_COLORS(inactive_workspace);
588 APPLY_COLORS(urgent_workspace);
589 APPLY_COLORS(binding_mode);
590
591 #undef APPLY_COLORS
592 }
593
594 CFGFUN(bar_socket_path, const char *socket_path) {
595 FREE(current_bar->socket_path);
596 current_bar->socket_path = sstrdup(socket_path);
597 }
598
599 CFGFUN(bar_tray_output, const char *output) {
600 struct tray_output_t *tray_output = scalloc(1, sizeof(struct tray_output_t));
601 tray_output->output = sstrdup(output);
602 TAILQ_INSERT_TAIL(&(current_bar->tray_outputs), tray_output, tray_outputs);
603 }
604
605 CFGFUN(bar_tray_padding, const long padding_px) {
606 current_bar->tray_padding = padding_px;
607 }
608
609 CFGFUN(bar_color_single, const char *colorclass, const char *color) {
610 if (strcmp(colorclass, "background") == 0)
611 current_bar->colors.background = sstrdup(color);
612 else if (strcmp(colorclass, "separator") == 0)
613 current_bar->colors.separator = sstrdup(color);
614 else if (strcmp(colorclass, "statusline") == 0)
615 current_bar->colors.statusline = sstrdup(color);
616 else if (strcmp(colorclass, "focused_background") == 0)
617 current_bar->colors.focused_background = sstrdup(color);
618 else if (strcmp(colorclass, "focused_separator") == 0)
619 current_bar->colors.focused_separator = sstrdup(color);
620 else
621 current_bar->colors.focused_statusline = sstrdup(color);
622 }
623
624 CFGFUN(bar_status_command, const char *command) {
625 FREE(current_bar->status_command);
626 current_bar->status_command = sstrdup(command);
627 }
628
629 CFGFUN(bar_binding_mode_indicator, const char *value) {
630 current_bar->hide_binding_mode_indicator = !eval_boolstr(value);
631 }
632
633 CFGFUN(bar_workspace_buttons, const char *value) {
634 current_bar->hide_workspace_buttons = !eval_boolstr(value);
635 }
636
637 CFGFUN(bar_strip_workspace_numbers, const char *value) {
638 current_bar->strip_workspace_numbers = eval_boolstr(value);
639 }
640
641 CFGFUN(bar_strip_workspace_name, const char *value) {
642 current_bar->strip_workspace_name = eval_boolstr(value);
643 }
644
645 CFGFUN(bar_start) {
646 current_bar = scalloc(1, sizeof(struct Barconfig));
647 TAILQ_INIT(&(current_bar->bar_bindings));
648 TAILQ_INIT(&(current_bar->tray_outputs));
649 current_bar->tray_padding = 2;
650 current_bar->modifier = XCB_KEY_BUT_MASK_MOD_4;
651 }
652
653 CFGFUN(bar_finish) {
654 DLOG("\t new bar configuration finished, saving.\n");
655 /* Generate a unique ID for this bar if not already configured */
656 if (current_bar->id == NULL)
657 sasprintf(&current_bar->id, "bar-%d", config.number_barconfigs);
658
659 config.number_barconfigs++;
660
661 /* If no font was explicitly set, we use the i3 font as default */
662 if (current_bar->font == NULL && font_pattern != NULL)
663 current_bar->font = sstrdup(font_pattern);
664
665 TAILQ_INSERT_TAIL(&barconfigs, current_bar, configs);
666 /* Simply reset the pointer, but don't free the resources. */
667 current_bar = NULL;
668 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * config_parser.c: hand-written parser to parse configuration directives.
7 *
8 * See also src/commands_parser.c for rationale on why we use a custom parser.
9 *
10 * This parser works VERY MUCH like src/commands_parser.c, so read that first.
11 * The differences are:
12 *
13 * 1. config_parser supports the 'number' token type (in addition to 'word' and
14 * 'string'). Numbers are referred to using &num (like $str).
15 *
16 * 2. Criteria are not executed immediately, they are just stored.
17 *
18 * 3. config_parser recognizes \n and \r as 'end' token, while commands_parser
19 * ignores them.
20 *
21 * 4. config_parser skips the current line on invalid inputs and follows the
22 * nearest <error> token.
23 *
24 */
25 #include "all.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <xcb/xcb_xrm.h>
38
39 // Macros to make the YAJL API a bit easier to use.
40 #define y(x, ...) yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__)
41 #define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str))
42
43 xcb_xrm_database_t *database = NULL;
44
45 #ifndef TEST_PARSER
46 pid_t config_error_nagbar_pid = -1;
47 static struct context *context;
48 #endif
49
50 /*******************************************************************************
51 * The data structures used for parsing. Essentially the current state and a
52 * list of tokens for that state.
53 *
54 * The GENERATED_* files are generated by generate-commands-parser.pl with the
55 * input parser-specs/configs.spec.
56 ******************************************************************************/
57
58 #include "GENERATED_config_enums.h"
59
60 typedef struct token {
61 char *name;
62 char *identifier;
63 /* This might be __CALL */
64 cmdp_state next_state;
65 union {
66 uint16_t call_identifier;
67 } extra;
68 } cmdp_token;
69
70 typedef struct tokenptr {
71 cmdp_token *array;
72 int n;
73 } cmdp_token_ptr;
74
75 #include "GENERATED_config_tokens.h"
76
77 /*******************************************************************************
78 * The (small) stack where identified literals are stored during the parsing
79 * of a single command (like $workspace).
80 ******************************************************************************/
81
82 struct stack_entry {
83 /* Just a pointer, not dynamically allocated. */
84 const char *identifier;
85 enum {
86 STACK_STR = 0,
87 STACK_LONG = 1,
88 } type;
89 union {
90 char *str;
91 long num;
92 } val;
93 };
94
95 /* 10 entries should be enough for everybody. */
96 static struct stack_entry stack[10];
97
98 /*
99 * Pushes a string (identified by 'identifier') on the stack. We simply use a
100 * single array, since the number of entries we have to store is very small.
101 *
102 */
103 static void push_string(const char *identifier, const char *str) {
104 for (int c = 0; c < 10; c++) {
105 if (stack[c].identifier != NULL &&
106 strcmp(stack[c].identifier, identifier) != 0)
107 continue;
108 if (stack[c].identifier == NULL) {
109 /* Found a free slot, let’s store it here. */
110 stack[c].identifier = identifier;
111 stack[c].val.str = sstrdup(str);
112 stack[c].type = STACK_STR;
113 } else {
114 /* Append the value. */
115 char *prev = stack[c].val.str;
116 sasprintf(&(stack[c].val.str), "%s,%s", prev, str);
117 free(prev);
118 }
119 return;
120 }
121
122 /* When we arrive here, the stack is full. This should not happen and
123 * means there’s either a bug in this parser or the specification
124 * contains a command with more than 10 identified tokens. */
125 fprintf(stderr, "BUG: config_parser stack full. This means either a bug "
126 "in the code, or a new command which contains more than "
127 "10 identified tokens.\n");
128 exit(1);
129 }
130
131 static void push_long(const char *identifier, long num) {
132 for (int c = 0; c < 10; c++) {
133 if (stack[c].identifier != NULL)
134 continue;
135 /* Found a free slot, let’s store it here. */
136 stack[c].identifier = identifier;
137 stack[c].val.num = num;
138 stack[c].type = STACK_LONG;
139 return;
140 }
141
142 /* When we arrive here, the stack is full. This should not happen and
143 * means there’s either a bug in this parser or the specification
144 * contains a command with more than 10 identified tokens. */
145 fprintf(stderr, "BUG: config_parser stack full. This means either a bug "
146 "in the code, or a new command which contains more than "
147 "10 identified tokens.\n");
148 exit(1);
149 }
150
151 static const char *get_string(const char *identifier) {
152 for (int c = 0; c < 10; c++) {
153 if (stack[c].identifier == NULL)
154 break;
155 if (strcmp(identifier, stack[c].identifier) == 0)
156 return stack[c].val.str;
157 }
158 return NULL;
159 }
160
161 static long get_long(const char *identifier) {
162 for (int c = 0; c < 10; c++) {
163 if (stack[c].identifier == NULL)
164 break;
165 if (strcmp(identifier, stack[c].identifier) == 0)
166 return stack[c].val.num;
167 }
168 return 0;
169 }
170
171 static void clear_stack(void) {
172 for (int c = 0; c < 10; c++) {
173 if (stack[c].type == STACK_STR)
174 free(stack[c].val.str);
175 stack[c].identifier = NULL;
176 stack[c].val.str = NULL;
177 stack[c].val.num = 0;
178 }
179 }
180
181 /*******************************************************************************
182 * The parser itself.
183 ******************************************************************************/
184
185 static cmdp_state state;
186 static Match current_match;
187 static struct ConfigResultIR subcommand_output;
188 static struct ConfigResultIR command_output;
189
190 /* A list which contains the states that lead to the current state, e.g.
191 * INITIAL, WORKSPACE_LAYOUT.
192 * When jumping back to INITIAL, statelist_idx will simply be set to 1
193 * (likewise for other states, e.g. MODE or BAR).
194 * This list is used to process the nearest error token. */
195 static cmdp_state statelist[10] = {INITIAL};
196 /* NB: statelist_idx points to where the next entry will be inserted */
197 static int statelist_idx = 1;
198
199 #include "GENERATED_config_call.h"
200
201 static void next_state(const cmdp_token *token) {
202 cmdp_state _next_state = token->next_state;
203
204 //printf("token = name %s identifier %s\n", token->name, token->identifier);
205 //printf("next_state = %d\n", token->next_state);
206 if (token->next_state == __CALL) {
207 subcommand_output.json_gen = command_output.json_gen;
208 GENERATED_call(token->extra.call_identifier, &subcommand_output);
209 _next_state = subcommand_output.next_state;
210 clear_stack();
211 }
212
213 state = _next_state;
214 if (state == INITIAL) {
215 clear_stack();
216 }
217
218 /* See if we are jumping back to a state in which we were in previously
219 * (statelist contains INITIAL) and just move statelist_idx accordingly. */
220 for (int i = 0; i < statelist_idx; i++) {
221 if (statelist[i] != _next_state)
222 continue;
223 statelist_idx = i + 1;
224 return;
225 }
226
227 /* Otherwise, the state is new and we add it to the list */
228 statelist[statelist_idx++] = _next_state;
229 }
230
231 /*
232 * Returns a pointer to the start of the line (one byte after the previous \r,
233 * \n) or the start of the input, if this is the first line.
234 *
235 */
236 static const char *start_of_line(const char *walk, const char *beginning) {
237 while (walk >= beginning && *walk != '\n' && *walk != '\r') {
238 walk--;
239 }
240
241 return walk + 1;
242 }
243
244 /*
245 * Copies the line and terminates it at the next \n, if any.
246 *
247 * The caller has to free() the result.
248 *
249 */
250 static char *single_line(const char *start) {
251 char *result = sstrdup(start);
252 char *end = strchr(result, '\n');
253 if (end != NULL)
254 *end = '\0';
255 return result;
256 }
257
258 struct ConfigResultIR *parse_config(const char *input, struct context *context) {
259 /* Dump the entire config file into the debug log. We cannot just use
260 * DLOG("%s", input); because one log message must not exceed 4 KiB. */
261 const char *dumpwalk = input;
262 int linecnt = 1;
263 while (*dumpwalk != '\0') {
264 char *next_nl = strchr(dumpwalk, '\n');
265 if (next_nl != NULL) {
266 DLOG("CONFIG(line %3d): %.*s\n", linecnt, (int)(next_nl - dumpwalk), dumpwalk);
267 dumpwalk = next_nl + 1;
268 } else {
269 DLOG("CONFIG(line %3d): %s\n", linecnt, dumpwalk);
270 break;
271 }
272 linecnt++;
273 }
274 state = INITIAL;
275 statelist_idx = 1;
276
277 /* A YAJL JSON generator used for formatting replies. */
278 command_output.json_gen = yajl_gen_alloc(NULL);
279
280 y(array_open);
281
282 const char *walk = input;
283 const size_t len = strlen(input);
284 int c;
285 const cmdp_token *token;
286 bool token_handled;
287 linecnt = 1;
288
289 // TODO: make this testable
290 #ifndef TEST_PARSER
291 cfg_criteria_init(&current_match, &subcommand_output, INITIAL);
292 #endif
293
294 /* The "<=" operator is intentional: We also handle the terminating 0-byte
295 * explicitly by looking for an 'end' token. */
296 while ((size_t)(walk - input) <= len) {
297 /* Skip whitespace before every token, newlines are relevant since they
298 * separate configuration directives. */
299 while ((*walk == ' ' || *walk == '\t') && *walk != '\0')
300 walk++;
301
302 //printf("remaining input: %s\n", walk);
303
304 cmdp_token_ptr *ptr = &(tokens[state]);
305 token_handled = false;
306 for (c = 0; c < ptr->n; c++) {
307 token = &(ptr->array[c]);
308
309 /* A literal. */
310 if (token->name[0] == '\'') {
311 if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
312 if (token->identifier != NULL)
313 push_string(token->identifier, token->name + 1);
314 walk += strlen(token->name) - 1;
315 next_state(token);
316 token_handled = true;
317 break;
318 }
319 continue;
320 }
321
322 if (strcmp(token->name, "number") == 0) {
323 /* Handle numbers. We only accept decimal numbers for now. */
324 char *end = NULL;
325 errno = 0;
326 long int num = strtol(walk, &end, 10);
327 if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
328 (errno != 0 && num == 0))
329 continue;
330
331 /* No valid numbers found */
332 if (end == walk)
333 continue;
334
335 if (token->identifier != NULL)
336 push_long(token->identifier, num);
337
338 /* Set walk to the first non-number character */
339 walk = end;
340 next_state(token);
341 token_handled = true;
342 break;
343 }
344
345 if (strcmp(token->name, "string") == 0 ||
346 strcmp(token->name, "word") == 0) {
347 const char *beginning = walk;
348 /* Handle quoted strings (or words). */
349 if (*walk == '"') {
350 beginning++;
351 walk++;
352 while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
353 walk++;
354 } else {
355 if (token->name[0] == 's') {
356 while (*walk != '\0' && *walk != '\r' && *walk != '\n')
357 walk++;
358 } else {
359 /* For a word, the delimiters are white space (' ' or
360 * '\t'), closing square bracket (]), comma (,) and
361 * semicolon (;). */
362 while (*walk != ' ' && *walk != '\t' &&
363 *walk != ']' && *walk != ',' &&
364 *walk != ';' && *walk != '\r' &&
365 *walk != '\n' && *walk != '\0')
366 walk++;
367 }
368 }
369 if (walk != beginning) {
370 char *str = scalloc(walk - beginning + 1, 1);
371 /* We copy manually to handle escaping of characters. */
372 int inpos, outpos;
373 for (inpos = 0, outpos = 0;
374 inpos < (walk - beginning);
375 inpos++, outpos++) {
376 /* We only handle escaped double quotes to not break
377 * backwards compatibility with people using \w in
378 * regular expressions etc. */
379 if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
380 inpos++;
381 str[outpos] = beginning[inpos];
382 }
383 if (token->identifier)
384 push_string(token->identifier, str);
385 free(str);
386 /* If we are at the end of a quoted string, skip the ending
387 * double quote. */
388 if (*walk == '"')
389 walk++;
390 next_state(token);
391 token_handled = true;
392 break;
393 }
394 }
395
396 if (strcmp(token->name, "line") == 0) {
397 while (*walk != '\0' && *walk != '\n' && *walk != '\r')
398 walk++;
399 next_state(token);
400 token_handled = true;
401 linecnt++;
402 walk++;
403 break;
404 }
405
406 if (strcmp(token->name, "end") == 0) {
407 //printf("checking for end: *%s*\n", walk);
408 if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
409 next_state(token);
410 token_handled = true;
411 /* To make sure we start with an appropriate matching
412 * datastructure for commands which do *not* specify any
413 * criteria, we re-initialize the criteria system after
414 * every command. */
415 // TODO: make this testable
416 #ifndef TEST_PARSER
417 cfg_criteria_init(&current_match, &subcommand_output, INITIAL);
418 #endif
419 linecnt++;
420 walk++;
421 break;
422 }
423 }
424 }
425
426 if (!token_handled) {
427 /* Figure out how much memory we will need to fill in the names of
428 * all tokens afterwards. */
429 int tokenlen = 0;
430 for (c = 0; c < ptr->n; c++)
431 tokenlen += strlen(ptr->array[c].name) + strlen("'', ");
432
433 /* Build up a decent error message. We include the problem, the
434 * full input, and underline the position where the parser
435 * currently is. */
436 char *errormessage;
437 char *possible_tokens = smalloc(tokenlen + 1);
438 char *tokenwalk = possible_tokens;
439 for (c = 0; c < ptr->n; c++) {
440 token = &(ptr->array[c]);
441 if (token->name[0] == '\'') {
442 /* A literal is copied to the error message enclosed with
443 * single quotes. */
444 *tokenwalk++ = '\'';
445 strcpy(tokenwalk, token->name + 1);
446 tokenwalk += strlen(token->name + 1);
447 *tokenwalk++ = '\'';
448 } else {
449 /* Skip error tokens in error messages, they are used
450 * internally only and might confuse users. */
451 if (strcmp(token->name, "error") == 0)
452 continue;
453 /* Any other token is copied to the error message enclosed
454 * with angle brackets. */
455 *tokenwalk++ = '<';
456 strcpy(tokenwalk, token->name);
457 tokenwalk += strlen(token->name);
458 *tokenwalk++ = '>';
459 }
460 if (c < (ptr->n - 1)) {
461 *tokenwalk++ = ',';
462 *tokenwalk++ = ' ';
463 }
464 }
465 *tokenwalk = '\0';
466 sasprintf(&errormessage, "Expected one of these tokens: %s",
467 possible_tokens);
468 free(possible_tokens);
469
470 /* Go back to the beginning of the line */
471 const char *error_line = start_of_line(walk, input);
472
473 /* Contains the same amount of characters as 'input' has, but with
474 * the unparseable part highlighted using ^ characters. */
475 char *position = scalloc(strlen(error_line) + 1, 1);
476 const char *copywalk;
477 for (copywalk = error_line;
478 *copywalk != '\n' && *copywalk != '\r' && *copywalk != '\0';
479 copywalk++)
480 position[(copywalk - error_line)] = (copywalk >= walk ? '^' : (*copywalk == '\t' ? '\t' : ' '));
481 position[(copywalk - error_line)] = '\0';
482
483 ELOG("CONFIG: %s\n", errormessage);
484 ELOG("CONFIG: (in file %s)\n", context->filename);
485 char *error_copy = single_line(error_line);
486
487 /* Print context lines *before* the error, if any. */
488 if (linecnt > 1) {
489 const char *context_p1_start = start_of_line(error_line - 2, input);
490 char *context_p1_line = single_line(context_p1_start);
491 if (linecnt > 2) {
492 const char *context_p2_start = start_of_line(context_p1_start - 2, input);
493 char *context_p2_line = single_line(context_p2_start);
494 ELOG("CONFIG: Line %3d: %s\n", linecnt - 2, context_p2_line);
495 free(context_p2_line);
496 }
497 ELOG("CONFIG: Line %3d: %s\n", linecnt - 1, context_p1_line);
498 free(context_p1_line);
499 }
500 ELOG("CONFIG: Line %3d: %s\n", linecnt, error_copy);
501 ELOG("CONFIG: %s\n", position);
502 free(error_copy);
503 /* Print context lines *after* the error, if any. */
504 for (int i = 0; i < 2; i++) {
505 char *error_line_end = strchr(error_line, '\n');
506 if (error_line_end != NULL && *(error_line_end + 1) != '\0') {
507 error_line = error_line_end + 1;
508 error_copy = single_line(error_line);
509 ELOG("CONFIG: Line %3d: %s\n", linecnt + i + 1, error_copy);
510 free(error_copy);
511 }
512 }
513
514 context->has_errors = true;
515
516 /* Format this error message as a JSON reply. */
517 y(map_open);
518 ystr("success");
519 y(bool, false);
520 /* We set parse_error to true to distinguish this from other
521 * errors. i3-nagbar is spawned upon keypresses only for parser
522 * errors. */
523 ystr("parse_error");
524 y(bool, true);
525 ystr("error");
526 ystr(errormessage);
527 ystr("input");
528 ystr(input);
529 ystr("errorposition");
530 ystr(position);
531 y(map_close);
532
533 /* Skip the rest of this line, but continue parsing. */
534 while ((size_t)(walk - input) <= len && *walk != '\n')
535 walk++;
536
537 free(position);
538 free(errormessage);
539 clear_stack();
540
541 /* To figure out in which state to go (e.g. MODE or INITIAL),
542 * we find the nearest state which contains an <error> token
543 * and follow that one. */
544 bool error_token_found = false;
545 for (int i = statelist_idx - 1; (i >= 0) && !error_token_found; i--) {
546 cmdp_token_ptr *errptr = &(tokens[statelist[i]]);
547 for (int j = 0; j < errptr->n; j++) {
548 if (strcmp(errptr->array[j].name, "error") != 0)
549 continue;
550 next_state(&(errptr->array[j]));
551 error_token_found = true;
552 break;
553 }
554 }
555
556 assert(error_token_found);
557 }
558 }
559
560 y(array_close);
561
562 return &command_output;
563 }
564
565 /*******************************************************************************
566 * Code for building the stand-alone binary test.commands_parser which is used
567 * by t/187-commands-parser.t.
568 ******************************************************************************/
569
570 #ifdef TEST_PARSER
571
572 /*
573 * Logs the given message to stdout while prefixing the current time to it,
574 * but only if debug logging was activated.
575 * This is to be called by DLOG() which includes filename/linenumber
576 *
577 */
578 void debuglog(char *fmt, ...) {
579 va_list args;
580
581 va_start(args, fmt);
582 fprintf(stdout, "# ");
583 vfprintf(stdout, fmt, args);
584 va_end(args);
585 }
586
587 void errorlog(char *fmt, ...) {
588 va_list args;
589
590 va_start(args, fmt);
591 vfprintf(stderr, fmt, args);
592 va_end(args);
593 }
594
595 static int criteria_next_state;
596
597 void cfg_criteria_init(I3_CFG, int _state) {
598 criteria_next_state = _state;
599 }
600
601 void cfg_criteria_add(I3_CFG, const char *ctype, const char *cvalue) {
602 }
603
604 void cfg_criteria_pop_state(I3_CFG) {
605 result->next_state = criteria_next_state;
606 }
607
608 int main(int argc, char *argv[]) {
609 if (argc < 2) {
610 fprintf(stderr, "Syntax: %s <command>\n", argv[0]);
611 return 1;
612 }
613 struct context context;
614 context.filename = "<stdin>";
615 parse_config(argv[1], &context);
616 }
617
618 #else
619
620 /*
621 * Goes through each line of buf (separated by \n) and checks for statements /
622 * commands which only occur in i3 v4 configuration files. If it finds any, it
623 * returns version 4, otherwise it returns version 3.
624 *
625 */
626 static int detect_version(char *buf) {
627 char *walk = buf;
628 char *line = buf;
629 while (*walk != '\0') {
630 if (*walk != '\n') {
631 walk++;
632 continue;
633 }
634
635 /* check for some v4-only statements */
636 if (strncasecmp(line, "bindcode", strlen("bindcode")) == 0 ||
637 strncasecmp(line, "force_focus_wrapping", strlen("force_focus_wrapping")) == 0 ||
638 strncasecmp(line, "# i3 config file (v4)", strlen("# i3 config file (v4)")) == 0 ||
639 strncasecmp(line, "workspace_layout", strlen("workspace_layout")) == 0) {
640 LOG("deciding for version 4 due to this line: %.*s\n", (int)(walk - line), line);
641 return 4;
642 }
643
644 /* if this is a bind statement, we can check the command */
645 if (strncasecmp(line, "bind", strlen("bind")) == 0) {
646 char *bind = strchr(line, ' ');
647 if (bind == NULL)
648 goto next;
649 while ((*bind == ' ' || *bind == '\t') && *bind != '\0')
650 bind++;
651 if (*bind == '\0')
652 goto next;
653 if ((bind = strchr(bind, ' ')) == NULL)
654 goto next;
655 while ((*bind == ' ' || *bind == '\t') && *bind != '\0')
656 bind++;
657 if (*bind == '\0')
658 goto next;
659 if (strncasecmp(bind, "layout", strlen("layout")) == 0 ||
660 strncasecmp(bind, "floating", strlen("floating")) == 0 ||
661 strncasecmp(bind, "workspace", strlen("workspace")) == 0 ||
662 strncasecmp(bind, "focus left", strlen("focus left")) == 0 ||
663 strncasecmp(bind, "focus right", strlen("focus right")) == 0 ||
664 strncasecmp(bind, "focus up", strlen("focus up")) == 0 ||
665 strncasecmp(bind, "focus down", strlen("focus down")) == 0 ||
666 strncasecmp(bind, "border normal", strlen("border normal")) == 0 ||
667 strncasecmp(bind, "border 1pixel", strlen("border 1pixel")) == 0 ||
668 strncasecmp(bind, "border pixel", strlen("border pixel")) == 0 ||
669 strncasecmp(bind, "border borderless", strlen("border borderless")) == 0 ||
670 strncasecmp(bind, "--no-startup-id", strlen("--no-startup-id")) == 0 ||
671 strncasecmp(bind, "bar", strlen("bar")) == 0) {
672 LOG("deciding for version 4 due to this line: %.*s\n", (int)(walk - line), line);
673 return 4;
674 }
675 }
676
677 next:
678 /* advance to the next line */
679 walk++;
680 line = walk;
681 }
682
683 return 3;
684 }
685
686 /*
687 * Calls i3-migrate-config-to-v4 to migrate a configuration file (input
688 * buffer).
689 *
690 * Returns the converted config file or NULL if there was an error (for
691 * example the script could not be found in $PATH or the i3 executable’s
692 * directory).
693 *
694 */
695 static char *migrate_config(char *input, off_t size) {
696 int writepipe[2];
697 int readpipe[2];
698
699 if (pipe(writepipe) != 0 ||
700 pipe(readpipe) != 0) {
701 warn("migrate_config: Could not create pipes");
702 return NULL;
703 }
704
705 pid_t pid = fork();
706 if (pid == -1) {
707 warn("Could not fork()");
708 return NULL;
709 }
710
711 /* child */
712 if (pid == 0) {
713 /* close writing end of writepipe, connect reading side to stdin */
714 close(writepipe[1]);
715 dup2(writepipe[0], 0);
716
717 /* close reading end of readpipe, connect writing side to stdout */
718 close(readpipe[0]);
719 dup2(readpipe[1], 1);
720
721 static char *argv[] = {
722 NULL, /* will be replaced by the executable path */
723 NULL};
724 exec_i3_utility("i3-migrate-config-to-v4", argv);
725 }
726
727 /* parent */
728
729 /* close reading end of the writepipe (connected to the script’s stdin) */
730 close(writepipe[0]);
731
732 /* write the whole config file to the pipe, the script will read everything
733 * immediately */
734 if (writeall(writepipe[1], input, size) == -1) {
735 warn("Could not write to pipe");
736 return NULL;
737 }
738 close(writepipe[1]);
739
740 /* close writing end of the readpipe (connected to the script’s stdout) */
741 close(readpipe[1]);
742
743 /* read the script’s output */
744 int conv_size = 65535;
745 char *converted = scalloc(conv_size, 1);
746 int read_bytes = 0, ret;
747 do {
748 if (read_bytes == conv_size) {
749 conv_size += 65535;
750 converted = srealloc(converted, conv_size);
751 }
752 ret = read(readpipe[0], converted + read_bytes, conv_size - read_bytes);
753 if (ret == -1) {
754 warn("Cannot read from pipe");
755 FREE(converted);
756 return NULL;
757 }
758 read_bytes += ret;
759 } while (ret > 0);
760
761 /* get the returncode */
762 int status;
763 wait(&status);
764 if (!WIFEXITED(status)) {
765 fprintf(stderr, "Child did not terminate normally, using old config file (will lead to broken behaviour)\n");
766 FREE(converted);
767 return NULL;
768 }
769
770 int returncode = WEXITSTATUS(status);
771 if (returncode != 0) {
772 fprintf(stderr, "Migration process exit code was != 0\n");
773 if (returncode == 2) {
774 fprintf(stderr, "could not start the migration script\n");
775 /* TODO: script was not found. tell the user to fix their system or create a v4 config */
776 } else if (returncode == 1) {
777 fprintf(stderr, "This already was a v4 config. Please add the following line to your config file:\n");
778 fprintf(stderr, "# i3 config file (v4)\n");
779 /* TODO: nag the user with a message to include a hint for i3 in their config file */
780 }
781 FREE(converted);
782 return NULL;
783 }
784
785 return converted;
786 }
787
788 /**
789 * Launch nagbar to indicate errors in the configuration file.
790 */
791 void start_config_error_nagbar(const char *configpath, bool has_errors) {
792 char *editaction, *pageraction;
793 sasprintf(&editaction, "i3-sensible-editor \"%s\" && i3-msg reload\n", configpath);
794 sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename);
795 char *argv[] = {
796 NULL, /* will be replaced by the executable path */
797 "-f",
798 (config.font.pattern ? config.font.pattern : "fixed"),
799 "-t",
800 (has_errors ? "error" : "warning"),
801 "-m",
802 (has_errors ? "You have an error in your i3 config file!" : "Your config is outdated. Please fix the warnings to make sure everything works."),
803 "-b",
804 "edit config",
805 editaction,
806 (errorfilename ? "-b" : NULL),
807 (has_errors ? "show errors" : "show warnings"),
808 pageraction,
809 NULL};
810
811 start_nagbar(&config_error_nagbar_pid, argv);
812 free(editaction);
813 free(pageraction);
814 }
815
816 /*
817 * Inserts or updates a variable assignment depending on whether it already exists.
818 *
819 */
820 static void upsert_variable(struct variables_head *variables, char *key, char *value) {
821 struct Variable *current;
822 SLIST_FOREACH(current, variables, variables) {
823 if (strcmp(current->key, key) != 0) {
824 continue;
825 }
826
827 DLOG("Updated variable: %s = %s -> %s\n", key, current->value, value);
828 FREE(current->value);
829 current->value = sstrdup(value);
830 return;
831 }
832
833 DLOG("Defined new variable: %s = %s\n", key, value);
834 struct Variable *new = scalloc(1, sizeof(struct Variable));
835 struct Variable *test = NULL, *loc = NULL;
836 new->key = sstrdup(key);
837 new->value = sstrdup(value);
838 /* ensure that the correct variable is matched in case of one being
839 * the prefix of another */
840 SLIST_FOREACH(test, variables, variables) {
841 if (strlen(new->key) >= strlen(test->key))
842 break;
843 loc = test;
844 }
845
846 if (loc == NULL) {
847 SLIST_INSERT_HEAD(variables, new, variables);
848 } else {
849 SLIST_INSERT_AFTER(loc, new, variables);
850 }
851 }
852
853 static char *get_resource(char *name) {
854 if (conn == NULL) {
855 return NULL;
856 }
857
858 /* Load the resource database lazily. */
859 if (database == NULL) {
860 database = xcb_xrm_database_from_default(conn);
861
862 if (database == NULL) {
863 ELOG("Failed to open the resource database.\n");
864
865 /* Load an empty database so we don't keep trying to load the
866 * default database over and over again. */
867 database = xcb_xrm_database_from_string("");
868
869 return NULL;
870 }
871 }
872
873 char *resource;
874 xcb_xrm_resource_get_string(database, name, NULL, &resource);
875 return resource;
876 }
877
878 /*
879 * Parses the given file by first replacing the variables, then calling
880 * parse_config and possibly launching i3-nagbar.
881 *
882 */
883 bool parse_file(const char *f, bool use_nagbar) {
884 struct variables_head variables = SLIST_HEAD_INITIALIZER(&variables);
885 int fd;
886 struct stat stbuf;
887 char *buf;
888 FILE *fstr;
889 char buffer[4096], key[512], value[4096], *continuation = NULL;
890
891 if ((fd = open(f, O_RDONLY)) == -1)
892 die("Could not open configuration file: %s\n", strerror(errno));
893
894 if (fstat(fd, &stbuf) == -1)
895 die("Could not fstat file: %s\n", strerror(errno));
896
897 buf = scalloc(stbuf.st_size + 1, 1);
898
899 if ((fstr = fdopen(fd, "r")) == NULL)
900 die("Could not fdopen: %s\n", strerror(errno));
901
902 FREE(current_config);
903 current_config = scalloc(stbuf.st_size + 1, 1);
904 if ((ssize_t)fread(current_config, 1, stbuf.st_size, fstr) != stbuf.st_size) {
905 die("Could not fread: %s\n", strerror(errno));
906 }
907 rewind(fstr);
908
909 bool invalid_sets = false;
910
911 while (!feof(fstr)) {
912 if (!continuation)
913 continuation = buffer;
914 if (fgets(continuation, sizeof(buffer) - (continuation - buffer), fstr) == NULL) {
915 if (feof(fstr))
916 break;
917 die("Could not read configuration file\n");
918 }
919 if (buffer[strlen(buffer) - 1] != '\n' && !feof(fstr)) {
920 ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer));
921 }
922
923 /* sscanf implicitly strips whitespace. */
924 value[0] = '\0';
925 const bool skip_line = (sscanf(buffer, "%511s %4095[^\n]", key, value) < 1 || strlen(key) < 3);
926 const bool comment = (key[0] == '#');
927 value[4095] = '\n';
928
929 continuation = strstr(buffer, "\\\n");
930 if (continuation) {
931 if (!comment) {
932 continue;
933 }
934 DLOG("line continuation in comment is ignored: \"%.*s\"\n", (int)strlen(buffer) - 1, buffer);
935 continuation = NULL;
936 }
937
938 strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1);
939
940 /* Skip comments and empty lines. */
941 if (skip_line || comment) {
942 continue;
943 }
944
945 if (strcasecmp(key, "set") == 0 && *value != '\0') {
946 char v_key[512];
947 char v_value[4096] = {'\0'};
948
949 if (sscanf(value, "%511s %4095[^\n]", v_key, v_value) < 1) {
950 ELOG("Failed to parse variable specification '%s', skipping it.\n", value);
951 invalid_sets = true;
952 continue;
953 }
954
955 if (v_key[0] != '$') {
956 ELOG("Malformed variable assignment, name has to start with $\n");
957 invalid_sets = true;
958 continue;
959 }
960
961 upsert_variable(&variables, v_key, v_value);
962 continue;
963 } else if (strcasecmp(key, "set_from_resource") == 0) {
964 char res_name[512] = {'\0'};
965 char v_key[512];
966 char fallback[4096] = {'\0'};
967
968 /* Ensure that this string is terminated. For example, a user might
969 * want a variable to be empty if the resource can't be found and
970 * uses
971 * set_from_resource $foo i3wm.foo
972 * Without explicitly terminating the string first, sscanf() will
973 * leave it uninitialized, causing garbage in the config.*/
974 fallback[0] = '\0';
975
976 if (sscanf(value, "%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) {
977 ELOG("Failed to parse resource specification '%s', skipping it.\n", value);
978 invalid_sets = true;
979 continue;
980 }
981
982 if (v_key[0] != '$') {
983 ELOG("Malformed variable assignment, name has to start with $\n");
984 invalid_sets = true;
985 continue;
986 }
987
988 char *res_value = get_resource(res_name);
989 if (res_value == NULL) {
990 DLOG("Could not get resource '%s', using fallback '%s'.\n", res_name, fallback);
991 res_value = sstrdup(fallback);
992 }
993
994 upsert_variable(&variables, v_key, res_value);
995 FREE(res_value);
996 continue;
997 }
998 }
999 fclose(fstr);
1000
1001 if (database != NULL) {
1002 xcb_xrm_database_free(database);
1003 /* Explicitly set the database to NULL again in case the config gets reloaded. */
1004 database = NULL;
1005 }
1006
1007 /* For every custom variable, see how often it occurs in the file and
1008 * how much extra bytes it requires when replaced. */
1009 struct Variable *current, *nearest;
1010 int extra_bytes = 0;
1011 /* We need to copy the buffer because we need to invalidate the
1012 * variables (otherwise we will count them twice, which is bad when
1013 * 'extra' is negative) */
1014 char *bufcopy = sstrdup(buf);
1015 SLIST_FOREACH(current, &variables, variables) {
1016 int extra = (strlen(current->value) - strlen(current->key));
1017 char *next;
1018 for (next = bufcopy;
1019 next < (bufcopy + stbuf.st_size) &&
1020 (next = strcasestr(next, current->key)) != NULL;
1021 next += strlen(current->key)) {
1022 *next = '_';
1023 extra_bytes += extra;
1024 }
1025 }
1026 FREE(bufcopy);
1027
1028 /* Then, allocate a new buffer and copy the file over to the new one,
1029 * but replace occurrences of our variables */
1030 char *walk = buf, *destwalk;
1031 char *new = scalloc(stbuf.st_size + extra_bytes + 1, 1);
1032 destwalk = new;
1033 while (walk < (buf + stbuf.st_size)) {
1034 /* Find the next variable */
1035 SLIST_FOREACH(current, &variables, variables)
1036 current->next_match = strcasestr(walk, current->key);
1037 nearest = NULL;
1038 int distance = stbuf.st_size;
1039 SLIST_FOREACH(current, &variables, variables) {
1040 if (current->next_match == NULL)
1041 continue;
1042 if ((current->next_match - walk) < distance) {
1043 distance = (current->next_match - walk);
1044 nearest = current;
1045 }
1046 }
1047 if (nearest == NULL) {
1048 /* If there are no more variables, we just copy the rest */
1049 strncpy(destwalk, walk, (buf + stbuf.st_size) - walk);
1050 destwalk += (buf + stbuf.st_size) - walk;
1051 *destwalk = '\0';
1052 break;
1053 } else {
1054 /* Copy until the next variable, then copy its value */
1055 strncpy(destwalk, walk, distance);
1056 strncpy(destwalk + distance, nearest->value, strlen(nearest->value));
1057 walk += distance + strlen(nearest->key);
1058 destwalk += distance + strlen(nearest->value);
1059 }
1060 }
1061
1062 /* analyze the string to find out whether this is an old config file (3.x)
1063 * or a new config file (4.x). If it’s old, we run the converter script. */
1064 int version = detect_version(buf);
1065 if (version == 3) {
1066 /* We need to convert this v3 configuration */
1067 char *converted = migrate_config(new, strlen(new));
1068 if (converted != NULL) {
1069 ELOG("\n");
1070 ELOG("****************************************************************\n");
1071 ELOG("NOTE: Automatically converted configuration file from v3 to v4.\n");
1072 ELOG("\n");
1073 ELOG("Please convert your config file to v4. You can use this command:\n");
1074 ELOG(" mv %s %s.O\n", f, f);
1075 ELOG(" i3-migrate-config-to-v4 %s.O > %s\n", f, f);
1076 ELOG("****************************************************************\n");
1077 ELOG("\n");
1078 free(new);
1079 new = converted;
1080 } else {
1081 LOG("\n");
1082 LOG("**********************************************************************\n");
1083 LOG("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4\n");
1084 LOG("was not correctly installed on your system?\n");
1085 LOG("**********************************************************************\n");
1086 LOG("\n");
1087 }
1088 }
1089
1090 context = scalloc(1, sizeof(struct context));
1091 context->filename = f;
1092
1093 struct ConfigResultIR *config_output = parse_config(new, context);
1094 yajl_gen_free(config_output->json_gen);
1095
1096 extract_workspace_names_from_bindings();
1097 check_for_duplicate_bindings(context);
1098 reorder_bindings();
1099
1100 if (use_nagbar && (context->has_errors || context->has_warnings || invalid_sets)) {
1101 ELOG("FYI: You are using i3 version %s\n", i3_version);
1102 if (version == 3)
1103 ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
1104
1105 start_config_error_nagbar(f, context->has_errors || invalid_sets);
1106 }
1107
1108 bool has_errors = context->has_errors;
1109
1110 FREE(context->line_copy);
1111 free(context);
1112 free(new);
1113 free(buf);
1114
1115 while (!SLIST_EMPTY(&variables)) {
1116 current = SLIST_FIRST(&variables);
1117 FREE(current->key);
1118 FREE(current->value);
1119 SLIST_REMOVE_HEAD(&variables, variables);
1120 FREE(current);
1121 }
1122
1123 return !has_errors;
1124 }
1125
1126 #endif
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * display_version.c: displays the running i3 version, runs as part of
7 * i3 --moreversion.
8 *
9 */
10 #include "all.h"
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/wait.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <fcntl.h>
18 #include <time.h>
19
20 static bool human_readable_key, loaded_config_file_name_key;
21 static char *human_readable_version, *loaded_config_file_name;
22
23 static int version_string(void *ctx, const unsigned char *val, size_t len) {
24 if (human_readable_key)
25 sasprintf(&human_readable_version, "%.*s", (int)len, val);
26 if (loaded_config_file_name_key)
27 sasprintf(&loaded_config_file_name, "%.*s", (int)len, val);
28 return 1;
29 }
30
31 static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
32 human_readable_key = (stringlen == strlen("human_readable") &&
33 strncmp((const char *)stringval, "human_readable", strlen("human_readable")) == 0);
34 loaded_config_file_name_key = (stringlen == strlen("loaded_config_file_name") &&
35 strncmp((const char *)stringval, "loaded_config_file_name", strlen("loaded_config_file_name")) == 0);
36 return 1;
37 }
38
39 static yajl_callbacks version_callbacks = {
40 .yajl_string = version_string,
41 .yajl_map_key = version_map_key,
42 };
43
44 /*
45 * Connects to i3 to find out the currently running version. Useful since it
46 * might be different from the version compiled into this binary (maybe the
47 * user didn’t correctly install i3 or forgot te restart it).
48 *
49 * The output looks like this:
50 * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
51 *
52 * The i3 binary you just called: /home/michael/i3/i3
53 * The i3 binary you are running: /home/michael/i3/i3
54 *
55 */
56 void display_running_version(void) {
57 char *pid_from_atom = root_atom_contents("I3_PID", conn, conn_screen);
58 if (pid_from_atom == NULL) {
59 /* If I3_PID is not set, the running version is older than 4.2-200. */
60 printf("\nRunning version: < 4.2-200\n");
61 exit(EXIT_SUCCESS);
62 }
63
64 /* Inform the user of what we are doing. While a single IPC request is
65 * really fast normally, in case i3 hangs, this will not terminate. */
66 printf("(Getting version from running i3, press ctrl-c to abort…)");
67 fflush(stdout);
68
69 int sockfd = ipc_connect(NULL);
70 if (ipc_send_message(sockfd, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION,
71 (uint8_t *)"") == -1)
72 err(EXIT_FAILURE, "IPC: write()");
73
74 uint32_t reply_length;
75 uint32_t reply_type;
76 uint8_t *reply;
77 int ret;
78 if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
79 if (ret == -1)
80 err(EXIT_FAILURE, "IPC: read()");
81 exit(EXIT_FAILURE);
82 }
83
84 if (reply_type != I3_IPC_MESSAGE_TYPE_GET_VERSION)
85 errx(EXIT_FAILURE, "Got reply type %d, but expected %d (GET_VERSION)", reply_type, I3_IPC_MESSAGE_TYPE_GET_VERSION);
86
87 yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
88
89 yajl_status state = yajl_parse(handle, (const unsigned char *)reply, (int)reply_length);
90 if (state != yajl_status_ok)
91 errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply);
92
93 printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom);
94
95 if (loaded_config_file_name) {
96 struct stat sb;
97 time_t now;
98 char mtime[64];
99 printf("Loaded i3 config: %s", loaded_config_file_name);
100 if (stat(loaded_config_file_name, &sb) == -1) {
101 printf("\n");
102 ELOG("Cannot stat config file \"%s\"\n", loaded_config_file_name);
103 } else {
104 strftime(mtime, sizeof(mtime), "%c", localtime(&(sb.st_mtime)));
105 time(&now);
106 printf(" (Last modified: %s, %.f seconds ago)\n", mtime, difftime(now, sb.st_mtime));
107 }
108 }
109
110 #ifdef __linux__
111 size_t destpath_size = 1024;
112 ssize_t linksize;
113 char *exepath;
114 char *destpath = smalloc(destpath_size);
115
116 sasprintf(&exepath, "/proc/%d/exe", getpid());
117
118 while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
119 destpath_size = destpath_size * 2;
120 destpath = srealloc(destpath, destpath_size);
121 }
122 if (linksize == -1)
123 err(EXIT_FAILURE, "readlink(%s)", exepath);
124
125 /* readlink() does not NULL-terminate strings, so we have to. */
126 destpath[linksize] = '\0';
127
128 printf("\n");
129 printf("The i3 binary you just called: %s\n", destpath);
130
131 free(exepath);
132 sasprintf(&exepath, "/proc/%s/exe", pid_from_atom);
133
134 while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
135 destpath_size = destpath_size * 2;
136 destpath = srealloc(destpath, destpath_size);
137 }
138 if (linksize == -1)
139 err(EXIT_FAILURE, "readlink(%s)", exepath);
140
141 /* readlink() does not NULL-terminate strings, so we have to. */
142 destpath[linksize] = '\0';
143
144 /* Check if "(deleted)" is the readlink result. If so, the running version
145 * does not match the file on disk. */
146 if (strstr(destpath, "(deleted)") != NULL)
147 printf("RUNNING BINARY DIFFERENT FROM BINARY ON DISK!\n");
148
149 /* Since readlink() might put a "(deleted)" somewhere in the buffer and
150 * stripping that out seems hackish and ugly, we read the process’s argv[0]
151 * instead. */
152 free(exepath);
153 sasprintf(&exepath, "/proc/%s/cmdline", pid_from_atom);
154
155 int fd;
156 if ((fd = open(exepath, O_RDONLY)) == -1)
157 err(EXIT_FAILURE, "open(%s)", exepath);
158 if (read(fd, destpath, sizeof(destpath)) == -1)
159 err(EXIT_FAILURE, "read(%s)", exepath);
160 close(fd);
161
162 printf("The i3 binary you are running: %s\n", destpath);
163
164 free(exepath);
165 free(destpath);
166 #endif
167
168 yajl_free(handle);
169 free(reply);
170 free(pid_from_atom);
171 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * ewmh.c: Get/set certain EWMH properties easily.
7 *
8 */
9 #include "all.h"
10
11 xcb_window_t ewmh_window;
12
13 /*
14 * Updates _NET_CURRENT_DESKTOP with the current desktop number.
15 *
16 * EWMH: The index of the current desktop. This is always an integer between 0
17 * and _NET_NUMBER_OF_DESKTOPS - 1.
18 *
19 */
20 void ewmh_update_current_desktop(void) {
21 const uint32_t idx = ewmh_get_workspace_index(focused);
22 if (idx != NET_WM_DESKTOP_NONE) {
23 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
24 }
25 }
26
27 /*
28 * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
29 * noninternal workspaces.
30 */
31 void ewmh_update_number_of_desktops(void) {
32 Con *output;
33 uint32_t idx = 0;
34
35 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
36 Con *ws;
37 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
38 if (STARTS_WITH(ws->name, "__"))
39 continue;
40 ++idx;
41 }
42 }
43
44 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
45 A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx);
46 }
47
48 /*
49 * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
50 * list of NULL-terminated strings in UTF-8 encoding"
51 */
52 void ewmh_update_desktop_names(void) {
53 Con *output;
54 int msg_length = 0;
55
56 /* count the size of the property message to set */
57 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
58 Con *ws;
59 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
60 if (STARTS_WITH(ws->name, "__"))
61 continue;
62 msg_length += strlen(ws->name) + 1;
63 }
64 }
65
66 char desktop_names[msg_length];
67 int current_position = 0;
68
69 /* fill the buffer with the names of the i3 workspaces */
70 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
71 Con *ws;
72 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
73 if (STARTS_WITH(ws->name, "__"))
74 continue;
75
76 for (size_t i = 0; i < strlen(ws->name) + 1; i++) {
77 desktop_names[current_position++] = ws->name[i];
78 }
79 }
80 }
81
82 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
83 A__NET_DESKTOP_NAMES, A_UTF8_STRING, 8, msg_length, desktop_names);
84 }
85
86 /*
87 * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
88 * define the top left corner of each desktop's viewport.
89 */
90 void ewmh_update_desktop_viewport(void) {
91 Con *output;
92 int num_desktops = 0;
93 /* count number of desktops */
94 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
95 Con *ws;
96 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
97 if (STARTS_WITH(ws->name, "__"))
98 continue;
99
100 num_desktops++;
101 }
102 }
103
104 uint32_t viewports[num_desktops * 2];
105
106 int current_position = 0;
107 /* fill the viewport buffer */
108 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
109 Con *ws;
110 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
111 if (STARTS_WITH(ws->name, "__"))
112 continue;
113
114 viewports[current_position++] = output->rect.x;
115 viewports[current_position++] = output->rect.y;
116 }
117 }
118
119 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
120 A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports);
121 }
122
123 static void ewmh_update_wm_desktop_recursively(Con *con, const uint32_t desktop) {
124 Con *child;
125
126 /* Recursively call this to descend through the entire subtree. */
127 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
128 ewmh_update_wm_desktop_recursively(child, desktop);
129 }
130
131 /* If con is a workspace, we also need to go through the floating windows on it. */
132 if (con->type == CT_WORKSPACE) {
133 TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
134 ewmh_update_wm_desktop_recursively(child, desktop);
135 }
136 }
137
138 if (!con_has_managed_window(con))
139 return;
140
141 uint32_t wm_desktop = desktop;
142 /* Sticky windows are only actually sticky when they are floating or inside
143 * a floating container. This is technically still slightly wrong, since
144 * sticky windows will only be on all workspaces on this output, but we
145 * ignore multi-monitor situations for this since the spec isn't too
146 * precise on this anyway. */
147 if (con_is_sticky(con) && con_is_floating(con)) {
148 wm_desktop = NET_WM_DESKTOP_ALL;
149 }
150
151 /* If the window is on the scratchpad we assign the sticky value to it
152 * since showing it works on any workspace. We cannot remove the property
153 * as per specification. */
154 Con *ws = con_get_workspace(con);
155 if (ws != NULL && con_is_internal(ws)) {
156 wm_desktop = NET_WM_DESKTOP_ALL;
157 }
158
159 /* If this is the cached value, we don't need to do anything. */
160 if (con->window->wm_desktop == wm_desktop)
161 return;
162 con->window->wm_desktop = wm_desktop;
163
164 const xcb_window_t window = con->window->id;
165 if (wm_desktop != NET_WM_DESKTOP_NONE) {
166 DLOG("Setting _NET_WM_DESKTOP = %d for window 0x%08x.\n", wm_desktop, window);
167 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, A__NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &wm_desktop);
168 } else {
169 /* If we can't determine the workspace index, delete the property. We'd
170 * rather not set it than lie. */
171 ELOG("Failed to determine the proper EWMH desktop index for window 0x%08x, deleting _NET_WM_DESKTOP.\n", window);
172 xcb_delete_property(conn, window, A__NET_WM_DESKTOP);
173 }
174 }
175
176 /*
177 * Updates _NET_WM_DESKTOP for all windows.
178 * A request will only be made if the cached value differs from the calculated value.
179 *
180 */
181 void ewmh_update_wm_desktop(void) {
182 uint32_t desktop = 0;
183
184 Con *output;
185 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
186 Con *workspace;
187 TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) {
188 ewmh_update_wm_desktop_recursively(workspace, desktop);
189
190 if (!con_is_internal(workspace)) {
191 ++desktop;
192 }
193 }
194 }
195 }
196
197 /*
198 * Updates _NET_ACTIVE_WINDOW with the currently focused window.
199 *
200 * EWMH: The window ID of the currently active window or None if no window has
201 * the focus.
202 *
203 */
204 void ewmh_update_active_window(xcb_window_t window) {
205 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
206 A__NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1, &window);
207 }
208
209 /*
210 * Updates _NET_WM_VISIBLE_NAME.
211 *
212 */
213 void ewmh_update_visible_name(xcb_window_t window, const char *name) {
214 if (name != NULL) {
215 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, A__NET_WM_VISIBLE_NAME, A_UTF8_STRING, 8, strlen(name), name);
216 } else {
217 xcb_delete_property(conn, window, A__NET_WM_VISIBLE_NAME);
218 }
219 }
220
221 /*
222 * i3 currently does not support _NET_WORKAREA, because it does not correspond
223 * to i3’s concept of workspaces. See also:
224 * https://bugs.i3wm.org/539
225 * https://bugs.i3wm.org/301
226 * https://bugs.i3wm.org/1038
227 *
228 * We need to actively delete this property because some display managers (e.g.
229 * LightDM) set it.
230 *
231 * EWMH: Contains a geometry for each desktop. These geometries specify an area
232 * that is completely contained within the viewport. Work area SHOULD be used by
233 * desktop applications to place desktop icons appropriately.
234 *
235 */
236 void ewmh_update_workarea(void) {
237 xcb_delete_property(conn, root, A__NET_WORKAREA);
238 }
239
240 /*
241 * Updates the _NET_CLIENT_LIST hint.
242 *
243 */
244 void ewmh_update_client_list(xcb_window_t *list, int num_windows) {
245 xcb_change_property(
246 conn,
247 XCB_PROP_MODE_REPLACE,
248 root,
249 A__NET_CLIENT_LIST,
250 XCB_ATOM_WINDOW,
251 32,
252 num_windows,
253 list);
254 }
255
256 /*
257 * Updates the _NET_CLIENT_LIST_STACKING hint.
258 *
259 */
260 void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows) {
261 xcb_change_property(
262 conn,
263 XCB_PROP_MODE_REPLACE,
264 root,
265 A__NET_CLIENT_LIST_STACKING,
266 XCB_ATOM_WINDOW,
267 32,
268 num_windows,
269 stack);
270 }
271
272 /*
273 * Set or remove _NET_WM_STATE_STICKY on the window.
274 *
275 */
276 void ewmh_update_sticky(xcb_window_t window, bool sticky) {
277 if (sticky) {
278 DLOG("Setting _NET_WM_STATE_STICKY for window = %d.\n", window);
279 xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_STICKY);
280 } else {
281 DLOG("Removing _NET_WM_STATE_STICKY for window = %d.\n", window);
282 xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_STICKY);
283 }
284 }
285
286 /*
287 * Set or remove _NEW_WM_STATE_FOCUSED on the window.
288 *
289 */
290 void ewmh_update_focused(xcb_window_t window, bool is_focused) {
291 if (is_focused) {
292 DLOG("Setting _NET_WM_STATE_FOCUSED for window = %d.\n", window);
293 xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
294 } else {
295 DLOG("Removing _NET_WM_STATE_FOCUSED for window = %d.\n", window);
296 xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
297 }
298 }
299
300 /*
301 * Set up the EWMH hints on the root window.
302 *
303 */
304 void ewmh_setup_hints(void) {
305 xcb_atom_t supported_atoms[] = {
306 #define xmacro(atom) A_##atom,
307 #include "atoms_NET_SUPPORTED.xmacro"
308 #undef xmacro
309 };
310
311 /* Set up the window manager’s name. According to EWMH, section "Root Window
312 * Properties", to indicate that an EWMH-compliant window manager is
313 * present, a child window has to be created (and kept alive as long as the
314 * window manager is running) which has the _NET_SUPPORTING_WM_CHECK and
315 * _NET_WM_ATOMS. */
316 ewmh_window = xcb_generate_id(conn);
317 /* We create the window and put it at (-1, -1) so that it is off-screen. */
318 xcb_create_window(
319 conn,
320 XCB_COPY_FROM_PARENT, /* depth */
321 ewmh_window, /* window id */
322 root, /* parent */
323 -1, -1, 1, 1, /* dimensions (x, y, w, h) */
324 0, /* border */
325 XCB_WINDOW_CLASS_INPUT_ONLY, /* window class */
326 XCB_COPY_FROM_PARENT, /* visual */
327 XCB_CW_OVERRIDE_REDIRECT,
328 (uint32_t[]){1});
329 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, ewmh_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &ewmh_window);
330 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, ewmh_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
331 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &ewmh_window);
332
333 /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
334 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
335
336 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, /* number of atoms */ sizeof(supported_atoms) / sizeof(xcb_atom_t), supported_atoms);
337
338 /* We need to map this window to be able to set the input focus to it if no other window is available to be focused. */
339 xcb_map_window(conn, ewmh_window);
340 xcb_configure_window(conn, ewmh_window, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_BELOW});
341 }
342
343 /*
344 * Returns the workspace container as enumerated by the EWMH desktop model.
345 * Returns NULL if no workspace could be found for the index.
346 *
347 * This is the reverse of ewmh_get_workspace_index.
348 *
349 */
350 Con *ewmh_get_workspace_by_index(uint32_t idx) {
351 if (idx == NET_WM_DESKTOP_NONE)
352 return NULL;
353
354 uint32_t current_index = 0;
355
356 Con *output;
357 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
358 Con *workspace;
359 TAILQ_FOREACH(workspace, &(output_get_content(output)->nodes_head), nodes) {
360 if (con_is_internal(workspace))
361 continue;
362
363 if (current_index == idx)
364 return workspace;
365
366 ++current_index;
367 }
368 }
369
370 return NULL;
371 }
372
373 /*
374 * Returns the EWMH desktop index for the workspace the given container is on.
375 * Returns NET_WM_DESKTOP_NONE if the desktop index cannot be determined.
376 *
377 * This is the reverse of ewmh_get_workspace_by_index.
378 *
379 */
380 uint32_t ewmh_get_workspace_index(Con *con) {
381 uint32_t index = 0;
382
383 Con *workspace = con_get_workspace(con);
384 Con *output;
385 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
386 Con *current;
387 TAILQ_FOREACH(current, &(output_get_content(output)->nodes_head), nodes) {
388 if (con_is_internal(current))
389 continue;
390
391 if (current == workspace)
392 return index;
393
394 ++index;
395 }
396 }
397
398 return NET_WM_DESKTOP_NONE;
399 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * Faking outputs is useful in pathological situations (like network X servers
7 * which don’t support multi-monitor in a useful way) and for our testsuite.
8 *
9 */
10 #include "all.h"
11
12 static int num_screens;
13
14 /*
15 * Looks in outputs for the Output whose start coordinates are x, y
16 *
17 */
18 static Output *get_screen_at(unsigned int x, unsigned int y) {
19 Output *output;
20 TAILQ_FOREACH(output, &outputs, outputs)
21 if (output->rect.x == x && output->rect.y == y)
22 return output;
23
24 return NULL;
25 }
26
27 /*
28 * Creates outputs according to the given specification.
29 * The specification must be in the format wxh+x+y, for example 1024x768+0+0,
30 * optionally followed by 'P' to indicate a primary output,
31 * with multiple outputs separated by commas:
32 * 1900x1200+0+0P,1280x1024+1900+0
33 *
34 */
35 void fake_outputs_init(const char *output_spec) {
36 const char *walk = output_spec;
37 unsigned int x, y, width, height;
38 int chars_consumed;
39 while (sscanf(walk, "%ux%u+%u+%u%n", &width, &height, &x, &y, &chars_consumed) == 4) {
40 walk += chars_consumed;
41 bool primary = false;
42 if (*walk == 'P') {
43 primary = true;
44 walk++;
45 }
46 if (*walk == ',')
47 walk++; /* Skip delimiter */
48 DLOG("Parsed output as width = %u, height = %u at (%u, %u)%s\n",
49 width, height, x, y, primary ? " (primary)" : "");
50
51 Output *new_output = get_screen_at(x, y);
52 if (new_output != NULL) {
53 DLOG("Re-used old output %p\n", new_output);
54 /* This screen already exists. We use the littlest screen so that the user
55 can always see the complete workspace */
56 new_output->rect.width = min(new_output->rect.width, width);
57 new_output->rect.height = min(new_output->rect.height, height);
58 } else {
59 struct output_name *output_name = scalloc(1, sizeof(struct output_name));
60 new_output = scalloc(1, sizeof(Output));
61 sasprintf(&(output_name->name), "fake-%d", num_screens);
62 SLIST_INIT(&(new_output->names_head));
63 SLIST_INSERT_HEAD(&(new_output->names_head), output_name, names);
64 DLOG("Created new fake output %s (%p)\n", output_primary_name(new_output), new_output);
65 new_output->active = true;
66 new_output->rect.x = x;
67 new_output->rect.y = y;
68 new_output->rect.width = width;
69 new_output->rect.height = height;
70 /* We always treat the screen at 0x0 as the primary screen */
71 if (new_output->rect.x == 0 && new_output->rect.y == 0)
72 TAILQ_INSERT_HEAD(&outputs, new_output, outputs);
73 else
74 TAILQ_INSERT_TAIL(&outputs, new_output, outputs);
75 output_init_con(new_output);
76 init_ws_for_output(new_output, output_get_content(new_output->con));
77 num_screens++;
78 }
79 new_output->primary = primary;
80 }
81
82 if (num_screens == 0) {
83 ELOG("No screens found. Please fix your setup. i3 will exit now.\n");
84 exit(0);
85 }
86 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * floating.c: Floating windows.
7 *
8 */
9 #include "all.h"
10
11 #ifndef MAX
12 #define MAX(x, y) ((x) > (y) ? (x) : (y))
13 #endif
14
15 /*
16 * Calculates sum of heights and sum of widths of all currently active outputs
17 *
18 */
19 static Rect total_outputs_dimensions(void) {
20 if (TAILQ_EMPTY(&outputs))
21 return (Rect){0, 0, root_screen->width_in_pixels, root_screen->height_in_pixels};
22
23 Output *output;
24 /* Use Rect to encapsulate dimensions, ignoring x/y */
25 Rect outputs_dimensions = {0, 0, 0, 0};
26 TAILQ_FOREACH(output, &outputs, outputs) {
27 outputs_dimensions.height += output->rect.height;
28 outputs_dimensions.width += output->rect.width;
29 }
30 return outputs_dimensions;
31 }
32
33 /*
34 * Updates I3_FLOATING_WINDOW by either setting or removing it on the con and
35 * all its children.
36 *
37 */
38 static void floating_set_hint_atom(Con *con, bool floating) {
39 if (!con_is_leaf(con)) {
40 Con *child;
41 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
42 floating_set_hint_atom(child, floating);
43 }
44 }
45
46 if (con->window == NULL) {
47 return;
48 }
49
50 if (floating) {
51 uint32_t val = 1;
52 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
53 A_I3_FLOATING_WINDOW, XCB_ATOM_CARDINAL, 32, 1, &val);
54 } else {
55 xcb_delete_property(conn, con->window->id, A_I3_FLOATING_WINDOW);
56 }
57
58 xcb_flush(conn);
59 }
60
61 /*
62 * Called when a floating window is created or resized.
63 * This function resizes the window if its size is higher or lower than the
64 * configured maximum/minimum size, respectively.
65 *
66 */
67 void floating_check_size(Con *floating_con) {
68 /* Define reasonable minimal and maximal sizes for floating windows */
69 const int floating_sane_min_height = 50;
70 const int floating_sane_min_width = 75;
71 Rect floating_sane_max_dimensions;
72 Con *focused_con = con_descend_focused(floating_con);
73
74 Rect border_rect = con_border_style_rect(focused_con);
75 /* We have to do the opposite calculations that render_con() do
76 * to get the exact size we want. */
77 border_rect.width = -border_rect.width;
78 border_rect.width += 2 * focused_con->border_width;
79 border_rect.height = -border_rect.height;
80 border_rect.height += 2 * focused_con->border_width;
81 if (con_border_style(focused_con) == BS_NORMAL) {
82 border_rect.height += render_deco_height();
83 }
84
85 if (focused_con->window != NULL) {
86 if (focused_con->window->min_width) {
87 floating_con->rect.width -= border_rect.width;
88 floating_con->rect.width = max(floating_con->rect.width, focused_con->window->min_width);
89 floating_con->rect.width += border_rect.width;
90 }
91
92 if (focused_con->window->min_height) {
93 floating_con->rect.height -= border_rect.height;
94 floating_con->rect.height = max(floating_con->rect.height, focused_con->window->min_height);
95 floating_con->rect.height += border_rect.height;
96 }
97
98 if (focused_con->window->max_width) {
99 floating_con->rect.width -= border_rect.width;
100 floating_con->rect.width = min(floating_con->rect.width, focused_con->window->max_width);
101 floating_con->rect.width += border_rect.width;
102 }
103
104 if (focused_con->window->max_height) {
105 floating_con->rect.height -= border_rect.height;
106 floating_con->rect.height = min(floating_con->rect.height, focused_con->window->max_height);
107 floating_con->rect.height += border_rect.height;
108 }
109
110 if (focused_con->window->height_increment &&
111 floating_con->rect.height >= focused_con->window->base_height + border_rect.height) {
112 floating_con->rect.height -= focused_con->window->base_height + border_rect.height;
113 floating_con->rect.height -= floating_con->rect.height % focused_con->window->height_increment;
114 floating_con->rect.height += focused_con->window->base_height + border_rect.height;
115 }
116
117 if (focused_con->window->width_increment &&
118 floating_con->rect.width >= focused_con->window->base_width + border_rect.width) {
119 floating_con->rect.width -= focused_con->window->base_width + border_rect.width;
120 floating_con->rect.width -= floating_con->rect.width % focused_con->window->width_increment;
121 floating_con->rect.width += focused_con->window->base_width + border_rect.width;
122 }
123 }
124
125 /* Unless user requests otherwise (-1), raise the width/height to
126 * reasonable minimum dimensions */
127 if (config.floating_minimum_height != -1) {
128 floating_con->rect.height -= border_rect.height;
129 if (config.floating_minimum_height == 0) {
130 floating_con->rect.height = max(floating_con->rect.height, floating_sane_min_height);
131 } else {
132 floating_con->rect.height = max(floating_con->rect.height, config.floating_minimum_height);
133 }
134 floating_con->rect.height += border_rect.height;
135 }
136
137 if (config.floating_minimum_width != -1) {
138 floating_con->rect.width -= border_rect.width;
139 if (config.floating_minimum_width == 0) {
140 floating_con->rect.width = max(floating_con->rect.width, floating_sane_min_width);
141 } else {
142 floating_con->rect.width = max(floating_con->rect.width, config.floating_minimum_width);
143 }
144 floating_con->rect.width += border_rect.width;
145 }
146
147 /* Unless user requests otherwise (-1), ensure width/height do not exceed
148 * configured maxima or, if unconfigured, limit to combined width of all
149 * outputs */
150 floating_sane_max_dimensions = total_outputs_dimensions();
151 if (config.floating_maximum_height != -1) {
152 floating_con->rect.height -= border_rect.height;
153 if (config.floating_maximum_height == 0) {
154 floating_con->rect.height = min(floating_con->rect.height, floating_sane_max_dimensions.height);
155 } else {
156 floating_con->rect.height = min(floating_con->rect.height, config.floating_maximum_height);
157 }
158 floating_con->rect.height += border_rect.height;
159 }
160
161 if (config.floating_maximum_width != -1) {
162 floating_con->rect.width -= border_rect.width;
163 if (config.floating_maximum_width == 0) {
164 floating_con->rect.width = min(floating_con->rect.width, floating_sane_max_dimensions.width);
165 } else {
166 floating_con->rect.width = min(floating_con->rect.width, config.floating_maximum_width);
167 }
168 floating_con->rect.width += border_rect.width;
169 }
170 }
171
172 void floating_enable(Con *con, bool automatic) {
173 bool set_focus = (con == focused);
174
175 if (con_is_docked(con)) {
176 LOG("Container is a dock window, not enabling floating mode.\n");
177 return;
178 }
179
180 if (con_is_floating(con)) {
181 LOG("Container is already in floating mode, not doing anything.\n");
182 return;
183 }
184
185 if (con->type == CT_WORKSPACE) {
186 LOG("Container is a workspace, not enabling floating mode.\n");
187 return;
188 }
189
190 Con *focus_head_placeholder = NULL;
191 bool focus_before_parent = true;
192 if (!set_focus) {
193 /* Find recursively the ancestor container which is a child of our workspace.
194 * We need to reuse its focus position later. */
195 Con *ancestor = con;
196 while (ancestor->parent->type != CT_WORKSPACE) {
197 focus_before_parent &= TAILQ_FIRST(&(ancestor->parent->focus_head)) == ancestor;
198 ancestor = ancestor->parent;
199 }
200 /* Consider the part of the focus stack of our current workspace:
201 * [ ... S_{i-1} S_{i} S_{i+1} ... ]
202 * Where S_{x} is a container tree and the container 'con' that is beeing switched to
203 * floating belongs in S_{i}. The new floating container, 'nc', will have the
204 * workspace as its parent so it needs to be placed in this stack. If C was focused
205 * we just need to call con_focus(). Otherwise, nc must be placed before or after S_{i}.
206 * We should avoid using the S_{i} container for our operations since it might get
207 * killed if it has no other children. So, the two possible positions are after S_{i-1}
208 * or before S_{i+1}.
209 */
210 if (focus_before_parent) {
211 focus_head_placeholder = TAILQ_PREV(ancestor, focus_head, focused);
212 } else {
213 focus_head_placeholder = TAILQ_NEXT(ancestor, focused);
214 }
215 }
216
217 /* 1: detach the container from its parent */
218 /* TODO: refactor this with tree_close_internal() */
219 con_detach(con);
220 con_fix_percent(con->parent);
221
222 /* 2: create a new container to render the decoration on, add
223 * it as a floating window to the workspace */
224 Con *nc = con_new(NULL, NULL);
225 /* we need to set the parent afterwards instead of passing it as an
226 * argument to con_new() because nc would be inserted into the tiling layer
227 * otherwise. */
228 Con *ws = con_get_workspace(con);
229 nc->parent = ws;
230 nc->type = CT_FLOATING_CON;
231 nc->layout = L_SPLITH;
232 /* We insert nc already, even though its rect is not yet calculated. This
233 * is necessary because otherwise the workspace might be empty (and get
234 * closed in tree_close_internal()) even though it’s not. */
235 TAILQ_INSERT_HEAD(&(ws->floating_head), nc, floating_windows);
236
237 struct focus_head *fh = &(ws->focus_head);
238 if (focus_before_parent) {
239 if (focus_head_placeholder) {
240 TAILQ_INSERT_AFTER(fh, focus_head_placeholder, nc, focused);
241 } else {
242 TAILQ_INSERT_HEAD(fh, nc, focused);
243 }
244 } else {
245 if (focus_head_placeholder) {
246 TAILQ_INSERT_BEFORE(focus_head_placeholder, nc, focused);
247 } else {
248 /* Also used for the set_focus case */
249 TAILQ_INSERT_TAIL(fh, nc, focused);
250 }
251 }
252
253 /* check if the parent container is empty and close it if so */
254 if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) &&
255 con_num_children(con->parent) == 0) {
256 DLOG("Old container empty after setting this child to floating, closing\n");
257 Con *parent = con->parent;
258 /* clear the pointer before calling tree_close_internal in which the memory is freed */
259 con->parent = NULL;
260 tree_close_internal(parent, DONT_KILL_WINDOW, false);
261 }
262
263 char *name;
264 sasprintf(&name, "[i3 con] floatingcon around %p", con);
265 x_set_name(nc, name);
266 free(name);
267
268 /* find the height for the decorations */
269 int deco_height = render_deco_height();
270
271 DLOG("Original rect: (%d, %d) with %d x %d\n", con->rect.x, con->rect.y, con->rect.width, con->rect.height);
272 DLOG("Geometry = (%d, %d) with %d x %d\n", con->geometry.x, con->geometry.y, con->geometry.width, con->geometry.height);
273 Rect zero = {0, 0, 0, 0};
274 nc->rect = con->geometry;
275 /* If the geometry was not set (split containers), we need to determine a
276 * sensible one by combining the geometry of all children */
277 if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) {
278 DLOG("Geometry not set, combining children\n");
279 Con *child;
280 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
281 DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
282 nc->rect.width += child->geometry.width;
283 nc->rect.height = max(nc->rect.height, child->geometry.height);
284 }
285 }
286
287 TAILQ_INSERT_TAIL(&(nc->nodes_head), con, nodes);
288 TAILQ_INSERT_TAIL(&(nc->focus_head), con, focused);
289
290 /* 3: attach the child to the new parent container. We need to do this
291 * because con_border_style_rect() needs to access con->parent. */
292 con->parent = nc;
293 con->percent = 1.0;
294 con->floating = FLOATING_USER_ON;
295
296 /* 4: set the border style as specified with new_float */
297 if (automatic)
298 con->border_style = config.default_floating_border;
299
300 /* Add pixels for the decoration. */
301 Rect border_style_rect = con_border_style_rect(con);
302
303 nc->rect.height -= border_style_rect.height;
304 nc->rect.width -= border_style_rect.width;
305
306 /* Add some more pixels for the title bar */
307 if (con_border_style(con) == BS_NORMAL) {
308 nc->rect.height += deco_height;
309 }
310
311 /* Honor the X11 border */
312 nc->rect.height += con->border_width * 2;
313 nc->rect.width += con->border_width * 2;
314
315 floating_check_size(nc);
316
317 /* Some clients (like GIMP’s color picker window) get mapped
318 * to (0, 0), so we push them to a reasonable position
319 * (centered over their leader) */
320 if (nc->rect.x == 0 && nc->rect.y == 0) {
321 Con *leader;
322 if (con->window && con->window->leader != XCB_NONE &&
323 (leader = con_by_window_id(con->window->leader)) != NULL) {
324 DLOG("Centering above leader\n");
325 floating_center(nc, leader->rect);
326 } else {
327 /* center the window on workspace as fallback */
328 floating_center(nc, ws->rect);
329 }
330 }
331
332 /* Sanity check: Are the coordinates on the appropriate output? If not, we
333 * need to change them */
334 Output *current_output = get_output_from_rect(nc->rect);
335 Con *correct_output = con_get_output(ws);
336 if (!current_output || current_output->con != correct_output) {
337 DLOG("This floating window is on the wrong output, fixing coordinates (currently (%d, %d))\n",
338 nc->rect.x, nc->rect.y);
339
340 /* If moving from one output to another, keep the relative position
341 * consistent (e.g. a centered dialog will remain centered). */
342 if (current_output) {
343 floating_fix_coordinates(nc, &current_output->con->rect, &correct_output->rect);
344 /* Make sure that the result is in the correct output. */
345 current_output = get_output_from_rect(nc->rect);
346 }
347 if (!current_output || current_output->con != correct_output) {
348 floating_center(nc, ws->rect);
349 }
350 }
351
352 DLOG("Floating rect: (%d, %d) with %d x %d\n", nc->rect.x, nc->rect.y, nc->rect.width, nc->rect.height);
353
354 /* 5: Subtract the deco_height in order to make the floating window appear
355 * at precisely the position it specified in its original geometry (which
356 * is what applications might remember). */
357 deco_height = (con->border_style == BS_NORMAL ? render_deco_height() : 0);
358 nc->rect.y -= deco_height;
359
360 DLOG("Corrected y = %d (deco_height = %d)\n", nc->rect.y, deco_height);
361
362 /* render the cons to get initial window_rect correct */
363 render_con(nc, false);
364 render_con(con, false);
365
366 if (set_focus)
367 con_activate(con);
368
369 floating_set_hint_atom(nc, true);
370 ipc_send_window_event("floating", con);
371 }
372
373 void floating_disable(Con *con, bool automatic) {
374 if (!con_is_floating(con)) {
375 LOG("Container isn't floating, not doing anything.\n");
376 return;
377 }
378
379 Con *ws = con_get_workspace(con);
380 if (con_is_internal(ws)) {
381 LOG("Can't disable floating for container in internal workspace.\n");
382 return;
383 }
384 Con *tiling_focused = con_descend_tiling_focused(ws);
385
386 if (tiling_focused->type == CT_WORKSPACE) {
387 Con *parent = con->parent;
388 con_detach(con);
389 con->parent = NULL;
390 tree_close_internal(parent, DONT_KILL_WINDOW, true);
391 con_attach(con, tiling_focused, false);
392 con->percent = 0.0;
393 con_fix_percent(con->parent);
394 } else {
395 insert_con_into(con, tiling_focused, AFTER);
396 }
397
398 con->floating = FLOATING_USER_OFF;
399 floating_set_hint_atom(con, false);
400 ipc_send_window_event("floating", con);
401 }
402
403 /*
404 * Toggles floating mode for the given container.
405 *
406 * If the automatic flag is set to true, this was an automatic update by a change of the
407 * window class from the application which can be overwritten by the user.
408 *
409 */
410 void toggle_floating_mode(Con *con, bool automatic) {
411 /* forbid the command to toggle floating on a CT_FLOATING_CON */
412 if (con->type == CT_FLOATING_CON) {
413 ELOG("Cannot toggle floating mode on con = %p because it is of type CT_FLOATING_CON.\n", con);
414 return;
415 }
416
417 /* see if the client is already floating */
418 if (con_is_floating(con)) {
419 LOG("already floating, re-setting to tiling\n");
420
421 floating_disable(con, automatic);
422 return;
423 }
424
425 floating_enable(con, automatic);
426 }
427
428 /*
429 * Raises the given container in the list of floating containers
430 *
431 */
432 void floating_raise_con(Con *con) {
433 DLOG("Raising floating con %p / %s\n", con, con->name);
434 TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows);
435 TAILQ_INSERT_TAIL(&(con->parent->floating_head), con, floating_windows);
436 }
437
438 /*
439 * Checks if con’s coordinates are within its workspace and re-assigns it to
440 * the actual workspace if not.
441 *
442 */
443 bool floating_maybe_reassign_ws(Con *con) {
444 Output *output = get_output_from_rect(con->rect);
445
446 if (!output) {
447 ELOG("No output found at destination coordinates?\n");
448 return false;
449 }
450
451 if (con_get_output(con) == output->con) {
452 DLOG("still the same ws\n");
453 return false;
454 }
455
456 DLOG("Need to re-assign!\n");
457
458 Con *content = output_get_content(output->con);
459 Con *ws = TAILQ_FIRST(&(content->focus_head));
460 DLOG("Moving con %p / %s to workspace %p / %s\n", con, con->name, ws, ws->name);
461 con_move_to_workspace(con, ws, false, true, false);
462 workspace_show(ws);
463 con_activate(con_descend_focused(con));
464 return true;
465 }
466
467 /*
468 * Centers a floating con above the specified rect.
469 *
470 */
471 void floating_center(Con *con, Rect rect) {
472 con->rect.x = rect.x + (rect.width / 2) - (con->rect.width / 2);
473 con->rect.y = rect.y + (rect.height / 2) - (con->rect.height / 2);
474 }
475
476 /*
477 * Moves the given floating con to the current pointer position.
478 *
479 */
480 void floating_move_to_pointer(Con *con) {
481 assert(con->type == CT_FLOATING_CON);
482
483 xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL);
484 if (reply == NULL) {
485 ELOG("could not query pointer position, not moving this container\n");
486 return;
487 }
488
489 Output *output = get_output_containing(reply->root_x, reply->root_y);
490 if (output == NULL) {
491 ELOG("The pointer is not on any output, cannot move the container here.\n");
492 return;
493 }
494
495 /* Determine where to put the window. */
496 int32_t x = reply->root_x - con->rect.width / 2;
497 int32_t y = reply->root_y - con->rect.height / 2;
498 FREE(reply);
499
500 /* Correct target coordinates to be in-bounds. */
501 x = MAX(x, (int32_t)output->rect.x);
502 y = MAX(y, (int32_t)output->rect.y);
503 if (x + con->rect.width > output->rect.x + output->rect.width)
504 x = output->rect.x + output->rect.width - con->rect.width;
505 if (y + con->rect.height > output->rect.y + output->rect.height)
506 y = output->rect.y + output->rect.height - con->rect.height;
507
508 /* Update container's coordinates to position it correctly. */
509 floating_reposition(con, (Rect){x, y, con->rect.width, con->rect.height});
510 }
511
512 DRAGGING_CB(drag_window_callback) {
513 const struct xcb_button_press_event_t *event = extra;
514
515 /* Reposition the client correctly while moving */
516 con->rect.x = old_rect->x + (new_x - event->root_x);
517 con->rect.y = old_rect->y + (new_y - event->root_y);
518
519 render_con(con, false);
520 x_push_node(con);
521 xcb_flush(conn);
522
523 /* Check if we cross workspace boundaries while moving */
524 if (!floating_maybe_reassign_ws(con))
525 return;
526 /* Ensure not to warp the pointer while dragging */
527 x_set_warp_to(NULL);
528 tree_render();
529 }
530
531 /*
532 * Called when the user clicked on the titlebar of a floating window.
533 * Calls the drag_pointer function with the drag_window callback
534 *
535 */
536 void floating_drag_window(Con *con, const xcb_button_press_event_t *event) {
537 DLOG("floating_drag_window\n");
538
539 /* Push changes before dragging, so that the window gets raised now and not
540 * after the user releases the mouse button */
541 tree_render();
542
543 /* Store the initial rect in case of user revert/cancel */
544 Rect initial_rect = con->rect;
545
546 /* Drag the window */
547 drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, XCURSOR_CURSOR_MOVE, drag_window_callback, event);
548
549 if (!con_exists(con)) {
550 DLOG("The container has been closed in the meantime.\n");
551 return;
552 }
553
554 /* If the user cancelled, undo the changes. */
555 if (drag_result == DRAG_REVERT) {
556 floating_reposition(con, initial_rect);
557 return;
558 }
559
560 /* If this is a scratchpad window, don't auto center it from now on. */
561 if (con->scratchpad_state == SCRATCHPAD_FRESH)
562 con->scratchpad_state = SCRATCHPAD_CHANGED;
563
564 tree_render();
565 }
566
567 /*
568 * This is an ugly data structure which we need because there is no standard
569 * way of having nested functions (only available as a gcc extension at the
570 * moment, clang doesn’t support it) or blocks (only available as a clang
571 * extension and only on Mac OS X systems at the moment).
572 *
573 */
574 struct resize_window_callback_params {
575 const border_t corner;
576 const bool proportional;
577 const xcb_button_press_event_t *event;
578 };
579
580 DRAGGING_CB(resize_window_callback) {
581 const struct resize_window_callback_params *params = extra;
582 const xcb_button_press_event_t *event = params->event;
583 border_t corner = params->corner;
584
585 int32_t dest_x = con->rect.x;
586 int32_t dest_y = con->rect.y;
587 uint32_t dest_width;
588 uint32_t dest_height;
589
590 double ratio = (double)old_rect->width / old_rect->height;
591
592 /* First guess: We resize by exactly the amount the mouse moved,
593 * taking into account in which corner the client was grabbed */
594 if (corner & BORDER_LEFT)
595 dest_width = old_rect->width - (new_x - event->root_x);
596 else
597 dest_width = old_rect->width + (new_x - event->root_x);
598
599 if (corner & BORDER_TOP)
600 dest_height = old_rect->height - (new_y - event->root_y);
601 else
602 dest_height = old_rect->height + (new_y - event->root_y);
603
604 /* User wants to keep proportions, so we may have to adjust our values */
605 if (params->proportional) {
606 dest_width = max(dest_width, (int)(dest_height * ratio));
607 dest_height = max(dest_height, (int)(dest_width / ratio));
608 }
609
610 con->rect = (Rect){dest_x, dest_y, dest_width, dest_height};
611
612 /* Obey window size */
613 floating_check_size(con);
614
615 /* If not the lower right corner is grabbed, we must also reposition
616 * the client by exactly the amount we resized it */
617 if (corner & BORDER_LEFT)
618 dest_x = old_rect->x + (old_rect->width - con->rect.width);
619
620 if (corner & BORDER_TOP)
621 dest_y = old_rect->y + (old_rect->height - con->rect.height);
622
623 con->rect.x = dest_x;
624 con->rect.y = dest_y;
625
626 /* TODO: don’t re-render the whole tree just because we change
627 * coordinates of a floating window */
628 tree_render();
629 x_push_changes(croot);
630 }
631
632 /*
633 * Called when the user clicked on a floating window while holding the
634 * floating_modifier and the right mouse button.
635 * Calls the drag_pointer function with the resize_window callback
636 *
637 */
638 void floating_resize_window(Con *con, const bool proportional,
639 const xcb_button_press_event_t *event) {
640 DLOG("floating_resize_window\n");
641
642 /* corner saves the nearest corner to the original click. It contains
643 * a bitmask of the nearest borders (BORDER_LEFT, BORDER_RIGHT, …) */
644 border_t corner = 0;
645
646 if (event->event_x <= (int16_t)(con->rect.width / 2))
647 corner |= BORDER_LEFT;
648 else
649 corner |= BORDER_RIGHT;
650
651 int cursor = 0;
652 if (event->event_y <= (int16_t)(con->rect.height / 2)) {
653 corner |= BORDER_TOP;
654 cursor = (corner & BORDER_LEFT) ? XCURSOR_CURSOR_TOP_LEFT_CORNER : XCURSOR_CURSOR_TOP_RIGHT_CORNER;
655 } else {
656 corner |= BORDER_BOTTOM;
657 cursor = (corner & BORDER_LEFT) ? XCURSOR_CURSOR_BOTTOM_LEFT_CORNER : XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER;
658 }
659
660 struct resize_window_callback_params params = {corner, proportional, event};
661
662 /* get the initial rect in case of revert/cancel */
663 Rect initial_rect = con->rect;
664
665 drag_result_t drag_result = drag_pointer(con, event, XCB_NONE, BORDER_TOP /* irrelevant */, cursor, resize_window_callback, &params);
666
667 if (!con_exists(con)) {
668 DLOG("The container has been closed in the meantime.\n");
669 return;
670 }
671
672 /* If the user cancels, undo the resize */
673 if (drag_result == DRAG_REVERT)
674 floating_reposition(con, initial_rect);
675
676 /* If this is a scratchpad window, don't auto center it from now on. */
677 if (con->scratchpad_state == SCRATCHPAD_FRESH)
678 con->scratchpad_state = SCRATCHPAD_CHANGED;
679 }
680
681 /* Custom data structure used to track dragging-related events. */
682 struct drag_x11_cb {
683 ev_prepare prepare;
684
685 /* Whether this modal event loop should be exited and with which result. */
686 drag_result_t result;
687
688 /* The container that is being dragged or resized, or NULL if this is a
689 * drag of the resize handle. */
690 Con *con;
691
692 /* The dimensions of con when the loop was started. */
693 Rect old_rect;
694
695 /* The callback to invoke after every pointer movement. */
696 callback_t callback;
697
698 /* User data pointer for callback. */
699 const void *extra;
700 };
701
702 static bool drain_drag_events(EV_P, struct drag_x11_cb *dragloop) {
703 xcb_motion_notify_event_t *last_motion_notify = NULL;
704 xcb_generic_event_t *event;
705
706 while ((event = xcb_poll_for_event(conn)) != NULL) {
707 if (event->response_type == 0) {
708 xcb_generic_error_t *error = (xcb_generic_error_t *)event;
709 DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n",
710 error->sequence, error->error_code);
711 free(event);
712 continue;
713 }
714
715 /* Strip off the highest bit (set if the event is generated) */
716 int type = (event->response_type & 0x7F);
717
718 switch (type) {
719 case XCB_BUTTON_RELEASE:
720 dragloop->result = DRAG_SUCCESS;
721 break;
722
723 case XCB_KEY_PRESS:
724 DLOG("A key was pressed during drag, reverting changes.\n");
725 dragloop->result = DRAG_REVERT;
726 handle_event(type, event);
727 break;
728
729 case XCB_UNMAP_NOTIFY: {
730 xcb_unmap_notify_event_t *unmap_event = (xcb_unmap_notify_event_t *)event;
731 Con *con = con_by_window_id(unmap_event->window);
732
733 if (con != NULL) {
734 DLOG("UnmapNotify for window 0x%08x (container %p)\n", unmap_event->window, con);
735
736 if (con_get_workspace(con) == con_get_workspace(focused)) {
737 DLOG("UnmapNotify for a managed window on the current workspace, aborting\n");
738 dragloop->result = DRAG_ABORT;
739 }
740 }
741
742 handle_event(type, event);
743 break;
744 }
745
746 case XCB_MOTION_NOTIFY:
747 /* motion_notify events are saved for later */
748 FREE(last_motion_notify);
749 last_motion_notify = (xcb_motion_notify_event_t *)event;
750 break;
751
752 default:
753 DLOG("Passing to original handler\n");
754 handle_event(type, event);
755 break;
756 }
757
758 if (last_motion_notify != (xcb_motion_notify_event_t *)event)
759 free(event);
760
761 if (dragloop->result != DRAGGING) {
762 ev_break(EV_A_ EVBREAK_ONE);
763 if (dragloop->result == DRAG_SUCCESS) {
764 /* Ensure motion notify events are handled. */
765 break;
766 } else {
767 free(last_motion_notify);
768 return true;
769 }
770 }
771 }
772
773 if (last_motion_notify == NULL) {
774 return true;
775 }
776
777 /* Ensure that we are either dragging the resize handle (con is NULL) or that the
778 * container still exists. The latter might not be true, e.g., if the window closed
779 * for any reason while the user was dragging it. */
780 if (!dragloop->con || con_exists(dragloop->con)) {
781 dragloop->callback(
782 dragloop->con,
783 &(dragloop->old_rect),
784 last_motion_notify->root_x,
785 last_motion_notify->root_y,
786 dragloop->extra);
787 }
788 FREE(last_motion_notify);
789
790 xcb_flush(conn);
791 return dragloop->result != DRAGGING;
792 }
793
794 static void xcb_drag_prepare_cb(EV_P_ ev_prepare *w, int revents) {
795 struct drag_x11_cb *dragloop = (struct drag_x11_cb *)w->data;
796 while (!drain_drag_events(EV_A, dragloop)) {
797 /* repeatedly drain events: draining might produce additional ones */
798 }
799 }
800
801 /*
802 * This function grabs your pointer and keyboard and lets you drag stuff around
803 * (borders). Every time you move your mouse, an XCB_MOTION_NOTIFY event will
804 * be received and the given callback will be called with the parameters
805 * specified (client, border on which the click originally was), the original
806 * rect of the client, the event and the new coordinates (x, y).
807 *
808 */
809 drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t confine_to,
810 border_t border, int cursor, callback_t callback, const void *extra) {
811 xcb_cursor_t xcursor = (cursor && xcursor_supported) ? xcursor_get_cursor(cursor) : XCB_NONE;
812
813 /* Grab the pointer */
814 xcb_grab_pointer_cookie_t cookie;
815 xcb_grab_pointer_reply_t *reply;
816 xcb_generic_error_t *error;
817
818 cookie = xcb_grab_pointer(conn,
819 false, /* get all pointer events specified by the following mask */
820 root, /* grab the root window */
821 XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, /* which events to let through */
822 XCB_GRAB_MODE_ASYNC, /* pointer events should continue as normal */
823 XCB_GRAB_MODE_ASYNC, /* keyboard mode */
824 confine_to, /* confine_to = in which window should the cursor stay */
825 xcursor, /* possibly display a special cursor */
826 XCB_CURRENT_TIME);
827
828 if ((reply = xcb_grab_pointer_reply(conn, cookie, &error)) == NULL) {
829 ELOG("Could not grab pointer (error_code = %d)\n", error->error_code);
830 free(error);
831 return DRAG_ABORT;
832 }
833
834 free(reply);
835
836 /* Grab the keyboard */
837 xcb_grab_keyboard_cookie_t keyb_cookie;
838 xcb_grab_keyboard_reply_t *keyb_reply;
839
840 keyb_cookie = xcb_grab_keyboard(conn,
841 false, /* get all keyboard events */
842 root, /* grab the root window */
843 XCB_CURRENT_TIME,
844 XCB_GRAB_MODE_ASYNC, /* continue processing pointer events as normal */
845 XCB_GRAB_MODE_ASYNC /* keyboard mode */
846 );
847
848 if ((keyb_reply = xcb_grab_keyboard_reply(conn, keyb_cookie, &error)) == NULL) {
849 ELOG("Could not grab keyboard (error_code = %d)\n", error->error_code);
850 free(error);
851 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
852 return DRAG_ABORT;
853 }
854
855 free(keyb_reply);
856
857 /* Go into our own event loop */
858 struct drag_x11_cb loop = {
859 .result = DRAGGING,
860 .con = con,
861 .callback = callback,
862 .extra = extra,
863 };
864 ev_prepare *prepare = &loop.prepare;
865 if (con)
866 loop.old_rect = con->rect;
867 ev_prepare_init(prepare, xcb_drag_prepare_cb);
868 prepare->data = &loop;
869 main_set_x11_cb(false);
870 ev_prepare_start(main_loop, prepare);
871
872 ev_loop(main_loop, 0);
873
874 ev_prepare_stop(main_loop, prepare);
875 main_set_x11_cb(true);
876
877 xcb_ungrab_keyboard(conn, XCB_CURRENT_TIME);
878 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
879 xcb_flush(conn);
880
881 return loop.result;
882 }
883
884 /*
885 * Repositions the CT_FLOATING_CON to have the coordinates specified by
886 * newrect, but only if the coordinates are not out-of-bounds. Also reassigns
887 * the floating con to a different workspace if this move was across different
888 * outputs.
889 *
890 */
891 bool floating_reposition(Con *con, Rect newrect) {
892 /* Sanity check: Are the new coordinates on any output? If not, we
893 * ignore that request. */
894 if (!output_containing_rect(newrect)) {
895 ELOG("No output found at destination coordinates. Not repositioning.\n");
896 return false;
897 }
898
899 con->rect = newrect;
900
901 bool reassigned = floating_maybe_reassign_ws(con);
902
903 /* If this is a scratchpad window, don't auto center it from now on. */
904 if (con->scratchpad_state == SCRATCHPAD_FRESH)
905 con->scratchpad_state = SCRATCHPAD_CHANGED;
906
907 /* Workspace change will already result in a tree_render. */
908 if (!reassigned) {
909 tree_render();
910 }
911 return true;
912 }
913
914 /*
915 * Sets size of the CT_FLOATING_CON to specified dimensions. Might limit the
916 * actual size with regard to size constraints taken from user settings.
917 * Additionally, the dimensions may be upscaled until they're divisible by the
918 * window's size hints.
919 *
920 */
921 void floating_resize(Con *floating_con, int x, int y) {
922 DLOG("floating resize to %dx%d px\n", x, y);
923 Rect *rect = &floating_con->rect;
924 Con *focused_con = con_descend_focused(floating_con);
925 if (focused_con->window == NULL) {
926 DLOG("No window is focused. Not resizing.\n");
927 return;
928 }
929 int wi = focused_con->window->width_increment;
930 int hi = focused_con->window->height_increment;
931 rect->width = x;
932 rect->height = y;
933 if (wi)
934 rect->width += (wi - 1 - rect->width) % wi;
935 if (hi)
936 rect->height += (hi - 1 - rect->height) % hi;
937
938 floating_check_size(floating_con);
939
940 /* If this is a scratchpad window, don't auto center it from now on. */
941 if (floating_con->scratchpad_state == SCRATCHPAD_FRESH)
942 floating_con->scratchpad_state = SCRATCHPAD_CHANGED;
943 }
944
945 /*
946 * Fixes the coordinates of the floating window whenever the window gets
947 * reassigned to a different output (or when the output’s rect changes).
948 *
949 */
950 void floating_fix_coordinates(Con *con, Rect *old_rect, Rect *new_rect) {
951 DLOG("Fixing coordinates of floating window %p (rect (%d, %d), %d x %d)\n",
952 con, con->rect.x, con->rect.y, con->rect.width, con->rect.height);
953 DLOG("old_rect = (%d, %d), %d x %d\n",
954 old_rect->x, old_rect->y, old_rect->width, old_rect->height);
955 DLOG("new_rect = (%d, %d), %d x %d\n",
956 new_rect->x, new_rect->y, new_rect->width, new_rect->height);
957 /* First we get the x/y coordinates relative to the x/y coordinates
958 * of the output on which the window is on */
959 int32_t rel_x = con->rect.x - old_rect->x + (int32_t)(con->rect.width / 2);
960 int32_t rel_y = con->rect.y - old_rect->y + (int32_t)(con->rect.height / 2);
961 /* Then we calculate a fraction, for example 0.63 for a window
962 * which is at y = 1212 of a 1920 px high output */
963 DLOG("rel_x = %d, rel_y = %d, fraction_x = %f, fraction_y = %f, output->w = %d, output->h = %d\n",
964 rel_x, rel_y, (double)rel_x / old_rect->width, (double)rel_y / old_rect->height,
965 old_rect->width, old_rect->height);
966 /* Here we have to multiply at first. Or we will lose precision when not compiled with -msse2 */
967 con->rect.x = (int32_t)new_rect->x + (double)(rel_x * (int32_t)new_rect->width) / (int32_t)old_rect->width - (int32_t)(con->rect.width / 2);
968 con->rect.y = (int32_t)new_rect->y + (double)(rel_y * (int32_t)new_rect->height) / (int32_t)old_rect->height - (int32_t)(con->rect.height / 2);
969 DLOG("Resulting coordinates: x = %d, y = %d\n", con->rect.x, con->rect.y);
970 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * handlers.c: Small handlers for various events (keypresses, focus changes,
7 * …).
8 *
9 */
10 #include "all.h"
11
12 #include <time.h>
13 #include <float.h>
14 #include <sys/time.h>
15 #include <xcb/randr.h>
16 #define SN_API_NOT_YET_FROZEN 1
17 #include <libsn/sn-monitor.h>
18
19 int randr_base = -1;
20 int xkb_base = -1;
21 int xkb_current_group;
22
23 /* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
24 since it’d trigger an infinite loop of switching between the different windows when
25 changing workspaces */
26 static SLIST_HEAD(ignore_head, Ignore_Event) ignore_events;
27
28 /*
29 * Adds the given sequence to the list of events which are ignored.
30 * If this ignore should only affect a specific response_type, pass
31 * response_type, otherwise, pass -1.
32 *
33 * Every ignored sequence number gets garbage collected after 5 seconds.
34 *
35 */
36 void add_ignore_event(const int sequence, const int response_type) {
37 struct Ignore_Event *event = smalloc(sizeof(struct Ignore_Event));
38
39 event->sequence = sequence;
40 event->response_type = response_type;
41 event->added = time(NULL);
42
43 SLIST_INSERT_HEAD(&ignore_events, event, ignore_events);
44 }
45
46 /*
47 * Checks if the given sequence is ignored and returns true if so.
48 *
49 */
50 bool event_is_ignored(const int sequence, const int response_type) {
51 struct Ignore_Event *event;
52 time_t now = time(NULL);
53 for (event = SLIST_FIRST(&ignore_events); event != SLIST_END(&ignore_events);) {
54 if ((now - event->added) > 5) {
55 struct Ignore_Event *save = event;
56 event = SLIST_NEXT(event, ignore_events);
57 SLIST_REMOVE(&ignore_events, save, Ignore_Event, ignore_events);
58 free(save);
59 } else
60 event = SLIST_NEXT(event, ignore_events);
61 }
62
63 SLIST_FOREACH(event, &ignore_events, ignore_events) {
64 if (event->sequence != sequence)
65 continue;
66
67 if (event->response_type != -1 &&
68 event->response_type != response_type)
69 continue;
70
71 /* instead of removing a sequence number we better wait until it gets
72 * garbage collected. it may generate multiple events (there are multiple
73 * enter_notifies for one configure_request, for example). */
74 //SLIST_REMOVE(&ignore_events, event, Ignore_Event, ignore_events);
75 //free(event);
76 return true;
77 }
78
79 return false;
80 }
81
82 /*
83 * Called with coordinates of an enter_notify event or motion_notify event
84 * to check if the user crossed virtual screen boundaries and adjust the
85 * current workspace, if so.
86 *
87 */
88 static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
89 Output *output;
90
91 /* If the user disable focus follows mouse, we have nothing to do here */
92 if (config.disable_focus_follows_mouse)
93 return;
94
95 if ((output = get_output_containing(x, y)) == NULL) {
96 ELOG("ERROR: No such screen\n");
97 return;
98 }
99
100 if (output->con == NULL) {
101 ELOG("ERROR: The screen is not recognized by i3 (no container associated)\n");
102 return;
103 }
104
105 /* Focus the output on which the user moved their cursor */
106 Con *old_focused = focused;
107 Con *next = con_descend_focused(output_get_content(output->con));
108 /* Since we are switching outputs, this *must* be a different workspace, so
109 * call workspace_show() */
110 workspace_show(con_get_workspace(next));
111 con_focus(next);
112
113 /* If the focus changed, we re-render to get updated decorations */
114 if (old_focused != focused)
115 tree_render();
116 }
117
118 /*
119 * When the user moves the mouse pointer onto a window, this callback gets called.
120 *
121 */
122 static void handle_enter_notify(xcb_enter_notify_event_t *event) {
123 Con *con;
124
125 last_timestamp = event->time;
126
127 DLOG("enter_notify for %08x, mode = %d, detail %d, serial %d\n",
128 event->event, event->mode, event->detail, event->sequence);
129 DLOG("coordinates %d, %d\n", event->event_x, event->event_y);
130 if (event->mode != XCB_NOTIFY_MODE_NORMAL) {
131 DLOG("This was not a normal notify, ignoring\n");
132 return;
133 }
134 /* Some events are not interesting, because they were not generated
135 * actively by the user, but by reconfiguration of windows */
136 if (event_is_ignored(event->sequence, XCB_ENTER_NOTIFY)) {
137 DLOG("Event ignored\n");
138 return;
139 }
140
141 bool enter_child = false;
142 /* Get container by frame or by child window */
143 if ((con = con_by_frame_id(event->event)) == NULL) {
144 con = con_by_window_id(event->event);
145 enter_child = true;
146 }
147
148 /* If we cannot find the container, the user moved their cursor to the root
149 * window. In this case and if they used it to a dock, we need to focus the
150 * workspace on the correct output. */
151 if (con == NULL || con->parent->type == CT_DOCKAREA) {
152 DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y);
153 check_crossing_screen_boundary(event->root_x, event->root_y);
154 return;
155 }
156
157 /* see if the user entered the window on a certain window decoration */
158 layout_t layout = (enter_child ? con->parent->layout : con->layout);
159 if (layout == L_DEFAULT) {
160 Con *child;
161 TAILQ_FOREACH(child, &(con->nodes_head), nodes)
162 if (rect_contains(child->deco_rect, event->event_x, event->event_y)) {
163 LOG("using child %p / %s instead!\n", child, child->name);
164 con = child;
165 break;
166 }
167 }
168
169 if (config.disable_focus_follows_mouse)
170 return;
171
172 /* if this container is already focused, there is nothing to do. */
173 if (con == focused)
174 return;
175
176 /* Get the currently focused workspace to check if the focus change also
177 * involves changing workspaces. If so, we need to call workspace_show() to
178 * correctly update state and send the IPC event. */
179 Con *ws = con_get_workspace(con);
180 if (ws != con_get_workspace(focused))
181 workspace_show(ws);
182
183 focused_id = XCB_NONE;
184 con_focus(con_descend_focused(con));
185 tree_render();
186 }
187
188 /*
189 * When the user moves the mouse but does not change the active window
190 * (e.g. when having no windows opened but moving mouse on the root screen
191 * and crossing virtual screen boundaries), this callback gets called.
192 *
193 */
194 static void handle_motion_notify(xcb_motion_notify_event_t *event) {
195 last_timestamp = event->time;
196
197 /* Skip events where the pointer was over a child window, we are only
198 * interested in events on the root window. */
199 if (event->child != XCB_NONE)
200 return;
201
202 Con *con;
203 if ((con = con_by_frame_id(event->event)) == NULL) {
204 DLOG("MotionNotify for an unknown container, checking if it crosses screen boundaries.\n");
205 check_crossing_screen_boundary(event->root_x, event->root_y);
206 return;
207 }
208
209 if (config.disable_focus_follows_mouse)
210 return;
211
212 if (con->layout != L_DEFAULT && con->layout != L_SPLITV && con->layout != L_SPLITH)
213 return;
214
215 /* see over which rect the user is */
216 Con *current;
217 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
218 if (!rect_contains(current->deco_rect, event->event_x, event->event_y))
219 continue;
220
221 /* We found the rect, let’s see if this window is focused */
222 if (TAILQ_FIRST(&(con->focus_head)) == current)
223 return;
224
225 con_focus(current);
226 x_push_changes(croot);
227 return;
228 }
229 }
230
231 /*
232 * Called when the keyboard mapping changes (for example by using Xmodmap),
233 * we need to update our key bindings then (re-translate symbols).
234 *
235 */
236 static void handle_mapping_notify(xcb_mapping_notify_event_t *event) {
237 if (event->request != XCB_MAPPING_KEYBOARD &&
238 event->request != XCB_MAPPING_MODIFIER)
239 return;
240
241 DLOG("Received mapping_notify for keyboard or modifier mapping, re-grabbing keys\n");
242 xcb_refresh_keyboard_mapping(keysyms, event);
243
244 xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
245
246 ungrab_all_keys(conn);
247 translate_keysyms();
248 grab_all_keys(conn);
249 }
250
251 /*
252 * A new window appeared on the screen (=was mapped), so let’s manage it.
253 *
254 */
255 static void handle_map_request(xcb_map_request_event_t *event) {
256 xcb_get_window_attributes_cookie_t cookie;
257
258 cookie = xcb_get_window_attributes_unchecked(conn, event->window);
259
260 DLOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence);
261 add_ignore_event(event->sequence, -1);
262
263 manage_window(event->window, cookie, false);
264 }
265
266 /*
267 * Configure requests are received when the application wants to resize windows
268 * on their own.
269 *
270 * We generate a synthethic configure notify event to signalize the client its
271 * "new" position.
272 *
273 */
274 static void handle_configure_request(xcb_configure_request_event_t *event) {
275 Con *con;
276
277 DLOG("window 0x%08x wants to be at %dx%d with %dx%d\n",
278 event->window, event->x, event->y, event->width, event->height);
279
280 /* For unmanaged windows, we just execute the configure request. As soon as
281 * it gets mapped, we will take over anyways. */
282 if ((con = con_by_window_id(event->window)) == NULL) {
283 DLOG("Configure request for unmanaged window, can do that.\n");
284
285 uint32_t mask = 0;
286 uint32_t values[7];
287 int c = 0;
288 #define COPY_MASK_MEMBER(mask_member, event_member) \
289 do { \
290 if (event->value_mask & mask_member) { \
291 mask |= mask_member; \
292 values[c++] = event->event_member; \
293 } \
294 } while (0)
295
296 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_X, x);
297 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_Y, y);
298 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_WIDTH, width);
299 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_HEIGHT, height);
300 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_BORDER_WIDTH, border_width);
301 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_SIBLING, sibling);
302 COPY_MASK_MEMBER(XCB_CONFIG_WINDOW_STACK_MODE, stack_mode);
303
304 xcb_configure_window(conn, event->window, mask, values);
305 xcb_flush(conn);
306
307 return;
308 }
309
310 DLOG("Configure request!\n");
311
312 Con *workspace = con_get_workspace(con);
313 if (workspace && (strcmp(workspace->name, "__i3_scratch") == 0)) {
314 DLOG("This is a scratchpad container, ignoring ConfigureRequest\n");
315 goto out;
316 }
317 Con *fullscreen = con_get_fullscreen_covering_ws(workspace);
318
319 if (fullscreen != con && con_is_floating(con) && con_is_leaf(con)) {
320 /* find the height for the decorations */
321 int deco_height = con->deco_rect.height;
322 /* we actually need to apply the size/position changes to the *parent*
323 * container */
324 Rect bsr = con_border_style_rect(con);
325 if (con->border_style == BS_NORMAL) {
326 bsr.y += deco_height;
327 bsr.height -= deco_height;
328 }
329 Con *floatingcon = con->parent;
330 Rect newrect = floatingcon->rect;
331
332 if (event->value_mask & XCB_CONFIG_WINDOW_X) {
333 newrect.x = event->x + (-1) * bsr.x;
334 DLOG("proposed x = %d, new x is %d\n", event->x, newrect.x);
335 }
336 if (event->value_mask & XCB_CONFIG_WINDOW_Y) {
337 newrect.y = event->y + (-1) * bsr.y;
338 DLOG("proposed y = %d, new y is %d\n", event->y, newrect.y);
339 }
340 if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
341 newrect.width = event->width + (-1) * bsr.width;
342 newrect.width += con->border_width * 2;
343 DLOG("proposed width = %d, new width is %d (x11 border %d)\n",
344 event->width, newrect.width, con->border_width);
345 }
346 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
347 newrect.height = event->height + (-1) * bsr.height;
348 newrect.height += con->border_width * 2;
349 DLOG("proposed height = %d, new height is %d (x11 border %d)\n",
350 event->height, newrect.height, con->border_width);
351 }
352
353 DLOG("Container is a floating leaf node, will do that.\n");
354 floating_reposition(floatingcon, newrect);
355 return;
356 }
357
358 /* Dock windows can be reconfigured in their height and moved to another output. */
359 if (con->parent && con->parent->type == CT_DOCKAREA) {
360 DLOG("Reconfiguring dock window (con = %p).\n", con);
361 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
362 DLOG("Dock client wants to change height to %d, we can do that.\n", event->height);
363
364 con->geometry.height = event->height;
365 tree_render();
366 }
367
368 if (event->value_mask & XCB_CONFIG_WINDOW_X || event->value_mask & XCB_CONFIG_WINDOW_Y) {
369 int16_t x = event->value_mask & XCB_CONFIG_WINDOW_X ? event->x : (int16_t)con->geometry.x;
370 int16_t y = event->value_mask & XCB_CONFIG_WINDOW_Y ? event->y : (int16_t)con->geometry.y;
371
372 Con *current_output = con_get_output(con);
373 Output *target = get_output_containing(x, y);
374 if (target != NULL && current_output != target->con) {
375 DLOG("Dock client is requested to be moved to output %s, moving it there.\n", output_primary_name(target));
376 Match *match;
377 Con *nc = con_for_window(target->con, con->window, &match);
378 DLOG("Dock client will be moved to container %p.\n", nc);
379 con_detach(con);
380 con_attach(con, nc, false);
381
382 tree_render();
383 } else {
384 DLOG("Dock client will not be moved, we only support moving it to another output.\n");
385 }
386 }
387 goto out;
388 }
389
390 if (event->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
391 DLOG("window 0x%08x wants to be stacked %d\n", event->window, event->stack_mode);
392
393 /* Emacs and IntelliJ Idea “request focus” by stacking their window
394 * above all others. */
395 if (event->stack_mode != XCB_STACK_MODE_ABOVE) {
396 DLOG("stack_mode != XCB_STACK_MODE_ABOVE, ignoring ConfigureRequest\n");
397 goto out;
398 }
399
400 if (fullscreen || !con_is_leaf(con)) {
401 DLOG("fullscreen or not a leaf, ignoring ConfigureRequest\n");
402 goto out;
403 }
404
405 if (workspace == NULL) {
406 DLOG("Window is not being managed, ignoring ConfigureRequest\n");
407 goto out;
408 }
409
410 if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(workspace))) {
411 DLOG("Focusing con = %p\n", con);
412 workspace_show(workspace);
413 con_activate(con);
414 tree_render();
415 } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(workspace))) {
416 DLOG("Marking con = %p urgent\n", con);
417 con_set_urgency(con, true);
418 tree_render();
419 } else {
420 DLOG("Ignoring request for con = %p.\n", con);
421 }
422 }
423
424 out:
425 fake_absolute_configure_notify(con);
426 }
427
428 /*
429 * Gets triggered upon a RandR screen change event, that is when the user
430 * changes the screen configuration in any way (mode, position, …)
431 *
432 */
433 static void handle_screen_change(xcb_generic_event_t *e) {
434 DLOG("RandR screen change\n");
435
436 /* The geometry of the root window is used for “fullscreen global” and
437 * changes when new outputs are added. */
438 xcb_get_geometry_cookie_t cookie = xcb_get_geometry(conn, root);
439 xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(conn, cookie, NULL);
440 if (reply == NULL) {
441 ELOG("Could not get geometry of the root window, exiting\n");
442 exit(1);
443 }
444 DLOG("root geometry reply: (%d, %d) %d x %d\n", reply->x, reply->y, reply->width, reply->height);
445
446 croot->rect.width = reply->width;
447 croot->rect.height = reply->height;
448
449 randr_query_outputs();
450
451 scratchpad_fix_resolution();
452
453 ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}");
454 }
455
456 /*
457 * Our window decorations were unmapped. That means, the window will be killed
458 * now, so we better clean up before.
459 *
460 */
461 static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
462 DLOG("UnmapNotify for 0x%08x (received from 0x%08x), serial %d\n", event->window, event->event, event->sequence);
463 xcb_get_input_focus_cookie_t cookie;
464 Con *con = con_by_window_id(event->window);
465 if (con == NULL) {
466 /* This could also be an UnmapNotify for the frame. We need to
467 * decrement the ignore_unmap counter. */
468 con = con_by_frame_id(event->window);
469 if (con == NULL) {
470 LOG("Not a managed window, ignoring UnmapNotify event\n");
471 return;
472 }
473
474 if (con->ignore_unmap > 0)
475 con->ignore_unmap--;
476 /* See the end of this function. */
477 cookie = xcb_get_input_focus(conn);
478 DLOG("ignore_unmap = %d for frame of container %p\n", con->ignore_unmap, con);
479 goto ignore_end;
480 }
481
482 /* See the end of this function. */
483 cookie = xcb_get_input_focus(conn);
484
485 if (con->ignore_unmap > 0) {
486 DLOG("ignore_unmap = %d, dec\n", con->ignore_unmap);
487 con->ignore_unmap--;
488 goto ignore_end;
489 }
490
491 /* Since we close the container, we need to unset _NET_WM_DESKTOP and
492 * _NET_WM_STATE according to the spec. */
493 xcb_delete_property(conn, event->window, A__NET_WM_DESKTOP);
494 xcb_delete_property(conn, event->window, A__NET_WM_STATE);
495
496 tree_close_internal(con, DONT_KILL_WINDOW, false);
497 tree_render();
498
499 ignore_end:
500 /* If the client (as opposed to i3) destroyed or unmapped a window, an
501 * EnterNotify event will follow (indistinguishable from an EnterNotify
502 * event caused by moving your mouse), causing i3 to set focus to whichever
503 * window is now visible.
504 *
505 * In a complex stacked or tabbed layout (take two v-split containers in a
506 * tabbed container), when the bottom window in tab2 is closed, the bottom
507 * window of tab1 is visible instead. X11 will thus send an EnterNotify
508 * event for the bottom window of tab1, while the focus should be set to
509 * the remaining window of tab2.
510 *
511 * Therefore, we ignore all EnterNotify events which have the same sequence
512 * as an UnmapNotify event. */
513 add_ignore_event(event->sequence, XCB_ENTER_NOTIFY);
514
515 /* Since we just ignored the sequence of this UnmapNotify, we want to make
516 * sure that following events use a different sequence. When putting xterm
517 * into fullscreen and moving the pointer to a different window, without
518 * using GetInputFocus, subsequent (legitimate) EnterNotify events arrived
519 * with the same sequence and thus were ignored (see ticket #609). */
520 free(xcb_get_input_focus_reply(conn, cookie, NULL));
521 }
522
523 /*
524 * A destroy notify event is sent when the window is not unmapped, but
525 * immediately destroyed (for example when starting a window and immediately
526 * killing the program which started it).
527 *
528 * We just pass on the event to the unmap notify handler (by copying the
529 * important fields in the event data structure).
530 *
531 */
532 static void handle_destroy_notify_event(xcb_destroy_notify_event_t *event) {
533 DLOG("destroy notify for 0x%08x, 0x%08x\n", event->event, event->window);
534
535 xcb_unmap_notify_event_t unmap;
536 unmap.sequence = event->sequence;
537 unmap.event = event->event;
538 unmap.window = event->window;
539
540 handle_unmap_notify_event(&unmap);
541 }
542
543 static bool window_name_changed(i3Window *window, char *old_name) {
544 if ((old_name == NULL) && (window->name == NULL))
545 return false;
546
547 /* Either the old or the new one is NULL, but not both. */
548 if ((old_name == NULL) ^ (window->name == NULL))
549 return true;
550
551 return (strcmp(old_name, i3string_as_utf8(window->name)) != 0);
552 }
553
554 /*
555 * Called when a window changes its title
556 *
557 */
558 static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
559 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
560 Con *con;
561 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
562 return false;
563
564 char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL);
565
566 window_update_name(con->window, prop, false);
567
568 x_push_changes(croot);
569
570 if (window_name_changed(con->window, old_name))
571 ipc_send_window_event("title", con);
572
573 FREE(old_name);
574
575 return true;
576 }
577
578 /*
579 * Handles legacy window name updates (WM_NAME), see also src/window.c,
580 * window_update_name_legacy().
581 *
582 */
583 static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state,
584 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
585 Con *con;
586 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
587 return false;
588
589 char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL);
590
591 window_update_name_legacy(con->window, prop, false);
592
593 x_push_changes(croot);
594
595 if (window_name_changed(con->window, old_name))
596 ipc_send_window_event("title", con);
597
598 FREE(old_name);
599
600 return true;
601 }
602
603 /*
604 * Called when a window changes its WM_WINDOW_ROLE.
605 *
606 */
607 static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t state,
608 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
609 Con *con;
610 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
611 return false;
612
613 window_update_role(con->window, prop, false);
614
615 return true;
616 }
617
618 /*
619 * Expose event means we should redraw our windows (= title bar)
620 *
621 */
622 static void handle_expose_event(xcb_expose_event_t *event) {
623 Con *parent;
624
625 DLOG("window = %08x\n", event->window);
626
627 if ((parent = con_by_frame_id(event->window)) == NULL) {
628 LOG("expose event for unknown window, ignoring\n");
629 return;
630 }
631
632 /* Since we render to our surface on every change anyways, expose events
633 * only tell us that the X server lost (parts of) the window contents. */
634 draw_util_copy_surface(&(parent->frame_buffer), &(parent->frame),
635 0, 0, 0, 0, parent->rect.width, parent->rect.height);
636 xcb_flush(conn);
637 }
638
639 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
640 #define _NET_WM_MOVERESIZE_SIZE_TOP 1
641 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
642 #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
643 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
644 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
645 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
646 #define _NET_WM_MOVERESIZE_SIZE_LEFT 7
647 #define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
648 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
649 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
650 #define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
651
652 #define _NET_MOVERESIZE_WINDOW_X (1 << 8)
653 #define _NET_MOVERESIZE_WINDOW_Y (1 << 9)
654 #define _NET_MOVERESIZE_WINDOW_WIDTH (1 << 10)
655 #define _NET_MOVERESIZE_WINDOW_HEIGHT (1 << 11)
656
657 /*
658 * Handle client messages (EWMH)
659 *
660 */
661 static void handle_client_message(xcb_client_message_event_t *event) {
662 /* If this is a startup notification ClientMessage, the library will handle
663 * it and call our monitor_event() callback. */
664 if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t *)event))
665 return;
666
667 LOG("ClientMessage for window 0x%08x\n", event->window);
668 if (event->type == A__NET_WM_STATE) {
669 if (event->format != 32 ||
670 (event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN &&
671 event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION &&
672 event->data.data32[1] != A__NET_WM_STATE_STICKY)) {
673 DLOG("Unknown atom in clientmessage of type %d\n", event->data.data32[1]);
674 return;
675 }
676
677 Con *con = con_by_window_id(event->window);
678 if (con == NULL) {
679 DLOG("Could not get window for client message\n");
680 return;
681 }
682
683 if (event->data.data32[1] == A__NET_WM_STATE_FULLSCREEN) {
684 /* Check if the fullscreen state should be toggled */
685 if ((con->fullscreen_mode != CF_NONE &&
686 (event->data.data32[0] == _NET_WM_STATE_REMOVE ||
687 event->data.data32[0] == _NET_WM_STATE_TOGGLE)) ||
688 (con->fullscreen_mode == CF_NONE &&
689 (event->data.data32[0] == _NET_WM_STATE_ADD ||
690 event->data.data32[0] == _NET_WM_STATE_TOGGLE))) {
691 DLOG("toggling fullscreen\n");
692 con_toggle_fullscreen(con, CF_OUTPUT);
693 }
694 } else if (event->data.data32[1] == A__NET_WM_STATE_DEMANDS_ATTENTION) {
695 /* Check if the urgent flag must be set or not */
696 if (event->data.data32[0] == _NET_WM_STATE_ADD)
697 con_set_urgency(con, true);
698 else if (event->data.data32[0] == _NET_WM_STATE_REMOVE)
699 con_set_urgency(con, false);
700 else if (event->data.data32[0] == _NET_WM_STATE_TOGGLE)
701 con_set_urgency(con, !con->urgent);
702 } else if (event->data.data32[1] == A__NET_WM_STATE_STICKY) {
703 DLOG("Received a client message to modify _NET_WM_STATE_STICKY.\n");
704 if (event->data.data32[0] == _NET_WM_STATE_ADD)
705 con->sticky = true;
706 else if (event->data.data32[0] == _NET_WM_STATE_REMOVE)
707 con->sticky = false;
708 else if (event->data.data32[0] == _NET_WM_STATE_TOGGLE)
709 con->sticky = !con->sticky;
710
711 DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
712 ewmh_update_sticky(con->window->id, con->sticky);
713 output_push_sticky_windows(focused);
714 ewmh_update_wm_desktop();
715 }
716
717 tree_render();
718 } else if (event->type == A__NET_ACTIVE_WINDOW) {
719 if (event->format != 32)
720 return;
721
722 DLOG("_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window);
723
724 Con *con = con_by_window_id(event->window);
725 if (con == NULL) {
726 DLOG("Could not get window for client message\n");
727 return;
728 }
729
730 Con *ws = con_get_workspace(con);
731 if (ws == NULL) {
732 DLOG("Window is not being managed, ignoring _NET_ACTIVE_WINDOW\n");
733 return;
734 }
735
736 if (con_is_internal(ws) && ws != workspace_get("__i3_scratch", NULL)) {
737 DLOG("Workspace is internal but not scratchpad, ignoring _NET_ACTIVE_WINDOW\n");
738 return;
739 }
740
741 /* data32[0] indicates the source of the request (application or pager) */
742 if (event->data.data32[0] == 2) {
743 /* Always focus the con if it is from a pager, because this is most
744 * likely from some user action */
745 DLOG("This request came from a pager. Focusing con = %p\n", con);
746
747 if (con_is_internal(ws)) {
748 scratchpad_show(con);
749 } else {
750 workspace_show(ws);
751 /* Re-set focus, even if unchanged from i3’s perspective. */
752 focused_id = XCB_NONE;
753 con_activate(con);
754 }
755 } else {
756 /* Request is from an application. */
757 if (con_is_internal(ws)) {
758 DLOG("Ignoring request to make con = %p active because it's on an internal workspace.\n", con);
759 return;
760 }
761
762 if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
763 DLOG("Focusing con = %p\n", con);
764 workspace_show(ws);
765 con_activate(con);
766 } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) {
767 DLOG("Marking con = %p urgent\n", con);
768 con_set_urgency(con, true);
769 } else
770 DLOG("Ignoring request for con = %p.\n", con);
771 }
772
773 tree_render();
774 } else if (event->type == A_I3_SYNC) {
775 xcb_window_t window = event->data.data32[0];
776 uint32_t rnd = event->data.data32[1];
777 sync_respond(window, rnd);
778 } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) {
779 /*
780 * A client can request an estimate for the frame size which the window
781 * manager will put around it before actually mapping its window. Java
782 * does this (as of openjdk-7).
783 *
784 * Note that the calculation below is not entirely accurate — once you
785 * set a different border type, it’s off. We _could_ request all the
786 * window properties (which have to be set up at this point according
787 * to EWMH), but that seems rather elaborate. The standard explicitly
788 * says the application must cope with an estimate that is not entirely
789 * accurate.
790 */
791 DLOG("_NET_REQUEST_FRAME_EXTENTS for window 0x%08x\n", event->window);
792
793 /* The reply data: approximate frame size */
794 Rect r = {
795 config.default_border_width, /* left */
796 config.default_border_width, /* right */
797 render_deco_height(), /* top */
798 config.default_border_width /* bottom */
799 };
800 xcb_change_property(
801 conn,
802 XCB_PROP_MODE_REPLACE,
803 event->window,
804 A__NET_FRAME_EXTENTS,
805 XCB_ATOM_CARDINAL, 32, 4,
806 &r);
807 xcb_flush(conn);
808 } else if (event->type == A_WM_CHANGE_STATE) {
809 /* http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 */
810 if (event->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) {
811 /* For compatiblity reasons, Wine will request iconic state and cannot ensure that the WM has agreed on it;
812 * immediately revert to normal to avoid being stuck in a paused state. */
813 DLOG("Client has requested iconic state, rejecting. (window = %d)\n", event->window);
814 long data[] = {XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE};
815 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, event->window,
816 A_WM_STATE, A_WM_STATE, 32, 2, data);
817 } else {
818 DLOG("Not handling WM_CHANGE_STATE request. (window = %d, state = %d)\n", event->window, event->data.data32[0]);
819 }
820 } else if (event->type == A__NET_CURRENT_DESKTOP) {
821 /* This request is used by pagers and bars to change the current
822 * desktop likely as a result of some user action. We interpret this as
823 * a request to focus the given workspace. See
824 * https://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
825 * */
826 DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
827 Con *ws = ewmh_get_workspace_by_index(event->data.data32[0]);
828 if (ws == NULL) {
829 ELOG("Could not determine workspace for this index, ignoring request.\n");
830 return;
831 }
832
833 DLOG("Handling request to focus workspace %s\n", ws->name);
834 workspace_show(ws);
835 tree_render();
836 } else if (event->type == A__NET_WM_DESKTOP) {
837 uint32_t index = event->data.data32[0];
838 DLOG("Request to move window %d to EWMH desktop index %d\n", event->window, index);
839
840 Con *con = con_by_window_id(event->window);
841 if (con == NULL) {
842 DLOG("Couldn't find con for window %d, ignoring the request.\n", event->window);
843 return;
844 }
845
846 if (index == NET_WM_DESKTOP_ALL) {
847 /* The window is requesting to be visible on all workspaces, so
848 * let's float it and make it sticky. */
849 DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n");
850
851 floating_enable(con, false);
852
853 con->sticky = true;
854 ewmh_update_sticky(con->window->id, true);
855 output_push_sticky_windows(focused);
856 } else {
857 Con *ws = ewmh_get_workspace_by_index(index);
858 if (ws == NULL) {
859 ELOG("Could not determine workspace for this index, ignoring request.\n");
860 return;
861 }
862
863 con_move_to_workspace(con, ws, true, false, false);
864 }
865
866 tree_render();
867 ewmh_update_wm_desktop();
868 } else if (event->type == A__NET_CLOSE_WINDOW) {
869 /*
870 * Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
871 * client message request to the root window.
872 * https://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896
873 */
874 Con *con = con_by_window_id(event->window);
875 if (con) {
876 DLOG("Handling _NET_CLOSE_WINDOW request (con = %p)\n", con);
877
878 if (event->data.data32[0])
879 last_timestamp = event->data.data32[0];
880
881 tree_close_internal(con, KILL_WINDOW, false);
882 tree_render();
883 } else {
884 DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window);
885 }
886 } else if (event->type == A__NET_WM_MOVERESIZE) {
887 /*
888 * Client-side decorated Gtk3 windows emit this signal when being
889 * dragged by their GtkHeaderBar
890 */
891 Con *con = con_by_window_id(event->window);
892 if (!con || !con_is_floating(con)) {
893 DLOG("Couldn't find con for _NET_WM_MOVERESIZE request, or con not floating (window = %d)\n", event->window);
894 return;
895 }
896 DLOG("Handling _NET_WM_MOVERESIZE request (con = %p)\n", con);
897 uint32_t direction = event->data.data32[2];
898 uint32_t x_root = event->data.data32[0];
899 uint32_t y_root = event->data.data32[1];
900 /* construct fake xcb_button_press_event_t */
901 xcb_button_press_event_t fake = {
902 .root_x = x_root,
903 .root_y = y_root,
904 .event_x = x_root - (con->rect.x),
905 .event_y = y_root - (con->rect.y)};
906 switch (direction) {
907 case _NET_WM_MOVERESIZE_MOVE:
908 floating_drag_window(con->parent, &fake);
909 break;
910 case _NET_WM_MOVERESIZE_SIZE_TOPLEFT ... _NET_WM_MOVERESIZE_SIZE_LEFT:
911 floating_resize_window(con->parent, false, &fake);
912 break;
913 default:
914 DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
915 break;
916 }
917 } else if (event->type == A__NET_MOVERESIZE_WINDOW) {
918 DLOG("Received _NET_MOVE_RESIZE_WINDOW. Handling by faking a configure request.\n");
919
920 void *_generated_event = scalloc(32, 1);
921 xcb_configure_request_event_t *generated_event = _generated_event;
922
923 generated_event->window = event->window;
924 generated_event->response_type = XCB_CONFIGURE_REQUEST;
925
926 generated_event->value_mask = 0;
927 if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_X) {
928 generated_event->value_mask |= XCB_CONFIG_WINDOW_X;
929 generated_event->x = event->data.data32[1];
930 }
931 if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_Y) {
932 generated_event->value_mask |= XCB_CONFIG_WINDOW_Y;
933 generated_event->y = event->data.data32[2];
934 }
935 if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_WIDTH) {
936 generated_event->value_mask |= XCB_CONFIG_WINDOW_WIDTH;
937 generated_event->width = event->data.data32[3];
938 }
939 if (event->data.data32[0] & _NET_MOVERESIZE_WINDOW_HEIGHT) {
940 generated_event->value_mask |= XCB_CONFIG_WINDOW_HEIGHT;
941 generated_event->height = event->data.data32[4];
942 }
943
944 handle_configure_request(generated_event);
945 FREE(generated_event);
946 } else {
947 DLOG("Skipping client message for unhandled type %d\n", event->type);
948 }
949 }
950
951 static bool handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
952 xcb_atom_t atom, xcb_get_property_reply_t *reply) {
953 Con *con;
954 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
955 return false;
956
957 window_update_type(con->window, reply);
958 return true;
959 }
960
961 /*
962 * Handles the size hints set by a window, but currently only the part necessary for displaying
963 * clients proportionally inside their frames (mplayer for example)
964 *
965 * See ICCCM 4.1.2.3 for more details
966 *
967 */
968 static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
969 xcb_atom_t name, xcb_get_property_reply_t *reply) {
970 Con *con = con_by_window_id(window);
971 if (con == NULL) {
972 DLOG("Received WM_NORMAL_HINTS for unknown client\n");
973 return false;
974 }
975
976 xcb_size_hints_t size_hints;
977
978 /* If the hints were already in this event, use them, if not, request them */
979 if (reply != NULL) {
980 xcb_icccm_get_wm_size_hints_from_reply(&size_hints, reply);
981 } else {
982 xcb_icccm_get_wm_normal_hints_reply(conn, xcb_icccm_get_wm_normal_hints_unchecked(conn, con->window->id), &size_hints, NULL);
983 }
984
985 int win_width = con->window_rect.width;
986 int win_height = con->window_rect.height;
987
988 if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)) {
989 DLOG("Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height);
990
991 con->window->min_width = size_hints.min_width;
992 con->window->min_height = size_hints.min_height;
993 }
994
995 if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) {
996 DLOG("Maximum size: %d (width) x %d (height)\n", size_hints.max_width, size_hints.max_height);
997
998 con->window->max_width = size_hints.max_width;
999 con->window->max_height = size_hints.max_height;
1000 }
1001
1002 if (con_is_floating(con)) {
1003 win_width = MAX(win_width, con->window->min_width);
1004 win_height = MAX(win_height, con->window->min_height);
1005 win_width = MIN(win_width, con->window->max_width);
1006 win_height = MIN(win_height, con->window->max_height);
1007 }
1008
1009 bool changed = false;
1010 if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)) {
1011 if (size_hints.width_inc > 0 && size_hints.width_inc < 0xFFFF) {
1012 if (con->window->width_increment != size_hints.width_inc) {
1013 con->window->width_increment = size_hints.width_inc;
1014 changed = true;
1015 }
1016 }
1017
1018 if (size_hints.height_inc > 0 && size_hints.height_inc < 0xFFFF) {
1019 if (con->window->height_increment != size_hints.height_inc) {
1020 con->window->height_increment = size_hints.height_inc;
1021 changed = true;
1022 }
1023 }
1024
1025 if (changed) {
1026 DLOG("resize increments changed\n");
1027 }
1028 }
1029
1030 bool has_base_size = false;
1031 int base_width = 0;
1032 int base_height = 0;
1033
1034 /* The base width / height is the desired size of the window. */
1035 if (size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
1036 base_width = size_hints.base_width;
1037 base_height = size_hints.base_height;
1038 has_base_size = true;
1039 }
1040
1041 /* If the window didn't specify a base size, the ICCCM tells us to fall
1042 * back to the minimum size instead, if available. */
1043 if (!has_base_size && size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
1044 base_width = size_hints.min_width;
1045 base_height = size_hints.min_height;
1046 }
1047
1048 // TODO XXX Should we only do this is the base size is > 0?
1049 if (base_width != con->window->base_width || base_height != con->window->base_height) {
1050 con->window->base_width = base_width;
1051 con->window->base_height = base_height;
1052
1053 DLOG("client's base_height changed to %d\n", base_height);
1054 DLOG("client's base_width changed to %d\n", base_width);
1055 changed = true;
1056 }
1057
1058 /* If no aspect ratio was set or if it was invalid, we ignore the hints */
1059 if (!(size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT) ||
1060 (size_hints.min_aspect_num <= 0) ||
1061 (size_hints.min_aspect_den <= 0)) {
1062 goto render_and_return;
1063 }
1064
1065 /* The ICCCM says to subtract the base size from the window size for aspect
1066 * ratio calculations. However, unlike determining the base size itself we
1067 * must not fall back to using the minimum size in this case according to
1068 * the ICCCM. */
1069 double width = win_width - base_width * has_base_size;
1070 double height = win_height - base_height * has_base_size;
1071
1072 /* Convert numerator/denominator to a double */
1073 double min_aspect = (double)size_hints.min_aspect_num / size_hints.min_aspect_den;
1074 double max_aspect = (double)size_hints.max_aspect_num / size_hints.max_aspect_den;
1075
1076 DLOG("Aspect ratio set: minimum %f, maximum %f\n", min_aspect, max_aspect);
1077 DLOG("width = %f, height = %f\n", width, height);
1078
1079 /* Sanity checks, this is user-input, in a way */
1080 if (max_aspect <= 0 || min_aspect <= 0 || height == 0 || (width / height) <= 0) {
1081 goto render_and_return;
1082 }
1083
1084 /* Check if we need to set proportional_* variables using the correct ratio */
1085 double aspect_ratio = 0.0;
1086 if ((width / height) < min_aspect) {
1087 aspect_ratio = min_aspect;
1088 } else if ((width / height) > max_aspect) {
1089 aspect_ratio = max_aspect;
1090 } else {
1091 goto render_and_return;
1092 }
1093
1094 if (fabs(con->window->aspect_ratio - aspect_ratio) > DBL_EPSILON) {
1095 con->window->aspect_ratio = aspect_ratio;
1096 changed = true;
1097 }
1098
1099 render_and_return:
1100 if (changed) {
1101 tree_render();
1102 }
1103
1104 FREE(reply);
1105 return true;
1106 }
1107
1108 /*
1109 * Handles the WM_HINTS property for extracting the urgency state of the window.
1110 *
1111 */
1112 static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
1113 xcb_atom_t name, xcb_get_property_reply_t *reply) {
1114 Con *con = con_by_window_id(window);
1115 if (con == NULL) {
1116 DLOG("Received WM_HINTS for unknown client\n");
1117 return false;
1118 }
1119
1120 bool urgency_hint;
1121 if (reply == NULL)
1122 reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL);
1123 window_update_hints(con->window, reply, &urgency_hint);
1124 con_set_urgency(con, urgency_hint);
1125 tree_render();
1126
1127 return true;
1128 }
1129
1130 /*
1131 * Handles the transient for hints set by a window, signalizing that this window is a popup window
1132 * for some other window.
1133 *
1134 * See ICCCM 4.1.2.6 for more details
1135 *
1136 */
1137 static bool handle_transient_for(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
1138 xcb_atom_t name, xcb_get_property_reply_t *prop) {
1139 Con *con;
1140
1141 if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
1142 DLOG("No such window\n");
1143 return false;
1144 }
1145
1146 if (prop == NULL) {
1147 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 0, 32),
1148 NULL);
1149 if (prop == NULL)
1150 return false;
1151 }
1152
1153 window_update_transient_for(con->window, prop);
1154
1155 return true;
1156 }
1157
1158 /*
1159 * Handles changes of the WM_CLIENT_LEADER atom which specifies if this is a
1160 * toolwindow (or similar) and to which window it belongs (logical parent).
1161 *
1162 */
1163 static bool handle_clientleader_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
1164 xcb_atom_t name, xcb_get_property_reply_t *prop) {
1165 Con *con;
1166 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
1167 return false;
1168
1169 if (prop == NULL) {
1170 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A_WM_CLIENT_LEADER, XCB_ATOM_WINDOW, 0, 32),
1171 NULL);
1172 if (prop == NULL)
1173 return false;
1174 }
1175
1176 window_update_leader(con->window, prop);
1177
1178 return true;
1179 }
1180
1181 /*
1182 * Handles FocusIn events which are generated by clients (i3’s focus changes
1183 * don’t generate FocusIn events due to a different EventMask) and updates the
1184 * decorations accordingly.
1185 *
1186 */
1187 static void handle_focus_in(xcb_focus_in_event_t *event) {
1188 DLOG("focus change in, for window 0x%08x\n", event->event);
1189
1190 if (event->event == root) {
1191 DLOG("Received focus in for root window, refocusing the focused window.\n");
1192 con_focus(focused);
1193 focused_id = XCB_NONE;
1194 x_push_changes(croot);
1195 }
1196
1197 Con *con;
1198 if ((con = con_by_window_id(event->event)) == NULL || con->window == NULL)
1199 return;
1200 DLOG("That is con %p / %s\n", con, con->name);
1201
1202 if (event->mode == XCB_NOTIFY_MODE_GRAB ||
1203 event->mode == XCB_NOTIFY_MODE_UNGRAB) {
1204 DLOG("FocusIn event for grab/ungrab, ignoring\n");
1205 return;
1206 }
1207
1208 if (event->detail == XCB_NOTIFY_DETAIL_POINTER) {
1209 DLOG("notify detail is pointer, ignoring this event\n");
1210 return;
1211 }
1212
1213 /* Floating windows should be refocused to ensure that they are on top of
1214 * other windows. */
1215 if (focused_id == event->event && !con_inside_floating(con)) {
1216 DLOG("focus matches the currently focused window, not doing anything\n");
1217 return;
1218 }
1219
1220 /* Skip dock clients, they cannot get the i3 focus. */
1221 if (con->parent->type == CT_DOCKAREA) {
1222 DLOG("This is a dock client, not focusing.\n");
1223 return;
1224 }
1225
1226 DLOG("focus is different / refocusing floating window: updating decorations\n");
1227
1228 /* Get the currently focused workspace to check if the focus change also
1229 * involves changing workspaces. If so, we need to call workspace_show() to
1230 * correctly update state and send the IPC event. */
1231 Con *ws = con_get_workspace(con);
1232 if (ws != con_get_workspace(focused))
1233 workspace_show(ws);
1234
1235 con_activate(con);
1236 /* We update focused_id because we don’t need to set focus again */
1237 focused_id = event->event;
1238 tree_render();
1239 }
1240
1241 /*
1242 * Handles ConfigureNotify events for the root window, which are generated when
1243 * the monitor configuration changed.
1244 *
1245 */
1246 static void handle_configure_notify(xcb_configure_notify_event_t *event) {
1247 if (event->event != root) {
1248 DLOG("ConfigureNotify for non-root window 0x%08x, ignoring\n", event->event);
1249 return;
1250 }
1251 DLOG("ConfigureNotify for root window 0x%08x\n", event->event);
1252
1253 if (force_xinerama) {
1254 return;
1255 }
1256 randr_query_outputs();
1257 }
1258
1259 /*
1260 * Handles the WM_CLASS property for assignments and criteria selection.
1261 *
1262 */
1263 static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
1264 xcb_atom_t name, xcb_get_property_reply_t *prop) {
1265 Con *con;
1266 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
1267 return false;
1268
1269 if (prop == NULL) {
1270 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32),
1271 NULL);
1272
1273 if (prop == NULL)
1274 return false;
1275 }
1276
1277 window_update_class(con->window, prop, false);
1278
1279 return true;
1280 }
1281
1282 /*
1283 * Handles the _MOTIF_WM_HINTS property of specifing window deocration settings.
1284 *
1285 */
1286 static bool handle_motif_hints_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
1287 xcb_atom_t name, xcb_get_property_reply_t *prop) {
1288 Con *con;
1289 if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
1290 return false;
1291
1292 if (prop == NULL) {
1293 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, false, window, A__MOTIF_WM_HINTS, XCB_GET_PROPERTY_TYPE_ANY, 0, 5 * sizeof(uint64_t)),
1294 NULL);
1295
1296 if (prop == NULL)
1297 return false;
1298 }
1299
1300 border_style_t motif_border_style;
1301 window_update_motif_hints(con->window, prop, &motif_border_style);
1302
1303 if (motif_border_style != con->border_style && motif_border_style != BS_NORMAL) {
1304 DLOG("Update border style of con %p to %d\n", con, motif_border_style);
1305 con_set_border_style(con, motif_border_style, con->current_border_width);
1306
1307 x_push_changes(croot);
1308 }
1309
1310 return true;
1311 }
1312
1313 /*
1314 * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients.
1315 *
1316 */
1317 static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
1318 xcb_atom_t name, xcb_get_property_reply_t *prop) {
1319 DLOG("strut partial change for window 0x%08x\n", window);
1320
1321 Con *con;
1322 if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
1323 return false;
1324 }
1325
1326 if (prop == NULL) {
1327 xcb_generic_error_t *err = NULL;
1328 xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL,
1329 XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX);
1330 prop = xcb_get_property_reply(conn, strut_cookie, &err);
1331
1332 if (err != NULL) {
1333 DLOG("got error when getting strut partial property: %d\n", err->error_code);
1334 free(err);
1335 return false;
1336 }
1337
1338 if (prop == NULL) {
1339 return false;
1340 }
1341 }
1342
1343 DLOG("That is con %p / %s\n", con, con->name);
1344
1345 window_update_strut_partial(con->window, prop);
1346
1347 /* we only handle this change for dock clients */
1348 if (con->parent == NULL || con->parent->type != CT_DOCKAREA) {
1349 return true;
1350 }
1351
1352 Con *search_at = croot;
1353 Con *output = con_get_output(con);
1354 if (output != NULL) {
1355 DLOG("Starting search at output %s\n", output->name);
1356 search_at = output;
1357 }
1358
1359 /* find out the desired position of this dock window */
1360 if (con->window->reserved.top > 0 && con->window->reserved.bottom == 0) {
1361 DLOG("Top dock client\n");
1362 con->window->dock = W_DOCK_TOP;
1363 } else if (con->window->reserved.top == 0 && con->window->reserved.bottom > 0) {
1364 DLOG("Bottom dock client\n");
1365 con->window->dock = W_DOCK_BOTTOM;
1366 } else {
1367 DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
1368 if (con->geometry.y < (search_at->rect.height / 2)) {
1369 DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
1370 con->geometry.y, (search_at->rect.height / 2));
1371 con->window->dock = W_DOCK_TOP;
1372 } else {
1373 DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
1374 con->geometry.y, (search_at->rect.height / 2));
1375 con->window->dock = W_DOCK_BOTTOM;
1376 }
1377 }
1378
1379 /* find the dockarea */
1380 Con *dockarea = con_for_window(search_at, con->window, NULL);
1381 assert(dockarea != NULL);
1382
1383 /* attach the dock to the dock area */
1384 con_detach(con);
1385 con->parent = dockarea;
1386 TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused);
1387 TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes);
1388
1389 tree_render();
1390
1391 return true;
1392 }
1393
1394 /* Returns false if the event could not be processed (e.g. the window could not
1395 * be found), true otherwise */
1396 typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
1397
1398 struct property_handler_t {
1399 xcb_atom_t atom;
1400 uint32_t long_len;
1401 cb_property_handler_t cb;
1402 };
1403
1404 static struct property_handler_t property_handlers[] = {
1405 {0, 128, handle_windowname_change},
1406 {0, UINT_MAX, handle_hints},
1407 {0, 128, handle_windowname_change_legacy},
1408 {0, UINT_MAX, handle_normal_hints},
1409 {0, UINT_MAX, handle_clientleader_change},
1410 {0, UINT_MAX, handle_transient_for},
1411 {0, 128, handle_windowrole_change},
1412 {0, 128, handle_class_change},
1413 {0, UINT_MAX, handle_strut_partial_change},
1414 {0, UINT_MAX, handle_window_type},
1415 {0, 5 * sizeof(uint64_t), handle_motif_hints_change}};
1416 #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
1417
1418 /*
1419 * Sets the appropriate atoms for the property handlers after the atoms were
1420 * received from X11
1421 *
1422 */
1423 void property_handlers_init(void) {
1424 sn_monitor_context_new(sndisplay, conn_screen, startup_monitor_event, NULL, NULL);
1425
1426 property_handlers[0].atom = A__NET_WM_NAME;
1427 property_handlers[1].atom = XCB_ATOM_WM_HINTS;
1428 property_handlers[2].atom = XCB_ATOM_WM_NAME;
1429 property_handlers[3].atom = XCB_ATOM_WM_NORMAL_HINTS;
1430 property_handlers[4].atom = A_WM_CLIENT_LEADER;
1431 property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
1432 property_handlers[6].atom = A_WM_WINDOW_ROLE;
1433 property_handlers[7].atom = XCB_ATOM_WM_CLASS;
1434 property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
1435 property_handlers[9].atom = A__NET_WM_WINDOW_TYPE;
1436 property_handlers[10].atom = A__MOTIF_WM_HINTS;
1437 }
1438
1439 static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
1440 struct property_handler_t *handler = NULL;
1441 xcb_get_property_reply_t *propr = NULL;
1442
1443 for (size_t c = 0; c < NUM_HANDLERS; c++) {
1444 if (property_handlers[c].atom != atom)
1445 continue;
1446
1447 handler = &property_handlers[c];
1448 break;
1449 }
1450
1451 if (handler == NULL) {
1452 //DLOG("Unhandled property notify for atom %d (0x%08x)\n", atom, atom);
1453 return;
1454 }
1455
1456 if (state != XCB_PROPERTY_DELETE) {
1457 xcb_get_property_cookie_t cookie = xcb_get_property(conn, 0, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, handler->long_len);
1458 propr = xcb_get_property_reply(conn, cookie, 0);
1459 }
1460
1461 /* the handler will free() the reply unless it returns false */
1462 if (!handler->cb(NULL, conn, state, window, atom, propr))
1463 FREE(propr);
1464 }
1465
1466 /*
1467 * Takes an xcb_generic_event_t and calls the appropriate handler, based on the
1468 * event type.
1469 *
1470 */
1471 void handle_event(int type, xcb_generic_event_t *event) {
1472 if (type != XCB_MOTION_NOTIFY)
1473 DLOG("event type %d, xkb_base %d\n", type, xkb_base);
1474
1475 if (randr_base > -1 &&
1476 type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
1477 handle_screen_change(event);
1478 return;
1479 }
1480
1481 if (xkb_base > -1 && type == xkb_base) {
1482 DLOG("xkb event, need to handle it.\n");
1483
1484 xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
1485 if (state->xkbType == XCB_XKB_NEW_KEYBOARD_NOTIFY) {
1486 DLOG("xkb new keyboard notify, sequence %d, time %d\n", state->sequence, state->time);
1487 xcb_key_symbols_free(keysyms);
1488 keysyms = xcb_key_symbols_alloc(conn);
1489 if (((xcb_xkb_new_keyboard_notify_event_t *)event)->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
1490 (void)load_keymap();
1491 ungrab_all_keys(conn);
1492 translate_keysyms();
1493 grab_all_keys(conn);
1494 } else if (state->xkbType == XCB_XKB_MAP_NOTIFY) {
1495 if (event_is_ignored(event->sequence, type)) {
1496 DLOG("Ignoring map notify event for sequence %d.\n", state->sequence);
1497 } else {
1498 DLOG("xkb map notify, sequence %d, time %d\n", state->sequence, state->time);
1499 add_ignore_event(event->sequence, type);
1500 xcb_key_symbols_free(keysyms);
1501 keysyms = xcb_key_symbols_alloc(conn);
1502 ungrab_all_keys(conn);
1503 translate_keysyms();
1504 grab_all_keys(conn);
1505 (void)load_keymap();
1506 }
1507 } else if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
1508 DLOG("xkb state group = %d\n", state->group);
1509 if (xkb_current_group == state->group)
1510 return;
1511 xkb_current_group = state->group;
1512 ungrab_all_keys(conn);
1513 grab_all_keys(conn);
1514 }
1515
1516 return;
1517 }
1518
1519 switch (type) {
1520 case XCB_KEY_PRESS:
1521 case XCB_KEY_RELEASE:
1522 handle_key_press((xcb_key_press_event_t *)event);
1523 break;
1524
1525 case XCB_BUTTON_PRESS:
1526 case XCB_BUTTON_RELEASE:
1527 handle_button_press((xcb_button_press_event_t *)event);
1528 break;
1529
1530 case XCB_MAP_REQUEST:
1531 handle_map_request((xcb_map_request_event_t *)event);
1532 break;
1533
1534 case XCB_UNMAP_NOTIFY:
1535 handle_unmap_notify_event((xcb_unmap_notify_event_t *)event);
1536 break;
1537
1538 case XCB_DESTROY_NOTIFY:
1539 handle_destroy_notify_event((xcb_destroy_notify_event_t *)event);
1540 break;
1541
1542 case XCB_EXPOSE:
1543 if (((xcb_expose_event_t *)event)->count == 0) {
1544 handle_expose_event((xcb_expose_event_t *)event);
1545 }
1546
1547 break;
1548
1549 case XCB_MOTION_NOTIFY:
1550 handle_motion_notify((xcb_motion_notify_event_t *)event);
1551 break;
1552
1553 /* Enter window = user moved their mouse over the window */
1554 case XCB_ENTER_NOTIFY:
1555 handle_enter_notify((xcb_enter_notify_event_t *)event);
1556 break;
1557
1558 /* Client message are sent to the root window. The only interesting
1559 * client message for us is _NET_WM_STATE, we honour
1560 * _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION */
1561 case XCB_CLIENT_MESSAGE:
1562 handle_client_message((xcb_client_message_event_t *)event);
1563 break;
1564
1565 /* Configure request = window tried to change size on its own */
1566 case XCB_CONFIGURE_REQUEST:
1567 handle_configure_request((xcb_configure_request_event_t *)event);
1568 break;
1569
1570 /* Mapping notify = keyboard mapping changed (Xmodmap), re-grab bindings */
1571 case XCB_MAPPING_NOTIFY:
1572 handle_mapping_notify((xcb_mapping_notify_event_t *)event);
1573 break;
1574
1575 case XCB_FOCUS_IN:
1576 handle_focus_in((xcb_focus_in_event_t *)event);
1577 break;
1578
1579 case XCB_PROPERTY_NOTIFY: {
1580 xcb_property_notify_event_t *e = (xcb_property_notify_event_t *)event;
1581 last_timestamp = e->time;
1582 property_notify(e->state, e->window, e->atom);
1583 break;
1584 }
1585
1586 case XCB_CONFIGURE_NOTIFY:
1587 handle_configure_notify((xcb_configure_notify_event_t *)event);
1588 break;
1589
1590 default:
1591 //DLOG("Unhandled event of type %d\n", type);
1592 break;
1593 }
1594 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
7 *
8 */
9 #include "all.h"
10
11 #include "yajl_utils.h"
12
13 #include <stdint.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <fcntl.h>
17 #include <libgen.h>
18 #include <ev.h>
19 #include <yajl/yajl_gen.h>
20 #include <yajl/yajl_parse.h>
21
22 char *current_socketpath = NULL;
23
24 TAILQ_HEAD(ipc_client_head, ipc_client)
25 all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
26
27 /*
28 * Puts the given socket file descriptor into non-blocking mode or dies if
29 * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our
30 * IPC model because we should by no means block the window manager.
31 *
32 */
33 static void set_nonblock(int sockfd) {
34 int flags = fcntl(sockfd, F_GETFL, 0);
35 flags |= O_NONBLOCK;
36 if (fcntl(sockfd, F_SETFL, flags) < 0)
37 err(-1, "Could not set O_NONBLOCK");
38 }
39
40 static void ipc_client_timeout(EV_P_ ev_timer *w, int revents);
41 static void ipc_socket_writeable_cb(EV_P_ struct ev_io *w, int revents);
42
43 static ev_tstamp kill_timeout = 10.0;
44
45 void ipc_set_kill_timeout(ev_tstamp new) {
46 kill_timeout = new;
47 }
48
49 /*
50 * Try to write the contents of the pending buffer to the client's subscription
51 * socket. Will set, reset or clear the timeout and io write callbacks depending
52 * on the result of the write operation.
53 *
54 */
55 static void ipc_push_pending(ipc_client *client) {
56 const ssize_t result = writeall_nonblock(client->fd, client->buffer, client->buffer_size);
57 if (result < 0) {
58 return;
59 }
60
61 if ((size_t)result == client->buffer_size) {
62 /* Everything was written successfully: clear the timer and stop the io
63 * callback. */
64 FREE(client->buffer);
65 client->buffer_size = 0;
66 if (client->timeout) {
67 ev_timer_stop(main_loop, client->timeout);
68 FREE(client->timeout);
69 }
70 ev_io_stop(main_loop, client->write_callback);
71 return;
72 }
73
74 /* Otherwise, make sure that the io callback is enabled and create a new
75 * timer if needed. */
76 ev_io_start(main_loop, client->write_callback);
77
78 if (!client->timeout) {
79 struct ev_timer *timeout = scalloc(1, sizeof(struct ev_timer));
80 ev_timer_init(timeout, ipc_client_timeout, kill_timeout, 0.);
81 timeout->data = client;
82 client->timeout = timeout;
83 ev_set_priority(timeout, EV_MINPRI);
84 ev_timer_start(main_loop, client->timeout);
85 } else if (result > 0) {
86 /* Keep the old timeout when nothing is written. Otherwise, we would
87 * keep a dead connection by continuously renewing its timeouts. */
88 ev_timer_stop(main_loop, client->timeout);
89 ev_timer_set(client->timeout, kill_timeout, 0.0);
90 ev_timer_start(main_loop, client->timeout);
91 }
92 if (result == 0) {
93 return;
94 }
95
96 /* Shift the buffer to the left and reduce the allocated space. */
97 client->buffer_size -= (size_t)result;
98 memmove(client->buffer, client->buffer + result, client->buffer_size);
99 client->buffer = srealloc(client->buffer, client->buffer_size);
100 }
101
102 /*
103 * Given a message and a message type, create the corresponding header, merge it
104 * with the message and append it to the given client's output buffer. Also,
105 * send the message if the client's buffer was empty.
106 *
107 */
108 static void ipc_send_client_message(ipc_client *client, size_t size, const uint32_t message_type, const uint8_t *payload) {
109 const i3_ipc_header_t header = {
110 .magic = {'i', '3', '-', 'i', 'p', 'c'},
111 .size = size,
112 .type = message_type};
113 const size_t header_size = sizeof(i3_ipc_header_t);
114 const size_t message_size = header_size + size;
115
116 const bool push_now = (client->buffer_size == 0);
117 client->buffer = srealloc(client->buffer, client->buffer_size + message_size);
118 memcpy(client->buffer + client->buffer_size, ((void *)&header), header_size);
119 memcpy(client->buffer + client->buffer_size + header_size, payload, size);
120 client->buffer_size += message_size;
121
122 if (push_now) {
123 ipc_push_pending(client);
124 }
125 }
126
127 static void free_ipc_client(ipc_client *client) {
128 DLOG("Disconnecting client on fd %d\n", client->fd);
129 close(client->fd);
130
131 ev_io_stop(main_loop, client->read_callback);
132 FREE(client->read_callback);
133 ev_io_stop(main_loop, client->write_callback);
134 FREE(client->write_callback);
135 if (client->timeout) {
136 ev_timer_stop(main_loop, client->timeout);
137 FREE(client->timeout);
138 }
139
140 free(client->buffer);
141
142 for (int i = 0; i < client->num_events; i++) {
143 free(client->events[i]);
144 }
145 free(client->events);
146 TAILQ_REMOVE(&all_clients, client, clients);
147 free(client);
148 }
149
150 /*
151 * Sends the specified event to all IPC clients which are currently connected
152 * and subscribed to this kind of event.
153 *
154 */
155 void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
156 ipc_client *current;
157 TAILQ_FOREACH(current, &all_clients, clients) {
158 for (int i = 0; i < current->num_events; i++) {
159 if (strcasecmp(current->events[i], event) == 0) {
160 ipc_send_client_message(current, strlen(payload), message_type, (uint8_t *)payload);
161 break;
162 }
163 }
164 }
165 }
166
167 /*
168 * For shutdown events, we send the reason for the shutdown.
169 */
170 static void ipc_send_shutdown_event(shutdown_reason_t reason) {
171 yajl_gen gen = ygenalloc();
172 y(map_open);
173
174 ystr("change");
175
176 if (reason == SHUTDOWN_REASON_RESTART) {
177 ystr("restart");
178 } else if (reason == SHUTDOWN_REASON_EXIT) {
179 ystr("exit");
180 }
181
182 y(map_close);
183
184 const unsigned char *payload;
185 ylength length;
186
187 y(get_buf, &payload, &length);
188 ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload);
189
190 y(free);
191 }
192
193 /*
194 * Calls shutdown() on each socket and closes it. This function is to be called
195 * when exiting or restarting only!
196 *
197 */
198 void ipc_shutdown(shutdown_reason_t reason) {
199 ipc_send_shutdown_event(reason);
200
201 ipc_client *current;
202 while (!TAILQ_EMPTY(&all_clients)) {
203 current = TAILQ_FIRST(&all_clients);
204 shutdown(current->fd, SHUT_RDWR);
205 free_ipc_client(current);
206 }
207 }
208
209 /*
210 * Executes the command and returns whether it could be successfully parsed
211 * or not (at the moment, always returns true).
212 *
213 */
214 IPC_HANDLER(run_command) {
215 /* To get a properly terminated buffer, we copy
216 * message_size bytes out of the buffer */
217 char *command = scalloc(message_size + 1, 1);
218 strncpy(command, (const char *)message, message_size);
219 LOG("IPC: received: *%s*\n", command);
220 yajl_gen gen = yajl_gen_alloc(NULL);
221
222 CommandResult *result = parse_command((const char *)command, gen);
223 free(command);
224
225 if (result->needs_tree_render)
226 tree_render();
227
228 command_result_free(result);
229
230 const unsigned char *reply;
231 ylength length;
232 yajl_gen_get_buf(gen, &reply, &length);
233
234 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_COMMAND,
235 (const uint8_t *)reply);
236
237 yajl_gen_free(gen);
238 }
239
240 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
241 ystr(name);
242 y(map_open);
243 ystr("x");
244 y(integer, r.x);
245 ystr("y");
246 y(integer, r.y);
247 ystr("width");
248 y(integer, r.width);
249 ystr("height");
250 y(integer, r.height);
251 y(map_close);
252 }
253
254 static void dump_event_state_mask(yajl_gen gen, Binding *bind) {
255 y(array_open);
256 for (int i = 0; i < 20; i++) {
257 if (bind->event_state_mask & (1 << i)) {
258 switch (1 << i) {
259 case XCB_KEY_BUT_MASK_SHIFT:
260 ystr("shift");
261 break;
262 case XCB_KEY_BUT_MASK_LOCK:
263 ystr("lock");
264 break;
265 case XCB_KEY_BUT_MASK_CONTROL:
266 ystr("ctrl");
267 break;
268 case XCB_KEY_BUT_MASK_MOD_1:
269 ystr("Mod1");
270 break;
271 case XCB_KEY_BUT_MASK_MOD_2:
272 ystr("Mod2");
273 break;
274 case XCB_KEY_BUT_MASK_MOD_3:
275 ystr("Mod3");
276 break;
277 case XCB_KEY_BUT_MASK_MOD_4:
278 ystr("Mod4");
279 break;
280 case XCB_KEY_BUT_MASK_MOD_5:
281 ystr("Mod5");
282 break;
283 case XCB_KEY_BUT_MASK_BUTTON_1:
284 ystr("Button1");
285 break;
286 case XCB_KEY_BUT_MASK_BUTTON_2:
287 ystr("Button2");
288 break;
289 case XCB_KEY_BUT_MASK_BUTTON_3:
290 ystr("Button3");
291 break;
292 case XCB_KEY_BUT_MASK_BUTTON_4:
293 ystr("Button4");
294 break;
295 case XCB_KEY_BUT_MASK_BUTTON_5:
296 ystr("Button5");
297 break;
298 case (I3_XKB_GROUP_MASK_1 << 16):
299 ystr("Group1");
300 break;
301 case (I3_XKB_GROUP_MASK_2 << 16):
302 ystr("Group2");
303 break;
304 case (I3_XKB_GROUP_MASK_3 << 16):
305 ystr("Group3");
306 break;
307 case (I3_XKB_GROUP_MASK_4 << 16):
308 ystr("Group4");
309 break;
310 }
311 }
312 }
313 y(array_close);
314 }
315
316 static void dump_binding(yajl_gen gen, Binding *bind) {
317 y(map_open);
318 ystr("input_code");
319 y(integer, bind->keycode);
320
321 ystr("input_type");
322 ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
323
324 ystr("symbol");
325 if (bind->symbol == NULL)
326 y(null);
327 else
328 ystr(bind->symbol);
329
330 ystr("command");
331 ystr(bind->command);
332
333 // This key is only provided for compatibility, new programs should use
334 // event_state_mask instead.
335 ystr("mods");
336 dump_event_state_mask(gen, bind);
337
338 ystr("event_state_mask");
339 dump_event_state_mask(gen, bind);
340
341 y(map_close);
342 }
343
344 void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
345 y(map_open);
346 ystr("id");
347 y(integer, (uintptr_t)con);
348
349 ystr("type");
350 switch (con->type) {
351 case CT_ROOT:
352 ystr("root");
353 break;
354 case CT_OUTPUT:
355 ystr("output");
356 break;
357 case CT_CON:
358 ystr("con");
359 break;
360 case CT_FLOATING_CON:
361 ystr("floating_con");
362 break;
363 case CT_WORKSPACE:
364 ystr("workspace");
365 break;
366 case CT_DOCKAREA:
367 ystr("dockarea");
368 break;
369 }
370
371 /* provided for backwards compatibility only. */
372 ystr("orientation");
373 if (!con_is_split(con))
374 ystr("none");
375 else {
376 if (con_orientation(con) == HORIZ)
377 ystr("horizontal");
378 else
379 ystr("vertical");
380 }
381
382 ystr("scratchpad_state");
383 switch (con->scratchpad_state) {
384 case SCRATCHPAD_NONE:
385 ystr("none");
386 break;
387 case SCRATCHPAD_FRESH:
388 ystr("fresh");
389 break;
390 case SCRATCHPAD_CHANGED:
391 ystr("changed");
392 break;
393 }
394
395 ystr("percent");
396 if (con->percent == 0.0)
397 y(null);
398 else
399 y(double, con->percent);
400
401 ystr("urgent");
402 y(bool, con->urgent);
403
404 if (!TAILQ_EMPTY(&(con->marks_head))) {
405 ystr("marks");
406 y(array_open);
407
408 mark_t *mark;
409 TAILQ_FOREACH(mark, &(con->marks_head), marks) {
410 ystr(mark->name);
411 }
412
413 y(array_close);
414 }
415
416 ystr("focused");
417 y(bool, (con == focused));
418
419 if (con->type != CT_ROOT && con->type != CT_OUTPUT) {
420 ystr("output");
421 ystr(con_get_output(con)->name);
422 }
423
424 ystr("layout");
425 switch (con->layout) {
426 case L_DEFAULT:
427 DLOG("About to dump layout=default, this is a bug in the code.\n");
428 assert(false);
429 break;
430 case L_SPLITV:
431 ystr("splitv");
432 break;
433 case L_SPLITH:
434 ystr("splith");
435 break;
436 case L_STACKED:
437 ystr("stacked");
438 break;
439 case L_TABBED:
440 ystr("tabbed");
441 break;
442 case L_DOCKAREA:
443 ystr("dockarea");
444 break;
445 case L_OUTPUT:
446 ystr("output");
447 break;
448 }
449
450 ystr("workspace_layout");
451 switch (con->workspace_layout) {
452 case L_DEFAULT:
453 ystr("default");
454 break;
455 case L_STACKED:
456 ystr("stacked");
457 break;
458 case L_TABBED:
459 ystr("tabbed");
460 break;
461 default:
462 DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
463 assert(false);
464 break;
465 }
466
467 ystr("last_split_layout");
468 switch (con->layout) {
469 case L_SPLITV:
470 ystr("splitv");
471 break;
472 default:
473 ystr("splith");
474 break;
475 }
476
477 ystr("border");
478 switch (con->border_style) {
479 case BS_NORMAL:
480 ystr("normal");
481 break;
482 case BS_NONE:
483 ystr("none");
484 break;
485 case BS_PIXEL:
486 ystr("pixel");
487 break;
488 }
489
490 ystr("current_border_width");
491 y(integer, con->current_border_width);
492
493 dump_rect(gen, "rect", con->rect);
494 dump_rect(gen, "deco_rect", con->deco_rect);
495 dump_rect(gen, "window_rect", con->window_rect);
496 dump_rect(gen, "geometry", con->geometry);
497
498 ystr("name");
499 if (con->window && con->window->name)
500 ystr(i3string_as_utf8(con->window->name));
501 else if (con->name != NULL)
502 ystr(con->name);
503 else
504 y(null);
505
506 if (con->title_format != NULL) {
507 ystr("title_format");
508 ystr(con->title_format);
509 }
510
511 if (con->type == CT_WORKSPACE) {
512 ystr("num");
513 y(integer, con->num);
514 }
515
516 ystr("window");
517 if (con->window)
518 y(integer, con->window->id);
519 else
520 y(null);
521
522 if (con->window && !inplace_restart) {
523 /* Window properties are useless to preserve when restarting because
524 * they will be queried again anyway. However, for i3-save-tree(1),
525 * they are very useful and save i3-save-tree dealing with X11. */
526 ystr("window_properties");
527 y(map_open);
528
529 #define DUMP_PROPERTY(key, prop_name) \
530 do { \
531 if (con->window->prop_name != NULL) { \
532 ystr(key); \
533 ystr(con->window->prop_name); \
534 } \
535 } while (0)
536
537 DUMP_PROPERTY("class", class_class);
538 DUMP_PROPERTY("instance", class_instance);
539 DUMP_PROPERTY("window_role", role);
540
541 if (con->window->name != NULL) {
542 ystr("title");
543 ystr(i3string_as_utf8(con->window->name));
544 }
545
546 ystr("transient_for");
547 if (con->window->transient_for == XCB_NONE)
548 y(null);
549 else
550 y(integer, con->window->transient_for);
551
552 y(map_close);
553 }
554
555 ystr("nodes");
556 y(array_open);
557 Con *node;
558 if (con->type != CT_DOCKAREA || !inplace_restart) {
559 TAILQ_FOREACH(node, &(con->nodes_head), nodes) {
560 dump_node(gen, node, inplace_restart);
561 }
562 }
563 y(array_close);
564
565 ystr("floating_nodes");
566 y(array_open);
567 TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
568 dump_node(gen, node, inplace_restart);
569 }
570 y(array_close);
571
572 ystr("focus");
573 y(array_open);
574 TAILQ_FOREACH(node, &(con->focus_head), focused) {
575 y(integer, (uintptr_t)node);
576 }
577 y(array_close);
578
579 ystr("fullscreen_mode");
580 y(integer, con->fullscreen_mode);
581
582 ystr("sticky");
583 y(bool, con->sticky);
584
585 ystr("floating");
586 switch (con->floating) {
587 case FLOATING_AUTO_OFF:
588 ystr("auto_off");
589 break;
590 case FLOATING_AUTO_ON:
591 ystr("auto_on");
592 break;
593 case FLOATING_USER_OFF:
594 ystr("user_off");
595 break;
596 case FLOATING_USER_ON:
597 ystr("user_on");
598 break;
599 }
600
601 ystr("swallows");
602 y(array_open);
603 Match *match;
604 TAILQ_FOREACH(match, &(con->swallow_head), matches) {
605 /* We will generate a new restart_mode match specification after this
606 * loop, so skip this one. */
607 if (match->restart_mode)
608 continue;
609 y(map_open);
610 if (match->dock != M_DONTCHECK) {
611 ystr("dock");
612 y(integer, match->dock);
613 ystr("insert_where");
614 y(integer, match->insert_where);
615 }
616
617 #define DUMP_REGEX(re_name) \
618 do { \
619 if (match->re_name != NULL) { \
620 ystr(#re_name); \
621 ystr(match->re_name->pattern); \
622 } \
623 } while (0)
624
625 DUMP_REGEX(class);
626 DUMP_REGEX(instance);
627 DUMP_REGEX(window_role);
628 DUMP_REGEX(title);
629
630 #undef DUMP_REGEX
631 y(map_close);
632 }
633
634 if (inplace_restart) {
635 if (con->window != NULL) {
636 y(map_open);
637 ystr("id");
638 y(integer, con->window->id);
639 ystr("restart_mode");
640 y(bool, true);
641 y(map_close);
642 }
643 }
644 y(array_close);
645
646 if (inplace_restart && con->window != NULL) {
647 ystr("depth");
648 y(integer, con->depth);
649 }
650
651 y(map_close);
652 }
653
654 static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
655 if (TAILQ_EMPTY(&(config->bar_bindings)))
656 return;
657
658 ystr("bindings");
659 y(array_open);
660
661 struct Barbinding *current;
662 TAILQ_FOREACH(current, &(config->bar_bindings), bindings) {
663 y(map_open);
664
665 ystr("input_code");
666 y(integer, current->input_code);
667 ystr("command");
668 ystr(current->command);
669 ystr("release");
670 y(bool, current->release == B_UPON_KEYRELEASE);
671
672 y(map_close);
673 }
674
675 y(array_close);
676 }
677
678 static char *canonicalize_output_name(char *name) {
679 /* Do not canonicalize special output names. */
680 if (strcasecmp(name, "primary") == 0) {
681 return name;
682 }
683 Output *output = get_output_by_name(name, false);
684 return output ? output_primary_name(output) : name;
685 }
686
687 static void dump_bar_config(yajl_gen gen, Barconfig *config) {
688 y(map_open);
689
690 ystr("id");
691 ystr(config->id);
692
693 if (config->num_outputs > 0) {
694 ystr("outputs");
695 y(array_open);
696 for (int c = 0; c < config->num_outputs; c++) {
697 /* Convert monitor names (RandR ≥ 1.5) or output names
698 * (RandR < 1.5) into monitor names. This way, existing
699 * configs which use output names transparently keep
700 * working. */
701 ystr(canonicalize_output_name(config->outputs[c]));
702 }
703 y(array_close);
704 }
705
706 if (!TAILQ_EMPTY(&(config->tray_outputs))) {
707 ystr("tray_outputs");
708 y(array_open);
709
710 struct tray_output_t *tray_output;
711 TAILQ_FOREACH(tray_output, &(config->tray_outputs), tray_outputs) {
712 ystr(canonicalize_output_name(tray_output->output));
713 }
714
715 y(array_close);
716 }
717
718 #define YSTR_IF_SET(name) \
719 do { \
720 if (config->name) { \
721 ystr(#name); \
722 ystr(config->name); \
723 } \
724 } while (0)
725
726 ystr("tray_padding");
727 y(integer, config->tray_padding);
728
729 YSTR_IF_SET(socket_path);
730
731 ystr("mode");
732 switch (config->mode) {
733 case M_HIDE:
734 ystr("hide");
735 break;
736 case M_INVISIBLE:
737 ystr("invisible");
738 break;
739 case M_DOCK:
740 default:
741 ystr("dock");
742 break;
743 }
744
745 ystr("hidden_state");
746 switch (config->hidden_state) {
747 case S_SHOW:
748 ystr("show");
749 break;
750 case S_HIDE:
751 default:
752 ystr("hide");
753 break;
754 }
755
756 ystr("modifier");
757 y(integer, config->modifier);
758
759 dump_bar_bindings(gen, config);
760
761 ystr("position");
762 if (config->position == P_BOTTOM)
763 ystr("bottom");
764 else
765 ystr("top");
766
767 YSTR_IF_SET(status_command);
768 YSTR_IF_SET(font);
769
770 if (config->separator_symbol) {
771 ystr("separator_symbol");
772 ystr(config->separator_symbol);
773 }
774
775 ystr("workspace_buttons");
776 y(bool, !config->hide_workspace_buttons);
777
778 ystr("strip_workspace_numbers");
779 y(bool, config->strip_workspace_numbers);
780
781 ystr("strip_workspace_name");
782 y(bool, config->strip_workspace_name);
783
784 ystr("binding_mode_indicator");
785 y(bool, !config->hide_binding_mode_indicator);
786
787 ystr("verbose");
788 y(bool, config->verbose);
789
790 #undef YSTR_IF_SET
791 #define YSTR_IF_SET(name) \
792 do { \
793 if (config->colors.name) { \
794 ystr(#name); \
795 ystr(config->colors.name); \
796 } \
797 } while (0)
798
799 ystr("colors");
800 y(map_open);
801 YSTR_IF_SET(background);
802 YSTR_IF_SET(statusline);
803 YSTR_IF_SET(separator);
804 YSTR_IF_SET(focused_background);
805 YSTR_IF_SET(focused_statusline);
806 YSTR_IF_SET(focused_separator);
807 YSTR_IF_SET(focused_workspace_border);
808 YSTR_IF_SET(focused_workspace_bg);
809 YSTR_IF_SET(focused_workspace_text);
810 YSTR_IF_SET(active_workspace_border);
811 YSTR_IF_SET(active_workspace_bg);
812 YSTR_IF_SET(active_workspace_text);
813 YSTR_IF_SET(inactive_workspace_border);
814 YSTR_IF_SET(inactive_workspace_bg);
815 YSTR_IF_SET(inactive_workspace_text);
816 YSTR_IF_SET(urgent_workspace_border);
817 YSTR_IF_SET(urgent_workspace_bg);
818 YSTR_IF_SET(urgent_workspace_text);
819 YSTR_IF_SET(binding_mode_border);
820 YSTR_IF_SET(binding_mode_bg);
821 YSTR_IF_SET(binding_mode_text);
822 y(map_close);
823
824 y(map_close);
825 #undef YSTR_IF_SET
826 }
827
828 IPC_HANDLER(tree) {
829 setlocale(LC_NUMERIC, "C");
830 yajl_gen gen = ygenalloc();
831 dump_node(gen, croot, false);
832 setlocale(LC_NUMERIC, "");
833
834 const unsigned char *payload;
835 ylength length;
836 y(get_buf, &payload, &length);
837
838 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_TREE, payload);
839 y(free);
840 }
841
842 /*
843 * Formats the reply message for a GET_WORKSPACES request and sends it to the
844 * client
845 *
846 */
847 IPC_HANDLER(get_workspaces) {
848 yajl_gen gen = ygenalloc();
849 y(array_open);
850
851 Con *focused_ws = con_get_workspace(focused);
852
853 Con *output;
854 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
855 if (con_is_internal(output))
856 continue;
857 Con *ws;
858 TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
859 assert(ws->type == CT_WORKSPACE);
860 y(map_open);
861
862 ystr("num");
863 y(integer, ws->num);
864
865 ystr("name");
866 ystr(ws->name);
867
868 ystr("visible");
869 y(bool, workspace_is_visible(ws));
870
871 ystr("focused");
872 y(bool, ws == focused_ws);
873
874 ystr("rect");
875 y(map_open);
876 ystr("x");
877 y(integer, ws->rect.x);
878 ystr("y");
879 y(integer, ws->rect.y);
880 ystr("width");
881 y(integer, ws->rect.width);
882 ystr("height");
883 y(integer, ws->rect.height);
884 y(map_close);
885
886 ystr("output");
887 ystr(output->name);
888
889 ystr("urgent");
890 y(bool, ws->urgent);
891
892 y(map_close);
893 }
894 }
895
896 y(array_close);
897
898 const unsigned char *payload;
899 ylength length;
900 y(get_buf, &payload, &length);
901
902 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
903 y(free);
904 }
905
906 /*
907 * Formats the reply message for a GET_OUTPUTS request and sends it to the
908 * client
909 *
910 */
911 IPC_HANDLER(get_outputs) {
912 yajl_gen gen = ygenalloc();
913 y(array_open);
914
915 Output *output;
916 TAILQ_FOREACH(output, &outputs, outputs) {
917 y(map_open);
918
919 ystr("name");
920 ystr(output_primary_name(output));
921
922 ystr("active");
923 y(bool, output->active);
924
925 ystr("primary");
926 y(bool, output->primary);
927
928 ystr("rect");
929 y(map_open);
930 ystr("x");
931 y(integer, output->rect.x);
932 ystr("y");
933 y(integer, output->rect.y);
934 ystr("width");
935 y(integer, output->rect.width);
936 ystr("height");
937 y(integer, output->rect.height);
938 y(map_close);
939
940 ystr("current_workspace");
941 Con *ws = NULL;
942 if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
943 ystr(ws->name);
944 else
945 y(null);
946
947 y(map_close);
948 }
949
950 y(array_close);
951
952 const unsigned char *payload;
953 ylength length;
954 y(get_buf, &payload, &length);
955
956 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
957 y(free);
958 }
959
960 /*
961 * Formats the reply message for a GET_MARKS request and sends it to the
962 * client
963 *
964 */
965 IPC_HANDLER(get_marks) {
966 yajl_gen gen = ygenalloc();
967 y(array_open);
968
969 Con *con;
970 TAILQ_FOREACH(con, &all_cons, all_cons) {
971 mark_t *mark;
972 TAILQ_FOREACH(mark, &(con->marks_head), marks) {
973 ystr(mark->name);
974 }
975 }
976
977 y(array_close);
978
979 const unsigned char *payload;
980 ylength length;
981 y(get_buf, &payload, &length);
982
983 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_MARKS, payload);
984 y(free);
985 }
986
987 /*
988 * Returns the version of i3
989 *
990 */
991 IPC_HANDLER(get_version) {
992 yajl_gen gen = ygenalloc();
993 y(map_open);
994
995 ystr("major");
996 y(integer, MAJOR_VERSION);
997
998 ystr("minor");
999 y(integer, MINOR_VERSION);
1000
1001 ystr("patch");
1002 y(integer, PATCH_VERSION);
1003
1004 ystr("human_readable");
1005 ystr(i3_version);
1006
1007 ystr("loaded_config_file_name");
1008 ystr(current_configpath);
1009
1010 y(map_close);
1011
1012 const unsigned char *payload;
1013 ylength length;
1014 y(get_buf, &payload, &length);
1015
1016 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_VERSION, payload);
1017 y(free);
1018 }
1019
1020 /*
1021 * Formats the reply message for a GET_BAR_CONFIG request and sends it to the
1022 * client.
1023 *
1024 */
1025 IPC_HANDLER(get_bar_config) {
1026 yajl_gen gen = ygenalloc();
1027
1028 /* If no ID was passed, we return a JSON array with all IDs */
1029 if (message_size == 0) {
1030 y(array_open);
1031 Barconfig *current;
1032 TAILQ_FOREACH(current, &barconfigs, configs) {
1033 ystr(current->id);
1034 }
1035 y(array_close);
1036
1037 const unsigned char *payload;
1038 ylength length;
1039 y(get_buf, &payload, &length);
1040
1041 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
1042 y(free);
1043 return;
1044 }
1045
1046 /* To get a properly terminated buffer, we copy
1047 * message_size bytes out of the buffer */
1048 char *bar_id = NULL;
1049 sasprintf(&bar_id, "%.*s", message_size, message);
1050 LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
1051 Barconfig *current, *config = NULL;
1052 TAILQ_FOREACH(current, &barconfigs, configs) {
1053 if (strcmp(current->id, bar_id) != 0)
1054 continue;
1055
1056 config = current;
1057 break;
1058 }
1059 free(bar_id);
1060
1061 if (!config) {
1062 /* If we did not find a config for the given ID, the reply will contain
1063 * a null 'id' field. */
1064 y(map_open);
1065
1066 ystr("id");
1067 y(null);
1068
1069 y(map_close);
1070 } else {
1071 dump_bar_config(gen, config);
1072 }
1073
1074 const unsigned char *payload;
1075 ylength length;
1076 y(get_buf, &payload, &length);
1077
1078 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
1079 y(free);
1080 }
1081
1082 /*
1083 * Returns a list of configured binding modes
1084 *
1085 */
1086 IPC_HANDLER(get_binding_modes) {
1087 yajl_gen gen = ygenalloc();
1088
1089 y(array_open);
1090 struct Mode *mode;
1091 SLIST_FOREACH(mode, &modes, modes) {
1092 ystr(mode->name);
1093 }
1094 y(array_close);
1095
1096 const unsigned char *payload;
1097 ylength length;
1098 y(get_buf, &payload, &length);
1099
1100 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_BINDING_MODES, payload);
1101 y(free);
1102 }
1103
1104 /*
1105 * Callback for the YAJL parser (will be called when a string is parsed).
1106 *
1107 */
1108 static int add_subscription(void *extra, const unsigned char *s,
1109 ylength len) {
1110 ipc_client *client = extra;
1111
1112 DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
1113 int event = client->num_events;
1114
1115 client->num_events++;
1116 client->events = srealloc(client->events, client->num_events * sizeof(char *));
1117 /* We copy the string because it is not null-terminated and strndup()
1118 * is missing on some BSD systems */
1119 client->events[event] = scalloc(len + 1, 1);
1120 memcpy(client->events[event], s, len);
1121
1122 DLOG("client is now subscribed to:\n");
1123 for (int i = 0; i < client->num_events; i++) {
1124 DLOG("event %s\n", client->events[i]);
1125 }
1126 DLOG("(done)\n");
1127
1128 return 1;
1129 }
1130
1131 /*
1132 * Subscribes this connection to the event types which were given as a JSON
1133 * serialized array in the payload field of the message.
1134 *
1135 */
1136 IPC_HANDLER(subscribe) {
1137 yajl_handle p;
1138 yajl_status stat;
1139
1140 /* Setup the JSON parser */
1141 static yajl_callbacks callbacks = {
1142 .yajl_string = add_subscription,
1143 };
1144
1145 p = yalloc(&callbacks, (void *)client);
1146 stat = yajl_parse(p, (const unsigned char *)message, message_size);
1147 if (stat != yajl_status_ok) {
1148 unsigned char *err;
1149 err = yajl_get_error(p, true, (const unsigned char *)message,
1150 message_size);
1151 ELOG("YAJL parse error: %s\n", err);
1152 yajl_free_error(p, err);
1153
1154 const char *reply = "{\"success\":false}";
1155 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1156 yajl_free(p);
1157 return;
1158 }
1159 yajl_free(p);
1160 const char *reply = "{\"success\":true}";
1161 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
1162
1163 if (client->first_tick_sent) {
1164 return;
1165 }
1166
1167 bool is_tick = false;
1168 for (int i = 0; i < client->num_events; i++) {
1169 if (strcmp(client->events[i], "tick") == 0) {
1170 is_tick = true;
1171 break;
1172 }
1173 }
1174 if (!is_tick) {
1175 return;
1176 }
1177
1178 client->first_tick_sent = true;
1179 const char *payload = "{\"first\":true,\"payload\":\"\"}";
1180 ipc_send_client_message(client, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload);
1181 }
1182
1183 /*
1184 * Returns the raw last loaded i3 configuration file contents.
1185 */
1186 IPC_HANDLER(get_config) {
1187 yajl_gen gen = ygenalloc();
1188
1189 y(map_open);
1190
1191 ystr("config");
1192 ystr(current_config);
1193
1194 y(map_close);
1195
1196 const unsigned char *payload;
1197 ylength length;
1198 y(get_buf, &payload, &length);
1199
1200 ipc_send_client_message(client, length, I3_IPC_REPLY_TYPE_CONFIG, payload);
1201 y(free);
1202 }
1203
1204 /*
1205 * Sends the tick event from the message payload to subscribers. Establishes a
1206 * synchronization point in event-related tests.
1207 */
1208 IPC_HANDLER(send_tick) {
1209 yajl_gen gen = ygenalloc();
1210
1211 y(map_open);
1212
1213 ystr("first");
1214 y(bool, false);
1215
1216 ystr("payload");
1217 yajl_gen_string(gen, (unsigned char *)message, message_size);
1218
1219 y(map_close);
1220
1221 const unsigned char *payload;
1222 ylength length;
1223 y(get_buf, &payload, &length);
1224
1225 ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload);
1226 y(free);
1227
1228 const char *reply = "{\"success\":true}";
1229 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply);
1230 DLOG("Sent tick event\n");
1231 }
1232
1233 struct sync_state {
1234 char *last_key;
1235 uint32_t rnd;
1236 xcb_window_t window;
1237 };
1238
1239 static int _sync_json_key(void *extra, const unsigned char *val, size_t len) {
1240 struct sync_state *state = extra;
1241 FREE(state->last_key);
1242 state->last_key = scalloc(len + 1, 1);
1243 memcpy(state->last_key, val, len);
1244 return 1;
1245 }
1246
1247 static int _sync_json_int(void *extra, long long val) {
1248 struct sync_state *state = extra;
1249 if (strcasecmp(state->last_key, "rnd") == 0) {
1250 state->rnd = val;
1251 } else if (strcasecmp(state->last_key, "window") == 0) {
1252 state->window = (xcb_window_t)val;
1253 }
1254 return 1;
1255 }
1256
1257 IPC_HANDLER(sync) {
1258 yajl_handle p;
1259 yajl_status stat;
1260
1261 /* Setup the JSON parser */
1262 static yajl_callbacks callbacks = {
1263 .yajl_map_key = _sync_json_key,
1264 .yajl_integer = _sync_json_int,
1265 };
1266
1267 struct sync_state state;
1268 memset(&state, '\0', sizeof(struct sync_state));
1269 p = yalloc(&callbacks, (void *)&state);
1270 stat = yajl_parse(p, (const unsigned char *)message, message_size);
1271 FREE(state.last_key);
1272 if (stat != yajl_status_ok) {
1273 unsigned char *err;
1274 err = yajl_get_error(p, true, (const unsigned char *)message,
1275 message_size);
1276 ELOG("YAJL parse error: %s\n", err);
1277 yajl_free_error(p, err);
1278
1279 const char *reply = "{\"success\":false}";
1280 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
1281 yajl_free(p);
1282 return;
1283 }
1284 yajl_free(p);
1285
1286 DLOG("received IPC sync request (rnd = %d, window = 0x%08x)\n", state.rnd, state.window);
1287 sync_respond(state.window, state.rnd);
1288 const char *reply = "{\"success\":true}";
1289 ipc_send_client_message(client, strlen(reply), I3_IPC_REPLY_TYPE_SYNC, (const uint8_t *)reply);
1290 }
1291
1292 /* The index of each callback function corresponds to the numeric
1293 * value of the message type (see include/i3/ipc.h) */
1294 handler_t handlers[12] = {
1295 handle_run_command,
1296 handle_get_workspaces,
1297 handle_subscribe,
1298 handle_get_outputs,
1299 handle_tree,
1300 handle_get_marks,
1301 handle_get_bar_config,
1302 handle_get_version,
1303 handle_get_binding_modes,
1304 handle_get_config,
1305 handle_send_tick,
1306 handle_sync,
1307 };
1308
1309 /*
1310 * Handler for activity on a client connection, receives a message from a
1311 * client.
1312 *
1313 * For now, the maximum message size is 2048. I’m not sure for what the
1314 * IPC interface will be used in the future, thus I’m not implementing a
1315 * mechanism for arbitrarily long messages, as it seems like overkill
1316 * at the moment.
1317 *
1318 */
1319 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
1320 uint32_t message_type;
1321 uint32_t message_length;
1322 uint8_t *message = NULL;
1323 ipc_client *client = (ipc_client *)w->data;
1324 assert(client->fd == w->fd);
1325
1326 int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
1327 /* EOF or other error */
1328 if (ret < 0) {
1329 /* Was this a spurious read? See ev(3) */
1330 if (ret == -1 && errno == EAGAIN) {
1331 FREE(message);
1332 return;
1333 }
1334
1335 /* If not, there was some kind of error. We don’t bother and close the
1336 * connection. Delete the client from the list of clients. */
1337 free_ipc_client(client);
1338 FREE(message);
1339 return;
1340 }
1341
1342 if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
1343 DLOG("Unhandled message type: %d\n", message_type);
1344 else {
1345 handler_t h = handlers[message_type];
1346 h(client, message, 0, message_length, message_type);
1347 }
1348
1349 FREE(message);
1350 }
1351
1352 static void ipc_client_timeout(EV_P_ ev_timer *w, int revents) {
1353 /* No need to be polite and check for writeability, the other callback would
1354 * have been called by now. */
1355 ipc_client *client = (ipc_client *)w->data;
1356
1357 char *cmdline = NULL;
1358 #if defined(__linux__) && defined(SO_PEERCRED)
1359 struct ucred peercred;
1360 socklen_t so_len = sizeof(peercred);
1361 if (getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
1362 goto end;
1363 }
1364 char *exepath;
1365 sasprintf(&exepath, "/proc/%d/cmdline", peercred.pid);
1366
1367 int fd = open(exepath, O_RDONLY);
1368 free(exepath);
1369 if (fd == -1) {
1370 goto end;
1371 }
1372 char buf[512] = {'\0'}; /* cut off cmdline for the error message. */
1373 const ssize_t n = read(fd, buf, sizeof(buf));
1374 close(fd);
1375 if (n < 0) {
1376 goto end;
1377 }
1378 for (char *walk = buf; walk < buf + n - 1; walk++) {
1379 if (*walk == '\0') {
1380 *walk = ' ';
1381 }
1382 }
1383 cmdline = buf;
1384
1385 if (cmdline) {
1386 ELOG("client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->fd);
1387 }
1388
1389 end:
1390 #endif
1391 if (!cmdline) {
1392 ELOG("client %p on fd %d timed out, killing\n", client, client->fd);
1393 }
1394
1395 free_ipc_client(client);
1396 }
1397
1398 static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents) {
1399 DLOG("fd %d writeable\n", w->fd);
1400 ipc_client *client = (ipc_client *)w->data;
1401
1402 /* If this callback is called then there should be a corresponding active
1403 * timer. */
1404 assert(client->timeout != NULL);
1405 ipc_push_pending(client);
1406 }
1407
1408 /*
1409 * Handler for activity on the listening socket, meaning that a new client
1410 * has just connected and we should accept() him. Sets up the event handler
1411 * for activity on the new connection and inserts the file descriptor into
1412 * the list of clients.
1413 *
1414 */
1415 void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
1416 struct sockaddr_un peer;
1417 socklen_t len = sizeof(struct sockaddr_un);
1418 int fd;
1419 if ((fd = accept(w->fd, (struct sockaddr *)&peer, &len)) < 0) {
1420 if (errno != EINTR) {
1421 perror("accept()");
1422 }
1423 return;
1424 }
1425
1426 /* Close this file descriptor on exec() */
1427 (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
1428
1429 set_nonblock(fd);
1430
1431 ipc_client *client = scalloc(1, sizeof(ipc_client));
1432 client->fd = fd;
1433
1434 client->read_callback = scalloc(1, sizeof(struct ev_io));
1435 client->read_callback->data = client;
1436 ev_io_init(client->read_callback, ipc_receive_message, fd, EV_READ);
1437 ev_io_start(EV_A_ client->read_callback);
1438
1439 client->write_callback = scalloc(1, sizeof(struct ev_io));
1440 client->write_callback->data = client;
1441 ev_io_init(client->write_callback, ipc_socket_writeable_cb, fd, EV_WRITE);
1442
1443 DLOG("IPC: new client connected on fd %d\n", w->fd);
1444 TAILQ_INSERT_TAIL(&all_clients, client, clients);
1445 }
1446
1447 /*
1448 * Creates the UNIX domain socket at the given path, sets it to non-blocking
1449 * mode, bind()s and listen()s on it.
1450 *
1451 */
1452 int ipc_create_socket(const char *filename) {
1453 int sockfd;
1454
1455 FREE(current_socketpath);
1456
1457 char *resolved = resolve_tilde(filename);
1458 DLOG("Creating IPC-socket at %s\n", resolved);
1459 char *copy = sstrdup(resolved);
1460 const char *dir = dirname(copy);
1461 if (!path_exists(dir))
1462 mkdirp(dir, DEFAULT_DIR_MODE);
1463 free(copy);
1464
1465 /* Unlink the unix domain socket before */
1466 unlink(resolved);
1467
1468 if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
1469 perror("socket()");
1470 free(resolved);
1471 return -1;
1472 }
1473
1474 (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
1475
1476 struct sockaddr_un addr;
1477 memset(&addr, 0, sizeof(struct sockaddr_un));
1478 addr.sun_family = AF_LOCAL;
1479 strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1);
1480 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
1481 perror("bind()");
1482 free(resolved);
1483 return -1;
1484 }
1485
1486 set_nonblock(sockfd);
1487
1488 if (listen(sockfd, 5) < 0) {
1489 perror("listen()");
1490 free(resolved);
1491 return -1;
1492 }
1493
1494 current_socketpath = resolved;
1495 return sockfd;
1496 }
1497
1498 /*
1499 * Generates a json workspace event. Returns a dynamically allocated yajl
1500 * generator. Free with yajl_gen_free().
1501 */
1502 yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
1503 setlocale(LC_NUMERIC, "C");
1504 yajl_gen gen = ygenalloc();
1505
1506 y(map_open);
1507
1508 ystr("change");
1509 ystr(change);
1510
1511 ystr("current");
1512 if (current == NULL)
1513 y(null);
1514 else
1515 dump_node(gen, current, false);
1516
1517 ystr("old");
1518 if (old == NULL)
1519 y(null);
1520 else
1521 dump_node(gen, old, false);
1522
1523 y(map_close);
1524
1525 setlocale(LC_NUMERIC, "");
1526
1527 return gen;
1528 }
1529
1530 /*
1531 * For the workspace events we send, along with the usual "change" field, also
1532 * the workspace container in "current". For focus events, we send the
1533 * previously focused workspace in "old".
1534 */
1535 void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
1536 yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
1537
1538 const unsigned char *payload;
1539 ylength length;
1540 y(get_buf, &payload, &length);
1541
1542 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
1543
1544 y(free);
1545 }
1546
1547 /*
1548 * For the window events we send, along the usual "change" field,
1549 * also the window container, in "container".
1550 */
1551 void ipc_send_window_event(const char *property, Con *con) {
1552 DLOG("Issue IPC window %s event (con = %p, window = 0x%08x)\n",
1553 property, con, (con->window ? con->window->id : XCB_WINDOW_NONE));
1554
1555 setlocale(LC_NUMERIC, "C");
1556 yajl_gen gen = ygenalloc();
1557
1558 y(map_open);
1559
1560 ystr("change");
1561 ystr(property);
1562
1563 ystr("container");
1564 dump_node(gen, con, false);
1565
1566 y(map_close);
1567
1568 const unsigned char *payload;
1569 ylength length;
1570 y(get_buf, &payload, &length);
1571
1572 ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
1573 y(free);
1574 setlocale(LC_NUMERIC, "");
1575 }
1576
1577 /*
1578 * For the barconfig update events, we send the serialized barconfig.
1579 */
1580 void ipc_send_barconfig_update_event(Barconfig *barconfig) {
1581 DLOG("Issue barconfig_update event for id = %s\n", barconfig->id);
1582 setlocale(LC_NUMERIC, "C");
1583 yajl_gen gen = ygenalloc();
1584
1585 dump_bar_config(gen, barconfig);
1586
1587 const unsigned char *payload;
1588 ylength length;
1589 y(get_buf, &payload, &length);
1590
1591 ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, (const char *)payload);
1592 y(free);
1593 setlocale(LC_NUMERIC, "");
1594 }
1595
1596 /*
1597 * For the binding events, we send the serialized binding struct.
1598 */
1599 void ipc_send_binding_event(const char *event_type, Binding *bind) {
1600 DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
1601
1602 setlocale(LC_NUMERIC, "C");
1603
1604 yajl_gen gen = ygenalloc();
1605
1606 y(map_open);
1607
1608 ystr("change");
1609 ystr(event_type);
1610
1611 ystr("binding");
1612 dump_binding(gen, bind);
1613
1614 y(map_close);
1615
1616 const unsigned char *payload;
1617 ylength length;
1618 y(get_buf, &payload, &length);
1619
1620 ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
1621
1622 y(free);
1623 setlocale(LC_NUMERIC, "");
1624 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * key_press.c: key press handler
7 *
8 */
9 #include "all.h"
10
11 /*
12 * There was a KeyPress or KeyRelease (both events have the same fields). We
13 * compare this key code with our bindings table and pass the bound action to
14 * parse_command().
15 *
16 */
17 void handle_key_press(xcb_key_press_event_t *event) {
18 const bool key_release = (event->response_type == XCB_KEY_RELEASE);
19
20 last_timestamp = event->time;
21
22 DLOG("%s %d, state raw = 0x%x\n", (key_release ? "KeyRelease" : "KeyPress"), event->detail, event->state);
23
24 Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
25
26 /* if we couldn't find a binding, we are done */
27 if (bind == NULL)
28 return;
29
30 CommandResult *result = run_binding(bind, NULL);
31 command_result_free(result);
32 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * load_layout.c: Restore (parts of) the layout, for example after an inplace
7 * restart.
8 *
9 */
10 #include "all.h"
11
12 #include <yajl/yajl_common.h>
13 #include <yajl/yajl_gen.h>
14 #include <yajl/yajl_parse.h>
15 #include <yajl/yajl_version.h>
16
17 /* TODO: refactor the whole parsing thing */
18
19 static char *last_key;
20 static int incomplete;
21 static Con *json_node;
22 static Con *to_focus;
23 static bool parsing_swallows;
24 static bool parsing_rect;
25 static bool parsing_deco_rect;
26 static bool parsing_window_rect;
27 static bool parsing_geometry;
28 static bool parsing_focus;
29 static bool parsing_marks;
30 struct Match *current_swallow;
31 static bool swallow_is_empty;
32 static int num_marks;
33 static char **marks;
34
35 /* This list is used for reordering the focus stack after parsing the 'focus'
36 * array. */
37 struct focus_mapping {
38 int old_id;
39
40 TAILQ_ENTRY(focus_mapping)
41 focus_mappings;
42 };
43
44 static TAILQ_HEAD(focus_mappings_head, focus_mapping) focus_mappings =
45 TAILQ_HEAD_INITIALIZER(focus_mappings);
46
47 static int json_start_map(void *ctx) {
48 LOG("start of map, last_key = %s\n", last_key);
49 if (parsing_swallows) {
50 LOG("creating new swallow\n");
51 current_swallow = smalloc(sizeof(Match));
52 match_init(current_swallow);
53 current_swallow->dock = M_DONTCHECK;
54 TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
55 swallow_is_empty = true;
56 } else {
57 if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
58 if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
59 DLOG("New floating_node\n");
60 Con *ws = con_get_workspace(json_node);
61 json_node = con_new_skeleton(NULL, NULL);
62 json_node->name = NULL;
63 json_node->parent = ws;
64 DLOG("Parent is workspace = %p\n", ws);
65 } else {
66 Con *parent = json_node;
67 json_node = con_new_skeleton(NULL, NULL);
68 json_node->name = NULL;
69 json_node->parent = parent;
70 }
71 /* json_node is incomplete and should be removed if parsing fails */
72 incomplete++;
73 DLOG("incomplete = %d\n", incomplete);
74 }
75 }
76 return 1;
77 }
78
79 static int json_end_map(void *ctx) {
80 LOG("end of map\n");
81 if (!parsing_swallows && !parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
82 /* Set a few default values to simplify manually crafted layout files. */
83 if (json_node->layout == L_DEFAULT) {
84 DLOG("Setting layout = L_SPLITH\n");
85 json_node->layout = L_SPLITH;
86 }
87
88 /* Sanity check: swallow criteria don’t make any sense on a split
89 * container. */
90 if (con_is_split(json_node) > 0 && !TAILQ_EMPTY(&(json_node->swallow_head))) {
91 DLOG("sanity check: removing swallows specification from split container\n");
92 while (!TAILQ_EMPTY(&(json_node->swallow_head))) {
93 Match *match = TAILQ_FIRST(&(json_node->swallow_head));
94 TAILQ_REMOVE(&(json_node->swallow_head), match, matches);
95 match_free(match);
96 free(match);
97 }
98 }
99
100 if (json_node->type == CT_WORKSPACE) {
101 /* Ensure the workspace has a name. */
102 DLOG("Attaching workspace. name = %s\n", json_node->name);
103 if (json_node->name == NULL || strcmp(json_node->name, "") == 0) {
104 json_node->name = sstrdup("unnamed");
105 }
106
107 /* Prevent name clashes when appending a workspace, e.g. when the
108 * user tries to restore a workspace called “1” but already has a
109 * workspace called “1”. */
110 char *base = sstrdup(json_node->name);
111 int cnt = 1;
112 while (get_existing_workspace_by_name(json_node->name) != NULL) {
113 FREE(json_node->name);
114 sasprintf(&(json_node->name), "%s_%d", base, cnt++);
115 }
116 free(base);
117
118 /* Set num accordingly so that i3bar will properly sort it. */
119 json_node->num = ws_name_to_number(json_node->name);
120 }
121
122 // When appending JSON layout files that only contain the workspace
123 // _contents_, we might not have an upfront signal that the
124 // container we’re currently parsing is a floating container (like
125 // the “floating_nodes” key of the workspace container itself).
126 // That’s why we make sure the con is attached at the right place
127 // in the hierarchy in case it’s floating.
128 if (json_node->type == CT_FLOATING_CON) {
129 DLOG("fixing parent which currently is %p / %s\n", json_node->parent, json_node->parent->name);
130 json_node->parent = con_get_workspace(json_node->parent);
131
132 // Also set a size if none was supplied, otherwise the placeholder
133 // window cannot be created as X11 requests with width=0 or
134 // height=0 are invalid.
135 const Rect zero = {0, 0, 0, 0};
136 if (memcmp(&(json_node->rect), &zero, sizeof(Rect)) == 0) {
137 DLOG("Geometry not set, combining children\n");
138 Con *child;
139 TAILQ_FOREACH(child, &(json_node->nodes_head), nodes) {
140 DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
141 json_node->rect.width += child->geometry.width;
142 json_node->rect.height = max(json_node->rect.height, child->geometry.height);
143 }
144 }
145
146 floating_check_size(json_node);
147 }
148
149 if (num_marks > 0) {
150 for (int i = 0; i < num_marks; i++) {
151 con_mark(json_node, marks[i], MM_ADD);
152 free(marks[i]);
153 }
154
155 FREE(marks);
156 num_marks = 0;
157 }
158
159 LOG("attaching\n");
160 con_attach(json_node, json_node->parent, true);
161 LOG("Creating window\n");
162 x_con_init(json_node);
163 json_node = json_node->parent;
164 incomplete--;
165 DLOG("incomplete = %d\n", incomplete);
166 }
167
168 if (parsing_swallows && swallow_is_empty) {
169 /* We parsed an empty swallow definition. This is an invalid layout
170 * definition, hence we reject it. */
171 ELOG("Layout file is invalid: found an empty swallow definition.\n");
172 return 0;
173 }
174
175 parsing_rect = false;
176 parsing_deco_rect = false;
177 parsing_window_rect = false;
178 parsing_geometry = false;
179 return 1;
180 }
181
182 static int json_end_array(void *ctx) {
183 LOG("end of array\n");
184 if (!parsing_swallows && !parsing_focus && !parsing_marks) {
185 con_fix_percent(json_node);
186 }
187 if (parsing_swallows) {
188 parsing_swallows = false;
189 }
190 if (parsing_marks) {
191 parsing_marks = false;
192 }
193
194 if (parsing_focus) {
195 /* Clear the list of focus mappings */
196 struct focus_mapping *mapping;
197 TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
198 LOG("focus (reverse) %d\n", mapping->old_id);
199 Con *con;
200 TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
201 if (con->old_id != mapping->old_id)
202 continue;
203 LOG("got it! %p\n", con);
204 /* Move this entry to the top of the focus list. */
205 TAILQ_REMOVE(&(json_node->focus_head), con, focused);
206 TAILQ_INSERT_HEAD(&(json_node->focus_head), con, focused);
207 break;
208 }
209 }
210 while (!TAILQ_EMPTY(&focus_mappings)) {
211 mapping = TAILQ_FIRST(&focus_mappings);
212 TAILQ_REMOVE(&focus_mappings, mapping, focus_mappings);
213 free(mapping);
214 }
215 parsing_focus = false;
216 }
217 return 1;
218 }
219
220 static int json_key(void *ctx, const unsigned char *val, size_t len) {
221 LOG("key: %.*s\n", (int)len, val);
222 FREE(last_key);
223 last_key = scalloc(len + 1, 1);
224 memcpy(last_key, val, len);
225 if (strcasecmp(last_key, "swallows") == 0)
226 parsing_swallows = true;
227
228 if (strcasecmp(last_key, "rect") == 0)
229 parsing_rect = true;
230
231 if (strcasecmp(last_key, "deco_rect") == 0)
232 parsing_deco_rect = true;
233
234 if (strcasecmp(last_key, "window_rect") == 0)
235 parsing_window_rect = true;
236
237 if (strcasecmp(last_key, "geometry") == 0)
238 parsing_geometry = true;
239
240 if (strcasecmp(last_key, "focus") == 0)
241 parsing_focus = true;
242
243 if (strcasecmp(last_key, "marks") == 0) {
244 num_marks = 0;
245 parsing_marks = true;
246 }
247
248 return 1;
249 }
250
251 static int json_string(void *ctx, const unsigned char *val, size_t len) {
252 LOG("string: %.*s for key %s\n", (int)len, val, last_key);
253 if (parsing_swallows) {
254 char *sval;
255 sasprintf(&sval, "%.*s", len, val);
256 if (strcasecmp(last_key, "class") == 0) {
257 current_swallow->class = regex_new(sval);
258 swallow_is_empty = false;
259 } else if (strcasecmp(last_key, "instance") == 0) {
260 current_swallow->instance = regex_new(sval);
261 swallow_is_empty = false;
262 } else if (strcasecmp(last_key, "window_role") == 0) {
263 current_swallow->window_role = regex_new(sval);
264 swallow_is_empty = false;
265 } else if (strcasecmp(last_key, "title") == 0) {
266 current_swallow->title = regex_new(sval);
267 swallow_is_empty = false;
268 } else {
269 ELOG("swallow key %s unknown\n", last_key);
270 }
271 free(sval);
272 } else if (parsing_marks) {
273 char *mark;
274 sasprintf(&mark, "%.*s", (int)len, val);
275
276 marks = srealloc(marks, (++num_marks) * sizeof(char *));
277 marks[num_marks - 1] = sstrdup(mark);
278 } else {
279 if (strcasecmp(last_key, "name") == 0) {
280 json_node->name = scalloc(len + 1, 1);
281 memcpy(json_node->name, val, len);
282 } else if (strcasecmp(last_key, "title_format") == 0) {
283 json_node->title_format = scalloc(len + 1, 1);
284 memcpy(json_node->title_format, val, len);
285 } else if (strcasecmp(last_key, "sticky_group") == 0) {
286 json_node->sticky_group = scalloc(len + 1, 1);
287 memcpy(json_node->sticky_group, val, len);
288 LOG("sticky_group of this container is %s\n", json_node->sticky_group);
289 } else if (strcasecmp(last_key, "orientation") == 0) {
290 /* Upgrade path from older versions of i3 (doing an inplace restart
291 * to a newer version):
292 * "orientation" is dumped before "layout". Therefore, we store
293 * whether the orientation was horizontal or vertical in the
294 * last_split_layout. When we then encounter layout == "default",
295 * we will use the last_split_layout as layout instead. */
296 char *buf = NULL;
297 sasprintf(&buf, "%.*s", (int)len, val);
298 if (strcasecmp(buf, "none") == 0 ||
299 strcasecmp(buf, "horizontal") == 0)
300 json_node->last_split_layout = L_SPLITH;
301 else if (strcasecmp(buf, "vertical") == 0)
302 json_node->last_split_layout = L_SPLITV;
303 else
304 LOG("Unhandled orientation: %s\n", buf);
305 free(buf);
306 } else if (strcasecmp(last_key, "border") == 0) {
307 char *buf = NULL;
308 sasprintf(&buf, "%.*s", (int)len, val);
309 if (strcasecmp(buf, "none") == 0)
310 json_node->border_style = BS_NONE;
311 else if (strcasecmp(buf, "1pixel") == 0) {
312 json_node->border_style = BS_PIXEL;
313 json_node->current_border_width = 1;
314 } else if (strcasecmp(buf, "pixel") == 0)
315 json_node->border_style = BS_PIXEL;
316 else if (strcasecmp(buf, "normal") == 0)
317 json_node->border_style = BS_NORMAL;
318 else
319 LOG("Unhandled \"border\": %s\n", buf);
320 free(buf);
321 } else if (strcasecmp(last_key, "type") == 0) {
322 char *buf = NULL;
323 sasprintf(&buf, "%.*s", (int)len, val);
324 if (strcasecmp(buf, "root") == 0)
325 json_node->type = CT_ROOT;
326 else if (strcasecmp(buf, "output") == 0)
327 json_node->type = CT_OUTPUT;
328 else if (strcasecmp(buf, "con") == 0)
329 json_node->type = CT_CON;
330 else if (strcasecmp(buf, "floating_con") == 0)
331 json_node->type = CT_FLOATING_CON;
332 else if (strcasecmp(buf, "workspace") == 0)
333 json_node->type = CT_WORKSPACE;
334 else if (strcasecmp(buf, "dockarea") == 0)
335 json_node->type = CT_DOCKAREA;
336 else
337 LOG("Unhandled \"type\": %s\n", buf);
338 free(buf);
339 } else if (strcasecmp(last_key, "layout") == 0) {
340 char *buf = NULL;
341 sasprintf(&buf, "%.*s", (int)len, val);
342 if (strcasecmp(buf, "default") == 0)
343 /* This set above when we read "orientation". */
344 json_node->layout = json_node->last_split_layout;
345 else if (strcasecmp(buf, "stacked") == 0)
346 json_node->layout = L_STACKED;
347 else if (strcasecmp(buf, "tabbed") == 0)
348 json_node->layout = L_TABBED;
349 else if (strcasecmp(buf, "dockarea") == 0)
350 json_node->layout = L_DOCKAREA;
351 else if (strcasecmp(buf, "output") == 0)
352 json_node->layout = L_OUTPUT;
353 else if (strcasecmp(buf, "splith") == 0)
354 json_node->layout = L_SPLITH;
355 else if (strcasecmp(buf, "splitv") == 0)
356 json_node->layout = L_SPLITV;
357 else
358 LOG("Unhandled \"layout\": %s\n", buf);
359 free(buf);
360 } else if (strcasecmp(last_key, "workspace_layout") == 0) {
361 char *buf = NULL;
362 sasprintf(&buf, "%.*s", (int)len, val);
363 if (strcasecmp(buf, "default") == 0)
364 json_node->workspace_layout = L_DEFAULT;
365 else if (strcasecmp(buf, "stacked") == 0)
366 json_node->workspace_layout = L_STACKED;
367 else if (strcasecmp(buf, "tabbed") == 0)
368 json_node->workspace_layout = L_TABBED;
369 else
370 LOG("Unhandled \"workspace_layout\": %s\n", buf);
371 free(buf);
372 } else if (strcasecmp(last_key, "last_split_layout") == 0) {
373 char *buf = NULL;
374 sasprintf(&buf, "%.*s", (int)len, val);
375 if (strcasecmp(buf, "splith") == 0)
376 json_node->last_split_layout = L_SPLITH;
377 else if (strcasecmp(buf, "splitv") == 0)
378 json_node->last_split_layout = L_SPLITV;
379 else
380 LOG("Unhandled \"last_splitlayout\": %s\n", buf);
381 free(buf);
382 } else if (strcasecmp(last_key, "mark") == 0) {
383 DLOG("Found deprecated key \"mark\".\n");
384
385 char *buf = NULL;
386 sasprintf(&buf, "%.*s", (int)len, val);
387
388 con_mark(json_node, buf, MM_REPLACE);
389 } else if (strcasecmp(last_key, "floating") == 0) {
390 char *buf = NULL;
391 sasprintf(&buf, "%.*s", (int)len, val);
392 if (strcasecmp(buf, "auto_off") == 0)
393 json_node->floating = FLOATING_AUTO_OFF;
394 else if (strcasecmp(buf, "auto_on") == 0)
395 json_node->floating = FLOATING_AUTO_ON;
396 else if (strcasecmp(buf, "user_off") == 0)
397 json_node->floating = FLOATING_USER_OFF;
398 else if (strcasecmp(buf, "user_on") == 0)
399 json_node->floating = FLOATING_USER_ON;
400 free(buf);
401 } else if (strcasecmp(last_key, "scratchpad_state") == 0) {
402 char *buf = NULL;
403 sasprintf(&buf, "%.*s", (int)len, val);
404 if (strcasecmp(buf, "none") == 0)
405 json_node->scratchpad_state = SCRATCHPAD_NONE;
406 else if (strcasecmp(buf, "fresh") == 0)
407 json_node->scratchpad_state = SCRATCHPAD_FRESH;
408 else if (strcasecmp(buf, "changed") == 0)
409 json_node->scratchpad_state = SCRATCHPAD_CHANGED;
410 free(buf);
411 }
412 }
413 return 1;
414 }
415
416 static int json_int(void *ctx, long long val) {
417 LOG("int %lld for key %s\n", val, last_key);
418 /* For backwards compatibility with i3 < 4.8 */
419 if (strcasecmp(last_key, "type") == 0)
420 json_node->type = val;
421
422 if (strcasecmp(last_key, "fullscreen_mode") == 0)
423 json_node->fullscreen_mode = val;
424
425 if (strcasecmp(last_key, "num") == 0)
426 json_node->num = val;
427
428 if (strcasecmp(last_key, "current_border_width") == 0)
429 json_node->current_border_width = val;
430
431 if (strcasecmp(last_key, "depth") == 0)
432 json_node->depth = val;
433
434 if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
435 json_node->old_id = val;
436
437 if (parsing_focus) {
438 struct focus_mapping *focus_mapping = scalloc(1, sizeof(struct focus_mapping));
439 focus_mapping->old_id = val;
440 TAILQ_INSERT_TAIL(&focus_mappings, focus_mapping, focus_mappings);
441 }
442
443 if (parsing_rect || parsing_window_rect || parsing_geometry) {
444 Rect *r;
445 if (parsing_rect)
446 r = &(json_node->rect);
447 else if (parsing_window_rect)
448 r = &(json_node->window_rect);
449 else
450 r = &(json_node->geometry);
451 if (strcasecmp(last_key, "x") == 0)
452 r->x = val;
453 else if (strcasecmp(last_key, "y") == 0)
454 r->y = val;
455 else if (strcasecmp(last_key, "width") == 0)
456 r->width = val;
457 else if (strcasecmp(last_key, "height") == 0)
458 r->height = val;
459 else
460 ELOG("WARNING: unknown key %s in rect\n", last_key);
461 DLOG("rect now: (%d, %d, %d, %d)\n",
462 r->x, r->y, r->width, r->height);
463 }
464 if (parsing_swallows) {
465 if (strcasecmp(last_key, "id") == 0) {
466 current_swallow->id = val;
467 swallow_is_empty = false;
468 }
469 if (strcasecmp(last_key, "dock") == 0) {
470 current_swallow->dock = val;
471 swallow_is_empty = false;
472 }
473 if (strcasecmp(last_key, "insert_where") == 0) {
474 current_swallow->insert_where = val;
475 swallow_is_empty = false;
476 }
477 }
478
479 return 1;
480 }
481
482 static int json_bool(void *ctx, int val) {
483 LOG("bool %d for key %s\n", val, last_key);
484 if (strcasecmp(last_key, "focused") == 0 && val) {
485 to_focus = json_node;
486 }
487
488 if (strcasecmp(last_key, "sticky") == 0)
489 json_node->sticky = val;
490
491 if (parsing_swallows) {
492 if (strcasecmp(last_key, "restart_mode") == 0) {
493 current_swallow->restart_mode = val;
494 swallow_is_empty = false;
495 }
496 }
497
498 return 1;
499 }
500
501 static int json_double(void *ctx, double val) {
502 LOG("double %f for key %s\n", val, last_key);
503 if (strcasecmp(last_key, "percent") == 0) {
504 json_node->percent = val;
505 }
506 return 1;
507 }
508
509 static json_content_t content_result;
510 static int content_level;
511
512 static int json_determine_content_deeper(void *ctx) {
513 content_level++;
514 return 1;
515 }
516
517 static int json_determine_content_shallower(void *ctx) {
518 content_level--;
519 return 1;
520 }
521
522 static int json_determine_content_string(void *ctx, const unsigned char *val, size_t len) {
523 if (strcasecmp(last_key, "type") != 0 || content_level > 1)
524 return 1;
525
526 DLOG("string = %.*s, last_key = %s\n", (int)len, val, last_key);
527 if (strncasecmp((const char *)val, "workspace", len) == 0)
528 content_result = JSON_CONTENT_WORKSPACE;
529 return 0;
530 }
531
532 /*
533 * Returns true if the provided JSON could be parsed by yajl.
534 *
535 */
536 bool json_validate(const char *buf, const size_t len) {
537 bool valid = true;
538 yajl_handle hand = yajl_alloc(NULL, NULL, NULL);
539 /* Allowing comments allows for more user-friendly layout files. */
540 yajl_config(hand, yajl_allow_comments, true);
541 /* Allow multiple values, i.e. multiple nodes to attach */
542 yajl_config(hand, yajl_allow_multiple_values, true);
543
544 setlocale(LC_NUMERIC, "C");
545 if (yajl_parse(hand, (const unsigned char *)buf, len) != yajl_status_ok) {
546 unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len);
547 ELOG("JSON parsing error: %s\n", str);
548 yajl_free_error(hand, str);
549 valid = false;
550 }
551 setlocale(LC_NUMERIC, "");
552
553 yajl_complete_parse(hand);
554 yajl_free(hand);
555
556 return valid;
557 }
558
559 /* Parses the given JSON file until it encounters the first “type” property to
560 * determine whether the file contains workspaces or regular containers, which
561 * is important to know when deciding where (and how) to append the contents.
562 * */
563 json_content_t json_determine_content(const char *buf, const size_t len) {
564 // We default to JSON_CONTENT_CON because it is legal to not include
565 // “"type": "con"” in the JSON files for better readability.
566 content_result = JSON_CONTENT_CON;
567 content_level = 0;
568 static yajl_callbacks callbacks = {
569 .yajl_string = json_determine_content_string,
570 .yajl_map_key = json_key,
571 .yajl_start_array = json_determine_content_deeper,
572 .yajl_start_map = json_determine_content_deeper,
573 .yajl_end_map = json_determine_content_shallower,
574 .yajl_end_array = json_determine_content_shallower,
575 };
576 yajl_handle hand = yajl_alloc(&callbacks, NULL, NULL);
577 /* Allowing comments allows for more user-friendly layout files. */
578 yajl_config(hand, yajl_allow_comments, true);
579 /* Allow multiple values, i.e. multiple nodes to attach */
580 yajl_config(hand, yajl_allow_multiple_values, true);
581 setlocale(LC_NUMERIC, "C");
582 const yajl_status stat = yajl_parse(hand, (const unsigned char *)buf, len);
583 if (stat != yajl_status_ok && stat != yajl_status_client_canceled) {
584 unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len);
585 ELOG("JSON parsing error: %s\n", str);
586 yajl_free_error(hand, str);
587 }
588
589 setlocale(LC_NUMERIC, "");
590 yajl_complete_parse(hand);
591 yajl_free(hand);
592
593 return content_result;
594 }
595
596 void tree_append_json(Con *con, const char *buf, const size_t len, char **errormsg) {
597 static yajl_callbacks callbacks = {
598 .yajl_boolean = json_bool,
599 .yajl_integer = json_int,
600 .yajl_double = json_double,
601 .yajl_string = json_string,
602 .yajl_start_map = json_start_map,
603 .yajl_map_key = json_key,
604 .yajl_end_map = json_end_map,
605 .yajl_end_array = json_end_array,
606 };
607 yajl_handle hand = yajl_alloc(&callbacks, NULL, NULL);
608 /* Allowing comments allows for more user-friendly layout files. */
609 yajl_config(hand, yajl_allow_comments, true);
610 /* Allow multiple values, i.e. multiple nodes to attach */
611 yajl_config(hand, yajl_allow_multiple_values, true);
612 /* We don't need to validate that the input is valid UTF8 here.
613 * tree_append_json is called in two cases:
614 * 1. With the append_layout command. json_validate is called first and will
615 * fail on invalid UTF8 characters so we don't need to recheck.
616 * 2. With an in-place restart. The rest of the codebase should be
617 * responsible for producing valid UTF8 JSON output. If not,
618 * tree_append_json will just preserve invalid UTF8 strings in the tree
619 * instead of failing to parse the layout file which could lead to
620 * problems like in #3156.
621 * Either way, disabling UTF8 validation slightly speeds up yajl. */
622 yajl_config(hand, yajl_dont_validate_strings, true);
623 json_node = con;
624 to_focus = NULL;
625 incomplete = 0;
626 parsing_swallows = false;
627 parsing_rect = false;
628 parsing_deco_rect = false;
629 parsing_window_rect = false;
630 parsing_geometry = false;
631 parsing_focus = false;
632 parsing_marks = false;
633 setlocale(LC_NUMERIC, "C");
634 const yajl_status stat = yajl_parse(hand, (const unsigned char *)buf, len);
635 if (stat != yajl_status_ok) {
636 unsigned char *str = yajl_get_error(hand, 1, (const unsigned char *)buf, len);
637 ELOG("JSON parsing error: %s\n", str);
638 if (errormsg != NULL)
639 *errormsg = sstrdup((const char *)str);
640 yajl_free_error(hand, str);
641 while (incomplete-- > 0) {
642 Con *parent = json_node->parent;
643 DLOG("freeing incomplete container %p\n", json_node);
644 if (json_node == to_focus) {
645 to_focus = NULL;
646 }
647 con_free(json_node);
648 json_node = parent;
649 }
650 }
651
652 /* In case not all containers were restored, we need to fix the
653 * percentages, otherwise i3 will crash immediately when rendering the
654 * next time. */
655 con_fix_percent(con);
656
657 setlocale(LC_NUMERIC, "");
658 yajl_complete_parse(hand);
659 yajl_free(hand);
660
661 if (to_focus) {
662 con_activate(to_focus);
663 }
664 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * log.c: Logging functions.
7 *
8 */
9 #include <config.h>
10
11 #include <stdarg.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdbool.h>
15 #include <stdlib.h>
16 #include <sys/time.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <errno.h>
22 #if !defined(__OpenBSD__)
23 #include <pthread.h>
24 #endif
25
26 #include "util.h"
27 #include "log.h"
28 #include "i3.h"
29 #include "libi3.h"
30 #include "shmlog.h"
31
32 #if defined(__APPLE__)
33 #include <sys/sysctl.h>
34 #endif
35
36 static bool debug_logging = false;
37 static bool verbose = false;
38 static FILE *errorfile;
39 char *errorfilename;
40
41 /* SHM logging variables */
42
43 /* The name for the SHM (/i3-log-%pid). Will end up on /dev/shm on most
44 * systems. Global so that we can clean up at exit. */
45 char *shmlogname = "";
46 /* Size limit for the SHM log, by default 25 MiB. Can be overwritten using the
47 * flag --shmlog-size. */
48 int shmlog_size = 0;
49 /* If enabled, logbuffer will point to a memory mapping of the i3 SHM log. */
50 static char *logbuffer;
51 /* A pointer (within logbuffer) where data will be written to next. */
52 static char *logwalk;
53 /* A pointer to the shmlog header */
54 static i3_shmlog_header *header;
55 /* A pointer to the byte where we last wrapped. Necessary to not print the
56 * left-overs at the end of the ringbuffer. */
57 static char *loglastwrap;
58 /* Size (in bytes) of the i3 SHM log. */
59 static int logbuffer_size;
60 /* File descriptor for shm_open. */
61 static int logbuffer_shm;
62 /* Size (in bytes) of physical memory */
63 static long long physical_mem_bytes;
64
65 /*
66 * Writes the offsets for the next write and for the last wrap to the
67 * shmlog_header.
68 * Necessary to print the i3 SHM log in the correct order.
69 *
70 */
71 static void store_log_markers(void) {
72 header->offset_next_write = (logwalk - logbuffer);
73 header->offset_last_wrap = (loglastwrap - logbuffer);
74 header->size = logbuffer_size;
75 }
76
77 /*
78 * Initializes logging by creating an error logfile in /tmp (or
79 * XDG_RUNTIME_DIR, see get_process_filename()).
80 *
81 * Will be called twice if --shmlog-size is specified.
82 *
83 */
84 void init_logging(void) {
85 if (!errorfilename) {
86 if (!(errorfilename = get_process_filename("errorlog")))
87 fprintf(stderr, "Could not initialize errorlog\n");
88 else {
89 errorfile = fopen(errorfilename, "w");
90 if (!errorfile) {
91 fprintf(stderr, "Could not initialize errorlog on %s: %s\n",
92 errorfilename, strerror(errno));
93 } else {
94 if (fcntl(fileno(errorfile), F_SETFD, FD_CLOEXEC)) {
95 fprintf(stderr, "Could not set close-on-exec flag\n");
96 }
97 }
98 }
99 }
100 if (physical_mem_bytes == 0) {
101 #if defined(__APPLE__)
102 int mib[2] = {CTL_HW, HW_MEMSIZE};
103 size_t length = sizeof(long long);
104 sysctl(mib, 2, &physical_mem_bytes, &length, NULL, 0);
105 #else
106 physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
107 sysconf(_SC_PAGESIZE);
108 #endif
109 }
110 /* Start SHM logging if shmlog_size is > 0. shmlog_size is SHMLOG_SIZE by
111 * default on development versions, and 0 on release versions. If it is
112 * not > 0, the user has turned it off, so let's close the logbuffer. */
113 if (shmlog_size > 0 && logbuffer == NULL)
114 open_logbuffer();
115 else if (shmlog_size <= 0 && logbuffer)
116 close_logbuffer();
117 atexit(purge_zerobyte_logfile);
118 }
119
120 /*
121 * Opens the logbuffer.
122 *
123 */
124 void open_logbuffer(void) {
125 /* Reserve 1% of the RAM for the logfile, but at max 25 MiB.
126 * For 512 MiB of RAM this will lead to a 5 MiB log buffer.
127 * At the moment (2011-12-10), no testcase leads to an i3 log
128 * of more than ~ 600 KiB. */
129 logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
130 #if defined(__FreeBSD__)
131 sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid());
132 #else
133 sasprintf(&shmlogname, "/i3-log-%d", getpid());
134 #endif
135 logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
136 if (logbuffer_shm == -1) {
137 fprintf(stderr, "Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
138 return;
139 }
140
141 #if defined(__OpenBSD__) || defined(__APPLE__)
142 if (ftruncate(logbuffer_shm, logbuffer_size) == -1) {
143 fprintf(stderr, "Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
144 #else
145 int ret;
146 if ((ret = posix_fallocate(logbuffer_shm, 0, logbuffer_size)) != 0) {
147 fprintf(stderr, "Could not ftruncate SHM segment for the i3 log: %s\n", strerror(ret));
148 #endif
149 close(logbuffer_shm);
150 shm_unlink(shmlogname);
151 return;
152 }
153
154 logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
155 if (logbuffer == MAP_FAILED) {
156 close_logbuffer();
157 fprintf(stderr, "Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
158 return;
159 }
160
161 /* Initialize with 0-bytes, just to be sure… */
162 memset(logbuffer, '\0', logbuffer_size);
163
164 header = (i3_shmlog_header *)logbuffer;
165
166 #if !defined(__OpenBSD__)
167 pthread_condattr_t cond_attr;
168 pthread_condattr_init(&cond_attr);
169 if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0)
170 fprintf(stderr, "pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
171 pthread_cond_init(&(header->condvar), &cond_attr);
172 #endif
173
174 logwalk = logbuffer + sizeof(i3_shmlog_header);
175 loglastwrap = logbuffer + logbuffer_size;
176 store_log_markers();
177 }
178
179 /*
180 * Closes the logbuffer.
181 *
182 */
183 void close_logbuffer(void) {
184 close(logbuffer_shm);
185 shm_unlink(shmlogname);
186 free(shmlogname);
187 logbuffer = NULL;
188 shmlogname = "";
189 }
190
191 /*
192 * Set verbosity of i3. If verbose is set to true, informative messages will
193 * be printed to stdout. If verbose is set to false, only errors will be
194 * printed.
195 *
196 */
197 void set_verbosity(bool _verbose) {
198 verbose = _verbose;
199 }
200
201 /*
202 * Get debug logging.
203 *
204 */
205 bool get_debug_logging(void) {
206 return debug_logging;
207 }
208
209 /*
210 * Set debug logging.
211 *
212 */
213 void set_debug_logging(const bool _debug_logging) {
214 debug_logging = _debug_logging;
215 }
216
217 /*
218 * Logs the given message to stdout (if print is true) while prefixing the
219 * current time to it. Additionally, the message will be saved in the i3 SHM
220 * log if enabled.
221 * This is to be called by *LOG() which includes filename/linenumber/function.
222 *
223 */
224 static void vlog(const bool print, const char *fmt, va_list args) {
225 /* Precisely one page to not consume too much memory but to hold enough
226 * data to be useful. */
227 static char message[4096];
228 static struct tm result;
229 static time_t t;
230 static struct tm *tmp;
231 static size_t len;
232
233 /* Get current time */
234 t = time(NULL);
235 /* Convert time to local time (determined by the locale) */
236 tmp = localtime_r(&t, &result);
237 /* Generate time prefix */
238 len = strftime(message, sizeof(message), "%x %X - ", tmp);
239
240 /*
241 * logbuffer print
242 * ----------------
243 * true true format message, save, print
244 * true false format message, save
245 * false true print message only
246 * false false INVALID, never called
247 */
248 if (!logbuffer) {
249 #ifdef DEBUG_TIMING
250 struct timeval tv;
251 gettimeofday(&tv, NULL);
252 printf("%s%d.%d - ", message, tv.tv_sec, tv.tv_usec);
253 #else
254 printf("%s", message);
255 #endif
256 vprintf(fmt, args);
257 } else {
258 len += vsnprintf(message + len, sizeof(message) - len, fmt, args);
259 if (len >= sizeof(message)) {
260 fprintf(stderr, "BUG: single log message > 4k\n");
261
262 /* vsnprintf returns the number of bytes that *would have been written*,
263 * not the actual amount written. Thus, limit len to sizeof(message) to avoid
264 * memory corruption and outputting garbage later. */
265 len = sizeof(message);
266
267 /* Punch in a newline so the next log message is not dangling at
268 * the end of the truncated message. */
269 message[len - 2] = '\n';
270 }
271
272 /* If there is no space for the current message in the ringbuffer, we
273 * need to wrap and write to the beginning again. */
274 if (len >= (size_t)(logbuffer_size - (logwalk - logbuffer))) {
275 loglastwrap = logwalk;
276 logwalk = logbuffer + sizeof(i3_shmlog_header);
277 store_log_markers();
278 header->wrap_count++;
279 }
280
281 /* Copy the buffer, move the write pointer to the byte after our
282 * current message. */
283 strncpy(logwalk, message, len);
284 logwalk += len;
285
286 store_log_markers();
287
288 #if !defined(__OpenBSD__)
289 /* Wake up all (i3-dump-log) processes waiting for condvar. */
290 pthread_cond_broadcast(&(header->condvar));
291 #endif
292
293 if (print)
294 fwrite(message, len, 1, stdout);
295 }
296 }
297
298 /*
299 * Logs the given message to stdout while prefixing the current time to it,
300 * but only if verbose mode is activated.
301 *
302 */
303 void verboselog(char *fmt, ...) {
304 va_list args;
305
306 if (!logbuffer && !verbose)
307 return;
308
309 va_start(args, fmt);
310 vlog(verbose, fmt, args);
311 va_end(args);
312 }
313
314 /*
315 * Logs the given message to stdout while prefixing the current time to it.
316 *
317 */
318 void errorlog(char *fmt, ...) {
319 va_list args;
320
321 va_start(args, fmt);
322 vlog(true, fmt, args);
323 va_end(args);
324
325 /* also log to the error logfile, if opened */
326 va_start(args, fmt);
327 vfprintf(errorfile, fmt, args);
328 fflush(errorfile);
329 va_end(args);
330 }
331
332 /*
333 * Logs the given message to stdout while prefixing the current time to it,
334 * but only if debug logging was activated.
335 * This is to be called by DLOG() which includes filename/linenumber
336 *
337 */
338 void debuglog(char *fmt, ...) {
339 va_list args;
340
341 if (!logbuffer && !(debug_logging))
342 return;
343
344 va_start(args, fmt);
345 vlog(debug_logging, fmt, args);
346 va_end(args);
347 }
348
349 /*
350 * Deletes the unused log files. Useful if i3 exits immediately, eg.
351 * because --get-socketpath was called. We don't care for syscall
352 * failures. This function is invoked automatically when exiting.
353 */
354 void purge_zerobyte_logfile(void) {
355 struct stat st;
356 char *slash;
357
358 if (!errorfilename)
359 return;
360
361 /* don't delete the log file if it contains something */
362 if ((stat(errorfilename, &st)) == -1 || st.st_size > 0)
363 return;
364
365 if (unlink(errorfilename) == -1)
366 return;
367
368 if ((slash = strrchr(errorfilename, '/')) != NULL) {
369 *slash = '\0';
370 /* possibly fails with ENOTEMPTY if there are files (or
371 * sockets) left. */
372 rmdir(errorfilename);
373 }
374 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * main.c: Initialization, main loop
7 *
8 */
9 #include "all.h"
10
11 #include <ev.h>
12 #include <fcntl.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <sys/time.h>
17 #include <sys/resource.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <libgen.h>
21 #include "shmlog.h"
22
23 #ifdef I3_ASAN_ENABLED
24 #include <sanitizer/lsan_interface.h>
25 #endif
26
27 #include "sd-daemon.h"
28
29 /* The original value of RLIMIT_CORE when i3 was started. We need to restore
30 * this before starting any other process, since we set RLIMIT_CORE to
31 * RLIM_INFINITY for i3 debugging versions. */
32 struct rlimit original_rlimit_core;
33
34 /* The number of file descriptors passed via socket activation. */
35 int listen_fds;
36
37 /* We keep the xcb_prepare watcher around to be able to enable and disable it
38 * temporarily for drag_pointer(). */
39 static struct ev_prepare *xcb_prepare;
40
41 char **start_argv;
42
43 xcb_connection_t *conn;
44 /* The screen (0 when you are using DISPLAY=:0) of the connection 'conn' */
45 int conn_screen;
46
47 /* Display handle for libstartup-notification */
48 SnDisplay *sndisplay;
49
50 /* The last timestamp we got from X11 (timestamps are included in some events
51 * and are used for some things, like determining a unique ID in startup
52 * notification). */
53 xcb_timestamp_t last_timestamp = XCB_CURRENT_TIME;
54
55 xcb_screen_t *root_screen;
56 xcb_window_t root;
57
58 /* Color depth, visual id and colormap to use when creating windows and
59 * pixmaps. Will use 32 bit depth and an appropriate visual, if available,
60 * otherwise the root window’s default (usually 24 bit TrueColor). */
61 uint8_t root_depth;
62 xcb_visualtype_t *visual_type;
63 xcb_colormap_t colormap;
64
65 struct ev_loop *main_loop;
66
67 xcb_key_symbols_t *keysyms;
68
69 /* Default shmlog size if not set by user. */
70 const int default_shmlog_size = 25 * 1024 * 1024;
71
72 /* The list of key bindings */
73 struct bindings_head *bindings;
74
75 /* The list of exec-lines */
76 struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);
77
78 /* The list of exec_always lines */
79 struct autostarts_always_head autostarts_always = TAILQ_HEAD_INITIALIZER(autostarts_always);
80
81 /* The list of assignments */
82 struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments);
83
84 /* The list of workspace assignments (which workspace should end up on which
85 * output) */
86 struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignments);
87
88 /* We hope that those are supported and set them to true */
89 bool xcursor_supported = true;
90 bool xkb_supported = true;
91
92 bool force_xinerama = false;
93
94 /*
95 * This callback is only a dummy, see xcb_prepare_cb.
96 * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop
97 *
98 */
99 static void xcb_got_event(EV_P_ struct ev_io *w, int revents) {
100 /* empty, because xcb_prepare_cb are used */
101 }
102
103 /*
104 * Called just before the event loop sleeps. Ensures xcb’s incoming and outgoing
105 * queues are empty so that any activity will trigger another event loop
106 * iteration, and hence another xcb_prepare_cb invocation.
107 *
108 */
109 static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
110 /* Process all queued (and possibly new) events before the event loop
111 sleeps. */
112 xcb_generic_event_t *event;
113
114 while ((event = xcb_poll_for_event(conn)) != NULL) {
115 if (event->response_type == 0) {
116 if (event_is_ignored(event->sequence, 0))
117 DLOG("Expected X11 Error received for sequence %x\n", event->sequence);
118 else {
119 xcb_generic_error_t *error = (xcb_generic_error_t *)event;
120 DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n",
121 error->sequence, error->error_code);
122 }
123 free(event);
124 continue;
125 }
126
127 /* Strip off the highest bit (set if the event is generated) */
128 int type = (event->response_type & 0x7F);
129
130 handle_event(type, event);
131
132 free(event);
133 }
134
135 /* Flush all queued events to X11. */
136 xcb_flush(conn);
137 }
138
139 /*
140 * Enable or disable the main X11 event handling function.
141 * This is used by drag_pointer() which has its own, modal event handler, which
142 * takes precedence over the normal event handler.
143 *
144 */
145 void main_set_x11_cb(bool enable) {
146 DLOG("Setting main X11 callback to enabled=%d\n", enable);
147 if (enable) {
148 ev_prepare_start(main_loop, xcb_prepare);
149 /* Trigger the watcher explicitly to handle all remaining X11 events.
150 * drag_pointer()’s event handler exits in the middle of the loop. */
151 ev_feed_event(main_loop, xcb_prepare, 0);
152 } else {
153 ev_prepare_stop(main_loop, xcb_prepare);
154 }
155 }
156
157 /*
158 * Exit handler which destroys the main_loop. Will trigger cleanup handlers.
159 *
160 */
161 static void i3_exit(void) {
162 if (*shmlogname != '\0') {
163 fprintf(stderr, "Closing SHM log \"%s\"\n", shmlogname);
164 fflush(stderr);
165 shm_unlink(shmlogname);
166 }
167 ipc_shutdown(SHUTDOWN_REASON_EXIT);
168 unlink(config.ipc_socket_path);
169 xcb_disconnect(conn);
170
171 /* We need ev >= 4 for the following code. Since it is not *that* important (it
172 * only makes sure that there are no i3-nagbar instances left behind) we still
173 * support old systems with libev 3. */
174 #if EV_VERSION_MAJOR >= 4
175 ev_loop_destroy(main_loop);
176 #endif
177
178 #ifdef I3_ASAN_ENABLED
179 __lsan_do_leak_check();
180 #endif
181 }
182
183 /*
184 * (One-shot) Handler for all signals with default action "Core", see signal(7)
185 *
186 * Unlinks the SHM log and re-raises the signal.
187 *
188 */
189 static void handle_core_signal(int sig, siginfo_t *info, void *data) {
190 if (*shmlogname != '\0') {
191 shm_unlink(shmlogname);
192 }
193 raise(sig);
194 }
195
196 /*
197 * (One-shot) Handler for all signals with default action "Term", see signal(7)
198 *
199 * Exits the program gracefully.
200 *
201 */
202 static void handle_term_signal(struct ev_loop *loop, ev_signal *signal, int revents) {
203 /* We exit gracefully here in the sense that cleanup handlers
204 * installed via atexit are invoked. */
205 exit(128 + signal->signum);
206 }
207
208 /*
209 * Set up handlers for all signals with default action "Term", see signal(7)
210 *
211 */
212 static void setup_term_handlers(void) {
213 static struct ev_signal signal_watchers[6];
214 size_t num_watchers = sizeof(signal_watchers) / sizeof(signal_watchers[0]);
215
216 /* We have to rely on libev functionality here and should not use
217 * sigaction handlers because we need to invoke the exit handlers
218 * and cannot do so from an asynchronous signal handling context as
219 * not all code triggered during exit is signal safe (and exiting
220 * the main loop from said handler is not easily possible). libev's
221 * signal handlers does not impose such a constraint on us. */
222 ev_signal_init(&signal_watchers[0], handle_term_signal, SIGHUP);
223 ev_signal_init(&signal_watchers[1], handle_term_signal, SIGINT);
224 ev_signal_init(&signal_watchers[2], handle_term_signal, SIGALRM);
225 ev_signal_init(&signal_watchers[3], handle_term_signal, SIGTERM);
226 ev_signal_init(&signal_watchers[4], handle_term_signal, SIGUSR1);
227 ev_signal_init(&signal_watchers[5], handle_term_signal, SIGUSR1);
228 for (size_t i = 0; i < num_watchers; i++) {
229 ev_signal_start(main_loop, &signal_watchers[i]);
230 /* The signal handlers should not block ev_run from returning
231 * and so none of the signal handlers should hold a reference to
232 * the main loop. */
233 ev_unref(main_loop);
234 }
235 }
236
237 int main(int argc, char *argv[]) {
238 /* Keep a symbol pointing to the I3_VERSION string constant so that we have
239 * it in gdb backtraces. */
240 static const char *_i3_version __attribute__((used)) = I3_VERSION;
241 char *override_configpath = NULL;
242 bool autostart = true;
243 char *layout_path = NULL;
244 bool delete_layout_path = false;
245 bool disable_randr15 = false;
246 char *fake_outputs = NULL;
247 bool disable_signalhandler = false;
248 bool only_check_config = false;
249 static struct option long_options[] = {
250 {"no-autostart", no_argument, 0, 'a'},
251 {"config", required_argument, 0, 'c'},
252 {"version", no_argument, 0, 'v'},
253 {"moreversion", no_argument, 0, 'm'},
254 {"more-version", no_argument, 0, 'm'},
255 {"more_version", no_argument, 0, 'm'},
256 {"help", no_argument, 0, 'h'},
257 {"layout", required_argument, 0, 'L'},
258 {"restart", required_argument, 0, 0},
259 {"force-xinerama", no_argument, 0, 0},
260 {"force_xinerama", no_argument, 0, 0},
261 {"disable-randr15", no_argument, 0, 0},
262 {"disable_randr15", no_argument, 0, 0},
263 {"disable-signalhandler", no_argument, 0, 0},
264 {"shmlog-size", required_argument, 0, 0},
265 {"shmlog_size", required_argument, 0, 0},
266 {"get-socketpath", no_argument, 0, 0},
267 {"get_socketpath", no_argument, 0, 0},
268 {"fake_outputs", required_argument, 0, 0},
269 {"fake-outputs", required_argument, 0, 0},
270 {"force-old-config-parser-v4.4-only", no_argument, 0, 0},
271 {0, 0, 0, 0}};
272 int option_index = 0, opt;
273
274 setlocale(LC_ALL, "");
275
276 /* Get the RLIMIT_CORE limit at startup time to restore this before
277 * starting processes. */
278 getrlimit(RLIMIT_CORE, &original_rlimit_core);
279
280 /* Disable output buffering to make redirects in .xsession actually useful for debugging */
281 if (!isatty(fileno(stdout)))
282 setbuf(stdout, NULL);
283
284 srand(time(NULL));
285
286 /* Init logging *before* initializing debug_build to guarantee early
287 * (file) logging. */
288 init_logging();
289
290 /* On release builds, disable SHM logging by default. */
291 shmlog_size = (is_debug_build() || strstr(argv[0], "i3-with-shmlog") != NULL ? default_shmlog_size : 0);
292
293 start_argv = argv;
294
295 while ((opt = getopt_long(argc, argv, "c:CvmaL:hld:V", long_options, &option_index)) != -1) {
296 switch (opt) {
297 case 'a':
298 LOG("Autostart disabled using -a\n");
299 autostart = false;
300 break;
301 case 'L':
302 FREE(layout_path);
303 layout_path = sstrdup(optarg);
304 delete_layout_path = false;
305 break;
306 case 'c':
307 FREE(override_configpath);
308 override_configpath = sstrdup(optarg);
309 break;
310 case 'C':
311 LOG("Checking configuration file only (-C)\n");
312 only_check_config = true;
313 break;
314 case 'v':
315 printf("i3 version %s © 2009 Michael Stapelberg and contributors\n", i3_version);
316 exit(EXIT_SUCCESS);
317 break;
318 case 'm':
319 printf("Binary i3 version: %s © 2009 Michael Stapelberg and contributors\n", i3_version);
320 display_running_version();
321 exit(EXIT_SUCCESS);
322 break;
323 case 'V':
324 set_verbosity(true);
325 break;
326 case 'd':
327 LOG("Enabling debug logging\n");
328 set_debug_logging(true);
329 break;
330 case 'l':
331 /* DEPRECATED, ignored for the next 3 versions (3.e, 3.f, 3.g) */
332 break;
333 case 0:
334 if (strcmp(long_options[option_index].name, "force-xinerama") == 0 ||
335 strcmp(long_options[option_index].name, "force_xinerama") == 0) {
336 force_xinerama = true;
337 ELOG("Using Xinerama instead of RandR. This option should be "
338 "avoided at all cost because it does not refresh the list "
339 "of screens, so you cannot configure displays at runtime. "
340 "Please check if your driver really does not support RandR "
341 "and disable this option as soon as you can.\n");
342 break;
343 } else if (strcmp(long_options[option_index].name, "disable-randr15") == 0 ||
344 strcmp(long_options[option_index].name, "disable_randr15") == 0) {
345 disable_randr15 = true;
346 break;
347 } else if (strcmp(long_options[option_index].name, "disable-signalhandler") == 0) {
348 disable_signalhandler = true;
349 break;
350 } else if (strcmp(long_options[option_index].name, "get-socketpath") == 0 ||
351 strcmp(long_options[option_index].name, "get_socketpath") == 0) {
352 char *socket_path = root_atom_contents("I3_SOCKET_PATH", NULL, 0);
353 if (socket_path) {
354 printf("%s\n", socket_path);
355 exit(EXIT_SUCCESS);
356 }
357
358 exit(EXIT_FAILURE);
359 } else if (strcmp(long_options[option_index].name, "shmlog-size") == 0 ||
360 strcmp(long_options[option_index].name, "shmlog_size") == 0) {
361 shmlog_size = atoi(optarg);
362 /* Re-initialize logging immediately to get as many
363 * logmessages as possible into the SHM log. */
364 init_logging();
365 LOG("Limiting SHM log size to %d bytes\n", shmlog_size);
366 break;
367 } else if (strcmp(long_options[option_index].name, "restart") == 0) {
368 FREE(layout_path);
369 layout_path = sstrdup(optarg);
370 delete_layout_path = true;
371 break;
372 } else if (strcmp(long_options[option_index].name, "fake-outputs") == 0 ||
373 strcmp(long_options[option_index].name, "fake_outputs") == 0) {
374 LOG("Initializing fake outputs: %s\n", optarg);
375 fake_outputs = sstrdup(optarg);
376 break;
377 } else if (strcmp(long_options[option_index].name, "force-old-config-parser-v4.4-only") == 0) {
378 ELOG("You are passing --force-old-config-parser-v4.4-only, but that flag was removed by now.\n");
379 break;
380 }
381 /* fall-through */
382 default:
383 fprintf(stderr, "Usage: %s [-c configfile] [-d all] [-a] [-v] [-V] [-C]\n", argv[0]);
384 fprintf(stderr, "\n");
385 fprintf(stderr, "\t-a disable autostart ('exec' lines in config)\n");
386 fprintf(stderr, "\t-c <file> use the provided configfile instead\n");
387 fprintf(stderr, "\t-C validate configuration file and exit\n");
388 fprintf(stderr, "\t-d all enable debug output\n");
389 fprintf(stderr, "\t-L <file> path to the serialized layout during restarts\n");
390 fprintf(stderr, "\t-v display version and exit\n");
391 fprintf(stderr, "\t-V enable verbose mode\n");
392 fprintf(stderr, "\n");
393 fprintf(stderr, "\t--force-xinerama\n"
394 "\tUse Xinerama instead of RandR.\n"
395 "\tThis option should only be used if you are stuck with the\n"
396 "\told nVidia closed source driver (older than 302.17), which does\n"
397 "\tnot support RandR.\n");
398 fprintf(stderr, "\n");
399 fprintf(stderr, "\t--get-socketpath\n"
400 "\tRetrieve the i3 IPC socket path from X11, print it, then exit.\n");
401 fprintf(stderr, "\n");
402 fprintf(stderr, "\t--shmlog-size <limit>\n"
403 "\tLimits the size of the i3 SHM log to <limit> bytes. Setting this\n"
404 "\tto 0 disables SHM logging entirely.\n"
405 "\tThe default is %d bytes.\n",
406 shmlog_size);
407 fprintf(stderr, "\n");
408 fprintf(stderr, "If you pass plain text arguments, i3 will interpret them as a command\n"
409 "to send to a currently running i3 (like i3-msg). This allows you to\n"
410 "use nice and logical commands, such as:\n"
411 "\n"
412 "\ti3 border none\n"
413 "\ti3 floating toggle\n"
414 "\ti3 kill window\n"
415 "\n");
416 exit(EXIT_FAILURE);
417 }
418 }
419
420 if (only_check_config) {
421 exit(parse_configuration(override_configpath, false) ? 0 : 1);
422 }
423
424 /* If the user passes more arguments, we act like i3-msg would: Just send
425 * the arguments as an IPC message to i3. This allows for nice semantic
426 * commands such as 'i3 border none'. */
427 if (optind < argc) {
428 /* We enable verbose mode so that the user knows what’s going on.
429 * This should make it easier to find mistakes when the user passes
430 * arguments by mistake. */
431 set_verbosity(true);
432
433 LOG("Additional arguments passed. Sending them as a command to i3.\n");
434 char *payload = NULL;
435 while (optind < argc) {
436 if (!payload) {
437 payload = sstrdup(argv[optind]);
438 } else {
439 char *both;
440 sasprintf(&both, "%s %s", payload, argv[optind]);
441 free(payload);
442 payload = both;
443 }
444 optind++;
445 }
446 DLOG("Command is: %s (%zd bytes)\n", payload, strlen(payload));
447 char *socket_path = root_atom_contents("I3_SOCKET_PATH", NULL, 0);
448 if (!socket_path) {
449 ELOG("Could not get i3 IPC socket path\n");
450 return 1;
451 }
452
453 int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
454 if (sockfd == -1)
455 err(EXIT_FAILURE, "Could not create socket");
456
457 struct sockaddr_un addr;
458 memset(&addr, 0, sizeof(struct sockaddr_un));
459 addr.sun_family = AF_LOCAL;
460 strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
461 FREE(socket_path);
462 if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
463 err(EXIT_FAILURE, "Could not connect to i3");
464
465 if (ipc_send_message(sockfd, strlen(payload), I3_IPC_MESSAGE_TYPE_RUN_COMMAND,
466 (uint8_t *)payload) == -1)
467 err(EXIT_FAILURE, "IPC: write()");
468 FREE(payload);
469
470 uint32_t reply_length;
471 uint32_t reply_type;
472 uint8_t *reply;
473 int ret;
474 if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
475 if (ret == -1)
476 err(EXIT_FAILURE, "IPC: read()");
477 return 1;
478 }
479 if (reply_type != I3_IPC_REPLY_TYPE_COMMAND)
480 errx(EXIT_FAILURE, "IPC: received reply of type %d but expected %d (COMMAND)", reply_type, I3_IPC_REPLY_TYPE_COMMAND);
481 printf("%.*s\n", reply_length, reply);
482 FREE(reply);
483 return 0;
484 }
485
486 /* Enable logging to handle the case when the user did not specify --shmlog-size */
487 init_logging();
488
489 /* Try to enable core dumps by default when running a debug build */
490 if (is_debug_build()) {
491 struct rlimit limit = {RLIM_INFINITY, RLIM_INFINITY};
492 setrlimit(RLIMIT_CORE, &limit);
493
494 /* The following code is helpful, but not required. We thus don’t pay
495 * much attention to error handling, non-linux or other edge cases. */
496 LOG("CORE DUMPS: You are running a development version of i3, so coredumps were automatically enabled (ulimit -c unlimited).\n");
497 size_t cwd_size = 1024;
498 char *cwd = smalloc(cwd_size);
499 char *cwd_ret;
500 while ((cwd_ret = getcwd(cwd, cwd_size)) == NULL && errno == ERANGE) {
501 cwd_size = cwd_size * 2;
502 cwd = srealloc(cwd, cwd_size);
503 }
504 if (cwd_ret != NULL)
505 LOG("CORE DUMPS: Your current working directory is \"%s\".\n", cwd);
506 int patternfd;
507 if ((patternfd = open("/proc/sys/kernel/core_pattern", O_RDONLY)) >= 0) {
508 memset(cwd, '\0', cwd_size);
509 if (read(patternfd, cwd, cwd_size) > 0)
510 /* a trailing newline is included in cwd */
511 LOG("CORE DUMPS: Your core_pattern is: %s", cwd);
512 close(patternfd);
513 }
514 free(cwd);
515 }
516
517 LOG("i3 %s starting\n", i3_version);
518
519 conn = xcb_connect(NULL, &conn_screen);
520 if (xcb_connection_has_error(conn))
521 errx(EXIT_FAILURE, "Cannot open display\n");
522
523 sndisplay = sn_xcb_display_new(conn, NULL, NULL);
524
525 /* Initialize the libev event loop. This needs to be done before loading
526 * the config file because the parser will install an ev_child watcher
527 * for the nagbar when config errors are found. */
528 main_loop = EV_DEFAULT;
529 if (main_loop == NULL)
530 die("Could not initialize libev. Bad LIBEV_FLAGS?\n");
531
532 root_screen = xcb_aux_get_screen(conn, conn_screen);
533 root = root_screen->root;
534
535 /* Place requests for the atoms we need as soon as possible */
536 #define xmacro(atom) \
537 xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
538 #include "atoms.xmacro"
539 #undef xmacro
540
541 root_depth = root_screen->root_depth;
542 colormap = root_screen->default_colormap;
543 visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, 32);
544 if (visual_type != NULL) {
545 root_depth = xcb_aux_get_depth_of_visual(root_screen, visual_type->visual_id);
546 colormap = xcb_generate_id(conn);
547
548 xcb_void_cookie_t cm_cookie = xcb_create_colormap_checked(conn,
549 XCB_COLORMAP_ALLOC_NONE,
550 colormap,
551 root,
552 visual_type->visual_id);
553
554 xcb_generic_error_t *error = xcb_request_check(conn, cm_cookie);
555 if (error != NULL) {
556 ELOG("Could not create colormap. Error code: %d\n", error->error_code);
557 exit(EXIT_FAILURE);
558 }
559 } else {
560 visual_type = get_visualtype(root_screen);
561 }
562
563 init_dpi();
564
565 DLOG("root_depth = %d, visual_id = 0x%08x.\n", root_depth, visual_type->visual_id);
566 DLOG("root_screen->height_in_pixels = %d, root_screen->height_in_millimeters = %d\n",
567 root_screen->height_in_pixels, root_screen->height_in_millimeters);
568 DLOG("One logical pixel corresponds to %d physical pixels on this display.\n", logical_px(1));
569
570 xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root);
571 xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
572
573 /* Setup NetWM atoms */
574 #define xmacro(name) \
575 do { \
576 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
577 if (!reply) { \
578 ELOG("Could not get atom " #name "\n"); \
579 exit(-1); \
580 } \
581 A_##name = reply->atom; \
582 free(reply); \
583 } while (0);
584 #include "atoms.xmacro"
585 #undef xmacro
586
587 load_configuration(conn, override_configpath, false);
588
589 if (config.ipc_socket_path == NULL) {
590 /* Fall back to a file name in /tmp/ based on the PID */
591 if ((config.ipc_socket_path = getenv("I3SOCK")) == NULL)
592 config.ipc_socket_path = get_process_filename("ipc-socket");
593 else
594 config.ipc_socket_path = sstrdup(config.ipc_socket_path);
595 }
596
597 if (config.force_xinerama) {
598 force_xinerama = true;
599 }
600
601 xcb_void_cookie_t cookie;
602 cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
603 xcb_generic_error_t *error = xcb_request_check(conn, cookie);
604 if (error != NULL) {
605 ELOG("Another window manager seems to be running (X error %d)\n", error->error_code);
606 #ifdef I3_ASAN_ENABLED
607 __lsan_do_leak_check();
608 #endif
609 return 1;
610 }
611
612 xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL);
613 if (greply == NULL) {
614 ELOG("Could not get geometry of the root window, exiting\n");
615 return 1;
616 }
617 DLOG("root geometry reply: (%d, %d) %d x %d\n", greply->x, greply->y, greply->width, greply->height);
618
619 xcursor_load_cursors();
620
621 /* Set a cursor for the root window (otherwise the root window will show no
622 cursor until the first client is launched). */
623 if (xcursor_supported)
624 xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER);
625 else
626 xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
627
628 const xcb_query_extension_reply_t *extreply;
629 extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
630 xkb_supported = extreply->present;
631 if (!extreply->present) {
632 DLOG("xkb is not present on this server\n");
633 } else {
634 DLOG("initializing xcb-xkb\n");
635 xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
636 xcb_xkb_select_events(conn,
637 XCB_XKB_ID_USE_CORE_KBD,
638 XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY,
639 0,
640 XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY,
641 0xff,
642 0xff,
643 NULL);
644
645 /* Setting both, XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE and
646 * XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED, will lead to the
647 * X server sending us the full XKB state in KeyPress and KeyRelease:
648 * https://cgit.freedesktop.org/xorg/xserver/tree/xkb/xkbEvents.c?h=xorg-server-1.20.0#n927
649 *
650 * XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT enable detectable autorepeat:
651 * https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Detectable_Autorepeat
652 * This affects bindings using the --release flag: instead of getting multiple KeyRelease
653 * events we get only one event when the key is physically released by the user.
654 */
655 const uint32_t mask = XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE |
656 XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED |
657 XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT;
658 xcb_xkb_per_client_flags_reply_t *pcf_reply;
659 /* The last three parameters are unset because they are only relevant
660 * when using a feature called “automatic reset of boolean controls”:
661 * https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Automatic_Reset_of_Boolean_Controls
662 * */
663 pcf_reply = xcb_xkb_per_client_flags_reply(
664 conn,
665 xcb_xkb_per_client_flags(
666 conn,
667 XCB_XKB_ID_USE_CORE_KBD,
668 mask,
669 mask,
670 0 /* uint32_t ctrlsToChange */,
671 0 /* uint32_t autoCtrls */,
672 0 /* uint32_t autoCtrlsValues */),
673 NULL);
674
675 #define PCF_REPLY_ERROR(_value) \
676 do { \
677 if (pcf_reply == NULL || !(pcf_reply->value & (_value))) { \
678 ELOG("Could not set " #_value "\n"); \
679 } \
680 } while (0)
681
682 PCF_REPLY_ERROR(XCB_XKB_PER_CLIENT_FLAG_GRABS_USE_XKB_STATE);
683 PCF_REPLY_ERROR(XCB_XKB_PER_CLIENT_FLAG_LOOKUP_STATE_WHEN_GRABBED);
684 PCF_REPLY_ERROR(XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT);
685
686 free(pcf_reply);
687 xkb_base = extreply->first_event;
688 }
689
690 restore_connect();
691
692 property_handlers_init();
693
694 ewmh_setup_hints();
695
696 keysyms = xcb_key_symbols_alloc(conn);
697
698 xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
699
700 if (!load_keymap())
701 die("Could not load keymap\n");
702
703 translate_keysyms();
704 grab_all_keys(conn);
705
706 bool needs_tree_init = true;
707 if (layout_path != NULL) {
708 LOG("Trying to restore the layout from \"%s\".\n", layout_path);
709 needs_tree_init = !tree_restore(layout_path, greply);
710 if (delete_layout_path) {
711 unlink(layout_path);
712 const char *dir = dirname(layout_path);
713 /* possibly fails with ENOTEMPTY if there are files (or
714 * sockets) left. */
715 rmdir(dir);
716 }
717 }
718 if (needs_tree_init)
719 tree_init(greply);
720
721 free(greply);
722
723 /* Setup fake outputs for testing */
724 if (fake_outputs == NULL && config.fake_outputs != NULL)
725 fake_outputs = config.fake_outputs;
726
727 if (fake_outputs != NULL) {
728 fake_outputs_init(fake_outputs);
729 FREE(fake_outputs);
730 config.fake_outputs = NULL;
731 } else if (force_xinerama) {
732 /* Force Xinerama (for drivers which don't support RandR yet, esp. the
733 * nVidia binary graphics driver), when specified either in the config
734 * file or on command-line */
735 xinerama_init();
736 } else {
737 DLOG("Checking for XRandR...\n");
738 randr_init(&randr_base, disable_randr15 || config.disable_randr15);
739 }
740
741 /* We need to force disabling outputs which have been loaded from the
742 * layout file but are no longer active. This can happen if the output has
743 * been disabled in the short time between writing the restart layout file
744 * and restarting i3. See #2326. */
745 if (layout_path != NULL && randr_base > -1) {
746 Con *con;
747 TAILQ_FOREACH(con, &(croot->nodes_head), nodes) {
748 Output *output;
749 TAILQ_FOREACH(output, &outputs, outputs) {
750 if (output->active || strcmp(con->name, output_primary_name(output)) != 0)
751 continue;
752
753 /* This will correctly correlate the output with its content
754 * container. We need to make the connection to properly
755 * disable the output. */
756 if (output->con == NULL) {
757 output_init_con(output);
758 output->changed = false;
759 }
760
761 output->to_be_disabled = true;
762 randr_disable_output(output);
763 }
764 }
765 }
766 FREE(layout_path);
767
768 scratchpad_fix_resolution();
769
770 xcb_query_pointer_reply_t *pointerreply;
771 Output *output = NULL;
772 if (!(pointerreply = xcb_query_pointer_reply(conn, pointercookie, NULL))) {
773 ELOG("Could not query pointer position, using first screen\n");
774 } else {
775 DLOG("Pointer at %d, %d\n", pointerreply->root_x, pointerreply->root_y);
776 output = get_output_containing(pointerreply->root_x, pointerreply->root_y);
777 if (!output) {
778 ELOG("ERROR: No screen at (%d, %d), starting on the first screen\n",
779 pointerreply->root_x, pointerreply->root_y);
780 output = get_first_output();
781 }
782
783 con_activate(con_descend_focused(output_get_content(output->con)));
784 free(pointerreply);
785 }
786
787 tree_render();
788
789 /* Create the UNIX domain socket for IPC */
790 int ipc_socket = ipc_create_socket(config.ipc_socket_path);
791 if (ipc_socket == -1) {
792 ELOG("Could not create the IPC socket, IPC disabled\n");
793 } else {
794 struct ev_io *ipc_io = scalloc(1, sizeof(struct ev_io));
795 ev_io_init(ipc_io, ipc_new_client, ipc_socket, EV_READ);
796 ev_io_start(main_loop, ipc_io);
797 }
798
799 /* Also handle the UNIX domain sockets passed via socket activation. The
800 * parameter 1 means "remove the environment variables", we don’t want to
801 * pass these to child processes. */
802 listen_fds = sd_listen_fds(0);
803 if (listen_fds < 0)
804 ELOG("socket activation: Error in sd_listen_fds\n");
805 else if (listen_fds == 0)
806 DLOG("socket activation: no sockets passed\n");
807 else {
808 int flags;
809 for (int fd = SD_LISTEN_FDS_START;
810 fd < (SD_LISTEN_FDS_START + listen_fds);
811 fd++) {
812 DLOG("socket activation: also listening on fd %d\n", fd);
813
814 /* sd_listen_fds() enables FD_CLOEXEC by default.
815 * However, we need to keep the file descriptors open for in-place
816 * restarting, therefore we explicitly disable FD_CLOEXEC. */
817 if ((flags = fcntl(fd, F_GETFD)) < 0 ||
818 fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) {
819 ELOG("Could not disable FD_CLOEXEC on fd %d\n", fd);
820 }
821
822 struct ev_io *ipc_io = scalloc(1, sizeof(struct ev_io));
823 ev_io_init(ipc_io, ipc_new_client, fd, EV_READ);
824 ev_io_start(main_loop, ipc_io);
825 }
826 }
827
828 /* Set up i3 specific atoms like I3_SOCKET_PATH and I3_CONFIG_PATH */
829 x_set_i3_atoms();
830 ewmh_update_workarea();
831
832 /* Set the ewmh desktop properties. */
833 ewmh_update_current_desktop();
834 ewmh_update_number_of_desktops();
835 ewmh_update_desktop_names();
836 ewmh_update_desktop_viewport();
837
838 struct ev_io *xcb_watcher = scalloc(1, sizeof(struct ev_io));
839 xcb_prepare = scalloc(1, sizeof(struct ev_prepare));
840
841 ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
842 ev_io_start(main_loop, xcb_watcher);
843
844 ev_prepare_init(xcb_prepare, xcb_prepare_cb);
845 ev_prepare_start(main_loop, xcb_prepare);
846
847 xcb_flush(conn);
848
849 /* What follows is a fugly consequence of X11 protocol race conditions like
850 * the following: In an i3 in-place restart, i3 will reparent all windows
851 * to the root window, then exec() itself. In the new process, it calls
852 * manage_existing_windows. However, in case any application sent a
853 * generated UnmapNotify message to the WM (as GIMP does), this message
854 * will be handled by i3 *after* managing the window, thus i3 thinks the
855 * window just closed itself. In reality, the message was sent in the time
856 * period where i3 wasn’t running yet.
857 *
858 * To prevent this, we grab the server (disables processing of any other
859 * connections), then discard all pending events (since we didn’t do
860 * anything, there cannot be any meaningful responses), then ungrab the
861 * server. */
862 xcb_grab_server(conn);
863 {
864 xcb_aux_sync(conn);
865 xcb_generic_event_t *event;
866 while ((event = xcb_poll_for_event(conn)) != NULL) {
867 if (event->response_type == 0) {
868 free(event);
869 continue;
870 }
871
872 /* Strip off the highest bit (set if the event is generated) */
873 int type = (event->response_type & 0x7F);
874
875 /* We still need to handle MapRequests which are sent in the
876 * timespan starting from when we register as a window manager and
877 * this piece of code which drops events. */
878 if (type == XCB_MAP_REQUEST)
879 handle_event(type, event);
880
881 free(event);
882 }
883 manage_existing_windows(root);
884 }
885 xcb_ungrab_server(conn);
886
887 if (autostart) {
888 LOG("This is not an in-place restart, copying root window contents to a pixmap\n");
889 xcb_screen_t *root = xcb_aux_get_screen(conn, conn_screen);
890 uint16_t width = root->width_in_pixels;
891 uint16_t height = root->height_in_pixels;
892 xcb_pixmap_t pixmap = xcb_generate_id(conn);
893 xcb_gcontext_t gc = xcb_generate_id(conn);
894
895 xcb_create_pixmap(conn, root->root_depth, pixmap, root->root, width, height);
896
897 xcb_create_gc(conn, gc, root->root,
898 XCB_GC_FUNCTION | XCB_GC_PLANE_MASK | XCB_GC_FILL_STYLE | XCB_GC_SUBWINDOW_MODE,
899 (uint32_t[]){XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
900
901 xcb_copy_area(conn, root->root, pixmap, gc, 0, 0, 0, 0, width, height);
902 xcb_change_window_attributes(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap});
903 xcb_flush(conn);
904 xcb_free_gc(conn, gc);
905 xcb_free_pixmap(conn, pixmap);
906 }
907
908 #if defined(__OpenBSD__)
909 if (pledge("stdio rpath wpath cpath proc exec unix", NULL) == -1)
910 err(EXIT_FAILURE, "pledge");
911 #endif
912
913 if (!disable_signalhandler)
914 setup_signal_handler();
915 else {
916 struct sigaction action;
917
918 action.sa_sigaction = handle_core_signal;
919 action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
920 sigemptyset(&action.sa_mask);
921
922 /* Catch all signals with default action "Core", see signal(7) */
923 if (sigaction(SIGQUIT, &action, NULL) == -1 ||
924 sigaction(SIGILL, &action, NULL) == -1 ||
925 sigaction(SIGABRT, &action, NULL) == -1 ||
926 sigaction(SIGFPE, &action, NULL) == -1 ||
927 sigaction(SIGSEGV, &action, NULL) == -1)
928 ELOG("Could not setup signal handler.\n");
929 }
930
931 setup_term_handlers();
932 /* Ignore SIGPIPE to survive errors when an IPC client disconnects
933 * while we are sending them a message */
934 signal(SIGPIPE, SIG_IGN);
935
936 /* Autostarting exec-lines */
937 if (autostart) {
938 while (!TAILQ_EMPTY(&autostarts)) {
939 struct Autostart *exec = TAILQ_FIRST(&autostarts);
940
941 LOG("auto-starting %s\n", exec->command);
942 start_application(exec->command, exec->no_startup_id);
943
944 FREE(exec->command);
945 TAILQ_REMOVE(&autostarts, exec, autostarts);
946 FREE(exec);
947 }
948 }
949
950 /* Autostarting exec_always-lines */
951 while (!TAILQ_EMPTY(&autostarts_always)) {
952 struct Autostart *exec_always = TAILQ_FIRST(&autostarts_always);
953
954 LOG("auto-starting (always!) %s\n", exec_always->command);
955 start_application(exec_always->command, exec_always->no_startup_id);
956
957 FREE(exec_always->command);
958 TAILQ_REMOVE(&autostarts_always, exec_always, autostarts_always);
959 FREE(exec_always);
960 }
961
962 /* Start i3bar processes for all configured bars */
963 Barconfig *barconfig;
964 TAILQ_FOREACH(barconfig, &barconfigs, configs) {
965 char *command = NULL;
966 sasprintf(&command, "%s %s --bar_id=%s --socket=\"%s\"",
967 barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar",
968 barconfig->verbose ? "-V" : "",
969 barconfig->id, current_socketpath);
970 LOG("Starting bar process: %s\n", command);
971 start_application(command, true);
972 free(command);
973 }
974
975 /* Make sure to destroy the event loop to invoke the cleanup callbacks
976 * when calling exit() */
977 atexit(i3_exit);
978
979 ev_loop(main_loop, 0);
980 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * manage.c: Initially managing new windows (or existing ones on restart).
7 *
8 */
9 #include "all.h"
10
11 #include "yajl_utils.h"
12
13 #include <yajl/yajl_gen.h>
14
15 /*
16 * Go through all existing windows (if the window manager is restarted) and manage them
17 *
18 */
19 void manage_existing_windows(xcb_window_t root) {
20 xcb_query_tree_reply_t *reply;
21 int i, len;
22 xcb_window_t *children;
23 xcb_get_window_attributes_cookie_t *cookies;
24
25 /* Get the tree of windows whose parent is the root window (= all) */
26 if ((reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, root), 0)) == NULL)
27 return;
28
29 len = xcb_query_tree_children_length(reply);
30 cookies = smalloc(len * sizeof(*cookies));
31
32 /* Request the window attributes for every window */
33 children = xcb_query_tree_children(reply);
34 for (i = 0; i < len; ++i)
35 cookies[i] = xcb_get_window_attributes(conn, children[i]);
36
37 /* Call manage_window with the attributes for every window */
38 for (i = 0; i < len; ++i)
39 manage_window(children[i], cookies[i], true);
40
41 free(reply);
42 free(cookies);
43 }
44
45 /*
46 * Restores the geometry of each window by reparenting it to the root window
47 * at the position of its frame.
48 *
49 * This is to be called *only* before exiting/restarting i3 because of evil
50 * side-effects which are to be expected when continuing to run i3.
51 *
52 */
53 void restore_geometry(void) {
54 DLOG("Restoring geometry\n");
55
56 Con *con;
57 TAILQ_FOREACH(con, &all_cons, all_cons)
58 if (con->window) {
59 DLOG("Re-adding X11 border of %d px\n", con->border_width);
60 con->window_rect.width += (2 * con->border_width);
61 con->window_rect.height += (2 * con->border_width);
62 xcb_set_window_rect(conn, con->window->id, con->window_rect);
63 DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y);
64 xcb_reparent_window(conn, con->window->id, root,
65 con->rect.x, con->rect.y);
66 }
67
68 /* Strictly speaking, this line doesn’t really belong here, but since we
69 * are syncing, let’s un-register as a window manager first */
70 xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
71
72 /* Make sure our changes reach the X server, we restart/exit now */
73 xcb_aux_sync(conn);
74 }
75
76 /*
77 * Do some sanity checks and then reparent the window.
78 *
79 */
80 void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
81 bool needs_to_be_mapped) {
82 xcb_drawable_t d = {window};
83 xcb_get_geometry_cookie_t geomc;
84 xcb_get_geometry_reply_t *geom;
85 xcb_get_window_attributes_reply_t *attr = NULL;
86
87 xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
88 utf8_title_cookie, title_cookie,
89 class_cookie, leader_cookie, transient_cookie,
90 role_cookie, startup_id_cookie, wm_hints_cookie,
91 wm_normal_hints_cookie, motif_wm_hints_cookie, wm_user_time_cookie, wm_desktop_cookie;
92
93 geomc = xcb_get_geometry(conn, d);
94
95 /* Check if the window is mapped (it could be not mapped when intializing and
96 calling manage_window() for every window) */
97 if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
98 DLOG("Could not get attributes\n");
99 xcb_discard_reply(conn, geomc.sequence);
100 return;
101 }
102
103 if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
104 xcb_discard_reply(conn, geomc.sequence);
105 goto out;
106 }
107
108 /* Don’t manage clients with the override_redirect flag */
109 if (attr->override_redirect) {
110 xcb_discard_reply(conn, geomc.sequence);
111 goto out;
112 }
113
114 /* Check if the window is already managed */
115 if (con_by_window_id(window) != NULL) {
116 DLOG("already managed (by con %p)\n", con_by_window_id(window));
117 xcb_discard_reply(conn, geomc.sequence);
118 goto out;
119 }
120
121 /* Get the initial geometry (position, size, …) */
122 if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) {
123 DLOG("could not get geometry\n");
124 goto out;
125 }
126
127 uint32_t values[1];
128
129 /* Set a temporary event mask for the new window, consisting only of
130 * PropertyChange and StructureNotify. We need to be notified of
131 * PropertyChanges because the client can change its properties *after* we
132 * requested them but *before* we actually reparented it and have set our
133 * final event mask.
134 * We need StructureNotify because the client may unmap the window before
135 * we get to re-parent it.
136 * If this request fails, we assume the client has already unmapped the
137 * window between the MapRequest and our event mask change. */
138 values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE |
139 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
140 xcb_void_cookie_t event_mask_cookie =
141 xcb_change_window_attributes_checked(conn, window, XCB_CW_EVENT_MASK, values);
142 if (xcb_request_check(conn, event_mask_cookie) != NULL) {
143 LOG("Could not change event mask, the window probably already disappeared.\n");
144 goto out;
145 }
146
147 #define GET_PROPERTY(atom, len) xcb_get_property(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len)
148
149 wm_type_cookie = GET_PROPERTY(A__NET_WM_WINDOW_TYPE, UINT32_MAX);
150 strut_cookie = GET_PROPERTY(A__NET_WM_STRUT_PARTIAL, UINT32_MAX);
151 state_cookie = GET_PROPERTY(A__NET_WM_STATE, UINT32_MAX);
152 utf8_title_cookie = GET_PROPERTY(A__NET_WM_NAME, 128);
153 leader_cookie = GET_PROPERTY(A_WM_CLIENT_LEADER, UINT32_MAX);
154 transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX);
155 title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128);
156 class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128);
157 role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128);
158 startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512);
159 wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window);
160 wm_normal_hints_cookie = xcb_icccm_get_wm_normal_hints(conn, window);
161 motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t));
162 wm_user_time_cookie = GET_PROPERTY(A__NET_WM_USER_TIME, UINT32_MAX);
163 wm_desktop_cookie = GET_PROPERTY(A__NET_WM_DESKTOP, UINT32_MAX);
164
165 DLOG("Managing window 0x%08x\n", window);
166
167 i3Window *cwindow = scalloc(1, sizeof(i3Window));
168 cwindow->id = window;
169 cwindow->depth = get_visual_depth(attr->visual);
170
171 int *buttons = bindings_get_buttons_to_grab();
172 xcb_grab_buttons(conn, window, buttons);
173 FREE(buttons);
174
175 /* update as much information as possible so far (some replies may be NULL) */
176 window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true);
177 window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true);
178 window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true);
179 window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL));
180 window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
181 window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL));
182 window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true);
183 bool urgency_hint;
184 window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint);
185 border_style_t motif_border_style = BS_NORMAL;
186 window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style);
187 xcb_size_hints_t wm_size_hints;
188 if (!xcb_icccm_get_wm_size_hints_reply(conn, wm_normal_hints_cookie, &wm_size_hints, NULL))
189 memset(&wm_size_hints, '\0', sizeof(xcb_size_hints_t));
190 xcb_get_property_reply_t *type_reply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
191 xcb_get_property_reply_t *state_reply = xcb_get_property_reply(conn, state_cookie, NULL);
192
193 xcb_get_property_reply_t *startup_id_reply;
194 startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL);
195 char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply);
196 DLOG("startup workspace = %s\n", startup_ws);
197
198 /* Get _NET_WM_DESKTOP if it was set. */
199 xcb_get_property_reply_t *wm_desktop_reply;
200 wm_desktop_reply = xcb_get_property_reply(conn, wm_desktop_cookie, NULL);
201 cwindow->wm_desktop = NET_WM_DESKTOP_NONE;
202 if (wm_desktop_reply != NULL && xcb_get_property_value_length(wm_desktop_reply) != 0) {
203 uint32_t *wm_desktops = xcb_get_property_value(wm_desktop_reply);
204 cwindow->wm_desktop = (int32_t)wm_desktops[0];
205 }
206 FREE(wm_desktop_reply);
207
208 /* check if the window needs WM_TAKE_FOCUS */
209 cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS);
210
211 /* read the preferred _NET_WM_WINDOW_TYPE atom */
212 cwindow->window_type = xcb_get_preferred_window_type(type_reply);
213
214 /* Where to start searching for a container that swallows the new one? */
215 Con *search_at = croot;
216
217 if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DOCK)) {
218 LOG("This window is of type dock\n");
219 Output *output = get_output_containing(geom->x, geom->y);
220 if (output != NULL) {
221 DLOG("Starting search at output %s\n", output_primary_name(output));
222 search_at = output->con;
223 }
224
225 /* find out the desired position of this dock window */
226 if (cwindow->reserved.top > 0 && cwindow->reserved.bottom == 0) {
227 DLOG("Top dock client\n");
228 cwindow->dock = W_DOCK_TOP;
229 } else if (cwindow->reserved.top == 0 && cwindow->reserved.bottom > 0) {
230 DLOG("Bottom dock client\n");
231 cwindow->dock = W_DOCK_BOTTOM;
232 } else {
233 DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
234 if (geom->y < (int16_t)(search_at->rect.height / 2)) {
235 DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
236 geom->y, (search_at->rect.height / 2));
237 cwindow->dock = W_DOCK_TOP;
238 } else {
239 DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
240 geom->y, (search_at->rect.height / 2));
241 cwindow->dock = W_DOCK_BOTTOM;
242 }
243 }
244 }
245
246 DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height);
247
248 /* See if any container swallows this new window */
249 Match *match = NULL;
250 Con *nc = con_for_window(search_at, cwindow, &match);
251 const bool match_from_restart_mode = (match && match->restart_mode);
252 if (nc == NULL) {
253 Con *wm_desktop_ws = NULL;
254 Assignment *assignment;
255
256 /* If not, check if it is assigned to a specific workspace */
257 if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE)) ||
258 (assignment = assignment_for(cwindow, A_TO_WORKSPACE_NUMBER))) {
259 DLOG("Assignment matches (%p)\n", match);
260
261 Con *assigned_ws = NULL;
262 if (assignment->type == A_TO_WORKSPACE_NUMBER) {
263 long parsed_num = ws_name_to_number(assignment->dest.workspace);
264
265 assigned_ws = get_existing_workspace_by_num(parsed_num);
266 }
267 /* A_TO_WORKSPACE type assignment or fallback from A_TO_WORKSPACE_NUMBER
268 * when the target workspace number does not exist yet. */
269 if (!assigned_ws) {
270 assigned_ws = workspace_get(assignment->dest.workspace, NULL);
271 }
272
273 nc = con_descend_tiling_focused(assigned_ws);
274 DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name);
275 if (nc->type == CT_WORKSPACE)
276 nc = tree_open_con(nc, cwindow);
277 else
278 nc = tree_open_con(nc->parent, cwindow);
279
280 /* set the urgency hint on the window if the workspace is not visible */
281 if (!workspace_is_visible(assigned_ws))
282 urgency_hint = true;
283 } else if (cwindow->wm_desktop != NET_WM_DESKTOP_NONE &&
284 cwindow->wm_desktop != NET_WM_DESKTOP_ALL &&
285 (wm_desktop_ws = ewmh_get_workspace_by_index(cwindow->wm_desktop)) != NULL) {
286 /* If _NET_WM_DESKTOP is set to a specific desktop, we open it
287 * there. Note that we ignore the special value 0xFFFFFFFF here
288 * since such a window will be made sticky anyway. */
289
290 DLOG("Using workspace %p / %s because _NET_WM_DESKTOP = %d.\n",
291 wm_desktop_ws, wm_desktop_ws->name, cwindow->wm_desktop);
292
293 nc = con_descend_tiling_focused(wm_desktop_ws);
294 if (nc->type == CT_WORKSPACE)
295 nc = tree_open_con(nc, cwindow);
296 else
297 nc = tree_open_con(nc->parent, cwindow);
298 } else if (startup_ws) {
299 /* If it was started on a specific workspace, we want to open it there. */
300 DLOG("Using workspace on which this application was started (%s)\n", startup_ws);
301 nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL));
302 DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name);
303 if (nc->type == CT_WORKSPACE)
304 nc = tree_open_con(nc, cwindow);
305 else
306 nc = tree_open_con(nc->parent, cwindow);
307 } else {
308 /* If not, insert it at the currently focused position */
309 if (focused->type == CT_CON && con_accepts_window(focused)) {
310 LOG("using current container, focused = %p, focused->name = %s\n",
311 focused, focused->name);
312 nc = focused;
313 } else
314 nc = tree_open_con(NULL, cwindow);
315 }
316
317 if ((assignment = assignment_for(cwindow, A_TO_OUTPUT))) {
318 con_move_to_output_name(nc, assignment->dest.output, true);
319 }
320 } else {
321 /* M_BELOW inserts the new window as a child of the one which was
322 * matched (e.g. dock areas) */
323 if (match != NULL && match->insert_where == M_BELOW) {
324 nc = tree_open_con(nc, cwindow);
325 }
326
327 /* If M_BELOW is not used, the container is replaced. This happens with
328 * "swallows" criteria that are used for stored layouts, in which case
329 * we need to remove that criterion, because they should only be valid
330 * once. */
331 if (match != NULL && match->insert_where != M_BELOW) {
332 DLOG("Removing match %p from container %p\n", match, nc);
333 TAILQ_REMOVE(&(nc->swallow_head), match, matches);
334 match_free(match);
335 FREE(match);
336 }
337 }
338
339 DLOG("new container = %p\n", nc);
340 if (nc->window != NULL && nc->window != cwindow) {
341 if (!restore_kill_placeholder(nc->window->id)) {
342 DLOG("Uh?! Container without a placeholder, but with a window, has swallowed this to-be-managed window?!\n");
343 } else {
344 /* Remove remaining criteria, the first swallowed window wins. */
345 while (!TAILQ_EMPTY(&(nc->swallow_head))) {
346 Match *first = TAILQ_FIRST(&(nc->swallow_head));
347 TAILQ_REMOVE(&(nc->swallow_head), first, matches);
348 match_free(first);
349 free(first);
350 }
351 }
352 }
353 xcb_window_t old_frame = XCB_NONE;
354 if (nc->window != cwindow && nc->window != NULL) {
355 window_free(nc->window);
356 /* Match frame and window depth. This is needed because X will refuse to reparent a
357 * window whose background is ParentRelative under a window with a different depth. */
358 if (nc->depth != cwindow->depth) {
359 old_frame = nc->frame.id;
360 nc->depth = cwindow->depth;
361 x_con_reframe(nc);
362 }
363 }
364 nc->window = cwindow;
365 x_reinit(nc);
366
367 nc->border_width = geom->border_width;
368
369 char *name;
370 sasprintf(&name, "[i3 con] container around %p", cwindow);
371 x_set_name(nc, name);
372 free(name);
373
374 /* handle fullscreen containers */
375 Con *ws = con_get_workspace(nc);
376 Con *fs = con_get_fullscreen_covering_ws(ws);
377
378 if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_FULLSCREEN)) {
379 /* If this window is already fullscreen (after restarting!), skip
380 * toggling fullscreen, that would drop it out of fullscreen mode. */
381 if (fs != nc) {
382 Output *output = get_output_with_dimensions((Rect){geom->x, geom->y, geom->width, geom->height});
383 /* If the requested window geometry spans the whole area
384 * of an output, move the window to that output. This is
385 * needed e.g. for LibreOffice Impress multi-monitor
386 * presentations to work out of the box. */
387 if (output != NULL)
388 con_move_to_output(nc, output, false);
389 con_toggle_fullscreen(nc, CF_OUTPUT);
390 }
391 fs = NULL;
392 }
393
394 bool set_focus = false;
395
396 if (fs == NULL) {
397 DLOG("Not in fullscreen mode, focusing\n");
398 if (!cwindow->dock) {
399 /* Check that the workspace is visible and on the same output as
400 * the current focused container. If the window was assigned to an
401 * invisible workspace, we should not steal focus. */
402 Con *current_output = con_get_output(focused);
403 Con *target_output = con_get_output(ws);
404
405 if (workspace_is_visible(ws) && current_output == target_output) {
406 if (!match_from_restart_mode) {
407 set_focus = true;
408 } else {
409 DLOG("not focusing, matched with restart_mode == true\n");
410 }
411 } else {
412 DLOG("workspace not visible, not focusing\n");
413 }
414 } else {
415 DLOG("dock, not focusing\n");
416 }
417 } else {
418 DLOG("fs = %p, ws = %p, not focusing\n", fs, ws);
419 /* Insert the new container in focus stack *after* the currently
420 * focused (fullscreen) con. This way, the new container will be
421 * focused after we return from fullscreen mode */
422 Con *first = TAILQ_FIRST(&(nc->parent->focus_head));
423 if (first != nc) {
424 /* We only modify the focus stack if the container is not already
425 * the first one. This can happen when existing containers swallow
426 * new windows, for example when restarting. */
427 TAILQ_REMOVE(&(nc->parent->focus_head), nc, focused);
428 TAILQ_INSERT_AFTER(&(nc->parent->focus_head), first, nc, focused);
429 }
430 }
431
432 /* set floating if necessary */
433 bool want_floating = false;
434 if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DIALOG) ||
435 xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_UTILITY) ||
436 xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) ||
437 xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_SPLASH) ||
438 xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_MODAL) ||
439 (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE &&
440 wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE &&
441 wm_size_hints.min_height == wm_size_hints.max_height &&
442 wm_size_hints.min_width == wm_size_hints.max_width)) {
443 LOG("This window is a dialog window, setting floating\n");
444 want_floating = true;
445 }
446
447 if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY))
448 nc->sticky = true;
449
450 /* We ignore the hint for an internal workspace because windows in the
451 * scratchpad also have this value, but upon restarting i3 we don't want
452 * them to become sticky windows. */
453 if (cwindow->wm_desktop == NET_WM_DESKTOP_ALL && (ws == NULL || !con_is_internal(ws))) {
454 DLOG("This window has _NET_WM_DESKTOP = 0xFFFFFFFF. Will float it and make it sticky.\n");
455 nc->sticky = true;
456 want_floating = true;
457 }
458
459 FREE(state_reply);
460 FREE(type_reply);
461
462 if (cwindow->transient_for != XCB_NONE ||
463 (cwindow->leader != XCB_NONE &&
464 cwindow->leader != cwindow->id &&
465 con_by_window_id(cwindow->leader) != NULL)) {
466 LOG("This window is transient for another window, setting floating\n");
467 want_floating = true;
468
469 if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN &&
470 fs != NULL) {
471 LOG("There is a fullscreen window, leaving fullscreen mode\n");
472 con_toggle_fullscreen(fs, CF_OUTPUT);
473 } else if (config.popup_during_fullscreen == PDF_SMART &&
474 fs != NULL &&
475 fs->window != NULL) {
476 i3Window *transient_win = cwindow;
477 while (transient_win != NULL &&
478 transient_win->transient_for != XCB_NONE) {
479 if (transient_win->transient_for == fs->window->id) {
480 LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n");
481 set_focus = true;
482 break;
483 }
484 Con *next_transient = con_by_window_id(transient_win->transient_for);
485 if (next_transient == NULL)
486 break;
487 /* Some clients (e.g. x11-ssh-askpass) actually set
488 * WM_TRANSIENT_FOR to their own window id, so break instead of
489 * looping endlessly. */
490 if (transient_win == next_transient->window)
491 break;
492 transient_win = next_transient->window;
493 }
494 }
495 }
496
497 /* dock clients cannot be floating, that makes no sense */
498 if (cwindow->dock)
499 want_floating = false;
500
501 /* Plasma windows set their geometry in WM_SIZE_HINTS. */
502 if ((wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_US_POSITION || wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_POSITION) &&
503 (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_US_SIZE || wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_SIZE)) {
504 DLOG("We are setting geometry according to wm_size_hints x=%d y=%d w=%d h=%d\n",
505 wm_size_hints.x, wm_size_hints.y, wm_size_hints.width, wm_size_hints.height);
506 geom->x = wm_size_hints.x;
507 geom->y = wm_size_hints.y;
508 geom->width = wm_size_hints.width;
509 geom->height = wm_size_hints.height;
510 }
511
512 if (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
513 DLOG("Window specifies minimum size %d x %d\n", wm_size_hints.min_width, wm_size_hints.min_height);
514 nc->window->min_width = wm_size_hints.min_width;
515 nc->window->min_height = wm_size_hints.min_height;
516 }
517
518 if (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) {
519 DLOG("Window specifies maximum size %d x %d\n", wm_size_hints.max_width, wm_size_hints.max_height);
520 nc->window->max_width = wm_size_hints.max_width;
521 nc->window->max_height = wm_size_hints.max_height;
522 }
523
524 /* Store the requested geometry. The width/height gets raised to at least
525 * 75x50 when entering floating mode, which is the minimum size for a
526 * window to be useful (smaller windows are usually overlays/toolbars/…
527 * which are not managed by the wm anyways). We store the original geometry
528 * here because it’s used for dock clients. */
529 if (nc->geometry.width == 0)
530 nc->geometry = (Rect){geom->x, geom->y, geom->width, geom->height};
531
532 if (motif_border_style != BS_NORMAL) {
533 DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
534 if (want_floating) {
535 con_set_border_style(nc, motif_border_style, config.default_floating_border_width);
536 } else {
537 con_set_border_style(nc, motif_border_style, config.default_border_width);
538 }
539 }
540
541 if (want_floating) {
542 DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
543 /* automatically set the border to the default value if a motif border
544 * was not specified */
545 bool automatic_border = (motif_border_style == BS_NORMAL);
546
547 floating_enable(nc, automatic_border);
548 }
549
550 /* explicitly set the border width to the default */
551 if (nc->current_border_width == -1) {
552 nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
553 }
554
555 /* to avoid getting an UnmapNotify event due to reparenting, we temporarily
556 * declare no interest in any state change event of this window */
557 values[0] = XCB_NONE;
558 xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
559
560 xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame.id, 0, 0);
561 if (xcb_request_check(conn, rcookie) != NULL) {
562 LOG("Could not reparent the window, aborting\n");
563 goto geom_out;
564 }
565
566 values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
567 xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
568 xcb_flush(conn);
569
570 /* Put the client inside the save set. Upon termination (whether killed or
571 * normal exit does not matter) of the window manager, these clients will
572 * be correctly reparented to their most closest living ancestor (=
573 * cleanup) */
574 xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window);
575
576 /* Check if any assignments match */
577 run_assignments(cwindow);
578
579 /* 'ws' may be invalid because of the assignments, e.g. when the user uses
580 * "move window to workspace 1", but had it assigned to workspace 2. */
581 ws = con_get_workspace(nc);
582
583 /* If this window was put onto an invisible workspace (via assignments), we
584 * render this workspace. It wouldn’t be rendered in our normal code path
585 * because only the visible workspaces get rendered.
586 *
587 * By rendering the workspace, we assign proper coordinates (read: not
588 * width=0, height=0) to the window, which is important for windows who
589 * actually use them to position their GUI elements, e.g. rhythmbox. */
590 if (ws && !workspace_is_visible(ws)) {
591 /* This is a bit hackish: we need to copy the content container’s rect
592 * to the workspace, because calling render_con() on the content
593 * container would also take the shortcut and not render the invisible
594 * workspace at all. However, just calling render_con() on the
595 * workspace isn’t enough either — it needs the rect. */
596 ws->rect = ws->parent->rect;
597 render_con(ws, true);
598 /* Disable setting focus, otherwise we’d move focus to an invisible
599 * workspace, which we generally prevent (e.g. in
600 * con_move_to_workspace). */
601 set_focus = false;
602 }
603 render_con(croot, false);
604
605 /* Send an event about window creation */
606 ipc_send_window_event("new", nc);
607
608 if (set_focus && assignment_for(cwindow, A_NO_FOCUS) != NULL) {
609 /* The first window on a workspace should always be focused. We have to
610 * compare with == 1 because the container has already been inserted at
611 * this point. */
612 if (con_num_windows(ws) == 1) {
613 DLOG("This is the first window on this workspace, ignoring no_focus.\n");
614 } else {
615 DLOG("no_focus was set for con = %p, not setting focus.\n", nc);
616 set_focus = false;
617 }
618 }
619
620 if (set_focus) {
621 DLOG("Checking con = %p for _NET_WM_USER_TIME.\n", nc);
622
623 uint32_t *wm_user_time;
624 xcb_get_property_reply_t *wm_user_time_reply = xcb_get_property_reply(conn, wm_user_time_cookie, NULL);
625 if (wm_user_time_reply != NULL && xcb_get_property_value_length(wm_user_time_reply) != 0 &&
626 (wm_user_time = xcb_get_property_value(wm_user_time_reply)) &&
627 wm_user_time[0] == 0) {
628 DLOG("_NET_WM_USER_TIME set to 0, not focusing con = %p.\n", nc);
629 set_focus = false;
630 }
631
632 FREE(wm_user_time_reply);
633 } else {
634 xcb_discard_reply(conn, wm_user_time_cookie.sequence);
635 }
636
637 if (set_focus) {
638 /* Even if the client doesn't want focus, we still need to focus the
639 * container to not break focus workflows. Our handling towards X will
640 * take care of not setting the input focus. However, one exception to
641 * this are clients using the globally active input model which we
642 * don't want to focus at all. */
643 if (nc->window->doesnt_accept_focus && !nc->window->needs_take_focus) {
644 set_focus = false;
645 }
646 }
647
648 /* Defer setting focus after the 'new' event has been sent to ensure the
649 * proper window event sequence. */
650 if (set_focus && nc->mapped) {
651 DLOG("Now setting focus.\n");
652 con_activate(nc);
653 }
654
655 tree_render();
656
657 /* Destroy the old frame if we had to reframe the container. This needs to be done
658 * after rendering in order to prevent the background from flickering in its place. */
659 if (old_frame != XCB_NONE) {
660 xcb_destroy_window(conn, old_frame);
661 }
662
663 /* Windows might get managed with the urgency hint already set (Pidgin is
664 * known to do that), so check for that and handle the hint accordingly.
665 * This code needs to be in this part of manage_window() because the window
666 * needs to be on the final workspace first. */
667 con_set_urgency(nc, urgency_hint);
668
669 /* Update _NET_WM_DESKTOP. We invalidate the cached value first to force an update. */
670 cwindow->wm_desktop = NET_WM_DESKTOP_NONE;
671 ewmh_update_wm_desktop();
672
673 /* If a sticky window was mapped onto another workspace, make sure to pop it to the front. */
674 output_push_sticky_windows(focused);
675
676 geom_out:
677 free(geom);
678 out:
679 free(attr);
680 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * A "match" is a data structure which acts like a mask or expression to match
7 * certain windows or not. For example, when using commands, you can specify a
8 * command like this: [title="*Firefox*"] kill. The title member of the match
9 * data structure will then be filled and i3 will check each window using
10 * match_matches_window() to find the windows affected by this command.
11 *
12 */
13 #include "all.h"
14
15 /* From sys/time.h, not sure if it’s available on all systems. */
16 #define _i3_timercmp(a, b, CMP) \
17 (((a).tv_sec == (b).tv_sec) ? ((a).tv_usec CMP(b).tv_usec) : ((a).tv_sec CMP(b).tv_sec))
18
19 /*
20 * Initializes the Match data structure. This function is necessary because the
21 * members representing boolean values (like dock) need to be initialized with
22 * -1 instead of 0.
23 *
24 */
25 void match_init(Match *match) {
26 memset(match, 0, sizeof(Match));
27 match->urgent = U_DONTCHECK;
28 match->window_mode = WM_ANY;
29 /* we use this as the placeholder value for "not set". */
30 match->window_type = UINT32_MAX;
31 }
32
33 /*
34 * Check if a match is empty. This is necessary while parsing commands to see
35 * whether the user specified a match at all.
36 *
37 */
38 bool match_is_empty(Match *match) {
39 /* we cannot simply use memcmp() because the structure is part of a
40 * TAILQ and I don’t want to start with things like assuming that the
41 * last member of a struct really is at the end in memory… */
42 return (match->title == NULL &&
43 match->mark == NULL &&
44 match->application == NULL &&
45 match->class == NULL &&
46 match->instance == NULL &&
47 match->window_role == NULL &&
48 match->workspace == NULL &&
49 match->urgent == U_DONTCHECK &&
50 match->id == XCB_NONE &&
51 match->window_type == UINT32_MAX &&
52 match->con_id == NULL &&
53 match->dock == M_NODOCK &&
54 match->window_mode == WM_ANY);
55 }
56
57 /*
58 * Copies the data of a match from src to dest.
59 *
60 */
61 void match_copy(Match *dest, Match *src) {
62 memcpy(dest, src, sizeof(Match));
63
64 /* The DUPLICATE_REGEX macro creates a new regular expression from the
65 * ->pattern of the old one. It therefore does use a little more memory then
66 * with a refcounting system, but it’s easier this way. */
67 #define DUPLICATE_REGEX(field) \
68 do { \
69 if (src->field != NULL) \
70 dest->field = regex_new(src->field->pattern); \
71 } while (0)
72
73 DUPLICATE_REGEX(title);
74 DUPLICATE_REGEX(mark);
75 DUPLICATE_REGEX(application);
76 DUPLICATE_REGEX(class);
77 DUPLICATE_REGEX(instance);
78 DUPLICATE_REGEX(window_role);
79 DUPLICATE_REGEX(workspace);
80 }
81
82 /*
83 * Check if a match data structure matches the given window.
84 *
85 */
86 bool match_matches_window(Match *match, i3Window *window) {
87 LOG("Checking window 0x%08x (class %s)\n", window->id, window->class_class);
88
89 #define GET_FIELD_str(field) (field)
90 #define GET_FIELD_i3string(field) (i3string_as_utf8(field))
91 #define CHECK_WINDOW_FIELD(match_field, window_field, type) \
92 do { \
93 if (match->match_field != NULL) { \
94 if (window->window_field == NULL) { \
95 return false; \
96 } \
97 \
98 const char *window_field_str = GET_FIELD_##type(window->window_field); \
99 if (strcmp(match->match_field->pattern, "__focused__") == 0 && \
100 focused && focused->window && focused->window->window_field && \
101 strcmp(window_field_str, GET_FIELD_##type(focused->window->window_field)) == 0) { \
102 LOG("window " #match_field " matches focused window\n"); \
103 } else if (regex_matches(match->match_field, window_field_str)) { \
104 LOG("window " #match_field " matches (%s)\n", window_field_str); \
105 } else { \
106 return false; \
107 } \
108 } \
109 } while (0)
110
111 CHECK_WINDOW_FIELD(class, class_class, str);
112 CHECK_WINDOW_FIELD(instance, class_instance, str);
113
114 if (match->id != XCB_NONE) {
115 if (window->id == match->id) {
116 LOG("match made by window id (%d)\n", window->id);
117 } else {
118 LOG("window id does not match\n");
119 return false;
120 }
121 }
122
123 CHECK_WINDOW_FIELD(title, name, i3string);
124 CHECK_WINDOW_FIELD(window_role, role, str);
125
126 if (match->window_type != UINT32_MAX) {
127 if (window->window_type == match->window_type) {
128 LOG("window_type matches (%i)\n", match->window_type);
129 } else {
130 return false;
131 }
132 }
133
134 Con *con = NULL;
135 if (match->urgent == U_LATEST) {
136 /* if the window isn't urgent, no sense in searching */
137 if (window->urgent.tv_sec == 0) {
138 return false;
139 }
140 /* if we find a window that is newer than this one, bail */
141 TAILQ_FOREACH(con, &all_cons, all_cons) {
142 if ((con->window != NULL) &&
143 _i3_timercmp(con->window->urgent, window->urgent, >)) {
144 return false;
145 }
146 }
147 LOG("urgent matches latest\n");
148 }
149
150 if (match->urgent == U_OLDEST) {
151 /* if the window isn't urgent, no sense in searching */
152 if (window->urgent.tv_sec == 0) {
153 return false;
154 }
155 /* if we find a window that is older than this one (and not 0), bail */
156 TAILQ_FOREACH(con, &all_cons, all_cons) {
157 if ((con->window != NULL) &&
158 (con->window->urgent.tv_sec != 0) &&
159 _i3_timercmp(con->window->urgent, window->urgent, <)) {
160 return false;
161 }
162 }
163 LOG("urgent matches oldest\n");
164 }
165
166 if (match->workspace != NULL) {
167 if ((con = con_by_window_id(window->id)) == NULL)
168 return false;
169
170 Con *ws = con_get_workspace(con);
171 if (ws == NULL)
172 return false;
173
174 if (strcmp(match->workspace->pattern, "__focused__") == 0 &&
175 strcmp(ws->name, con_get_workspace(focused)->name) == 0) {
176 LOG("workspace matches focused workspace\n");
177 } else if (regex_matches(match->workspace, ws->name)) {
178 LOG("workspace matches (%s)\n", ws->name);
179 } else {
180 return false;
181 }
182 }
183
184 if (match->dock != M_DONTCHECK) {
185 if ((window->dock == W_DOCK_TOP && match->dock == M_DOCK_TOP) ||
186 (window->dock == W_DOCK_BOTTOM && match->dock == M_DOCK_BOTTOM) ||
187 ((window->dock == W_DOCK_TOP || window->dock == W_DOCK_BOTTOM) &&
188 match->dock == M_DOCK_ANY) ||
189 (window->dock == W_NODOCK && match->dock == M_NODOCK)) {
190 LOG("dock status matches\n");
191 } else {
192 LOG("dock status does not match\n");
193 return false;
194 }
195 }
196
197 if (match->mark != NULL) {
198 if ((con = con_by_window_id(window->id)) == NULL)
199 return false;
200
201 bool matched = false;
202 mark_t *mark;
203 TAILQ_FOREACH(mark, &(con->marks_head), marks) {
204 if (regex_matches(match->mark, mark->name)) {
205 matched = true;
206 break;
207 }
208 }
209
210 if (matched) {
211 LOG("mark matches\n");
212 } else {
213 LOG("mark does not match\n");
214 return false;
215 }
216 }
217
218 if (match->window_mode != WM_ANY) {
219 if ((con = con_by_window_id(window->id)) == NULL)
220 return false;
221
222 const bool floating = (con_inside_floating(con) != NULL);
223
224 if ((match->window_mode == WM_TILING && floating) ||
225 (match->window_mode == WM_FLOATING && !floating)) {
226 LOG("window_mode does not match\n");
227 return false;
228 }
229
230 LOG("window_mode matches\n");
231 }
232
233 return true;
234 }
235
236 /*
237 * Frees the given match. It must not be used afterwards!
238 *
239 */
240 void match_free(Match *match) {
241 FREE(match->error);
242 regex_free(match->title);
243 regex_free(match->application);
244 regex_free(match->class);
245 regex_free(match->instance);
246 regex_free(match->mark);
247 regex_free(match->window_role);
248 regex_free(match->workspace);
249 }
250
251 /*
252 * Interprets a ctype=cvalue pair and adds it to the given match specification.
253 *
254 */
255 void match_parse_property(Match *match, const char *ctype, const char *cvalue) {
256 assert(match != NULL);
257 DLOG("ctype=*%s*, cvalue=*%s*\n", ctype, cvalue);
258
259 if (strcmp(ctype, "class") == 0) {
260 regex_free(match->class);
261 match->class = regex_new(cvalue);
262 return;
263 }
264
265 if (strcmp(ctype, "instance") == 0) {
266 regex_free(match->instance);
267 match->instance = regex_new(cvalue);
268 return;
269 }
270
271 if (strcmp(ctype, "window_role") == 0) {
272 regex_free(match->window_role);
273 match->window_role = regex_new(cvalue);
274 return;
275 }
276
277 if (strcmp(ctype, "con_id") == 0) {
278 if (strcmp(cvalue, "__focused__") == 0) {
279 match->con_id = focused;
280 return;
281 }
282
283 long parsed;
284 if (!parse_long(cvalue, &parsed, 0)) {
285 ELOG("Could not parse con id \"%s\"\n", cvalue);
286 match->error = sstrdup("invalid con_id");
287 } else {
288 match->con_id = (Con *)parsed;
289 DLOG("id as int = %p\n", match->con_id);
290 }
291 return;
292 }
293
294 if (strcmp(ctype, "id") == 0) {
295 long parsed;
296 if (!parse_long(cvalue, &parsed, 0)) {
297 ELOG("Could not parse window id \"%s\"\n", cvalue);
298 match->error = sstrdup("invalid id");
299 } else {
300 match->id = parsed;
301 DLOG("window id as int = %d\n", match->id);
302 }
303 return;
304 }
305
306 if (strcmp(ctype, "window_type") == 0) {
307 if (strcasecmp(cvalue, "normal") == 0) {
308 match->window_type = A__NET_WM_WINDOW_TYPE_NORMAL;
309 } else if (strcasecmp(cvalue, "dialog") == 0) {
310 match->window_type = A__NET_WM_WINDOW_TYPE_DIALOG;
311 } else if (strcasecmp(cvalue, "utility") == 0) {
312 match->window_type = A__NET_WM_WINDOW_TYPE_UTILITY;
313 } else if (strcasecmp(cvalue, "toolbar") == 0) {
314 match->window_type = A__NET_WM_WINDOW_TYPE_TOOLBAR;
315 } else if (strcasecmp(cvalue, "splash") == 0) {
316 match->window_type = A__NET_WM_WINDOW_TYPE_SPLASH;
317 } else if (strcasecmp(cvalue, "menu") == 0) {
318 match->window_type = A__NET_WM_WINDOW_TYPE_MENU;
319 } else if (strcasecmp(cvalue, "dropdown_menu") == 0) {
320 match->window_type = A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
321 } else if (strcasecmp(cvalue, "popup_menu") == 0) {
322 match->window_type = A__NET_WM_WINDOW_TYPE_POPUP_MENU;
323 } else if (strcasecmp(cvalue, "tooltip") == 0) {
324 match->window_type = A__NET_WM_WINDOW_TYPE_TOOLTIP;
325 } else if (strcasecmp(cvalue, "notification") == 0) {
326 match->window_type = A__NET_WM_WINDOW_TYPE_NOTIFICATION;
327 } else {
328 ELOG("unknown window_type value \"%s\"\n", cvalue);
329 match->error = sstrdup("unknown window_type value");
330 }
331
332 return;
333 }
334
335 if (strcmp(ctype, "con_mark") == 0) {
336 regex_free(match->mark);
337 match->mark = regex_new(cvalue);
338 return;
339 }
340
341 if (strcmp(ctype, "title") == 0) {
342 regex_free(match->title);
343 match->title = regex_new(cvalue);
344 return;
345 }
346
347 if (strcmp(ctype, "urgent") == 0) {
348 if (strcasecmp(cvalue, "latest") == 0 ||
349 strcasecmp(cvalue, "newest") == 0 ||
350 strcasecmp(cvalue, "recent") == 0 ||
351 strcasecmp(cvalue, "last") == 0) {
352 match->urgent = U_LATEST;
353 } else if (strcasecmp(cvalue, "oldest") == 0 ||
354 strcasecmp(cvalue, "first") == 0) {
355 match->urgent = U_OLDEST;
356 }
357 return;
358 }
359
360 if (strcmp(ctype, "workspace") == 0) {
361 regex_free(match->workspace);
362 match->workspace = regex_new(cvalue);
363 return;
364 }
365
366 if (strcmp(ctype, "tiling") == 0) {
367 match->window_mode = WM_TILING;
368 return;
369 }
370
371 if (strcmp(ctype, "floating") == 0) {
372 match->window_mode = WM_FLOATING;
373 return;
374 }
375
376 ELOG("Unknown criterion: %s\n", ctype);
377 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * move.c: Moving containers into some direction.
7 *
8 */
9 #include "all.h"
10
11 /*
12 * Returns the lowest container in the tree that has both a and b as descendants.
13 *
14 */
15 static Con *lowest_common_ancestor(Con *a, Con *b) {
16 Con *parent_a = a;
17 while (parent_a) {
18 Con *parent_b = b;
19 while (parent_b) {
20 if (parent_a == parent_b) {
21 return parent_a;
22 }
23 parent_b = parent_b->parent;
24 }
25 parent_a = parent_a->parent;
26 }
27 assert(false);
28 }
29
30 /*
31 * Returns the direct child of ancestor that contains con.
32 *
33 */
34 static Con *child_containing_con_recursively(Con *ancestor, Con *con) {
35 Con *child = con;
36 while (child && child->parent != ancestor) {
37 child = child->parent;
38 assert(child->parent);
39 }
40 return child;
41 }
42
43 /*
44 * Returns true if the given container is the focused descendant of ancestor, recursively.
45 *
46 */
47 static bool is_focused_descendant(Con *con, Con *ancestor) {
48 Con *current = con;
49 while (current != ancestor) {
50 if (TAILQ_FIRST(&(current->parent->focus_head)) != current) {
51 return false;
52 }
53 current = current->parent;
54 assert(current->parent);
55 }
56 return true;
57 }
58
59 /*
60 * This function detaches 'con' from its parent and inserts it either before or
61 * after 'target'.
62 *
63 */
64 void insert_con_into(Con *con, Con *target, position_t position) {
65 Con *parent = target->parent;
66 /* We need to preserve the old con->parent. While it might still be used to
67 * insert the entry before/after it, we call the on_remove_child callback
68 * afterwards which might then close the con if it is empty. */
69 Con *old_parent = con->parent;
70
71 /* We compare the focus order of the children of the lowest common ancestor. If con or
72 * its ancestor is before target's ancestor then con should be placed before the target
73 * in the focus stack. */
74 Con *lca = lowest_common_ancestor(con, parent);
75 if (lca == con) {
76 ELOG("Container is being inserted into one of its descendants.\n");
77 return;
78 }
79
80 Con *con_ancestor = child_containing_con_recursively(lca, con);
81 Con *target_ancestor = child_containing_con_recursively(lca, target);
82 bool moves_focus_from_ancestor = is_focused_descendant(con, con_ancestor);
83 bool focus_before;
84
85 /* Determine if con is going to be placed before or after target in the parent's focus stack. */
86 if (con_ancestor == target_ancestor) {
87 /* Happens when the target is con's old parent. Eg with layout V [ A H [ B C ] ],
88 * if we move C up. Target will be H. */
89 focus_before = moves_focus_from_ancestor;
90 } else {
91 /* Look at the focus stack order of the children of the lowest common ancestor. */
92 Con *current;
93 TAILQ_FOREACH(current, &(lca->focus_head), focused) {
94 if (current == con_ancestor || current == target_ancestor) {
95 break;
96 }
97 }
98 focus_before = (current == con_ancestor);
99 }
100
101 /* If con is the focused container in our old ancestor we place the new ancestor
102 * before the old ancestor in the focus stack. Example:
103 * Consider the layout [ H [ V1 [ A* B ] V2 [ C ] ] ] where A is focused. We move to
104 * a second workspace and from there we move A to the right and switch back to the
105 * original workspace. Without the change focus would move to B instead of staying
106 * with A. */
107 if (moves_focus_from_ancestor && focus_before) {
108 Con *place = TAILQ_PREV(con_ancestor, focus_head, focused);
109 TAILQ_REMOVE(&(lca->focus_head), target_ancestor, focused);
110 if (place) {
111 TAILQ_INSERT_AFTER(&(lca->focus_head), place, target_ancestor, focused);
112 } else {
113 TAILQ_INSERT_HEAD(&(lca->focus_head), target_ancestor, focused);
114 }
115 }
116
117 con_detach(con);
118 con_fix_percent(con->parent);
119
120 /* When moving to a workspace, we respect the user’s configured
121 * workspace_layout */
122 if (parent->type == CT_WORKSPACE) {
123 Con *split = workspace_attach_to(parent);
124 if (split != parent) {
125 DLOG("Got a new split con, using that one instead\n");
126 con->parent = split;
127 con_attach(con, split, false);
128 DLOG("attached\n");
129 con->percent = 0.0;
130 con_fix_percent(split);
131 con = split;
132 DLOG("ok, continuing with con %p instead\n", con);
133 con_detach(con);
134 }
135 }
136
137 con->parent = parent;
138
139 if (parent == lca) {
140 if (focus_before) {
141 /* Example layout: H [ A B* ], we move A up/down. 'target' will be H. */
142 TAILQ_INSERT_BEFORE(target, con, focused);
143 } else {
144 /* Example layout: H [ A B* ], we move A up/down. 'target' will be H. */
145 TAILQ_INSERT_AFTER(&(parent->focus_head), target, con, focused);
146 }
147 } else {
148 if (focus_before) {
149 /* Example layout: V [ H [ A B ] C* ], we move C up. 'target' will be A. */
150 TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused);
151 } else {
152 /* Example layout: V [ H [ A* B ] C ], we move C up. 'target' will be A. */
153 TAILQ_INSERT_TAIL(&(parent->focus_head), con, focused);
154 }
155 }
156
157 if (position == BEFORE) {
158 TAILQ_INSERT_BEFORE(target, con, nodes);
159 } else if (position == AFTER) {
160 TAILQ_INSERT_AFTER(&(parent->nodes_head), target, con, nodes);
161 }
162
163 /* Pretend the con was just opened with regards to size percent values.
164 * Since the con is moved to a completely different con, the old value
165 * does not make sense anyways. */
166 con->percent = 0.0;
167 con_fix_percent(parent);
168
169 CALL(old_parent, on_remove_child);
170 }
171
172 /*
173 * This function detaches 'con' from its parent and puts it in the given
174 * workspace. Position is determined by the direction of movement into the
175 * workspace container.
176 *
177 */
178 static void attach_to_workspace(Con *con, Con *ws, direction_t direction) {
179 con_detach(con);
180 Con *old_parent = con->parent;
181 con->parent = ws;
182
183 if (direction == D_RIGHT || direction == D_DOWN) {
184 TAILQ_INSERT_HEAD(&(ws->nodes_head), con, nodes);
185 } else {
186 TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes);
187 }
188 TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused);
189
190 /* Pretend the con was just opened with regards to size percent values.
191 * Since the con is moved to a completely different con, the old value
192 * does not make sense anyways. */
193 con->percent = 0.0;
194 con_fix_percent(ws);
195
196 con_fix_percent(old_parent);
197 CALL(old_parent, on_remove_child);
198 }
199
200 /*
201 * Moves the given container to the closest output in the given direction if
202 * such an output exists.
203 *
204 */
205 static void move_to_output_directed(Con *con, direction_t direction) {
206 Output *current_output = get_output_for_con(con);
207 Output *output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
208
209 if (!output) {
210 DLOG("No output in this direction found. Not moving.\n");
211 return;
212 }
213
214 Con *ws = NULL;
215 GREP_FIRST(ws, output_get_content(output->con), workspace_is_visible(child));
216
217 if (!ws) {
218 DLOG("No workspace on output in this direction found. Not moving.\n");
219 return;
220 }
221
222 Con *old_ws = con_get_workspace(con);
223 const bool moves_focus = (focused == con);
224 attach_to_workspace(con, ws, direction);
225 if (moves_focus) {
226 /* workspace_show will not correctly update the active workspace because
227 * the focused container, con, is now a child of ws. To work around this
228 * and still produce the correct workspace focus events (see
229 * 517-regress-move-direction-ipc.t) we need to temporarily set focused
230 * to the old workspace. */
231 focused = old_ws;
232 workspace_show(ws);
233 con_focus(con);
234 }
235
236 /* force re-painting the indicators */
237 FREE(con->deco_render_params);
238
239 tree_flatten(croot);
240 ipc_send_window_event("move", con);
241 ewmh_update_wm_desktop();
242 }
243
244 /*
245 * Moves the given container in the given direction (D_LEFT, D_RIGHT,
246 * D_UP, D_DOWN).
247 *
248 */
249 void tree_move(Con *con, int direction) {
250 position_t position;
251 Con *target;
252
253 DLOG("Moving in direction %d\n", direction);
254
255 /* 1: get the first parent with the same orientation */
256
257 if (con->type == CT_WORKSPACE) {
258 DLOG("Not moving workspace\n");
259 return;
260 }
261
262 if (con->fullscreen_mode == CF_GLOBAL) {
263 DLOG("Not moving fullscreen global container\n");
264 return;
265 }
266
267 if ((con->fullscreen_mode == CF_OUTPUT) ||
268 (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1)) {
269 /* This is the only con on this workspace */
270 move_to_output_directed(con, direction);
271 return;
272 }
273
274 orientation_t o = orientation_from_direction(direction);
275
276 Con *same_orientation = con_parent_with_orientation(con, o);
277 /* The do {} while is used to 'restart' at this point with a different
278 * same_orientation, see the very last lines before the end of this block
279 * */
280 do {
281 /* There is no parent container with the same orientation */
282 if (!same_orientation) {
283 if (con_is_floating(con)) {
284 /* this is a floating con, we just disable floating */
285 floating_disable(con, true);
286 return;
287 }
288 if (con_inside_floating(con)) {
289 /* 'con' should be moved out of a floating container */
290 DLOG("Inside floating, moving to workspace\n");
291 attach_to_workspace(con, con_get_workspace(con), direction);
292 goto end;
293 }
294 DLOG("Force-changing orientation\n");
295 ws_force_orientation(con_get_workspace(con), o);
296 same_orientation = con_parent_with_orientation(con, o);
297 }
298
299 /* easy case: the move is within this container */
300 if (same_orientation == con->parent) {
301 Con *swap = (direction == D_LEFT || direction == D_UP)
302 ? TAILQ_PREV(con, nodes_head, nodes)
303 : TAILQ_NEXT(con, nodes);
304 if (swap) {
305 if (!con_is_leaf(swap)) {
306 DLOG("Moving into our bordering branch\n");
307 target = con_descend_direction(swap, direction);
308 position = (con_orientation(target->parent) != o ||
309 direction == D_UP ||
310 direction == D_LEFT
311 ? AFTER
312 : BEFORE);
313 insert_con_into(con, target, position);
314 goto end;
315 }
316
317 DLOG("Swapping with sibling.\n");
318 if (direction == D_LEFT || direction == D_UP) {
319 TAILQ_SWAP(swap, con, &(swap->parent->nodes_head), nodes);
320 } else {
321 TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes);
322 }
323
324 ipc_send_window_event("move", con);
325 return;
326 }
327
328 if (con->parent == con_get_workspace(con)) {
329 /* If we couldn't find a place to move it on this workspace, try
330 * to move it to a workspace on a different output */
331 move_to_output_directed(con, direction);
332 return;
333 }
334
335 /* If there was no con with which we could swap the current one,
336 * search again, but starting one level higher. */
337 same_orientation = con_parent_with_orientation(con->parent, o);
338 }
339 } while (same_orientation == NULL);
340
341 /* this time, we have to move to another container */
342 /* This is the container *above* 'con' (an ancestor of con) which is inside
343 * 'same_orientation' */
344 Con *above = con;
345 while (above->parent != same_orientation)
346 above = above->parent;
347
348 /* Enforce the fullscreen focus restrictions. */
349 if (!con_fullscreen_permits_focusing(above->parent)) {
350 LOG("Cannot move out of fullscreen container\n");
351 return;
352 }
353
354 DLOG("above = %p\n", above);
355
356 Con *next = (direction == D_UP || direction == D_LEFT ? TAILQ_PREV(above, nodes_head, nodes) : TAILQ_NEXT(above, nodes));
357
358 if (next && !con_is_leaf(next)) {
359 DLOG("Moving into the bordering branch of our adjacent container\n");
360 target = con_descend_direction(next, direction);
361 position = (con_orientation(target->parent) != o ||
362 direction == D_UP ||
363 direction == D_LEFT
364 ? AFTER
365 : BEFORE);
366 insert_con_into(con, target, position);
367 } else if (!next &&
368 con->parent->parent->type == CT_WORKSPACE &&
369 con->parent->layout != L_DEFAULT &&
370 con_num_children(con->parent) == 1) {
371 /* Con is the lone child of a non-default layout container at the edge
372 * of the workspace. Treat it as though the workspace is its parent
373 * and move it to the next output. */
374 DLOG("Grandparent is workspace\n");
375 move_to_output_directed(con, direction);
376 return;
377 } else {
378 DLOG("Moving into container above\n");
379 position = (direction == D_UP || direction == D_LEFT ? BEFORE : AFTER);
380 insert_con_into(con, above, position);
381 }
382
383 end:
384 /* force re-painting the indicators */
385 FREE(con->deco_render_params);
386
387 tree_flatten(croot);
388 ipc_send_window_event("move", con);
389 ewmh_update_wm_desktop();
390 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * output.c: Output (monitor) related functions.
7 *
8 */
9 #include "all.h"
10
11 /*
12 * Returns the content container below the given output container.
13 *
14 */
15 Con *output_get_content(Con *output) {
16 Con *child;
17
18 TAILQ_FOREACH(child, &(output->nodes_head), nodes)
19 if (child->type == CT_CON)
20 return child;
21
22 return NULL;
23 }
24
25 /*
26 * Returns an 'output' corresponding to one of left/right/down/up or a specific
27 * output name.
28 *
29 */
30 Output *get_output_from_string(Output *current_output, const char *output_str) {
31 if (strcasecmp(output_str, "current") == 0) {
32 return get_output_for_con(focused);
33 } else if (strcasecmp(output_str, "left") == 0) {
34 return get_output_next_wrap(D_LEFT, current_output);
35 } else if (strcasecmp(output_str, "right") == 0) {
36 return get_output_next_wrap(D_RIGHT, current_output);
37 } else if (strcasecmp(output_str, "up") == 0) {
38 return get_output_next_wrap(D_UP, current_output);
39 } else if (strcasecmp(output_str, "down") == 0) {
40 return get_output_next_wrap(D_DOWN, current_output);
41 }
42
43 return get_output_by_name(output_str, true);
44 }
45
46 /*
47 * Retrieves the primary name of an output.
48 *
49 */
50 char *output_primary_name(Output *output) {
51 return SLIST_FIRST(&output->names_head)->name;
52 }
53
54 Output *get_output_for_con(Con *con) {
55 Con *output_con = con_get_output(con);
56 if (output_con == NULL) {
57 ELOG("Could not get the output container for con = %p.\n", con);
58 return NULL;
59 }
60
61 Output *output = get_output_by_name(output_con->name, true);
62 if (output == NULL) {
63 ELOG("Could not get output from name \"%s\".\n", output_con->name);
64 return NULL;
65 }
66
67 return output;
68 }
69
70 /*
71 * Iterates over all outputs and pushes sticky windows to the currently visible
72 * workspace on that output.
73 *
74 * old_focus is used to determine if a sticky window is going to be focused.
75 * old_focus might be different than the currently focused container because the
76 * caller might need to temporarily change the focus and then call
77 * output_push_sticky_windows. For example, workspace_show needs to set focus to
78 * one of its descendants first, then call output_push_sticky_windows that
79 * should focus a sticky window if it was the focused in the previous workspace.
80 *
81 */
82 void output_push_sticky_windows(Con *old_focus) {
83 Con *output;
84 TAILQ_FOREACH(output, &(croot->focus_head), focused) {
85 Con *workspace, *visible_ws = NULL;
86 GREP_FIRST(visible_ws, output_get_content(output), workspace_is_visible(child));
87
88 /* We use this loop instead of TAILQ_FOREACH to avoid problems if the
89 * sticky window was the last window on that workspace as moving it in
90 * this case will close the workspace. */
91 for (workspace = TAILQ_FIRST(&(output_get_content(output)->focus_head));
92 workspace != TAILQ_END(&(output_get_content(output)->focus_head));) {
93 Con *current_ws = workspace;
94 workspace = TAILQ_NEXT(workspace, focused);
95
96 /* Since moving the windows actually removes them from the list of
97 * floating windows on this workspace, here too we need to use
98 * another loop than TAILQ_FOREACH. */
99 Con *child;
100 for (child = TAILQ_FIRST(&(current_ws->focus_head));
101 child != TAILQ_END(&(current_ws->focus_head));) {
102 Con *current = child;
103 child = TAILQ_NEXT(child, focused);
104 if (current->type != CT_FLOATING_CON || !con_is_sticky(current)) {
105 continue;
106 }
107
108 bool ignore_focus = (old_focus == NULL) || (current != old_focus->parent);
109 con_move_to_workspace(current, visible_ws, true, false, ignore_focus);
110 if (!ignore_focus) {
111 Con *current_ws = con_get_workspace(focused);
112 con_activate(con_descend_focused(current));
113 /* Pushing sticky windows shouldn't change the focused workspace. */
114 con_activate(con_descend_focused(current_ws));
115 }
116 }
117 }
118 }
119 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * For more information on RandR, please see the X.org RandR specification at
7 * https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
8 * (take your time to read it completely, it answers all questions).
9 *
10 */
11 #include "all.h"
12
13 #include <time.h>
14 #include <xcb/randr.h>
15
16 /* Pointer to the result of the query for primary output */
17 xcb_randr_get_output_primary_reply_t *primary;
18
19 /* Stores all outputs available in your current session. */
20 struct outputs_head outputs = TAILQ_HEAD_INITIALIZER(outputs);
21
22 /* This is the output covering the root window */
23 static Output *root_output;
24 static bool has_randr_1_5 = false;
25
26 /*
27 * Get a specific output by its internal X11 id. Used by randr_query_outputs
28 * to check if the output is new (only in the first scan) or if we are
29 * re-scanning.
30 *
31 */
32 static Output *get_output_by_id(xcb_randr_output_t id) {
33 Output *output;
34 TAILQ_FOREACH(output, &outputs, outputs)
35 if (output->id == id)
36 return output;
37
38 return NULL;
39 }
40
41 /*
42 * Returns the output with the given name or NULL.
43 * If require_active is true, only active outputs are considered.
44 *
45 */
46 Output *get_output_by_name(const char *name, const bool require_active) {
47 Output *output;
48 bool get_primary = (strcasecmp("primary", name) == 0);
49 TAILQ_FOREACH(output, &outputs, outputs) {
50 if (output->primary && get_primary) {
51 return output;
52 }
53 if (require_active && !output->active) {
54 continue;
55 }
56 struct output_name *output_name;
57 SLIST_FOREACH(output_name, &output->names_head, names) {
58 if (strcasecmp(output_name->name, name) == 0) {
59 return output;
60 }
61 }
62 }
63
64 return NULL;
65 }
66
67 /*
68 * Returns the first output which is active.
69 *
70 */
71 Output *get_first_output(void) {
72 Output *output;
73
74 TAILQ_FOREACH(output, &outputs, outputs)
75 if (output->active)
76 return output;
77
78 die("No usable outputs available.\n");
79 }
80
81 /*
82 * Check whether there are any active outputs (excluding the root output).
83 *
84 */
85 static bool any_randr_output_active(void) {
86 Output *output;
87
88 TAILQ_FOREACH(output, &outputs, outputs) {
89 if (output != root_output && !output->to_be_disabled && output->active)
90 return true;
91 }
92
93 return false;
94 }
95
96 /*
97 * Returns the active (!) output which contains the coordinates x, y or NULL
98 * if there is no output which contains these coordinates.
99 *
100 */
101 Output *get_output_containing(unsigned int x, unsigned int y) {
102 Output *output;
103 TAILQ_FOREACH(output, &outputs, outputs) {
104 if (!output->active)
105 continue;
106 DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
107 x, y, output->rect.x, output->rect.y, output->rect.width, output->rect.height);
108 if (x >= output->rect.x && x < (output->rect.x + output->rect.width) &&
109 y >= output->rect.y && y < (output->rect.y + output->rect.height))
110 return output;
111 }
112
113 return NULL;
114 }
115
116 /*
117 * Returns the active output which contains the midpoint of the given rect. If
118 * such an output doesn't exist, returns the output which contains most of the
119 * rectangle or NULL if there is no output which intersects with it.
120 *
121 */
122 Output *get_output_from_rect(Rect rect) {
123 unsigned int mid_x = rect.x + rect.width / 2;
124 unsigned int mid_y = rect.y + rect.height / 2;
125 Output *output = get_output_containing(mid_x, mid_y);
126
127 return output ? output : output_containing_rect(rect);
128 }
129
130 /*
131 * Returns the active output which spans exactly the area specified by
132 * rect or NULL if there is no output like this.
133 *
134 */
135 Output *get_output_with_dimensions(Rect rect) {
136 Output *output;
137 TAILQ_FOREACH(output, &outputs, outputs) {
138 if (!output->active)
139 continue;
140 DLOG("comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n",
141 rect.x, rect.y, rect.width, rect.height,
142 output->rect.x, output->rect.y, output->rect.width, output->rect.height);
143 if (rect.x == output->rect.x && rect.width == output->rect.width &&
144 rect.y == output->rect.y && rect.height == output->rect.height)
145 return output;
146 }
147
148 return NULL;
149 }
150
151 /*
152 * In output_containing_rect, we check if any active output contains part of the container.
153 * We do this by checking if the output rect is intersected by the Rect.
154 * This is the 2-dimensional counterpart of get_output_containing.
155 * Returns the output with the maximum intersecting area.
156 *
157 */
158 Output *output_containing_rect(Rect rect) {
159 Output *output;
160 int lx = rect.x, uy = rect.y;
161 int rx = rect.x + rect.width, by = rect.y + rect.height;
162 long max_area = 0;
163 Output *result = NULL;
164 TAILQ_FOREACH(output, &outputs, outputs) {
165 if (!output->active)
166 continue;
167 int lx_o = (int)output->rect.x, uy_o = (int)output->rect.y;
168 int rx_o = (int)(output->rect.x + output->rect.width), by_o = (int)(output->rect.y + output->rect.height);
169 DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
170 rect.x, rect.y, output->rect.x, output->rect.y, output->rect.width, output->rect.height);
171 int left = max(lx, lx_o);
172 int right = min(rx, rx_o);
173 int bottom = min(by, by_o);
174 int top = max(uy, uy_o);
175 if (left < right && bottom > top) {
176 long area = (right - left) * (bottom - top);
177 if (area > max_area) {
178 result = output;
179 }
180 }
181 }
182 return result;
183 }
184
185 /*
186 * Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
187 *
188 * For example if get_output_next(D_DOWN, x, FARTHEST_OUTPUT) = NULL, then
189 * get_output_next_wrap(D_DOWN, x) will return the topmost output.
190 *
191 * This function always returns a output: if no active outputs can be found,
192 * current itself is returned.
193 *
194 */
195 Output *get_output_next_wrap(direction_t direction, Output *current) {
196 Output *best = get_output_next(direction, current, CLOSEST_OUTPUT);
197 /* If no output can be found, wrap */
198 if (!best) {
199 direction_t opposite;
200 if (direction == D_RIGHT)
201 opposite = D_LEFT;
202 else if (direction == D_LEFT)
203 opposite = D_RIGHT;
204 else if (direction == D_DOWN)
205 opposite = D_UP;
206 else
207 opposite = D_DOWN;
208 best = get_output_next(opposite, current, FARTHEST_OUTPUT);
209 }
210 if (!best)
211 best = current;
212 DLOG("current = %s, best = %s\n", output_primary_name(current), output_primary_name(best));
213 return best;
214 }
215
216 /*
217 * Gets the output which is the next one in the given direction.
218 *
219 * If close_far == CLOSEST_OUTPUT, then the output next to the current one will
220 * selected. If close_far == FARTHEST_OUTPUT, the output which is the last one
221 * in the given direction will be selected.
222 *
223 * NULL will be returned when no active outputs are present in the direction
224 * specified (note that “current” counts as such an output).
225 *
226 */
227 Output *get_output_next(direction_t direction, Output *current, output_close_far_t close_far) {
228 Rect *cur = &(current->rect),
229 *other;
230 Output *output,
231 *best = NULL;
232 TAILQ_FOREACH(output, &outputs, outputs) {
233 if (!output->active)
234 continue;
235
236 other = &(output->rect);
237
238 if ((direction == D_RIGHT && other->x > cur->x) ||
239 (direction == D_LEFT && other->x < cur->x)) {
240 /* Skip the output when it doesn’t overlap the other one’s y
241 * coordinate at all. */
242 if ((other->y + other->height) <= cur->y ||
243 (cur->y + cur->height) <= other->y)
244 continue;
245 } else if ((direction == D_DOWN && other->y > cur->y) ||
246 (direction == D_UP && other->y < cur->y)) {
247 /* Skip the output when it doesn’t overlap the other one’s x
248 * coordinate at all. */
249 if ((other->x + other->width) <= cur->x ||
250 (cur->x + cur->width) <= other->x)
251 continue;
252 } else
253 continue;
254
255 /* No candidate yet? Start with this one. */
256 if (!best) {
257 best = output;
258 continue;
259 }
260
261 if (close_far == CLOSEST_OUTPUT) {
262 /* Is this output better (closer to the current output) than our
263 * current best bet? */
264 if ((direction == D_RIGHT && other->x < best->rect.x) ||
265 (direction == D_LEFT && other->x > best->rect.x) ||
266 (direction == D_DOWN && other->y < best->rect.y) ||
267 (direction == D_UP && other->y > best->rect.y)) {
268 best = output;
269 continue;
270 }
271 } else {
272 /* Is this output better (farther to the current output) than our
273 * current best bet? */
274 if ((direction == D_RIGHT && other->x > best->rect.x) ||
275 (direction == D_LEFT && other->x < best->rect.x) ||
276 (direction == D_DOWN && other->y > best->rect.y) ||
277 (direction == D_UP && other->y < best->rect.y)) {
278 best = output;
279 continue;
280 }
281 }
282 }
283
284 DLOG("current = %s, best = %s\n", output_primary_name(current), (best ? output_primary_name(best) : "NULL"));
285 return best;
286 }
287
288 /*
289 * Creates an output covering the root window.
290 *
291 */
292 Output *create_root_output(xcb_connection_t *conn) {
293 Output *s = scalloc(1, sizeof(Output));
294
295 s->active = false;
296 s->rect.x = 0;
297 s->rect.y = 0;
298 s->rect.width = root_screen->width_in_pixels;
299 s->rect.height = root_screen->height_in_pixels;
300
301 struct output_name *output_name = scalloc(1, sizeof(struct output_name));
302 output_name->name = "xroot-0";
303 SLIST_INIT(&s->names_head);
304 SLIST_INSERT_HEAD(&s->names_head, output_name, names);
305
306 return s;
307 }
308
309 /*
310 * Initializes a CT_OUTPUT Con (searches existing ones from inplace restart
311 * before) to use for the given Output.
312 *
313 */
314 void output_init_con(Output *output) {
315 Con *con = NULL, *current;
316 bool reused = false;
317
318 DLOG("init_con for output %s\n", output_primary_name(output));
319
320 /* Search for a Con with that name directly below the root node. There
321 * might be one from a restored layout. */
322 TAILQ_FOREACH(current, &(croot->nodes_head), nodes) {
323 if (strcmp(current->name, output_primary_name(output)) != 0)
324 continue;
325
326 con = current;
327 reused = true;
328 DLOG("Using existing con %p / %s\n", con, con->name);
329 break;
330 }
331
332 if (con == NULL) {
333 con = con_new(croot, NULL);
334 FREE(con->name);
335 con->name = sstrdup(output_primary_name(output));
336 con->type = CT_OUTPUT;
337 con->layout = L_OUTPUT;
338 con_fix_percent(croot);
339 }
340 con->rect = output->rect;
341 output->con = con;
342
343 char *name;
344 sasprintf(&name, "[i3 con] output %s", con->name);
345 x_set_name(con, name);
346 FREE(name);
347
348 if (reused) {
349 DLOG("Not adding workspace, this was a reused con\n");
350 return;
351 }
352
353 DLOG("Changing layout, adding top/bottom dockarea\n");
354 Con *topdock = con_new(NULL, NULL);
355 topdock->type = CT_DOCKAREA;
356 topdock->layout = L_DOCKAREA;
357 /* this container swallows dock clients */
358 Match *match = scalloc(1, sizeof(Match));
359 match_init(match);
360 match->dock = M_DOCK_TOP;
361 match->insert_where = M_BELOW;
362 TAILQ_INSERT_TAIL(&(topdock->swallow_head), match, matches);
363
364 FREE(topdock->name);
365 topdock->name = sstrdup("topdock");
366
367 sasprintf(&name, "[i3 con] top dockarea %s", con->name);
368 x_set_name(topdock, name);
369 FREE(name);
370 DLOG("attaching\n");
371 con_attach(topdock, con, false);
372
373 /* content container */
374
375 DLOG("adding main content container\n");
376 Con *content = con_new(NULL, NULL);
377 content->type = CT_CON;
378 content->layout = L_SPLITH;
379 FREE(content->name);
380 content->name = sstrdup("content");
381
382 sasprintf(&name, "[i3 con] content %s", con->name);
383 x_set_name(content, name);
384 FREE(name);
385 con_attach(content, con, false);
386
387 /* bottom dock container */
388 Con *bottomdock = con_new(NULL, NULL);
389 bottomdock->type = CT_DOCKAREA;
390 bottomdock->layout = L_DOCKAREA;
391 /* this container swallows dock clients */
392 match = scalloc(1, sizeof(Match));
393 match_init(match);
394 match->dock = M_DOCK_BOTTOM;
395 match->insert_where = M_BELOW;
396 TAILQ_INSERT_TAIL(&(bottomdock->swallow_head), match, matches);
397
398 FREE(bottomdock->name);
399 bottomdock->name = sstrdup("bottomdock");
400
401 sasprintf(&name, "[i3 con] bottom dockarea %s", con->name);
402 x_set_name(bottomdock, name);
403 FREE(name);
404 DLOG("attaching\n");
405 con_attach(bottomdock, con, false);
406
407 /* Change focus to the content container */
408 TAILQ_REMOVE(&(con->focus_head), content, focused);
409 TAILQ_INSERT_HEAD(&(con->focus_head), content, focused);
410 }
411
412 /*
413 * Initializes at least one workspace for this output, trying the following
414 * steps until there is at least one workspace:
415 *
416 * • Move existing workspaces, which are assigned to be on the given output, to
417 * the output.
418 * • Create the first assigned workspace for this output.
419 * • Create the first unused workspace.
420 *
421 */
422 void init_ws_for_output(Output *output, Con *content) {
423 /* go through all assignments and move the existing workspaces to this output */
424 struct Workspace_Assignment *assignment;
425 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
426 if (!output_triggers_assignment(output, assignment)) {
427 continue;
428 }
429 Con *workspace = get_existing_workspace_by_name(assignment->name);
430 if (workspace == NULL)
431 continue;
432
433 /* check that this workspace is not already attached (that means the
434 * user configured this assignment twice) */
435 Con *workspace_out = con_get_output(workspace);
436 if (workspace_out == output->con) {
437 LOG("Workspace \"%s\" assigned to output \"%s\", but it is already "
438 "there. Do you have two assignment directives for the same "
439 "workspace in your configuration file?\n",
440 workspace->name, output_primary_name(output));
441 continue;
442 }
443
444 /* if so, move it over */
445 LOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n",
446 workspace->name, workspace_out->name, output_primary_name(output));
447
448 /* if the workspace is currently visible on that output, we need to
449 * switch to a different workspace - otherwise the output would end up
450 * with no active workspace */
451 bool visible = workspace_is_visible(workspace);
452 Con *previous = NULL;
453 if (visible && (previous = TAILQ_NEXT(workspace, focused))) {
454 LOG("Switching to previously used workspace \"%s\" on output \"%s\"\n",
455 previous->name, workspace_out->name);
456 workspace_show(previous);
457 }
458
459 /* Render the output on which the workspace was to get correct Rects.
460 * Then, we need to work with the "content" container, since we cannot
461 * be sure that the workspace itself was rendered at all (in case it’s
462 * invisible, it won’t be rendered). */
463 render_con(workspace_out, false);
464 Con *ws_out_content = output_get_content(workspace_out);
465
466 Con *floating_con;
467 TAILQ_FOREACH(floating_con, &(workspace->floating_head), floating_windows)
468 /* NB: We use output->con here because content is not yet rendered,
469 * so it has a rect of {0, 0, 0, 0}. */
470 floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect));
471
472 con_detach(workspace);
473 con_attach(workspace, content, false);
474
475 /* In case the workspace we just moved was visible but there was no
476 * other workspace to switch to, we need to initialize the source
477 * output as well */
478 if (visible && previous == NULL) {
479 LOG("There is no workspace left on \"%s\", re-initializing\n",
480 workspace_out->name);
481 init_ws_for_output(get_output_by_name(workspace_out->name, true),
482 output_get_content(workspace_out));
483 DLOG("Done re-initializing, continuing with \"%s\"\n", output_primary_name(output));
484 }
485 }
486
487 /* if a workspace exists, we are done now */
488 if (!TAILQ_EMPTY(&(content->nodes_head))) {
489 /* ensure that one of the workspaces is actually visible (in fullscreen
490 * mode), if they were invisible before, this might not be the case. */
491 Con *visible = NULL;
492 GREP_FIRST(visible, content, child->fullscreen_mode == CF_OUTPUT);
493 if (!visible) {
494 visible = TAILQ_FIRST(&(content->nodes_head));
495 focused = content;
496 workspace_show(visible);
497 }
498 return;
499 }
500
501 /* otherwise, we create the first assigned ws for this output */
502 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
503 if (!output_triggers_assignment(output, assignment)) {
504 continue;
505 }
506
507 LOG("Initializing first assigned workspace \"%s\" for output \"%s\"\n",
508 assignment->name, assignment->output);
509 focused = content;
510 workspace_show_by_name(assignment->name);
511 return;
512 }
513
514 /* if there is still no workspace, we create the first free workspace */
515 DLOG("Now adding a workspace\n");
516 Con *ws = create_workspace_on_output(output, content);
517
518 /* TODO: Set focus in main.c */
519 con_focus(ws);
520 }
521
522 /*
523 * This function needs to be called when changing the mode of an output when
524 * it already has some workspaces (or a bar window) assigned.
525 *
526 * It reconfigures the bar window for the new mode, copies the new rect into
527 * each workspace on this output and forces all windows on the affected
528 * workspaces to be reconfigured.
529 *
530 * It is necessary to call render_layout() afterwards.
531 *
532 */
533 static void output_change_mode(xcb_connection_t *conn, Output *output) {
534 DLOG("Output mode changed, updating rect\n");
535 assert(output->con != NULL);
536 output->con->rect = output->rect;
537
538 Con *content, *workspace, *child;
539
540 /* Point content to the container of the workspaces */
541 content = output_get_content(output->con);
542
543 /* Fix the position of all floating windows on this output.
544 * The 'rect' of each workspace will be updated in src/render.c. */
545 TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
546 TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
547 floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect));
548 }
549 }
550
551 /* If default_orientation is NO_ORIENTATION, we change the orientation of
552 * the workspaces and their children depending on output resolution. This is
553 * only done for workspaces with maximum one child. */
554 if (config.default_orientation == NO_ORIENTATION) {
555 TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
556 /* Workspaces with more than one child are left untouched because
557 * we do not want to change an existing layout. */
558 if (con_num_children(workspace) > 1)
559 continue;
560
561 workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
562 DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout);
563 if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) {
564 if (child->layout == L_SPLITV || child->layout == L_SPLITH)
565 child->layout = workspace->layout;
566 DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout);
567 }
568 }
569 }
570 }
571
572 /*
573 * randr_query_outputs_15 uses RandR ≥ 1.5 to update outputs.
574 *
575 */
576 static bool randr_query_outputs_15(void) {
577 #if XCB_RANDR_MINOR_VERSION < 5
578 return false;
579 #else
580 /* RandR 1.5 available at compile-time, i.e. libxcb is new enough */
581 if (!has_randr_1_5) {
582 return false;
583 }
584 /* RandR 1.5 available at run-time (supported by the server and not
585 * disabled by the user) */
586 DLOG("Querying outputs using RandR 1.5\n");
587 xcb_generic_error_t *err;
588 xcb_randr_get_monitors_reply_t *monitors =
589 xcb_randr_get_monitors_reply(
590 conn, xcb_randr_get_monitors(conn, root, true), &err);
591 if (err != NULL) {
592 ELOG("Could not get RandR monitors: X11 error code %d\n", err->error_code);
593 free(err);
594 /* Fall back to RandR ≤ 1.4 */
595 return false;
596 }
597
598 /* Mark all outputs as to_be_disabled, since xcb_randr_get_monitors() will
599 * only return active outputs. */
600 Output *output;
601 TAILQ_FOREACH(output, &outputs, outputs) {
602 if (output != root_output) {
603 output->to_be_disabled = true;
604 }
605 }
606
607 DLOG("%d RandR monitors found (timestamp %d)\n",
608 xcb_randr_get_monitors_monitors_length(monitors),
609 monitors->timestamp);
610
611 xcb_randr_monitor_info_iterator_t iter;
612 for (iter = xcb_randr_get_monitors_monitors_iterator(monitors);
613 iter.rem;
614 xcb_randr_monitor_info_next(&iter)) {
615 const xcb_randr_monitor_info_t *monitor_info = iter.data;
616 xcb_get_atom_name_reply_t *atom_reply =
617 xcb_get_atom_name_reply(
618 conn, xcb_get_atom_name(conn, monitor_info->name), &err);
619 if (err != NULL) {
620 ELOG("Could not get RandR monitor name: X11 error code %d\n", err->error_code);
621 free(err);
622 continue;
623 }
624 char *name;
625 sasprintf(&name, "%.*s",
626 xcb_get_atom_name_name_length(atom_reply),
627 xcb_get_atom_name_name(atom_reply));
628 free(atom_reply);
629
630 Output *new = get_output_by_name(name, false);
631 if (new == NULL) {
632 new = scalloc(1, sizeof(Output));
633
634 SLIST_INIT(&new->names_head);
635
636 /* Register associated output names in addition to the monitor name */
637 xcb_randr_output_t *randr_outputs = xcb_randr_monitor_info_outputs(monitor_info);
638 int randr_output_len = xcb_randr_monitor_info_outputs_length(monitor_info);
639 for (int i = 0; i < randr_output_len; i++) {
640 xcb_randr_output_t randr_output = randr_outputs[i];
641
642 xcb_randr_get_output_info_reply_t *info =
643 xcb_randr_get_output_info_reply(conn,
644 xcb_randr_get_output_info(conn, randr_output, monitors->timestamp),
645 NULL);
646
647 if (info != NULL && info->crtc != XCB_NONE) {
648 char *oname;
649 sasprintf(&oname, "%.*s",
650 xcb_randr_get_output_info_name_length(info),
651 xcb_randr_get_output_info_name(info));
652
653 if (strcmp(name, oname) != 0) {
654 struct output_name *output_name = scalloc(1, sizeof(struct output_name));
655 output_name->name = sstrdup(oname);
656 SLIST_INSERT_HEAD(&new->names_head, output_name, names);
657 } else {
658 free(oname);
659 }
660 }
661 FREE(info);
662 }
663
664 /* Insert the monitor name last, so that it's used as the primary name */
665 struct output_name *output_name = scalloc(1, sizeof(struct output_name));
666 output_name->name = sstrdup(name);
667 SLIST_INSERT_HEAD(&new->names_head, output_name, names);
668
669 if (monitor_info->primary) {
670 TAILQ_INSERT_HEAD(&outputs, new, outputs);
671 } else {
672 TAILQ_INSERT_TAIL(&outputs, new, outputs);
673 }
674 }
675 /* We specified get_active == true in xcb_randr_get_monitors(), so we
676 * will only receive active outputs. */
677 new->active = true;
678 new->to_be_disabled = false;
679
680 new->primary = monitor_info->primary;
681
682 new->changed =
683 update_if_necessary(&(new->rect.x), monitor_info->x) |
684 update_if_necessary(&(new->rect.y), monitor_info->y) |
685 update_if_necessary(&(new->rect.width), monitor_info->width) |
686 update_if_necessary(&(new->rect.height), monitor_info->height);
687
688 DLOG("name %s, x %d, y %d, width %d px, height %d px, width %d mm, height %d mm, primary %d, automatic %d\n",
689 name,
690 monitor_info->x, monitor_info->y, monitor_info->width, monitor_info->height,
691 monitor_info->width_in_millimeters, monitor_info->height_in_millimeters,
692 monitor_info->primary, monitor_info->automatic);
693 free(name);
694 }
695 free(monitors);
696 return true;
697 #endif
698 }
699
700 /*
701 * Gets called by randr_query_outputs_14() for each output. The function adds
702 * new outputs to the list of outputs, checks if the mode of existing outputs
703 * has been changed or if an existing output has been disabled. It will then
704 * change either the "changed" or the "to_be_deleted" flag of the output, if
705 * appropriate.
706 *
707 */
708 static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
709 xcb_randr_get_output_info_reply_t *output,
710 xcb_timestamp_t cts,
711 xcb_randr_get_screen_resources_current_reply_t *res) {
712 /* each CRT controller has a position in which we are interested in */
713 xcb_randr_get_crtc_info_reply_t *crtc;
714
715 Output *new = get_output_by_id(id);
716 bool existing = (new != NULL);
717 if (!existing) {
718 new = scalloc(1, sizeof(Output));
719 SLIST_INIT(&new->names_head);
720 }
721 new->id = id;
722 new->primary = (primary && primary->output == id);
723 while (!SLIST_EMPTY(&new->names_head)) {
724 FREE(SLIST_FIRST(&new->names_head)->name);
725 struct output_name *old_head = SLIST_FIRST(&new->names_head);
726 SLIST_REMOVE_HEAD(&new->names_head, names);
727 FREE(old_head);
728 }
729 struct output_name *output_name = scalloc(1, sizeof(struct output_name));
730 sasprintf(&output_name->name, "%.*s",
731 xcb_randr_get_output_info_name_length(output),
732 xcb_randr_get_output_info_name(output));
733 SLIST_INSERT_HEAD(&new->names_head, output_name, names);
734
735 DLOG("found output with name %s\n", output_primary_name(new));
736
737 /* Even if no CRTC is used at the moment, we store the output so that
738 * we do not need to change the list ever again (we only update the
739 * position/size) */
740 if (output->crtc == XCB_NONE) {
741 if (!existing) {
742 if (new->primary)
743 TAILQ_INSERT_HEAD(&outputs, new, outputs);
744 else
745 TAILQ_INSERT_TAIL(&outputs, new, outputs);
746 } else if (new->active)
747 new->to_be_disabled = true;
748 return;
749 }
750
751 xcb_randr_get_crtc_info_cookie_t icookie;
752 icookie = xcb_randr_get_crtc_info(conn, output->crtc, cts);
753 if ((crtc = xcb_randr_get_crtc_info_reply(conn, icookie, NULL)) == NULL) {
754 DLOG("Skipping output %s: could not get CRTC (%p)\n",
755 output_primary_name(new), crtc);
756 free(new);
757 return;
758 }
759
760 bool updated = update_if_necessary(&(new->rect.x), crtc->x) |
761 update_if_necessary(&(new->rect.y), crtc->y) |
762 update_if_necessary(&(new->rect.width), crtc->width) |
763 update_if_necessary(&(new->rect.height), crtc->height);
764 free(crtc);
765 new->active = (new->rect.width != 0 && new->rect.height != 0);
766 if (!new->active) {
767 DLOG("width/height 0/0, disabling output\n");
768 return;
769 }
770
771 DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
772 new->rect.x, new->rect.y);
773
774 /* If we don’t need to change an existing output or if the output
775 * does not exist in the first place, the case is simple: we either
776 * need to insert the new output or we are done. */
777 if (!updated || !existing) {
778 if (!existing) {
779 if (new->primary)
780 TAILQ_INSERT_HEAD(&outputs, new, outputs);
781 else
782 TAILQ_INSERT_TAIL(&outputs, new, outputs);
783 }
784 return;
785 }
786
787 new->changed = true;
788 }
789
790 /*
791 * randr_query_outputs_14 uses RandR ≤ 1.4 to update outputs.
792 *
793 */
794 static void randr_query_outputs_14(void) {
795 DLOG("Querying outputs using RandR ≤ 1.4\n");
796
797 /* Get screen resources (primary output, crtcs, outputs, modes) */
798 xcb_randr_get_screen_resources_current_cookie_t rcookie;
799 rcookie = xcb_randr_get_screen_resources_current(conn, root);
800 xcb_randr_get_output_primary_cookie_t pcookie;
801 pcookie = xcb_randr_get_output_primary(conn, root);
802
803 if ((primary = xcb_randr_get_output_primary_reply(conn, pcookie, NULL)) == NULL)
804 ELOG("Could not get RandR primary output\n");
805 else
806 DLOG("primary output is %08x\n", primary->output);
807
808 xcb_randr_get_screen_resources_current_reply_t *res =
809 xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL);
810 if (res == NULL) {
811 ELOG("Could not query screen resources.\n");
812 return;
813 }
814
815 /* timestamp of the configuration so that we get consistent replies to all
816 * requests (if the configuration changes between our different calls) */
817 const xcb_timestamp_t cts = res->config_timestamp;
818
819 const int len = xcb_randr_get_screen_resources_current_outputs_length(res);
820
821 /* an output is VGA-1, LVDS-1, etc. (usually physical video outputs) */
822 xcb_randr_output_t *randr_outputs = xcb_randr_get_screen_resources_current_outputs(res);
823
824 /* Request information for each output */
825 xcb_randr_get_output_info_cookie_t ocookie[len];
826 for (int i = 0; i < len; i++)
827 ocookie[i] = xcb_randr_get_output_info(conn, randr_outputs[i], cts);
828
829 /* Loop through all outputs available for this X11 screen */
830 for (int i = 0; i < len; i++) {
831 xcb_randr_get_output_info_reply_t *output;
832
833 if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL)
834 continue;
835
836 handle_output(conn, randr_outputs[i], output, cts, res);
837 free(output);
838 }
839
840 FREE(res);
841 }
842
843 /*
844 * (Re-)queries the outputs via RandR and stores them in the list of outputs.
845 *
846 * If no outputs are found use the root window.
847 *
848 */
849 void randr_query_outputs(void) {
850 Output *output, *other;
851
852 if (!randr_query_outputs_15()) {
853 randr_query_outputs_14();
854 }
855
856 /* If there's no randr output, enable the output covering the root window. */
857 if (any_randr_output_active()) {
858 DLOG("Active RandR output found. Disabling root output.\n");
859 if (root_output && root_output->active) {
860 root_output->to_be_disabled = true;
861 }
862 } else {
863 DLOG("No active RandR output found. Enabling root output.\n");
864 root_output->active = true;
865 }
866
867 /* Check for clones, disable the clones and reduce the mode to the
868 * lowest common mode */
869 TAILQ_FOREACH(output, &outputs, outputs) {
870 if (!output->active || output->to_be_disabled)
871 continue;
872 DLOG("output %p / %s, position (%d, %d), checking for clones\n",
873 output, output_primary_name(output), output->rect.x, output->rect.y);
874
875 for (other = output;
876 other != TAILQ_END(&outputs);
877 other = TAILQ_NEXT(other, outputs)) {
878 if (other == output || !other->active || other->to_be_disabled)
879 continue;
880
881 if (other->rect.x != output->rect.x ||
882 other->rect.y != output->rect.y)
883 continue;
884
885 DLOG("output %p has the same position, his mode = %d x %d\n",
886 other, other->rect.width, other->rect.height);
887 uint32_t width = min(other->rect.width, output->rect.width);
888 uint32_t height = min(other->rect.height, output->rect.height);
889
890 if (update_if_necessary(&(output->rect.width), width) |
891 update_if_necessary(&(output->rect.height), height))
892 output->changed = true;
893
894 update_if_necessary(&(other->rect.width), width);
895 update_if_necessary(&(other->rect.height), height);
896
897 DLOG("disabling output %p (%s)\n", other, output_primary_name(other));
898 other->to_be_disabled = true;
899
900 DLOG("new output mode %d x %d, other mode %d x %d\n",
901 output->rect.width, output->rect.height,
902 other->rect.width, other->rect.height);
903 }
904 }
905
906 /* Ensure that all outputs which are active also have a con. This is
907 * necessary because in the next step, a clone might get disabled. Example:
908 * LVDS1 active, VGA1 gets activated as a clone of LVDS1 (has no con).
909 * LVDS1 gets disabled. */
910 TAILQ_FOREACH(output, &outputs, outputs) {
911 if (output->active && output->con == NULL) {
912 DLOG("Need to initialize a Con for output %s\n", output_primary_name(output));
913 output_init_con(output);
914 output->changed = false;
915 }
916 }
917
918 /* Handle outputs which have a new mode or are disabled now (either
919 * because the user disabled them or because they are clones) */
920 TAILQ_FOREACH(output, &outputs, outputs) {
921 if (output->to_be_disabled) {
922 randr_disable_output(output);
923 }
924
925 if (output->changed) {
926 output_change_mode(conn, output);
927 output->changed = false;
928 }
929 }
930
931 /* Just go through each active output and assign one workspace */
932 TAILQ_FOREACH(output, &outputs, outputs) {
933 if (!output->active)
934 continue;
935 Con *content = output_get_content(output->con);
936 if (!TAILQ_EMPTY(&(content->nodes_head)))
937 continue;
938 DLOG("Should add ws for output %s\n", output_primary_name(output));
939 init_ws_for_output(output, content);
940 }
941
942 /* Focus the primary screen, if possible */
943 TAILQ_FOREACH(output, &outputs, outputs) {
944 if (!output->primary || !output->con)
945 continue;
946
947 DLOG("Focusing primary output %s\n", output_primary_name(output));
948 Con *content = output_get_content(output->con);
949 Con *ws = TAILQ_FIRST(&(content)->focus_head);
950 workspace_show(ws);
951 }
952
953 /* render_layout flushes */
954 tree_render();
955
956 FREE(primary);
957 }
958
959 /*
960 * Disables the output and moves its content.
961 *
962 */
963 void randr_disable_output(Output *output) {
964 assert(output->to_be_disabled);
965
966 output->active = false;
967 DLOG("Output %s disabled, re-assigning workspaces/docks\n", output_primary_name(output));
968
969 Output *first = get_first_output();
970
971 /* TODO: refactor the following code into a nice function. maybe
972 * use an on_destroy callback which is implement differently for
973 * different container types (CT_CONTENT vs. CT_DOCKAREA)? */
974 Con *first_content = output_get_content(first->con);
975
976 if (output->con != NULL) {
977 /* We need to move the workspaces from the disappearing output to the first output */
978 /* 1: Get the con to focus next, if the disappearing ws is focused */
979 Con *next = NULL;
980 if (TAILQ_FIRST(&(croot->focus_head)) == output->con) {
981 DLOG("This output (%p) was focused! Getting next\n", output->con);
982 next = focused;
983 DLOG("next = %p\n", next);
984 }
985
986 /* 2: iterate through workspaces and re-assign them, fixing the coordinates
987 * of floating containers as we go */
988 Con *current;
989 Con *old_content = output_get_content(output->con);
990 while (!TAILQ_EMPTY(&(old_content->nodes_head))) {
991 current = TAILQ_FIRST(&(old_content->nodes_head));
992 if (current != next && TAILQ_EMPTY(&(current->focus_head))) {
993 /* the workspace is empty and not focused, get rid of it */
994 DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name);
995 tree_close_internal(current, DONT_KILL_WINDOW, false);
996 continue;
997 }
998 DLOG("Detaching current = %p / %s\n", current, current->name);
999 con_detach(current);
1000 DLOG("Re-attaching current = %p / %s\n", current, current->name);
1001 con_attach(current, first_content, false);
1002 DLOG("Fixing the coordinates of floating containers\n");
1003 Con *floating_con;
1004 TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows) {
1005 floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
1006 }
1007 DLOG("Done, next\n");
1008 }
1009 DLOG("re-attached all workspaces\n");
1010
1011 if (next) {
1012 DLOG("now focusing next = %p\n", next);
1013 con_activate(next);
1014 workspace_show(con_get_workspace(next));
1015 }
1016
1017 /* 3: move the dock clients to the first output */
1018 Con *child;
1019 TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) {
1020 if (child->type != CT_DOCKAREA)
1021 continue;
1022 DLOG("Handling dock con %p\n", child);
1023 Con *dock;
1024 while (!TAILQ_EMPTY(&(child->nodes_head))) {
1025 dock = TAILQ_FIRST(&(child->nodes_head));
1026 Con *nc;
1027 Match *match;
1028 nc = con_for_window(first->con, dock->window, &match);
1029 DLOG("Moving dock client %p to nc %p\n", dock, nc);
1030 con_detach(dock);
1031 DLOG("Re-attaching\n");
1032 con_attach(dock, nc, false);
1033 DLOG("Done\n");
1034 }
1035 }
1036
1037 DLOG("destroying disappearing con %p\n", output->con);
1038 Con *con = output->con;
1039 /* clear the pointer before calling tree_close_internal in which the memory is freed */
1040 output->con = NULL;
1041 tree_close_internal(con, DONT_KILL_WINDOW, true);
1042 DLOG("Done. Should be fine now\n");
1043 }
1044
1045 output->to_be_disabled = false;
1046 output->changed = false;
1047 }
1048
1049 static void fallback_to_root_output(void) {
1050 root_output->active = true;
1051 output_init_con(root_output);
1052 init_ws_for_output(root_output, output_get_content(root_output->con));
1053 }
1054
1055 /*
1056 * We have just established a connection to the X server and need the initial
1057 * XRandR information to setup workspaces for each screen.
1058 *
1059 */
1060 void randr_init(int *event_base, const bool disable_randr15) {
1061 const xcb_query_extension_reply_t *extreply;
1062
1063 root_output = create_root_output(conn);
1064 TAILQ_INSERT_TAIL(&outputs, root_output, outputs);
1065
1066 extreply = xcb_get_extension_data(conn, &xcb_randr_id);
1067 if (!extreply->present) {
1068 DLOG("RandR is not present, activating root output.\n");
1069 fallback_to_root_output();
1070 return;
1071 }
1072
1073 xcb_generic_error_t *err;
1074 xcb_randr_query_version_reply_t *randr_version =
1075 xcb_randr_query_version_reply(
1076 conn, xcb_randr_query_version(conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &err);
1077 if (err != NULL) {
1078 ELOG("Could not query RandR version: X11 error code %d\n", err->error_code);
1079 free(err);
1080 fallback_to_root_output();
1081 return;
1082 }
1083
1084 has_randr_1_5 = (randr_version->major_version >= 1) &&
1085 (randr_version->minor_version >= 5) &&
1086 !disable_randr15;
1087
1088 free(randr_version);
1089
1090 randr_query_outputs();
1091
1092 if (event_base != NULL)
1093 *event_base = extreply->first_event;
1094
1095 xcb_randr_select_input(conn, root,
1096 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
1097 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
1098 XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
1099 XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
1100
1101 xcb_flush(conn);
1102 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * regex.c: Interface to libPCRE (perl compatible regular expressions).
7 *
8 */
9 #include "all.h"
10
11 /*
12 * Creates a new 'regex' struct containing the given pattern and a PCRE
13 * compiled regular expression. Also, calls pcre_study because this regex will
14 * most likely be used often (like for every new window and on every relevant
15 * property change of existing windows).
16 *
17 * Returns NULL if the pattern could not be compiled into a regular expression
18 * (and ELOGs an appropriate error message).
19 *
20 */
21 struct regex *regex_new(const char *pattern) {
22 const char *error;
23 int errorcode, offset;
24
25 struct regex *re = scalloc(1, sizeof(struct regex));
26 re->pattern = sstrdup(pattern);
27 int options = PCRE_UTF8;
28 /* We use PCRE_UCP so that \B, \b, \D, \d, \S, \s, \W, \w and some POSIX
29 * character classes play nicely with Unicode */
30 options |= PCRE_UCP;
31 while (!(re->regex = pcre_compile2(pattern, options, &errorcode, &error, &offset, NULL))) {
32 /* If the error is that PCRE was not compiled with UTF-8 support we
33 * disable it and try again */
34 if (errorcode == 32) {
35 options &= ~PCRE_UTF8;
36 continue;
37 }
38 ELOG("PCRE regular expression compilation failed at %d: %s\n",
39 offset, error);
40 regex_free(re);
41 return NULL;
42 }
43 re->extra = pcre_study(re->regex, 0, &error);
44 /* If an error happened, we print the error message, but continue.
45 * Studying the regular expression leads to faster matching, but it’s not
46 * absolutely necessary. */
47 if (error) {
48 ELOG("PCRE regular expression studying failed: %s\n", error);
49 }
50 return re;
51 }
52
53 /*
54 * Frees the given regular expression. It must not be used afterwards!
55 *
56 */
57 void regex_free(struct regex *regex) {
58 if (!regex)
59 return;
60 FREE(regex->pattern);
61 FREE(regex->regex);
62 FREE(regex->extra);
63 FREE(regex);
64 }
65
66 /*
67 * Checks if the given regular expression matches the given input and returns
68 * true if it does. In either case, it logs the outcome using LOG(), so it will
69 * be visible without debug logging.
70 *
71 */
72 bool regex_matches(struct regex *regex, const char *input) {
73 int rc;
74
75 /* We use strlen() because pcre_exec() expects the length of the input
76 * string in bytes */
77 if ((rc = pcre_exec(regex->regex, regex->extra, input, strlen(input), 0, 0, NULL, 0)) == 0) {
78 LOG("Regular expression \"%s\" matches \"%s\"\n",
79 regex->pattern, input);
80 return true;
81 }
82
83 if (rc == PCRE_ERROR_NOMATCH) {
84 LOG("Regular expression \"%s\" does not match \"%s\"\n",
85 regex->pattern, input);
86 return false;
87 }
88
89 ELOG("PCRE error %d while trying to use regular expression \"%s\" on input \"%s\", see pcreapi(3)\n",
90 rc, regex->pattern, input);
91 return false;
92 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * render.c: Renders (determines position/sizes) the layout tree, updating the
7 * various rects. Needs to be pushed to X11 (see x.c) to be visible.
8 *
9 */
10 #include "all.h"
11
12 /* Forward declarations */
13 static int *precalculate_sizes(Con *con, render_params *p);
14 static void render_root(Con *con, Con *fullscreen);
15 static void render_output(Con *con);
16 static void render_con_split(Con *con, Con *child, render_params *p, int i);
17 static void render_con_stacked(Con *con, Con *child, render_params *p, int i);
18 static void render_con_tabbed(Con *con, Con *child, render_params *p, int i);
19 static void render_con_dockarea(Con *con, Con *child, render_params *p);
20
21 /*
22 * Returns the height for the decorations
23 */
24 int render_deco_height(void) {
25 int deco_height = config.font.height + 4;
26 if (config.font.height & 0x01)
27 ++deco_height;
28 return deco_height;
29 }
30
31 /*
32 * "Renders" the given container (and its children), meaning that all rects are
33 * updated correctly. Note that this function does not call any xcb_*
34 * functions, so the changes are completely done in memory only (and
35 * side-effect free). As soon as you call x_push_changes(), the changes will be
36 * updated in X11.
37 *
38 */
39 void render_con(Con *con, bool render_fullscreen) {
40 render_params params = {
41 .rect = con->rect,
42 .x = con->rect.x,
43 .y = con->rect.y,
44 .children = con_num_children(con)};
45
46 DLOG("Rendering %snode %p / %s / layout %d / children %d\n",
47 (render_fullscreen ? "fullscreen " : ""), con, con->name, con->layout,
48 params.children);
49
50 int i = 0;
51 con->mapped = true;
52
53 /* if this container contains a window, set the coordinates */
54 if (con->window) {
55 /* depending on the border style, the rect of the child window
56 * needs to be smaller */
57 Rect *inset = &(con->window_rect);
58 *inset = (Rect){0, 0, con->rect.width, con->rect.height};
59 if (!render_fullscreen)
60 *inset = rect_add(*inset, con_border_style_rect(con));
61
62 /* Obey x11 border */
63 inset->width -= (2 * con->border_width);
64 inset->height -= (2 * con->border_width);
65
66 /* Obey the aspect ratio, if any, unless we are in fullscreen mode.
67 *
68 * The spec isn’t explicit on whether the aspect ratio hints should be
69 * respected during fullscreen mode. Other WMs such as Openbox don’t do
70 * that, and this post suggests that this is the correct way to do it:
71 * https://mail.gnome.org/archives/wm-spec-list/2003-May/msg00007.html
72 *
73 * Ignoring aspect ratio during fullscreen was necessary to fix MPlayer
74 * subtitle rendering, see https://bugs.i3wm.org/594 */
75 if (!render_fullscreen && con->window->aspect_ratio > 0.0) {
76 DLOG("aspect_ratio = %f, current width/height are %d/%d\n",
77 con->window->aspect_ratio, inset->width, inset->height);
78 double new_height = inset->height + 1;
79 int new_width = inset->width;
80
81 while (new_height > inset->height) {
82 new_height = (1.0 / con->window->aspect_ratio) * new_width;
83
84 if (new_height > inset->height)
85 new_width--;
86 }
87 /* Center the window */
88 inset->y += ceil(inset->height / 2) - floor((new_height + .5) / 2);
89 inset->x += ceil(inset->width / 2) - floor(new_width / 2);
90
91 inset->height = new_height + .5;
92 inset->width = new_width;
93 }
94
95 /* NB: We used to respect resize increment size hints for tiling
96 * windows up until commit 0db93d9 here. However, since all terminal
97 * emulators cope with ignoring the size hints in a better way than we
98 * can (by providing their fake-transparency or background color), this
99 * code was removed. See also https://bugs.i3wm.org/540 */
100
101 DLOG("child will be at %dx%d with size %dx%d\n", inset->x, inset->y, inset->width, inset->height);
102 }
103
104 /* Check for fullscreen nodes */
105 Con *fullscreen = NULL;
106 if (con->type != CT_OUTPUT) {
107 fullscreen = con_get_fullscreen_con(con, (con->type == CT_ROOT ? CF_GLOBAL : CF_OUTPUT));
108 }
109 if (fullscreen) {
110 fullscreen->rect = params.rect;
111 x_raise_con(fullscreen);
112 render_con(fullscreen, true);
113 /* Fullscreen containers are either global (underneath the CT_ROOT
114 * container) or per-output (underneath the CT_CONTENT container). For
115 * global fullscreen containers, we cannot abort rendering here yet,
116 * because the floating windows (with popup_during_fullscreen smart)
117 * have not yet been rendered (see the CT_ROOT code path below). See
118 * also https://bugs.i3wm.org/1393 */
119 if (con->type != CT_ROOT) {
120 return;
121 }
122 }
123
124 /* find the height for the decorations */
125 params.deco_height = render_deco_height();
126
127 /* precalculate the sizes to be able to correct rounding errors */
128 params.sizes = precalculate_sizes(con, &params);
129
130 if (con->layout == L_OUTPUT) {
131 /* Skip i3-internal outputs */
132 if (con_is_internal(con))
133 goto free_params;
134 render_output(con);
135 } else if (con->type == CT_ROOT) {
136 render_root(con, fullscreen);
137 } else {
138 Con *child;
139 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
140 assert(params.children > 0);
141
142 if (con->layout == L_SPLITH || con->layout == L_SPLITV) {
143 render_con_split(con, child, &params, i);
144 } else if (con->layout == L_STACKED) {
145 render_con_stacked(con, child, &params, i);
146 } else if (con->layout == L_TABBED) {
147 render_con_tabbed(con, child, &params, i);
148 } else if (con->layout == L_DOCKAREA) {
149 render_con_dockarea(con, child, &params);
150 }
151
152 DLOG("child at (%d, %d) with (%d x %d)\n",
153 child->rect.x, child->rect.y, child->rect.width, child->rect.height);
154 x_raise_con(child);
155 render_con(child, false);
156 i++;
157 }
158
159 /* in a stacking or tabbed container, we ensure the focused client is raised */
160 if (con->layout == L_STACKED || con->layout == L_TABBED) {
161 TAILQ_FOREACH_REVERSE(child, &(con->focus_head), focus_head, focused)
162 x_raise_con(child);
163 if ((child = TAILQ_FIRST(&(con->focus_head)))) {
164 /* By rendering the stacked container again, we handle the case
165 * that we have a non-leaf-container inside the stack. In that
166 * case, the children of the non-leaf-container need to be raised
167 * as well. */
168 render_con(child, false);
169 }
170
171 if (params.children != 1)
172 /* Raise the stack con itself. This will put the stack decoration on
173 * top of every stack window. That way, when a new window is opened in
174 * the stack, the old window will not obscure part of the decoration
175 * (it’s unmapped afterwards). */
176 x_raise_con(con);
177 }
178 }
179
180 free_params:
181 FREE(params.sizes);
182 }
183
184 static int *precalculate_sizes(Con *con, render_params *p) {
185 if ((con->layout != L_SPLITH && con->layout != L_SPLITV) || p->children <= 0) {
186 return NULL;
187 }
188
189 int *sizes = smalloc(p->children * sizeof(int));
190 assert(!TAILQ_EMPTY(&con->nodes_head));
191
192 Con *child;
193 int i = 0, assigned = 0;
194 int total = con_orientation(con) == HORIZ ? p->rect.width : p->rect.height;
195 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
196 double percentage = child->percent > 0.0 ? child->percent : 1.0 / p->children;
197 assigned += sizes[i++] = lround(percentage * total);
198 }
199 assert(assigned == total ||
200 (assigned > total && assigned - total <= p->children * 2) ||
201 (assigned < total && total - assigned <= p->children * 2));
202 int signal = assigned < total ? 1 : -1;
203 while (assigned != total) {
204 for (i = 0; i < p->children && assigned != total; ++i) {
205 sizes[i] += signal;
206 assigned += signal;
207 }
208 }
209
210 return sizes;
211 }
212
213 static void render_root(Con *con, Con *fullscreen) {
214 Con *output;
215 if (!fullscreen) {
216 TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
217 render_con(output, false);
218 }
219 }
220
221 /* We need to render floating windows after rendering all outputs’
222 * tiling windows because they need to be on top of *every* output at
223 * all times. This is important when the user places floating
224 * windows/containers so that they overlap on another output. */
225 DLOG("Rendering floating windows:\n");
226 TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
227 if (con_is_internal(output))
228 continue;
229 /* Get the active workspace of that output */
230 Con *content = output_get_content(output);
231 if (!content || TAILQ_EMPTY(&(content->focus_head))) {
232 DLOG("Skipping this output because it is currently being destroyed.\n");
233 continue;
234 }
235 Con *workspace = TAILQ_FIRST(&(content->focus_head));
236 Con *fullscreen = con_get_fullscreen_covering_ws(workspace);
237 Con *child;
238 TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
239 if (fullscreen != NULL) {
240 /* Don’t render floating windows when there is a fullscreen
241 * window on that workspace. Necessary to make floating
242 * fullscreen work correctly (ticket #564). Exception to the
243 * above rule: smart popup_during_fullscreen handling (popups
244 * belonging to the fullscreen app will be rendered). */
245 if (config.popup_during_fullscreen != PDF_SMART || fullscreen->window == NULL) {
246 continue;
247 }
248
249 Con *floating_child = con_descend_focused(child);
250 Con *transient_con = floating_child;
251 bool is_transient_for = false;
252 while (transient_con != NULL &&
253 transient_con->window != NULL &&
254 transient_con->window->transient_for != XCB_NONE) {
255 DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n",
256 transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id);
257 if (transient_con->window->transient_for == fullscreen->window->id) {
258 is_transient_for = true;
259 break;
260 }
261 Con *next_transient = con_by_window_id(transient_con->window->transient_for);
262 if (next_transient == NULL)
263 break;
264 /* Some clients (e.g. x11-ssh-askpass) actually set
265 * WM_TRANSIENT_FOR to their own window id, so break instead of
266 * looping endlessly. */
267 if (transient_con == next_transient)
268 break;
269 transient_con = next_transient;
270 }
271
272 if (!is_transient_for)
273 continue;
274 else {
275 DLOG("Rendering floating child even though in fullscreen mode: "
276 "floating->transient_for (0x%08x) --> fullscreen->id (0x%08x)\n",
277 floating_child->window->transient_for, fullscreen->window->id);
278 }
279 }
280 DLOG("floating child at (%d,%d) with %d x %d\n",
281 child->rect.x, child->rect.y, child->rect.width, child->rect.height);
282 x_raise_con(child);
283 render_con(child, false);
284 }
285 }
286 }
287
288 /*
289 * Renders a container with layout L_OUTPUT. In this layout, all CT_DOCKAREAs
290 * get the height of their content and the remaining CT_CON gets the rest.
291 *
292 */
293 static void render_output(Con *con) {
294 Con *child, *dockchild;
295
296 int x = con->rect.x;
297 int y = con->rect.y;
298 int height = con->rect.height;
299
300 /* Find the content container and ensure that there is exactly one. Also
301 * check for any non-CT_DOCKAREA clients. */
302 Con *content = NULL;
303 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
304 if (child->type == CT_CON) {
305 if (content != NULL) {
306 DLOG("More than one CT_CON on output container\n");
307 assert(false);
308 }
309 content = child;
310 } else if (child->type != CT_DOCKAREA) {
311 DLOG("Child %p of type %d is inside the OUTPUT con\n", child, child->type);
312 assert(false);
313 }
314 }
315
316 if (content == NULL) {
317 DLOG("Skipping this output because it is currently being destroyed.\n");
318 return;
319 }
320
321 /* We need to find out if there is a fullscreen con on the current workspace
322 * and take the short-cut to render it directly (the user does not want to
323 * see the dockareas in that case) */
324 Con *ws = con_get_fullscreen_con(content, CF_OUTPUT);
325 if (!ws) {
326 DLOG("Skipping this output because it is currently being destroyed.\n");
327 return;
328 }
329 Con *fullscreen = con_get_fullscreen_con(ws, CF_OUTPUT);
330 if (fullscreen) {
331 fullscreen->rect = con->rect;
332 x_raise_con(fullscreen);
333 render_con(fullscreen, true);
334 return;
335 }
336
337 /* First pass: determine the height of all CT_DOCKAREAs (the sum of their
338 * children) and figure out how many pixels we have left for the rest */
339 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
340 if (child->type != CT_DOCKAREA)
341 continue;
342
343 child->rect.height = 0;
344 TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes)
345 child->rect.height += dockchild->geometry.height;
346
347 height -= child->rect.height;
348 }
349
350 /* Second pass: Set the widths/heights */
351 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
352 if (child->type == CT_CON) {
353 child->rect.x = x;
354 child->rect.y = y;
355 child->rect.width = con->rect.width;
356 child->rect.height = height;
357 }
358
359 child->rect.x = x;
360 child->rect.y = y;
361 child->rect.width = con->rect.width;
362
363 child->deco_rect.x = 0;
364 child->deco_rect.y = 0;
365 child->deco_rect.width = 0;
366 child->deco_rect.height = 0;
367
368 y += child->rect.height;
369
370 DLOG("child at (%d, %d) with (%d x %d)\n",
371 child->rect.x, child->rect.y, child->rect.width, child->rect.height);
372 x_raise_con(child);
373 render_con(child, false);
374 }
375 }
376
377 static void render_con_split(Con *con, Con *child, render_params *p, int i) {
378 assert(con->layout == L_SPLITH || con->layout == L_SPLITV);
379
380 if (con->layout == L_SPLITH) {
381 child->rect.x = p->x;
382 child->rect.y = p->y;
383 child->rect.width = p->sizes[i];
384 child->rect.height = p->rect.height;
385 p->x += child->rect.width;
386 } else {
387 child->rect.x = p->x;
388 child->rect.y = p->y;
389 child->rect.width = p->rect.width;
390 child->rect.height = p->sizes[i];
391 p->y += child->rect.height;
392 }
393
394 /* first we have the decoration, if this is a leaf node */
395 if (con_is_leaf(child)) {
396 if (child->border_style == BS_NORMAL) {
397 /* TODO: make a function for relative coords? */
398 child->deco_rect.x = child->rect.x - con->rect.x;
399 child->deco_rect.y = child->rect.y - con->rect.y;
400
401 child->rect.y += p->deco_height;
402 child->rect.height -= p->deco_height;
403
404 child->deco_rect.width = child->rect.width;
405 child->deco_rect.height = p->deco_height;
406 } else {
407 child->deco_rect.x = 0;
408 child->deco_rect.y = 0;
409 child->deco_rect.width = 0;
410 child->deco_rect.height = 0;
411 }
412 }
413 }
414
415 static void render_con_stacked(Con *con, Con *child, render_params *p, int i) {
416 assert(con->layout == L_STACKED);
417
418 child->rect.x = p->x;
419 child->rect.y = p->y;
420 child->rect.width = p->rect.width;
421 child->rect.height = p->rect.height;
422
423 child->deco_rect.x = p->x - con->rect.x;
424 child->deco_rect.y = p->y - con->rect.y + (i * p->deco_height);
425 child->deco_rect.width = child->rect.width;
426 child->deco_rect.height = p->deco_height;
427
428 if (p->children > 1 || (child->border_style != BS_PIXEL && child->border_style != BS_NONE)) {
429 child->rect.y += (p->deco_height * p->children);
430 child->rect.height -= (p->deco_height * p->children);
431 }
432 }
433
434 static void render_con_tabbed(Con *con, Con *child, render_params *p, int i) {
435 assert(con->layout == L_TABBED);
436
437 child->rect.x = p->x;
438 child->rect.y = p->y;
439 child->rect.width = p->rect.width;
440 child->rect.height = p->rect.height;
441
442 child->deco_rect.width = floor((float)child->rect.width / p->children);
443 child->deco_rect.x = p->x - con->rect.x + i * child->deco_rect.width;
444 child->deco_rect.y = p->y - con->rect.y;
445
446 /* Since the tab width may be something like 31,6 px per tab, we
447 * let the last tab have all the extra space (0,6 * children). */
448 if (i == (p->children - 1)) {
449 child->deco_rect.width += (child->rect.width - (child->deco_rect.x + child->deco_rect.width));
450 }
451
452 if (p->children > 1 || (child->border_style != BS_PIXEL && child->border_style != BS_NONE)) {
453 child->rect.y += p->deco_height;
454 child->rect.height -= p->deco_height;
455 child->deco_rect.height = p->deco_height;
456 } else {
457 child->deco_rect.height = (child->border_style == BS_PIXEL ? 1 : 0);
458 }
459 }
460
461 static void render_con_dockarea(Con *con, Con *child, render_params *p) {
462 assert(con->layout == L_DOCKAREA);
463
464 child->rect.x = p->x;
465 child->rect.y = p->y;
466 child->rect.width = p->rect.width;
467 child->rect.height = child->geometry.height;
468
469 child->deco_rect.x = 0;
470 child->deco_rect.y = 0;
471 child->deco_rect.width = 0;
472 child->deco_rect.height = 0;
473 p->y += child->rect.height;
474 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * resize.c: Interactive resizing.
7 *
8 */
9 #include "all.h"
10
11 /*
12 * This is an ugly data structure which we need because there is no standard
13 * way of having nested functions (only available as a gcc extension at the
14 * moment, clang doesn’t support it) or blocks (only available as a clang
15 * extension and only on Mac OS X systems at the moment).
16 *
17 */
18 struct callback_params {
19 orientation_t orientation;
20 Con *output;
21 xcb_window_t helpwin;
22 uint32_t *new_position;
23 };
24
25 DRAGGING_CB(resize_callback) {
26 const struct callback_params *params = extra;
27 Con *output = params->output;
28 DLOG("new x = %d, y = %d\n", new_x, new_y);
29 if (params->orientation == HORIZ) {
30 /* Check if the new coordinates are within screen boundaries */
31 if (new_x > (output->rect.x + output->rect.width - 25) ||
32 new_x < (output->rect.x + 25))
33 return;
34
35 *(params->new_position) = new_x;
36 xcb_configure_window(conn, params->helpwin, XCB_CONFIG_WINDOW_X, params->new_position);
37 } else {
38 if (new_y > (output->rect.y + output->rect.height - 25) ||
39 new_y < (output->rect.y + 25))
40 return;
41
42 *(params->new_position) = new_y;
43 xcb_configure_window(conn, params->helpwin, XCB_CONFIG_WINDOW_Y, params->new_position);
44 }
45
46 xcb_flush(conn);
47 }
48
49 bool resize_find_tiling_participants(Con **current, Con **other, direction_t direction, bool both_sides) {
50 DLOG("Find two participants for resizing container=%p in direction=%i\n", other, direction);
51 Con *first = *current;
52 Con *second = NULL;
53 if (first == NULL) {
54 DLOG("Current container is NULL, aborting.\n");
55 return false;
56 }
57
58 /* Go up in the tree and search for a container to resize */
59 const orientation_t search_orientation = orientation_from_direction(direction);
60 const bool dir_backwards = (direction == D_UP || direction == D_LEFT);
61 while (first->type != CT_WORKSPACE &&
62 first->type != CT_FLOATING_CON &&
63 second == NULL) {
64 /* get the appropriate first container with the matching
65 * orientation (skip stacked/tabbed cons) */
66 if ((con_orientation(first->parent) != search_orientation) ||
67 (first->parent->layout == L_STACKED) ||
68 (first->parent->layout == L_TABBED)) {
69 first = first->parent;
70 continue;
71 }
72
73 /* get the counterpart for this resizement */
74 if (dir_backwards) {
75 second = TAILQ_PREV(first, nodes_head, nodes);
76 if (second == NULL && both_sides == true) {
77 second = TAILQ_NEXT(first, nodes);
78 }
79 } else {
80 second = TAILQ_NEXT(first, nodes);
81 if (second == NULL && both_sides == true) {
82 second = TAILQ_PREV(first, nodes_head, nodes);
83 }
84 }
85
86 if (second == NULL) {
87 DLOG("No second container in this direction found, trying to look further up in the tree...\n");
88 first = first->parent;
89 }
90 }
91
92 DLOG("Found participants: first=%p and second=%p.\n", first, second);
93 *current = first;
94 *other = second;
95 if (first == NULL || second == NULL) {
96 DLOG("Could not find two participants for this resize request.\n");
97 return false;
98 }
99
100 return true;
101 }
102
103 /*
104 * Calculate the given container's new percent given a change in pixels.
105 *
106 */
107 double px_resize_to_percent(Con *con, int px_diff) {
108 Con *parent = con->parent;
109 const orientation_t o = con_orientation(parent);
110 const int total = (o == HORIZ ? parent->rect.width : parent->rect.height);
111 /* deco_rect.height is subtracted from each child in render_con_split */
112 const int target = px_diff + (o == HORIZ ? con->rect.width : con->rect.height + con->deco_rect.height);
113 return ((double)target / (double)total);
114 }
115
116 /*
117 * Calculate the minimum percent needed for the given container to be at least 1
118 * pixel.
119 *
120 */
121 double percent_for_1px(Con *con) {
122 Con *parent = con->parent;
123 const orientation_t o = con_orientation(parent);
124 const int total = (o == HORIZ ? parent->rect.width : parent->rect.height);
125 const int target = (o == HORIZ ? 1 : 1 + con->deco_rect.height);
126 return ((double)target / (double)total);
127 }
128
129 /*
130 * Resize the two given containers using the given amount of pixels or
131 * percentage points. One of the two needs to be 0. A positive amount means
132 * growing the first container while a negative means shrinking it.
133 * Returns false when the resize would result in one of the two containers
134 * having less than 1 pixel of size.
135 *
136 */
137 bool resize_neighboring_cons(Con *first, Con *second, int px, int ppt) {
138 assert(px * ppt == 0);
139
140 Con *parent = first->parent;
141 double new_first_percent;
142 double new_second_percent;
143 if (ppt) {
144 new_first_percent = first->percent + ((double)ppt / 100.0);
145 new_second_percent = second->percent - ((double)ppt / 100.0);
146 } else {
147 new_first_percent = px_resize_to_percent(first, px);
148 new_second_percent = second->percent + first->percent - new_first_percent;
149 }
150 /* Ensure that no container will be less than 1 pixel in the resizing
151 * direction. */
152 if (new_first_percent < percent_for_1px(first) || new_second_percent < percent_for_1px(second)) {
153 return false;
154 }
155
156 first->percent = new_first_percent;
157 second->percent = new_second_percent;
158 con_fix_percent(parent);
159 return true;
160 }
161
162 void resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event) {
163 Con *output = con_get_output(first);
164 DLOG("x = %d, width = %d\n", output->rect.x, output->rect.width);
165
166 x_mask_event_mask(~XCB_EVENT_MASK_ENTER_WINDOW);
167 xcb_flush(conn);
168
169 uint32_t mask = 0;
170 uint32_t values[2];
171
172 mask = XCB_CW_OVERRIDE_REDIRECT;
173 values[0] = 1;
174
175 /* Open a new window, the resizebar. Grab the pointer and move the window
176 * around as the user moves the pointer. */
177 xcb_window_t grabwin = create_window(conn, output->rect, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT,
178 XCB_WINDOW_CLASS_INPUT_ONLY, XCURSOR_CURSOR_POINTER, true, mask, values);
179
180 /* Keep track of the coordinate orthogonal to motion so we can determine the
181 * length of the resize afterward. */
182 uint32_t initial_position, new_position;
183
184 /* Configure the resizebar and snap the pointer. The resizebar runs along
185 * the rect of the second con and follows the motion of the pointer. */
186 Rect helprect;
187 helprect.x = second->rect.x;
188 helprect.y = second->rect.y;
189 if (orientation == HORIZ) {
190 helprect.width = logical_px(2);
191 helprect.height = second->rect.height;
192 initial_position = second->rect.x;
193 xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0,
194 second->rect.x, event->root_y);
195 } else {
196 helprect.width = second->rect.width;
197 helprect.height = logical_px(2);
198 initial_position = second->rect.y;
199 xcb_warp_pointer(conn, XCB_NONE, event->root, 0, 0, 0, 0,
200 event->root_x, second->rect.y);
201 }
202
203 mask = XCB_CW_BACK_PIXEL;
204 values[0] = config.client.focused.border.colorpixel;
205
206 mask |= XCB_CW_OVERRIDE_REDIRECT;
207 values[1] = 1;
208
209 xcb_window_t helpwin = create_window(conn, helprect, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT,
210 XCB_WINDOW_CLASS_INPUT_OUTPUT, (orientation == HORIZ ? XCURSOR_CURSOR_RESIZE_HORIZONTAL : XCURSOR_CURSOR_RESIZE_VERTICAL), true, mask, values);
211
212 xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin);
213
214 xcb_flush(conn);
215
216 /* `new_position' will be updated by the `resize_callback'. */
217 new_position = initial_position;
218
219 const struct callback_params params = {orientation, output, helpwin, &new_position};
220
221 /* `drag_pointer' blocks until the drag is completed. */
222 drag_result_t drag_result = drag_pointer(NULL, event, grabwin, BORDER_TOP, 0, resize_callback, &params);
223
224 xcb_destroy_window(conn, helpwin);
225 xcb_destroy_window(conn, grabwin);
226 xcb_flush(conn);
227
228 /* User cancelled the drag so no action should be taken. */
229 if (drag_result == DRAG_REVERT) {
230 return;
231 }
232
233 int pixels = (new_position - initial_position);
234 DLOG("Done, pixels = %d\n", pixels);
235
236 /* if we got thus far, the containers must have valid percentages. */
237 assert(first->percent > 0.0);
238 assert(second->percent > 0.0);
239 const bool result = resize_neighboring_cons(first, second, pixels, 0);
240 DLOG("Graphical resize %s: first->percent = %f, second->percent = %f.\n",
241 result ? "successful" : "failed", first->percent, second->percent);
242 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * restore_layout.c: Everything for restored containers that is not pure state
7 * parsing (which can be found in load_layout.c).
8 *
9 *
10 */
11 #include "all.h"
12
13 #ifdef I3_ASAN_ENABLED
14 #include <sanitizer/lsan_interface.h>
15 #endif
16
17 #define TEXT_PADDING logical_px(2)
18
19 typedef struct placeholder_state {
20 /** The X11 placeholder window. */
21 xcb_window_t window;
22 /** The container to which this placeholder window belongs. */
23 Con *con;
24
25 /** Current size of the placeholder window (to detect size changes). */
26 Rect rect;
27
28 /** The drawable surface */
29 surface_t surface;
30
31 TAILQ_ENTRY(placeholder_state)
32 state;
33 } placeholder_state;
34
35 static TAILQ_HEAD(state_head, placeholder_state) state_head =
36 TAILQ_HEAD_INITIALIZER(state_head);
37
38 static xcb_connection_t *restore_conn;
39
40 static struct ev_io *xcb_watcher;
41 static struct ev_prepare *xcb_prepare;
42
43 static void restore_handle_event(int type, xcb_generic_event_t *event);
44
45 /* Documentation for these functions can be found in src/main.c, starting at xcb_got_event */
46 static void restore_xcb_got_event(EV_P_ struct ev_io *w, int revents) {
47 }
48
49 static void restore_xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
50 xcb_generic_event_t *event;
51
52 if (xcb_connection_has_error(restore_conn)) {
53 DLOG("restore X11 connection has an error, reconnecting\n");
54 restore_connect();
55 return;
56 }
57
58 while ((event = xcb_poll_for_event(restore_conn)) != NULL) {
59 if (event->response_type == 0) {
60 xcb_generic_error_t *error = (xcb_generic_error_t *)event;
61 DLOG("X11 Error received (probably harmless)! sequence 0x%x, error_code = %d\n",
62 error->sequence, error->error_code);
63 free(event);
64 continue;
65 }
66
67 /* Strip off the highest bit (set if the event is generated) */
68 int type = (event->response_type & 0x7F);
69
70 restore_handle_event(type, event);
71
72 free(event);
73 }
74
75 xcb_flush(restore_conn);
76 }
77
78 /*
79 * Opens a separate connection to X11 for placeholder windows when restoring
80 * layouts. This is done as a safety measure (users can xkill a placeholder
81 * window without killing their window manager) and for better isolation, both
82 * on the wire to X11 and thus also in the code.
83 *
84 */
85 void restore_connect(void) {
86 if (restore_conn != NULL) {
87 /* This is not the initial connect, but a reconnect, most likely
88 * because our X11 connection was killed (e.g. by a user with xkill. */
89 ev_io_stop(main_loop, xcb_watcher);
90 ev_prepare_stop(main_loop, xcb_prepare);
91
92 placeholder_state *state;
93 while (!TAILQ_EMPTY(&state_head)) {
94 state = TAILQ_FIRST(&state_head);
95 TAILQ_REMOVE(&state_head, state, state);
96 free(state);
97 }
98
99 /* xcb_disconnect leaks memory in libxcb versions earlier than 1.11,
100 * but it’s the right function to call. See
101 * https://cgit.freedesktop.org/xcb/libxcb/commit/src/xcb_conn.c?id=4dcbfd77b
102 */
103 xcb_disconnect(restore_conn);
104 free(xcb_watcher);
105 free(xcb_prepare);
106 }
107
108 int screen;
109 restore_conn = xcb_connect(NULL, &screen);
110 if (restore_conn == NULL || xcb_connection_has_error(restore_conn)) {
111 if (restore_conn != NULL) {
112 xcb_disconnect(restore_conn);
113 }
114 #ifdef I3_ASAN_ENABLED
115 __lsan_do_leak_check();
116 #endif
117 errx(EXIT_FAILURE, "Cannot open display\n");
118 }
119
120 xcb_watcher = scalloc(1, sizeof(struct ev_io));
121 xcb_prepare = scalloc(1, sizeof(struct ev_prepare));
122
123 ev_io_init(xcb_watcher, restore_xcb_got_event, xcb_get_file_descriptor(restore_conn), EV_READ);
124 ev_io_start(main_loop, xcb_watcher);
125
126 ev_prepare_init(xcb_prepare, restore_xcb_prepare_cb);
127 ev_prepare_start(main_loop, xcb_prepare);
128 }
129
130 static void update_placeholder_contents(placeholder_state *state) {
131 const color_t foreground = config.client.placeholder.text;
132 const color_t background = config.client.placeholder.background;
133
134 draw_util_clear_surface(&(state->surface), background);
135
136 // TODO: make i3font functions per-connection, at least these two for now…?
137 xcb_flush(restore_conn);
138 xcb_aux_sync(restore_conn);
139
140 Match *swallows;
141 int n = 0;
142 TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) {
143 char *serialized = NULL;
144
145 #define APPEND_REGEX(re_name) \
146 do { \
147 if (swallows->re_name != NULL) { \
148 sasprintf(&serialized, "%s%s" #re_name "=\"%s\"", (serialized ? serialized : "["), (serialized ? " " : ""), swallows->re_name->pattern); \
149 } \
150 } while (0)
151
152 APPEND_REGEX(class);
153 APPEND_REGEX(instance);
154 APPEND_REGEX(window_role);
155 APPEND_REGEX(title);
156
157 if (serialized == NULL) {
158 DLOG("This swallows specification is not serializable?!\n");
159 continue;
160 }
161
162 sasprintf(&serialized, "%s]", serialized);
163 DLOG("con %p (placeholder 0x%08x) line %d: %s\n", state->con, state->window, n, serialized);
164
165 i3String *str = i3string_from_utf8(serialized);
166 draw_util_text(str, &(state->surface), foreground, background,
167 TEXT_PADDING,
168 (n * (config.font.height + TEXT_PADDING)) + TEXT_PADDING,
169 state->rect.width - 2 * TEXT_PADDING);
170 i3string_free(str);
171 n++;
172 free(serialized);
173 }
174
175 // TODO: render the watch symbol in a bigger font
176 i3String *line = i3string_from_utf8("⌚");
177 int text_width = predict_text_width(line);
178 int x = (state->rect.width / 2) - (text_width / 2);
179 int y = (state->rect.height / 2) - (config.font.height / 2);
180 draw_util_text(line, &(state->surface), foreground, background, x, y, text_width);
181 i3string_free(line);
182 xcb_flush(conn);
183 xcb_aux_sync(conn);
184 }
185
186 static void open_placeholder_window(Con *con) {
187 if (con_is_leaf(con) &&
188 (con->window == NULL || con->window->id == XCB_NONE) &&
189 !TAILQ_EMPTY(&(con->swallow_head)) &&
190 con->type == CT_CON) {
191 xcb_window_t placeholder = create_window(
192 restore_conn,
193 con->rect,
194 XCB_COPY_FROM_PARENT,
195 XCB_COPY_FROM_PARENT,
196 XCB_WINDOW_CLASS_INPUT_OUTPUT,
197 XCURSOR_CURSOR_POINTER,
198 true,
199 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
200 (uint32_t[]){
201 config.client.placeholder.background.colorpixel,
202 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
203 });
204 /* Make i3 not focus this window. */
205 xcb_icccm_wm_hints_t hints;
206 xcb_icccm_wm_hints_set_none(&hints);
207 xcb_icccm_wm_hints_set_input(&hints, 0);
208 xcb_icccm_set_wm_hints(restore_conn, placeholder, &hints);
209 /* Set the same name as was stored in the layout file. While perhaps
210 * slightly confusing in the first instant, this brings additional
211 * clarity to which placeholder is waiting for which actual window. */
212 if (con->name != NULL)
213 xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
214 A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name);
215 DLOG("Created placeholder window 0x%08x for leaf container %p / %s\n",
216 placeholder, con, con->name);
217
218 placeholder_state *state = scalloc(1, sizeof(placeholder_state));
219 state->window = placeholder;
220 state->con = con;
221 state->rect = con->rect;
222
223 draw_util_surface_init(conn, &(state->surface), placeholder, get_visualtype(root_screen), state->rect.width, state->rect.height);
224 update_placeholder_contents(state);
225 TAILQ_INSERT_TAIL(&state_head, state, state);
226
227 /* create temporary id swallow to match the placeholder */
228 Match *temp_id = smalloc(sizeof(Match));
229 match_init(temp_id);
230 temp_id->dock = M_DONTCHECK;
231 temp_id->id = placeholder;
232 TAILQ_INSERT_HEAD(&(con->swallow_head), temp_id, matches);
233 }
234
235 Con *child;
236 TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
237 open_placeholder_window(child);
238 }
239 TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
240 open_placeholder_window(child);
241 }
242 }
243
244 /*
245 * Open placeholder windows for all children of parent. The placeholder window
246 * will vanish as soon as a real window is swallowed by the container. Until
247 * then, it exposes the criteria that must be fulfilled for a window to be
248 * swallowed by this container.
249 *
250 */
251 void restore_open_placeholder_windows(Con *parent) {
252 Con *child;
253 TAILQ_FOREACH(child, &(parent->nodes_head), nodes) {
254 open_placeholder_window(child);
255 }
256 TAILQ_FOREACH(child, &(parent->floating_head), floating_windows) {
257 open_placeholder_window(child);
258 }
259
260 xcb_flush(restore_conn);
261 }
262
263 /*
264 * Kill the placeholder window, if placeholder refers to a placeholder window.
265 * This function is called when manage.c puts a window into an existing
266 * container. In order not to leak resources, we need to destroy the window and
267 * all associated X11 objects (pixmap/gc).
268 *
269 */
270 bool restore_kill_placeholder(xcb_window_t placeholder) {
271 placeholder_state *state;
272 TAILQ_FOREACH(state, &state_head, state) {
273 if (state->window != placeholder)
274 continue;
275
276 xcb_destroy_window(restore_conn, state->window);
277 draw_util_surface_free(restore_conn, &(state->surface));
278 TAILQ_REMOVE(&state_head, state, state);
279 free(state);
280 DLOG("placeholder window 0x%08x destroyed.\n", placeholder);
281 return true;
282 }
283
284 DLOG("0x%08x is not a placeholder window, ignoring.\n", placeholder);
285 return false;
286 }
287
288 static void expose_event(xcb_expose_event_t *event) {
289 placeholder_state *state;
290 TAILQ_FOREACH(state, &state_head, state) {
291 if (state->window != event->window)
292 continue;
293
294 DLOG("refreshing window 0x%08x contents (con %p)\n", state->window, state->con);
295
296 update_placeholder_contents(state);
297
298 return;
299 }
300
301 ELOG("Received ExposeEvent for unknown window 0x%08x\n", event->window);
302 }
303
304 /*
305 * Window size has changed. Update the width/height, then recreate the back
306 * buffer pixmap and the accompanying graphics context and force an immediate
307 * re-rendering.
308 *
309 */
310 static void configure_notify(xcb_configure_notify_event_t *event) {
311 placeholder_state *state;
312 TAILQ_FOREACH(state, &state_head, state) {
313 if (state->window != event->window)
314 continue;
315
316 DLOG("ConfigureNotify: window 0x%08x has now width=%d, height=%d (con %p)\n",
317 state->window, event->width, event->height, state->con);
318
319 state->rect.width = event->width;
320 state->rect.height = event->height;
321
322 draw_util_surface_set_size(&(state->surface), state->rect.width, state->rect.height);
323
324 update_placeholder_contents(state);
325
326 return;
327 }
328
329 ELOG("Received ConfigureNotify for unknown window 0x%08x\n", event->window);
330 }
331
332 static void restore_handle_event(int type, xcb_generic_event_t *event) {
333 switch (type) {
334 case XCB_EXPOSE:
335 if (((xcb_expose_event_t *)event)->count == 0) {
336 expose_event((xcb_expose_event_t *)event);
337 }
338
339 break;
340 case XCB_CONFIGURE_NOTIFY:
341 configure_notify((xcb_configure_notify_event_t *)event);
342 break;
343 default:
344 DLOG("Received unhandled X11 event of type %d\n", type);
345 break;
346 }
347 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * scratchpad.c: Moving windows to the scratchpad and making them visible again.
7 *
8 */
9 #include "all.h"
10
11 /*
12 * Moves the specified window to the __i3_scratch workspace, making it floating
13 * and setting the appropriate scratchpad_state.
14 *
15 * Gets called upon the command 'move scratchpad'.
16 *
17 */
18 void scratchpad_move(Con *con) {
19 if (con->type == CT_WORKSPACE) {
20 LOG("'move scratchpad' used on a workspace \"%s\". Calling it "
21 "recursively on all windows on this workspace.\n",
22 con->name);
23 Con *current;
24 current = TAILQ_FIRST(&(con->focus_head));
25 while (current) {
26 Con *next = TAILQ_NEXT(current, focused);
27 scratchpad_move(current);
28 current = next;
29 }
30 return;
31 }
32 DLOG("should move con %p to __i3_scratch\n", con);
33
34 Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
35 if (con_get_workspace(con) == __i3_scratch) {
36 DLOG("This window is already on __i3_scratch.\n");
37 return;
38 }
39
40 /* If the current con is in fullscreen mode, we need to disable that,
41 * as a scratchpad window should never be in fullscreen mode */
42 if (focused && focused->type != CT_WORKSPACE && focused->fullscreen_mode != CF_NONE) {
43 con_toggle_fullscreen(focused, CF_OUTPUT);
44 }
45
46 /* 1: Ensure the window or any parent is floating. From now on, we deal
47 * with the CT_FLOATING_CON. We use automatic == false because the user
48 * made the choice that this window should be a scratchpad (and floating).
49 */
50 Con *maybe_floating_con = con_inside_floating(con);
51 if (maybe_floating_con == NULL) {
52 floating_enable(con, false);
53 con = con->parent;
54 } else {
55 con = maybe_floating_con;
56 }
57
58 /* 2: Send the window to the __i3_scratch workspace, mainting its
59 * coordinates and not warping the pointer. */
60 con_move_to_workspace(con, __i3_scratch, true, true, false);
61
62 /* 3: If this is the first time this window is used as a scratchpad, we set
63 * the scratchpad_state to SCRATCHPAD_FRESH. The window will then be
64 * adjusted in size according to what the user specifies. */
65 if (con->scratchpad_state == SCRATCHPAD_NONE) {
66 DLOG("This window was never used as a scratchpad before.\n");
67 if (con == maybe_floating_con) {
68 DLOG("It was in floating mode before, set scratchpad state to changed.\n");
69 con->scratchpad_state = SCRATCHPAD_CHANGED;
70 } else {
71 DLOG("It was in tiling mode before, set scratchpad state to fresh.\n");
72 con->scratchpad_state = SCRATCHPAD_FRESH;
73 }
74 }
75 }
76
77 /*
78 * Either shows the top-most scratchpad window (con == NULL) or shows the
79 * specified con (if it is scratchpad window).
80 *
81 * When called with con == NULL and the currently focused window is a
82 * scratchpad window, this serves as a shortcut to hide it again (so the user
83 * can press the same key to quickly look something up).
84 *
85 */
86 bool scratchpad_show(Con *con) {
87 DLOG("should show scratchpad window %p\n", con);
88 Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
89 Con *floating;
90
91 /* If this was 'scratchpad show' without criteria, we check if the
92 * currently focused window is a scratchpad window and should be hidden
93 * again. */
94 if (!con &&
95 (floating = con_inside_floating(focused)) &&
96 floating->scratchpad_state != SCRATCHPAD_NONE) {
97 DLOG("Focused window is a scratchpad window, hiding it.\n");
98 scratchpad_move(focused);
99 return true;
100 }
101
102 /* If the current con or any of its parents are in fullscreen mode, we
103 * first need to disable it before showing the scratchpad con. */
104 Con *fs = focused;
105 while (fs && fs->fullscreen_mode == CF_NONE)
106 fs = fs->parent;
107
108 if (fs && fs->type != CT_WORKSPACE) {
109 con_toggle_fullscreen(fs, CF_OUTPUT);
110 }
111
112 /* If this was 'scratchpad show' without criteria, we check if there is a
113 * unfocused scratchpad on the current workspace and focus it */
114 Con *walk_con;
115 Con *focused_ws = con_get_workspace(focused);
116 TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) {
117 if (!con && (floating = con_inside_floating(walk_con)) &&
118 floating->scratchpad_state != SCRATCHPAD_NONE &&
119 floating != con_inside_floating(focused)) {
120 DLOG("Found an unfocused scratchpad window on this workspace\n");
121 DLOG("Focusing it: %p\n", walk_con);
122 /* use con_descend_tiling_focused to get the last focused
123 * window inside this scratch container in order to
124 * keep the focus the same within this container */
125 con_activate(con_descend_tiling_focused(walk_con));
126 return true;
127 }
128 }
129
130 /* If this was 'scratchpad show' without criteria, we check if there is a
131 * visible scratchpad window on another workspace. In this case we move it
132 * to the current workspace. */
133 focused_ws = con_get_workspace(focused);
134 TAILQ_FOREACH(walk_con, &all_cons, all_cons) {
135 Con *walk_ws = con_get_workspace(walk_con);
136 if (!con && walk_ws &&
137 !con_is_internal(walk_ws) && focused_ws != walk_ws &&
138 (floating = con_inside_floating(walk_con)) &&
139 floating->scratchpad_state != SCRATCHPAD_NONE) {
140 DLOG("Found a visible scratchpad window on another workspace,\n");
141 DLOG("moving it to this workspace: con = %p\n", walk_con);
142 con_move_to_workspace(walk_con, focused_ws, true, false, false);
143 con_activate(con_descend_focused(walk_con));
144 return true;
145 }
146 }
147
148 /* If this was 'scratchpad show' with criteria, we check if the window
149 * is actually in the scratchpad */
150 if (con && con->parent->scratchpad_state == SCRATCHPAD_NONE) {
151 DLOG("Window is not in the scratchpad, doing nothing.\n");
152 return false;
153 }
154
155 /* If this was 'scratchpad show' with criteria, we check if it matches a
156 * currently visible scratchpad window and hide it. */
157 Con *active = con_get_workspace(focused);
158 Con *current = con_get_workspace(con);
159 if (con &&
160 (floating = con_inside_floating(con)) &&
161 floating->scratchpad_state != SCRATCHPAD_NONE &&
162 current != __i3_scratch) {
163 /* If scratchpad window is on the active workspace, then we should hide
164 * it, otherwise we should move it to the active workspace. */
165 if (current == active) {
166 DLOG("Window is a scratchpad window, hiding it.\n");
167 scratchpad_move(con);
168 return true;
169 }
170 }
171
172 if (con == NULL) {
173 /* Use the container on __i3_scratch which is highest in the focus
174 * stack. When moving windows to __i3_scratch, they get inserted at the
175 * bottom of the stack. */
176 con = TAILQ_FIRST(&(__i3_scratch->floating_head));
177
178 if (!con) {
179 LOG("You don't have any scratchpad windows yet.\n");
180 LOG("Use 'move scratchpad' to move a window to the scratchpad.\n");
181 return false;
182 }
183 } else {
184 /* We used a criterion, so we need to do what follows (moving,
185 * resizing) on the floating parent. */
186 con = con_inside_floating(con);
187 }
188
189 /* 1: Move the window from __i3_scratch to the current workspace. */
190 con_move_to_workspace(con, active, true, false, false);
191
192 /* 2: Adjust the size if this window was not adjusted yet. */
193 if (con->scratchpad_state == SCRATCHPAD_FRESH) {
194 DLOG("Adjusting size of this window.\n");
195 Con *output = con_get_output(con);
196 con->rect.width = output->rect.width * 0.5;
197 con->rect.height = output->rect.height * 0.75;
198 floating_check_size(con);
199 floating_center(con, con_get_workspace(con)->rect);
200 }
201
202 /* Activate active workspace if window is from another workspace to ensure
203 * proper focus. */
204 if (current != active) {
205 workspace_show(active);
206 }
207
208 con_activate(con_descend_focused(con));
209
210 return true;
211 }
212
213 /*
214 * Greatest common divisor, implemented only for the least common multiple
215 * below.
216 *
217 */
218 static int _gcd(const int m, const int n) {
219 if (n == 0)
220 return m;
221 return _gcd(n, (m % n));
222 }
223
224 /*
225 * Least common multiple. We use it to determine the (ideally not too large)
226 * resolution for the __i3 pseudo-output on which the scratchpad is on (see
227 * below). We could just multiply the resolutions, but for some pathetic cases
228 * (many outputs), using the LCM will achieve better results.
229 *
230 * Man, when you were learning about these two algorithms for the first time,
231 * did you think you’d ever need them in a real-world software project of
232 * yours? I certainly didn’t until now. :-D
233 *
234 */
235 static int _lcm(const int m, const int n) {
236 const int o = _gcd(m, n);
237 return ((m * n) / o);
238 }
239
240 /*
241 * When starting i3 initially (and after each change to the connected outputs),
242 * this function fixes the resolution of the __i3 pseudo-output. When that
243 * resolution is not set to a function which shares a common divisor with every
244 * active output’s resolution, floating point calculation errors will lead to
245 * the scratchpad window moving when shown repeatedly.
246 *
247 */
248 void scratchpad_fix_resolution(void) {
249 Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
250 Con *__i3_output = con_get_output(__i3_scratch);
251 DLOG("Current resolution: (%d, %d) %d x %d\n",
252 __i3_output->rect.x, __i3_output->rect.y,
253 __i3_output->rect.width, __i3_output->rect.height);
254 Con *output;
255 int new_width = -1,
256 new_height = -1;
257 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
258 if (output == __i3_output)
259 continue;
260 DLOG("output %s's resolution: (%d, %d) %d x %d\n",
261 output->name, output->rect.x, output->rect.y,
262 output->rect.width, output->rect.height);
263 if (new_width == -1) {
264 new_width = output->rect.width;
265 new_height = output->rect.height;
266 } else {
267 new_width = _lcm(new_width, output->rect.width);
268 new_height = _lcm(new_height, output->rect.height);
269 }
270 }
271
272 Rect old_rect = __i3_output->rect;
273
274 DLOG("new width = %d, new height = %d\n",
275 new_width, new_height);
276 __i3_output->rect.width = new_width;
277 __i3_output->rect.height = new_height;
278
279 Rect new_rect = __i3_output->rect;
280
281 if (memcmp(&old_rect, &new_rect, sizeof(Rect)) == 0) {
282 DLOG("Scratchpad size unchanged.\n");
283 return;
284 }
285
286 DLOG("Fixing coordinates of scratchpad windows\n");
287 Con *con;
288 TAILQ_FOREACH(con, &(__i3_scratch->floating_head), floating_windows) {
289 floating_fix_coordinates(con, &old_rect, &new_rect);
290 }
291 }
0 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
1
2 /***
3 Copyright 2010 Lennart Poettering
4
5 Permission is hereby granted, free of charge, to any person
6 obtaining a copy of this software and associated documentation files
7 (the "Software"), to deal in the Software without restriction,
8 including without limitation the rights to use, copy, modify, merge,
9 publish, distribute, sublicense, and/or sell copies of the Software,
10 and to permit persons to whom the Software is furnished to do so,
11 subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be
14 included in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 ***/
25
26 #ifndef _GNU_SOURCE
27 #define _GNU_SOURCE
28 #endif
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <sys/fcntl.h>
35 #include <netinet/in.h>
36 #include <stdlib.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stddef.h>
43
44 #include "sd-daemon.h"
45
46 int sd_listen_fds(int unset_environment) {
47 int r, fd;
48 const char *e;
49 char *p = NULL;
50 unsigned long l;
51
52 if (!(e = getenv("LISTEN_PID"))) {
53 r = 0;
54 goto finish;
55 }
56
57 errno = 0;
58 l = strtoul(e, &p, 10);
59
60 if (errno != 0) {
61 r = -errno;
62 goto finish;
63 }
64
65 if (!p || *p || l <= 0) {
66 r = -EINVAL;
67 goto finish;
68 }
69
70 /* Is this for us? */
71 if (getpid() != (pid_t)l) {
72 r = 0;
73 goto finish;
74 }
75
76 if (!(e = getenv("LISTEN_FDS"))) {
77 r = 0;
78 goto finish;
79 }
80
81 errno = 0;
82 l = strtoul(e, &p, 10);
83
84 if (errno != 0) {
85 r = -errno;
86 goto finish;
87 }
88
89 if (!p || *p) {
90 r = -EINVAL;
91 goto finish;
92 }
93
94 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)l; fd++) {
95 int flags;
96
97 if ((flags = fcntl(fd, F_GETFD)) < 0) {
98 r = -errno;
99 goto finish;
100 }
101
102 if (flags & FD_CLOEXEC)
103 continue;
104
105 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
106 r = -errno;
107 goto finish;
108 }
109 }
110
111 r = (int)l;
112
113 finish:
114 if (unset_environment) {
115 unsetenv("LISTEN_PID");
116 unsetenv("LISTEN_FDS");
117 }
118
119 return r;
120 }
121
122 int sd_is_fifo(int fd, const char *path) {
123 struct stat st_fd;
124
125 if (fd < 0)
126 return -EINVAL;
127
128 memset(&st_fd, 0, sizeof(st_fd));
129 if (fstat(fd, &st_fd) < 0)
130 return -errno;
131
132 if (!S_ISFIFO(st_fd.st_mode))
133 return 0;
134
135 if (path) {
136 struct stat st_path;
137
138 memset(&st_path, 0, sizeof(st_path));
139 if (stat(path, &st_path) < 0) {
140 if (errno == ENOENT || errno == ENOTDIR)
141 return 0;
142
143 return -errno;
144 }
145
146 return st_path.st_dev == st_fd.st_dev &&
147 st_path.st_ino == st_fd.st_ino;
148 }
149
150 return 1;
151 }
152
153 static int sd_is_socket_internal(int fd, int type, int listening) {
154 struct stat st_fd;
155
156 if (fd < 0 || type < 0)
157 return -EINVAL;
158
159 if (fstat(fd, &st_fd) < 0)
160 return -errno;
161
162 if (!S_ISSOCK(st_fd.st_mode))
163 return 0;
164
165 if (type != 0) {
166 int other_type = 0;
167 socklen_t l = sizeof(other_type);
168
169 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
170 return -errno;
171
172 if (l != sizeof(other_type))
173 return -EINVAL;
174
175 if (other_type != type)
176 return 0;
177 }
178
179 if (listening >= 0) {
180 int accepting = 0;
181 socklen_t l = sizeof(accepting);
182
183 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
184 return -errno;
185
186 if (l != sizeof(accepting))
187 return -EINVAL;
188
189 if (!accepting != !listening)
190 return 0;
191 }
192
193 return 1;
194 }
195
196 union sockaddr_union {
197 struct sockaddr sa;
198 struct sockaddr_in in4;
199 struct sockaddr_in6 in6;
200 struct sockaddr_un un;
201 struct sockaddr_storage storage;
202 };
203
204 int sd_is_socket(int fd, int family, int type, int listening) {
205 int r;
206
207 if (family < 0)
208 return -EINVAL;
209
210 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
211 return r;
212
213 if (family > 0) {
214 union sockaddr_union sockaddr;
215 socklen_t l;
216
217 memset(&sockaddr, 0, sizeof(sockaddr));
218 l = sizeof(sockaddr);
219
220 if (getsockname(fd, &sockaddr.sa, &l) < 0)
221 return -errno;
222
223 if (l < sizeof(sa_family_t))
224 return -EINVAL;
225
226 return sockaddr.sa.sa_family == family;
227 }
228
229 return 1;
230 }
231
232 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
233 union sockaddr_union sockaddr;
234 socklen_t l;
235 int r;
236
237 if (family != 0 && family != AF_INET && family != AF_INET6)
238 return -EINVAL;
239
240 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
241 return r;
242
243 memset(&sockaddr, 0, sizeof(sockaddr));
244 l = sizeof(sockaddr);
245
246 if (getsockname(fd, &sockaddr.sa, &l) < 0)
247 return -errno;
248
249 if (l < sizeof(sa_family_t))
250 return -EINVAL;
251
252 if (sockaddr.sa.sa_family != AF_INET &&
253 sockaddr.sa.sa_family != AF_INET6)
254 return 0;
255
256 if (family > 0)
257 if (sockaddr.sa.sa_family != family)
258 return 0;
259
260 if (port > 0) {
261 if (sockaddr.sa.sa_family == AF_INET) {
262 if (l < sizeof(struct sockaddr_in))
263 return -EINVAL;
264
265 return htons(port) == sockaddr.in4.sin_port;
266 } else {
267 if (l < sizeof(struct sockaddr_in6))
268 return -EINVAL;
269
270 return htons(port) == sockaddr.in6.sin6_port;
271 }
272 }
273
274 return 1;
275 }
276
277 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
278 union sockaddr_union sockaddr;
279 socklen_t l;
280 int r;
281
282 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
283 return r;
284
285 memset(&sockaddr, 0, sizeof(sockaddr));
286 l = sizeof(sockaddr);
287
288 if (getsockname(fd, &sockaddr.sa, &l) < 0)
289 return -errno;
290
291 if (l < sizeof(sa_family_t))
292 return -EINVAL;
293
294 if (sockaddr.sa.sa_family != AF_UNIX)
295 return 0;
296
297 if (path) {
298 if (length <= 0)
299 length = strlen(path);
300
301 if (length <= 0)
302 /* Unnamed socket */
303 return l == offsetof(struct sockaddr_un, sun_path);
304
305 if (path[0])
306 /* Normal path socket */
307 return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
308 memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
309 else
310 /* Abstract namespace socket */
311 return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
312 memcmp(path, sockaddr.un.sun_path, length) == 0;
313 }
314
315 return 1;
316 }
317
318 int sd_notify(int unset_environment, const char *state) {
319 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
320 return 0;
321 #else
322 int fd = -1, r;
323 struct msghdr msghdr;
324 struct iovec iovec;
325 union sockaddr_union sockaddr;
326 const char *e;
327
328 if (!state) {
329 r = -EINVAL;
330 goto finish;
331 }
332
333 if (!(e = getenv("NOTIFY_SOCKET")))
334 return 0;
335
336 /* Must be an abstract socket, or an absolute path */
337 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
338 r = -EINVAL;
339 goto finish;
340 }
341
342 if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
343 r = -errno;
344 goto finish;
345 }
346
347 memset(&sockaddr, 0, sizeof(sockaddr));
348 sockaddr.sa.sa_family = AF_UNIX;
349 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
350
351 if (sockaddr.un.sun_path[0] == '@')
352 sockaddr.un.sun_path[0] = 0;
353
354 memset(&iovec, 0, sizeof(iovec));
355 iovec.iov_base = (char *)state;
356 iovec.iov_len = strlen(state);
357
358 memset(&msghdr, 0, sizeof(msghdr));
359 msghdr.msg_name = &sockaddr;
360 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
361
362 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
363 msghdr.msg_namelen = sizeof(struct sockaddr_un);
364
365 msghdr.msg_iov = &iovec;
366 msghdr.msg_iovlen = 1;
367
368 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
369 r = -errno;
370 goto finish;
371 }
372
373 r = 1;
374
375 finish:
376 if (unset_environment)
377 unsetenv("NOTIFY_SOCKET");
378
379 if (fd >= 0)
380 close(fd);
381
382 return r;
383 #endif
384 }
385
386 int sd_notifyf(int unset_environment, const char *format, ...) {
387 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
388 return 0;
389 #else
390 va_list ap;
391 char *p = NULL;
392 int r;
393
394 va_start(ap, format);
395 r = vasprintf(&p, format, ap);
396 va_end(ap);
397
398 if (r < 0 || !p)
399 return -ENOMEM;
400
401 r = sd_notify(unset_environment, p);
402 free(p);
403
404 return r;
405 #endif
406 }
407
408 int sd_booted(void) {
409 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
410 return 0;
411 #else
412
413 struct stat a, b;
414
415 /* We simply test whether the systemd cgroup hierarchy is
416 * mounted */
417
418 if (lstat("/sys/fs/cgroup", &a) < 0)
419 return 0;
420
421 if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
422 return 0;
423
424 return a.st_dev != b.st_dev;
425 #endif
426 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 */
7 #include "all.h"
8
9 #include <ev.h>
10 #include <iconv.h>
11 #include <signal.h>
12 #include <sys/wait.h>
13
14 #include <xcb/xcb_event.h>
15
16 #include <X11/keysym.h>
17
18 typedef struct dialog_t {
19 xcb_window_t id;
20 xcb_colormap_t colormap;
21 Rect dims;
22 surface_t surface;
23
24 TAILQ_ENTRY(dialog_t)
25 dialogs;
26 } dialog_t;
27
28 static TAILQ_HEAD(dialogs_head, dialog_t) dialogs = TAILQ_HEAD_INITIALIZER(dialogs);
29 static int raised_signal;
30 static int backtrace_done = 0;
31
32 static int sighandler_backtrace(void);
33 static void sighandler_setup(void);
34 static void sighandler_create_dialogs(void);
35 static void sighandler_destroy_dialogs(void);
36 static void sighandler_handle_expose(void);
37 static void sighandler_draw_dialog(dialog_t *dialog);
38 static void sighandler_handle_key_press(xcb_key_press_event_t *event);
39
40 static i3String *message_intro;
41 static i3String *message_intro2;
42 static i3String *message_option_backtrace;
43 static i3String *message_option_restart;
44 static i3String *message_option_forget;
45 static int dialog_width;
46 static int dialog_height;
47
48 static int border_width = 2;
49 static int margin = 4;
50
51 /*
52 * Attach gdb to pid_parent and dump a backtrace to i3-backtrace.$pid in the
53 * tmpdir
54 */
55 static int sighandler_backtrace(void) {
56 char *tmpdir = getenv("TMPDIR");
57 if (tmpdir == NULL)
58 tmpdir = "/tmp";
59
60 pid_t pid_parent = getpid();
61
62 char *filename = NULL;
63 int suffix = 0;
64 /* Find a unique filename for the backtrace (since the PID of i3 stays the
65 * same), so that we don’t overwrite earlier backtraces. */
66 do {
67 FREE(filename);
68 sasprintf(&filename, "%s/i3-backtrace.%d.%d.txt", tmpdir, pid_parent, suffix);
69 suffix++;
70 } while (path_exists(filename));
71
72 pid_t pid_gdb = fork();
73 if (pid_gdb < 0) {
74 DLOG("Failed to fork for GDB\n");
75 return -1;
76 } else if (pid_gdb == 0) {
77 /* child */
78 int stdin_pipe[2],
79 stdout_pipe[2];
80
81 if (pipe(stdin_pipe) == -1) {
82 ELOG("Failed to init stdin_pipe\n");
83 return -1;
84 }
85 if (pipe(stdout_pipe) == -1) {
86 ELOG("Failed to init stdout_pipe\n");
87 return -1;
88 }
89
90 /* close standard streams in case i3 is started from a terminal; gdb
91 * needs to run without controlling terminal for it to work properly in
92 * this situation */
93 close(STDIN_FILENO);
94 close(STDOUT_FILENO);
95 close(STDERR_FILENO);
96
97 /* We provide pipe file descriptors for stdin/stdout because gdb < 7.5
98 * crashes otherwise, see
99 * https://sourceware.org/bugzilla/show_bug.cgi?id=14114 */
100 dup2(stdin_pipe[0], STDIN_FILENO);
101 dup2(stdout_pipe[1], STDOUT_FILENO);
102
103 char *pid_s, *gdb_log_cmd;
104 sasprintf(&pid_s, "%d", pid_parent);
105 sasprintf(&gdb_log_cmd, "set logging file %s", filename);
106
107 char *args[] = {
108 "gdb",
109 start_argv[0],
110 "-p",
111 pid_s,
112 "-batch",
113 "-nx",
114 "-ex", gdb_log_cmd,
115 "-ex", "set logging on",
116 "-ex", "bt full",
117 "-ex", "quit",
118 NULL};
119 execvp(args[0], args);
120 DLOG("Failed to exec GDB\n");
121 exit(1);
122 }
123 int status = 0;
124
125 waitpid(pid_gdb, &status, 0);
126
127 /* see if the backtrace was successful or not */
128 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
129 DLOG("GDB did not run properly\n");
130 return -1;
131 } else if (!path_exists(filename)) {
132 DLOG("GDB executed successfully, but no backtrace was generated\n");
133 return -1;
134 }
135 return 1;
136 }
137
138 static void sighandler_setup(void) {
139 border_width = logical_px(border_width);
140 margin = logical_px(margin);
141
142 int num_lines = 5;
143 message_intro = i3string_from_utf8("i3 has just crashed. Please report a bug for this.");
144 message_intro2 = i3string_from_utf8("To debug this problem, you can either attach gdb or choose from the following options:");
145 message_option_backtrace = i3string_from_utf8("- 'b' to save a backtrace (requires gdb)");
146 message_option_restart = i3string_from_utf8("- 'r' to restart i3 in-place");
147 message_option_forget = i3string_from_utf8("- 'f' to forget the previous layout and restart i3");
148
149 int width_longest_message = predict_text_width(message_intro2);
150
151 dialog_width = width_longest_message + 2 * border_width + 2 * margin;
152 dialog_height = num_lines * config.font.height + 2 * border_width + 2 * margin;
153 }
154
155 static void sighandler_create_dialogs(void) {
156 Output *output;
157 TAILQ_FOREACH(output, &outputs, outputs) {
158 if (!output->active) {
159 continue;
160 }
161
162 dialog_t *dialog = scalloc(1, sizeof(struct dialog_t));
163 TAILQ_INSERT_TAIL(&dialogs, dialog, dialogs);
164
165 xcb_visualid_t visual = get_visualid_by_depth(root_depth);
166 dialog->colormap = xcb_generate_id(conn);
167 xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, dialog->colormap, root, visual);
168
169 uint32_t mask = 0;
170 uint32_t values[4];
171 int i = 0;
172
173 /* Needs to be set in the case of a 32-bit root depth. */
174 mask |= XCB_CW_BACK_PIXEL;
175 values[i++] = root_screen->black_pixel;
176
177 /* Needs to be set in the case of a 32-bit root depth. */
178 mask |= XCB_CW_BORDER_PIXEL;
179 values[i++] = root_screen->black_pixel;
180
181 mask |= XCB_CW_OVERRIDE_REDIRECT;
182 values[i++] = 1;
183
184 /* Needs to be set in the case of a 32-bit root depth. */
185 mask |= XCB_CW_COLORMAP;
186 values[i++] = dialog->colormap;
187
188 dialog->dims.x = output->rect.x + (output->rect.width / 2);
189 dialog->dims.y = output->rect.y + (output->rect.height / 2);
190 dialog->dims.width = dialog_width;
191 dialog->dims.height = dialog_height;
192
193 /* Make sure the dialog is centered. */
194 dialog->dims.x -= dialog->dims.width / 2;
195 dialog->dims.y -= dialog->dims.height / 2;
196
197 dialog->id = create_window(conn, dialog->dims, root_depth, visual,
198 XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER,
199 true, mask, values);
200
201 draw_util_surface_init(conn, &(dialog->surface), dialog->id, get_visualtype_by_id(visual),
202 dialog->dims.width, dialog->dims.height);
203
204 xcb_grab_keyboard(conn, false, dialog->id, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
205
206 /* Confine the pointer to the crash dialog. */
207 xcb_grab_pointer(conn, false, dialog->id, XCB_NONE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, dialog->id,
208 XCB_NONE, XCB_CURRENT_TIME);
209 }
210
211 sighandler_handle_expose();
212 xcb_flush(conn);
213 }
214
215 static void sighandler_destroy_dialogs(void) {
216 while (!TAILQ_EMPTY(&dialogs)) {
217 dialog_t *dialog = TAILQ_FIRST(&dialogs);
218
219 xcb_free_colormap(conn, dialog->colormap);
220 draw_util_surface_free(conn, &(dialog->surface));
221 xcb_destroy_window(conn, dialog->id);
222
223 TAILQ_REMOVE(&dialogs, dialog, dialogs);
224 free(dialog);
225 }
226
227 xcb_flush(conn);
228 }
229
230 static void sighandler_handle_expose(void) {
231 dialog_t *current;
232 TAILQ_FOREACH(current, &dialogs, dialogs) {
233 sighandler_draw_dialog(current);
234 }
235
236 xcb_flush(conn);
237 }
238
239 static void sighandler_draw_dialog(dialog_t *dialog) {
240 const color_t black = draw_util_hex_to_color("#000000");
241 const color_t white = draw_util_hex_to_color("#FFFFFF");
242 const color_t red = draw_util_hex_to_color("#FF0000");
243
244 /* Start with a clean slate and draw a red border. */
245 draw_util_clear_surface(&(dialog->surface), red);
246 draw_util_rectangle(&(dialog->surface), black, border_width, border_width,
247 dialog->dims.width - 2 * border_width, dialog->dims.height - 2 * border_width);
248
249 int y = border_width + margin;
250 const int x = border_width + margin;
251 const int max_width = dialog->dims.width - 2 * x;
252
253 draw_util_text(message_intro, &(dialog->surface), white, black, x, y, max_width);
254 y += config.font.height;
255
256 draw_util_text(message_intro2, &(dialog->surface), white, black, x, y, max_width);
257 y += config.font.height;
258
259 char *bt_color = "#FFFFFF";
260 if (backtrace_done < 0) {
261 bt_color = "#AA0000";
262 } else if (backtrace_done > 0) {
263 bt_color = "#00AA00";
264 }
265 draw_util_text(message_option_backtrace, &(dialog->surface), draw_util_hex_to_color(bt_color), black, x, y, max_width);
266 y += config.font.height;
267
268 draw_util_text(message_option_restart, &(dialog->surface), white, black, x, y, max_width);
269 y += config.font.height;
270
271 draw_util_text(message_option_forget, &(dialog->surface), white, black, x, y, max_width);
272 y += config.font.height;
273 }
274
275 static void sighandler_handle_key_press(xcb_key_press_event_t *event) {
276 uint16_t state = event->state;
277
278 /* Apparently, after activating numlock once, the numlock modifier
279 * stays turned on (use xev(1) to verify). So, to resolve useful
280 * keysyms, we remove the numlock flag from the event state */
281 state &= ~xcb_numlock_mask;
282
283 xcb_keysym_t sym = xcb_key_press_lookup_keysym(keysyms, event, state);
284
285 if (sym == 'b') {
286 DLOG("User issued core-dump command.\n");
287
288 /* fork and exec/attach GDB to the parent to get a backtrace in the
289 * tmpdir */
290 backtrace_done = sighandler_backtrace();
291 sighandler_handle_expose();
292 } else if (sym == 'r') {
293 sighandler_destroy_dialogs();
294 i3_restart(false);
295 } else if (sym == 'f') {
296 sighandler_destroy_dialogs();
297 i3_restart(true);
298 }
299 }
300
301 static void handle_signal(int sig, siginfo_t *info, void *data) {
302 DLOG("i3 crashed. SIG: %d\n", sig);
303
304 struct sigaction action;
305 action.sa_handler = SIG_DFL;
306 action.sa_flags = 0;
307 sigemptyset(&action.sa_mask);
308 sigaction(sig, &action, NULL);
309 raised_signal = sig;
310
311 sighandler_setup();
312 sighandler_create_dialogs();
313
314 xcb_generic_event_t *event;
315 /* Yay, more own eventhandlers… */
316 while ((event = xcb_wait_for_event(conn))) {
317 /* Strip off the highest bit (set if the event is generated) */
318 int type = (event->response_type & 0x7F);
319 switch (type) {
320 case XCB_KEY_PRESS:
321 sighandler_handle_key_press((xcb_key_press_event_t *)event);
322 break;
323 case XCB_EXPOSE:
324 if (((xcb_expose_event_t *)event)->count == 0) {
325 sighandler_handle_expose();
326 }
327
328 break;
329 }
330
331 free(event);
332 }
333 }
334
335 /*
336 * Configured a signal handler to gracefully handle crashes and allow the user
337 * to generate a backtrace and rescue their session.
338 *
339 */
340 void setup_signal_handler(void) {
341 struct sigaction action;
342
343 action.sa_sigaction = handle_signal;
344 action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
345 sigemptyset(&action.sa_mask);
346
347 /* Catch all signals with default action "Core", see signal(7) */
348 if (sigaction(SIGQUIT, &action, NULL) == -1 ||
349 sigaction(SIGILL, &action, NULL) == -1 ||
350 sigaction(SIGABRT, &action, NULL) == -1 ||
351 sigaction(SIGFPE, &action, NULL) == -1 ||
352 sigaction(SIGSEGV, &action, NULL) == -1)
353 ELOG("Could not setup signal handler.\n");
354 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * startup.c: Startup notification code. Ensures a startup notification context
7 * is setup when launching applications. We store the current
8 * workspace to open windows in that startup notification context on
9 * the appropriate workspace.
10 *
11 */
12 #include "all.h"
13
14 #include "sd-daemon.h"
15
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <paths.h>
19
20 #define SN_API_NOT_YET_FROZEN 1
21 #include <libsn/sn-launcher.h>
22
23 static TAILQ_HEAD(startup_sequence_head, Startup_Sequence) startup_sequences =
24 TAILQ_HEAD_INITIALIZER(startup_sequences);
25
26 /*
27 * After 60 seconds, a timeout will be triggered for each startup sequence.
28 *
29 * The timeout will just trigger completion of the sequence, so the normal
30 * completion process takes place (startup_monitor_event will free it).
31 *
32 */
33 static void startup_timeout(EV_P_ ev_timer *w, int revents) {
34 const char *id = sn_launcher_context_get_startup_id(w->data);
35 DLOG("Timeout for startup sequence %s\n", id);
36
37 struct Startup_Sequence *current, *sequence = NULL;
38 TAILQ_FOREACH(current, &startup_sequences, sequences) {
39 if (strcmp(current->id, id) != 0)
40 continue;
41
42 sequence = current;
43 break;
44 }
45
46 /* Unref the context (for the timeout itself, see start_application) */
47 sn_launcher_context_unref(w->data);
48
49 if (!sequence) {
50 DLOG("Sequence already deleted, nevermind.\n");
51 free(w);
52 return;
53 }
54
55 /* Complete the startup sequence, will trigger its deletion. */
56 sn_launcher_context_complete(w->data);
57 free(w);
58 }
59
60 /*
61 * Some applications (such as Firefox) mark a startup sequence as completed
62 * *before* they even map a window. Therefore, we cannot entirely delete the
63 * startup sequence once it’s marked as complete. Instead, we’ll mark it for
64 * deletion in 30 seconds and use that chance to delete old sequences.
65 *
66 * This function returns the number of active (!) startup notifications, that
67 * is, those which are not marked for deletion yet. This is used for changing
68 * the root window cursor.
69 *
70 */
71 static int _prune_startup_sequences(void) {
72 time_t current_time = time(NULL);
73 int active_sequences = 0;
74
75 /* Traverse the list and delete everything which was marked for deletion 30
76 * seconds ago or earlier. */
77 struct Startup_Sequence *current, *next;
78 for (next = TAILQ_FIRST(&startup_sequences);
79 next != TAILQ_END(&startup_sequences);) {
80 current = next;
81 next = TAILQ_NEXT(next, sequences);
82
83 if (current->delete_at == 0) {
84 active_sequences++;
85 continue;
86 }
87
88 if (current_time <= current->delete_at)
89 continue;
90
91 startup_sequence_delete(current);
92 }
93
94 return active_sequences;
95 }
96
97 /*
98 * Deletes a startup sequence, ignoring whether its timeout has elapsed.
99 * Useful when e.g. a window is moved between workspaces and its children
100 * shouldn't spawn on the original workspace.
101 *
102 */
103 void startup_sequence_delete(struct Startup_Sequence *sequence) {
104 assert(sequence != NULL);
105 DLOG("Deleting startup sequence %s, delete_at = %lld, current_time = %lld\n",
106 sequence->id, (long long)sequence->delete_at, (long long)time(NULL));
107
108 /* Unref the context, will be free()d */
109 sn_launcher_context_unref(sequence->context);
110
111 /* Delete our internal sequence */
112 TAILQ_REMOVE(&startup_sequences, sequence, sequences);
113
114 free(sequence->id);
115 free(sequence->workspace);
116 FREE(sequence);
117 }
118
119 /*
120 * Starts the given application by passing it through a shell. We use double
121 * fork to avoid zombie processes. As the started application’s parent exits
122 * (immediately), the application is reparented to init (process-id 1), which
123 * correctly handles children, so we don’t have to do it :-).
124 *
125 * The shell used to start applications is the system's bourne shell (i.e.,
126 * /bin/sh).
127 *
128 * The no_startup_id flag determines whether a startup notification context
129 * (and ID) should be created, which is the default and encouraged behavior.
130 *
131 */
132 void start_application(const char *command, bool no_startup_id) {
133 SnLauncherContext *context = NULL;
134
135 if (!no_startup_id) {
136 /* Create a startup notification context to monitor the progress of this
137 * startup. */
138 context = sn_launcher_context_new(sndisplay, conn_screen);
139 sn_launcher_context_set_name(context, "i3");
140 sn_launcher_context_set_description(context, "exec command in i3");
141 /* Chop off everything starting from the first space (if there are any
142 * spaces in the command), since we don’t want the parameters. */
143 char *first_word = sstrdup(command);
144 char *space = strchr(first_word, ' ');
145 if (space)
146 *space = '\0';
147 sn_launcher_context_initiate(context, "i3", first_word, last_timestamp);
148 free(first_word);
149
150 /* Trigger a timeout after 60 seconds */
151 struct ev_timer *timeout = scalloc(1, sizeof(struct ev_timer));
152 ev_timer_init(timeout, startup_timeout, 60.0, 0.);
153 timeout->data = context;
154 ev_timer_start(main_loop, timeout);
155
156 LOG("startup id = %s\n", sn_launcher_context_get_startup_id(context));
157
158 /* Save the ID and current workspace in our internal list of startup
159 * sequences */
160 Con *ws = con_get_workspace(focused);
161 struct Startup_Sequence *sequence = scalloc(1, sizeof(struct Startup_Sequence));
162 sequence->id = sstrdup(sn_launcher_context_get_startup_id(context));
163 sequence->workspace = sstrdup(ws->name);
164 sequence->context = context;
165 TAILQ_INSERT_TAIL(&startup_sequences, sequence, sequences);
166
167 /* Increase the refcount once (it starts with 1, so it will be 2 now) for
168 * the timeout. Even if the sequence gets completed, the timeout still
169 * needs the context (but will unref it then) */
170 sn_launcher_context_ref(context);
171 }
172
173 LOG("executing: %s\n", command);
174 if (fork() == 0) {
175 /* Child process */
176 setsid();
177 setrlimit(RLIMIT_CORE, &original_rlimit_core);
178 /* Close all socket activation file descriptors explicitly, we disabled
179 * FD_CLOEXEC to keep them open when restarting i3. */
180 for (int fd = SD_LISTEN_FDS_START;
181 fd < (SD_LISTEN_FDS_START + listen_fds);
182 fd++) {
183 close(fd);
184 }
185 unsetenv("LISTEN_PID");
186 unsetenv("LISTEN_FDS");
187 signal(SIGPIPE, SIG_DFL);
188 if (fork() == 0) {
189 /* Setup the environment variable(s) */
190 if (!no_startup_id)
191 sn_launcher_context_setup_child_process(context);
192
193 execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL);
194 /* not reached */
195 }
196 _exit(0);
197 }
198 wait(0);
199
200 if (!no_startup_id) {
201 /* Change the pointer of the root window to indicate progress */
202 if (xcursor_supported)
203 xcursor_set_root_cursor(XCURSOR_CURSOR_WATCH);
204 else
205 xcb_set_root_cursor(XCURSOR_CURSOR_WATCH);
206 }
207 }
208
209 /*
210 * Called by libstartup-notification when something happens
211 *
212 */
213 void startup_monitor_event(SnMonitorEvent *event, void *userdata) {
214 SnStartupSequence *snsequence;
215
216 snsequence = sn_monitor_event_get_startup_sequence(event);
217
218 /* Get the corresponding internal startup sequence */
219 const char *id = sn_startup_sequence_get_id(snsequence);
220 struct Startup_Sequence *current, *sequence = NULL;
221 TAILQ_FOREACH(current, &startup_sequences, sequences) {
222 if (strcmp(current->id, id) != 0)
223 continue;
224
225 sequence = current;
226 break;
227 }
228
229 if (!sequence) {
230 DLOG("Got event for startup sequence that we did not initiate (ID = %s). Ignoring.\n", id);
231 return;
232 }
233
234 switch (sn_monitor_event_get_type(event)) {
235 case SN_MONITOR_EVENT_COMPLETED:
236 DLOG("startup sequence %s completed\n", sn_startup_sequence_get_id(snsequence));
237
238 /* Mark the given sequence for deletion in 30 seconds. */
239 time_t current_time = time(NULL);
240 sequence->delete_at = current_time + 30;
241 DLOG("Will delete startup sequence %s at timestamp %lld\n",
242 sequence->id, (long long)sequence->delete_at);
243
244 if (_prune_startup_sequences() == 0) {
245 DLOG("No more startup sequences running, changing root window cursor to default pointer.\n");
246 /* Change the pointer of the root window to indicate progress */
247 if (xcursor_supported)
248 xcursor_set_root_cursor(XCURSOR_CURSOR_POINTER);
249 else
250 xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
251 }
252 break;
253 default:
254 /* ignore */
255 break;
256 }
257 }
258
259 /*
260 * Renames workspaces that are mentioned in the startup sequences.
261 *
262 */
263 void startup_sequence_rename_workspace(const char *old_name, const char *new_name) {
264 struct Startup_Sequence *current;
265 TAILQ_FOREACH(current, &startup_sequences, sequences) {
266 if (strcmp(current->workspace, old_name) != 0)
267 continue;
268 DLOG("Renaming workspace \"%s\" to \"%s\" in startup sequence %s.\n",
269 old_name, new_name, current->id);
270 free(current->workspace);
271 current->workspace = sstrdup(new_name);
272 }
273 }
274
275 /*
276 * Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
277 *
278 */
279 struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
280 xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader) {
281 /* The _NET_STARTUP_ID is only needed during this function, so we get it
282 * here and don’t save it in the 'cwindow'. */
283 if (startup_id_reply == NULL || xcb_get_property_value_length(startup_id_reply) == 0) {
284 FREE(startup_id_reply);
285 DLOG("No _NET_STARTUP_ID set on window 0x%08x\n", cwindow->id);
286 if (cwindow->leader == XCB_NONE)
287 return NULL;
288
289 /* This is a special case that causes the leader's startup sequence
290 * to only be returned if it has never been mapped, useful primarily
291 * when trying to delete a sequence.
292 *
293 * It's generally inappropriate to delete a leader's sequence when
294 * moving a child window, but if the leader has no container, it's
295 * likely permanently unmapped and the child is the "real" window. */
296 if (ignore_mapped_leader && con_by_window_id(cwindow->leader) != NULL) {
297 DLOG("Ignoring leader window 0x%08x\n", cwindow->leader);
298 return NULL;
299 }
300
301 DLOG("Checking leader window 0x%08x\n", cwindow->leader);
302
303 xcb_get_property_cookie_t cookie;
304
305 cookie = xcb_get_property(conn, false, cwindow->leader,
306 A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
307 startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
308
309 if (startup_id_reply == NULL ||
310 xcb_get_property_value_length(startup_id_reply) == 0) {
311 FREE(startup_id_reply);
312 DLOG("No _NET_STARTUP_ID set on the leader either\n");
313 return NULL;
314 }
315 }
316
317 char *startup_id;
318 sasprintf(&startup_id, "%.*s", xcb_get_property_value_length(startup_id_reply),
319 (char *)xcb_get_property_value(startup_id_reply));
320 struct Startup_Sequence *current, *sequence = NULL;
321 TAILQ_FOREACH(current, &startup_sequences, sequences) {
322 if (strcmp(current->id, startup_id) != 0)
323 continue;
324
325 sequence = current;
326 break;
327 }
328
329 if (!sequence) {
330 DLOG("WARNING: This sequence (ID %s) was not found\n", startup_id);
331 free(startup_id);
332 free(startup_id_reply);
333 return NULL;
334 }
335
336 free(startup_id);
337 free(startup_id_reply);
338
339 return sequence;
340 }
341
342 /*
343 * Checks if the given window belongs to a startup notification by checking if
344 * the _NET_STARTUP_ID property is set on the window (or on its leader, if it’s
345 * unset).
346 *
347 * If so, returns the workspace on which the startup was initiated.
348 * Returns NULL otherwise.
349 *
350 */
351 char *startup_workspace_for_window(i3Window *cwindow, xcb_get_property_reply_t *startup_id_reply) {
352 struct Startup_Sequence *sequence = startup_sequence_get(cwindow, startup_id_reply, false);
353 if (sequence == NULL)
354 return NULL;
355
356 /* If the startup sequence's time span has elapsed, delete it. */
357 time_t current_time = time(NULL);
358 if (sequence->delete_at > 0 && current_time > sequence->delete_at) {
359 DLOG("Deleting expired startup sequence %s\n", sequence->id);
360 startup_sequence_delete(sequence);
361 return NULL;
362 }
363
364 return sequence->workspace;
365 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * sync.c: i3 sync protocol: https://i3wm.org/docs/testsuite.html#i3_sync
7 *
8 */
9 #include "all.h"
10
11 void sync_respond(xcb_window_t window, uint32_t rnd) {
12 DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window);
13
14 void *reply = scalloc(32, 1);
15 xcb_client_message_event_t *ev = reply;
16
17 ev->response_type = XCB_CLIENT_MESSAGE;
18 ev->window = window;
19 ev->type = A_I3_SYNC;
20 ev->format = 32;
21 ev->data.data32[0] = window;
22 ev->data.data32[1] = rnd;
23
24 xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev);
25 xcb_flush(conn);
26 free(reply);
27 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * tree.c: Everything that primarily modifies the layout tree data structure.
7 *
8 */
9 #include "all.h"
10
11 struct Con *croot;
12 struct Con *focused;
13
14 struct all_cons_head all_cons = TAILQ_HEAD_INITIALIZER(all_cons);
15
16 /*
17 * Create the pseudo-output __i3. Output-independent workspaces such as
18 * __i3_scratch will live there.
19 *
20 */
21 static Con *_create___i3(void) {
22 Con *__i3 = con_new(croot, NULL);
23 FREE(__i3->name);
24 __i3->name = sstrdup("__i3");
25 __i3->type = CT_OUTPUT;
26 __i3->layout = L_OUTPUT;
27 con_fix_percent(croot);
28 x_set_name(__i3, "[i3 con] pseudo-output __i3");
29 /* For retaining the correct position/size of a scratchpad window, the
30 * dimensions of the real outputs should be multiples of the __i3
31 * pseudo-output. Ensuring that is the job of scratchpad_fix_resolution()
32 * which gets called after this function and after detecting all the
33 * outputs (or whenever an output changes). */
34 __i3->rect.width = 1280;
35 __i3->rect.height = 1024;
36
37 /* Add a content container. */
38 DLOG("adding main content container\n");
39 Con *content = con_new(NULL, NULL);
40 content->type = CT_CON;
41 FREE(content->name);
42 content->name = sstrdup("content");
43 content->layout = L_SPLITH;
44
45 x_set_name(content, "[i3 con] content __i3");
46 con_attach(content, __i3, false);
47
48 /* Attach the __i3_scratch workspace. */
49 Con *ws = con_new(NULL, NULL);
50 ws->type = CT_WORKSPACE;
51 ws->num = -1;
52 ws->name = sstrdup("__i3_scratch");
53 ws->layout = L_SPLITH;
54 con_attach(ws, content, false);
55 x_set_name(ws, "[i3 con] workspace __i3_scratch");
56 ws->fullscreen_mode = CF_OUTPUT;
57
58 return __i3;
59 }
60
61 /*
62 * Loads tree from 'path' (used for in-place restarts).
63 *
64 */
65 bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
66 bool result = false;
67 char *globbed = resolve_tilde(path);
68 char *buf = NULL;
69
70 if (!path_exists(globbed)) {
71 LOG("%s does not exist, not restoring tree\n", globbed);
72 goto out;
73 }
74
75 ssize_t len;
76 if ((len = slurp(globbed, &buf)) < 0) {
77 /* slurp already logged an error. */
78 goto out;
79 }
80
81 /* TODO: refactor the following */
82 croot = con_new(NULL, NULL);
83 croot->rect = (Rect){
84 geometry->x,
85 geometry->y,
86 geometry->width,
87 geometry->height};
88 focused = croot;
89
90 tree_append_json(focused, buf, len, NULL);
91
92 DLOG("appended tree, using new root\n");
93 croot = TAILQ_FIRST(&(croot->nodes_head));
94 if (!croot) {
95 /* tree_append_json failed. Continuing here would segfault. */
96 goto out;
97 }
98 DLOG("new root = %p\n", croot);
99 Con *out = TAILQ_FIRST(&(croot->nodes_head));
100 DLOG("out = %p\n", out);
101 Con *ws = TAILQ_FIRST(&(out->nodes_head));
102 DLOG("ws = %p\n", ws);
103
104 /* For in-place restarting into v4.2, we need to make sure the new
105 * pseudo-output __i3 is present. */
106 if (strcmp(out->name, "__i3") != 0) {
107 DLOG("Adding pseudo-output __i3 during inplace restart\n");
108 Con *__i3 = _create___i3();
109 /* Ensure that it is the first output, other places in the code make
110 * that assumption. */
111 TAILQ_REMOVE(&(croot->nodes_head), __i3, nodes);
112 TAILQ_INSERT_HEAD(&(croot->nodes_head), __i3, nodes);
113 }
114
115 restore_open_placeholder_windows(croot);
116 result = true;
117
118 out:
119 free(globbed);
120 free(buf);
121 return result;
122 }
123
124 /*
125 * Initializes the tree by creating the root node. The CT_OUTPUT Cons below the
126 * root node are created in randr.c for each Output.
127 *
128 */
129 void tree_init(xcb_get_geometry_reply_t *geometry) {
130 croot = con_new(NULL, NULL);
131 FREE(croot->name);
132 croot->name = "root";
133 croot->type = CT_ROOT;
134 croot->layout = L_SPLITH;
135 croot->rect = (Rect){
136 geometry->x,
137 geometry->y,
138 geometry->width,
139 geometry->height};
140
141 _create___i3();
142 }
143
144 /*
145 * Opens an empty container in the current container
146 *
147 */
148 Con *tree_open_con(Con *con, i3Window *window) {
149 if (con == NULL) {
150 /* every focusable Con has a parent (outputs have parent root) */
151 con = focused->parent;
152 /* If the parent is an output, we are on a workspace. In this case,
153 * the new container needs to be opened as a leaf of the workspace. */
154 if (con->parent->type == CT_OUTPUT && con->type != CT_DOCKAREA) {
155 con = focused;
156 }
157
158 /* If the currently focused container is a floating container, we
159 * attach the new container to the currently focused spot in its
160 * workspace. */
161 if (con->type == CT_FLOATING_CON) {
162 con = con_descend_tiling_focused(con->parent);
163 if (con->type != CT_WORKSPACE)
164 con = con->parent;
165 }
166 DLOG("con = %p\n", con);
167 }
168
169 assert(con != NULL);
170
171 /* 3. create the container and attach it to its parent */
172 Con *new = con_new(con, window);
173 new->layout = L_SPLITH;
174
175 /* 4: re-calculate child->percent for each child */
176 con_fix_percent(con);
177
178 return new;
179 }
180
181 /*
182 * Closes the given container including all children.
183 * Returns true if the container was killed or false if just WM_DELETE was sent
184 * and the window is expected to kill itself.
185 *
186 * The dont_kill_parent flag is specified when the function calls itself
187 * recursively while deleting a containers children.
188 *
189 */
190 bool tree_close_internal(Con *con, kill_window_t kill_window, bool dont_kill_parent) {
191 Con *parent = con->parent;
192
193 /* remove the urgency hint of the workspace (if set) */
194 if (con->urgent) {
195 con_set_urgency(con, false);
196 con_update_parents_urgency(con);
197 workspace_update_urgent_flag(con_get_workspace(con));
198 }
199
200 DLOG("closing %p, kill_window = %d\n", con, kill_window);
201 Con *child, *nextchild;
202 bool abort_kill = false;
203 /* We cannot use TAILQ_FOREACH because the children get deleted
204 * in their parent’s nodes_head */
205 for (child = TAILQ_FIRST(&(con->nodes_head)); child;) {
206 nextchild = TAILQ_NEXT(child, nodes);
207 DLOG("killing child=%p\n", child);
208 if (!tree_close_internal(child, kill_window, true)) {
209 abort_kill = true;
210 }
211 child = nextchild;
212 }
213
214 if (abort_kill) {
215 DLOG("One of the children could not be killed immediately (WM_DELETE sent), aborting.\n");
216 return false;
217 }
218
219 if (con->window != NULL) {
220 if (kill_window != DONT_KILL_WINDOW) {
221 x_window_kill(con->window->id, kill_window);
222 return false;
223 } else {
224 xcb_void_cookie_t cookie;
225 /* Ignore any further events by clearing the event mask,
226 * unmap the window,
227 * then reparent it to the root window. */
228 xcb_change_window_attributes(conn, con->window->id,
229 XCB_CW_EVENT_MASK, (uint32_t[]){XCB_NONE});
230 xcb_unmap_window(conn, con->window->id);
231 cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0);
232
233 /* Ignore X11 errors for the ReparentWindow request.
234 * X11 Errors are returned when the window was already destroyed */
235 add_ignore_event(cookie.sequence, 0);
236
237 /* We are no longer handling this window, thus set WM_STATE to
238 * WM_STATE_WITHDRAWN (see ICCCM 4.1.3.1) */
239 long data[] = {XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE};
240 cookie = xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
241 con->window->id, A_WM_STATE, A_WM_STATE, 32, 2, data);
242
243 /* Remove the window from the save set. All windows in the save set
244 * will be mapped when i3 closes its connection (e.g. when
245 * restarting). This is not what we want, since some apps keep
246 * unmapped windows around and don’t expect them to suddenly be
247 * mapped. See https://bugs.i3wm.org/1617 */
248 xcb_change_save_set(conn, XCB_SET_MODE_DELETE, con->window->id);
249
250 /* Ignore X11 errors for the ReparentWindow request.
251 * X11 Errors are returned when the window was already destroyed */
252 add_ignore_event(cookie.sequence, 0);
253 }
254 ipc_send_window_event("close", con);
255 window_free(con->window);
256 con->window = NULL;
257 }
258
259 Con *ws = con_get_workspace(con);
260
261 /* Figure out which container to focus next before detaching 'con'. */
262 Con *next = (con == focused) ? con_next_focused(con) : NULL;
263 DLOG("next = %p, focused = %p\n", next, focused);
264
265 /* Detach the container so that it will not be rendered anymore. */
266 con_detach(con);
267
268 /* disable urgency timer, if needed */
269 if (con->urgency_timer != NULL) {
270 DLOG("Removing urgency timer of con %p\n", con);
271 workspace_update_urgent_flag(ws);
272 ev_timer_stop(main_loop, con->urgency_timer);
273 FREE(con->urgency_timer);
274 }
275
276 if (con->type != CT_FLOATING_CON) {
277 /* If the container is *not* floating, we might need to re-distribute
278 * percentage values for the resized containers. */
279 con_fix_percent(parent);
280 }
281
282 /* Render the tree so that the surrounding containers take up the space
283 * which 'con' does no longer occupy. If we don’t render here, there will
284 * be a gap in our containers and that could trigger an EnterNotify for an
285 * underlying container, see ticket #660.
286 *
287 * Rendering has to be avoided when dont_kill_parent is set (when
288 * tree_close_internal calls itself recursively) because the tree is in a
289 * non-renderable state during that time. */
290 if (!dont_kill_parent)
291 tree_render();
292
293 /* kill the X11 part of this container */
294 x_con_kill(con);
295
296 if (ws == con) {
297 DLOG("Closing a workspace container, updating EWMH atoms\n");
298 ewmh_update_number_of_desktops();
299 ewmh_update_desktop_names();
300 ewmh_update_wm_desktop();
301 }
302
303 con_free(con);
304
305 if (next) {
306 con_activate(next);
307 } else {
308 DLOG("not changing focus, the container was not focused before\n");
309 }
310
311 /* check if the parent container is empty now and close it */
312 if (!dont_kill_parent)
313 CALL(parent, on_remove_child);
314
315 return true;
316 }
317
318 /*
319 * Splits (horizontally or vertically) the given container by creating a new
320 * container which contains the old one and the future ones.
321 *
322 */
323 void tree_split(Con *con, orientation_t orientation) {
324 if (con_is_floating(con)) {
325 DLOG("Floating containers can't be split.\n");
326 return;
327 }
328
329 if (con->type == CT_WORKSPACE) {
330 if (con_num_children(con) < 2) {
331 if (con_num_children(con) == 0) {
332 DLOG("Changing workspace_layout to L_DEFAULT\n");
333 con->workspace_layout = L_DEFAULT;
334 }
335 DLOG("Changing orientation of workspace\n");
336 con->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
337 return;
338 } else {
339 /* if there is more than one container on the workspace
340 * move them into a new container and handle this instead */
341 con = workspace_encapsulate(con);
342 }
343 }
344
345 Con *parent = con->parent;
346
347 /* Force re-rendering to make the indicator border visible. */
348 con_force_split_parents_redraw(con);
349
350 /* if we are in a container whose parent contains only one
351 * child (its split functionality is unused so far), we just change the
352 * orientation (more intuitive than splitting again) */
353 if (con_num_children(parent) == 1 &&
354 (parent->layout == L_SPLITH ||
355 parent->layout == L_SPLITV)) {
356 parent->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
357 DLOG("Just changing orientation of existing container\n");
358 return;
359 }
360
361 DLOG("Splitting in orientation %d\n", orientation);
362
363 /* 2: replace it with a new Con */
364 Con *new = con_new(NULL, NULL);
365 TAILQ_REPLACE(&(parent->nodes_head), con, new, nodes);
366 TAILQ_REPLACE(&(parent->focus_head), con, new, focused);
367 new->parent = parent;
368 new->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
369
370 /* 3: swap 'percent' (resize factor) */
371 new->percent = con->percent;
372 con->percent = 0.0;
373
374 /* 4: add it as a child to the new Con */
375 con_attach(con, new, false);
376 }
377
378 /*
379 * Moves focus one level up. Returns true if focus changed.
380 *
381 */
382 bool level_up(void) {
383 /* Skip over floating containers and go directly to the grandparent
384 * (which should always be a workspace) */
385 if (focused->parent->type == CT_FLOATING_CON) {
386 con_activate(focused->parent->parent);
387 return true;
388 }
389
390 /* We can focus up to the workspace, but not any higher in the tree */
391 if ((focused->parent->type != CT_CON &&
392 focused->parent->type != CT_WORKSPACE) ||
393 focused->type == CT_WORKSPACE) {
394 ELOG("'focus parent': Focus is already on the workspace, cannot go higher than that.\n");
395 return false;
396 }
397 con_activate(focused->parent);
398 return true;
399 }
400
401 /*
402 * Moves focus one level down. Returns true if focus changed.
403 *
404 */
405 bool level_down(void) {
406 /* Go down the focus stack of the current node */
407 Con *next = TAILQ_FIRST(&(focused->focus_head));
408 if (next == TAILQ_END(&(focused->focus_head))) {
409 DLOG("cannot go down\n");
410 return false;
411 } else if (next->type == CT_FLOATING_CON) {
412 /* Floating cons shouldn't be directly focused; try immediately
413 * going to the grandchild of the focused con. */
414 Con *child = TAILQ_FIRST(&(next->focus_head));
415 if (child == TAILQ_END(&(next->focus_head))) {
416 DLOG("cannot go down\n");
417 return false;
418 } else
419 next = TAILQ_FIRST(&(next->focus_head));
420 }
421
422 con_activate(next);
423 return true;
424 }
425
426 static void mark_unmapped(Con *con) {
427 Con *current;
428
429 con->mapped = false;
430 TAILQ_FOREACH(current, &(con->nodes_head), nodes)
431 mark_unmapped(current);
432 if (con->type == CT_WORKSPACE) {
433 /* We need to call mark_unmapped on floating nodes as well since we can
434 * make containers floating. */
435 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
436 mark_unmapped(current);
437 }
438 }
439
440 /*
441 * Renders the tree, that is rendering all outputs using render_con() and
442 * pushing the changes to X11 using x_push_changes().
443 *
444 */
445 void tree_render(void) {
446 if (croot == NULL)
447 return;
448
449 DLOG("-- BEGIN RENDERING --\n");
450 /* Reset map state for all nodes in tree */
451 /* TODO: a nicer method to walk all nodes would be good, maybe? */
452 mark_unmapped(croot);
453 croot->mapped = true;
454
455 render_con(croot, false);
456
457 x_push_changes(croot);
458 DLOG("-- END RENDERING --\n");
459 }
460
461 /*
462 * Recursive function to walk the tree until a con can be found to focus.
463 *
464 */
465 static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) {
466 /* When dealing with fullscreen containers, it's necessary to go up to the
467 * workspace level, because 'focus $dir' will start at the con's real
468 * position in the tree, and it may not be possible to get to the edge
469 * normally due to fullscreen focusing restrictions. */
470 if (con->fullscreen_mode == CF_OUTPUT && con->type != CT_WORKSPACE)
471 con = con_get_workspace(con);
472
473 /* Stop recursing at workspaces after attempting to switch to next
474 * workspace if possible. */
475 if (con->type == CT_WORKSPACE) {
476 if (con_get_fullscreen_con(con, CF_GLOBAL)) {
477 DLOG("Cannot change workspace while in global fullscreen mode.\n");
478 return false;
479 }
480 Output *current_output = get_output_containing(con->rect.x, con->rect.y);
481 Output *next_output;
482
483 if (!current_output)
484 return false;
485 DLOG("Current output is %s\n", output_primary_name(current_output));
486
487 /* Try to find next output */
488 direction_t direction;
489 if (way == 'n' && orientation == HORIZ)
490 direction = D_RIGHT;
491 else if (way == 'p' && orientation == HORIZ)
492 direction = D_LEFT;
493 else if (way == 'n' && orientation == VERT)
494 direction = D_DOWN;
495 else if (way == 'p' && orientation == VERT)
496 direction = D_UP;
497 else
498 return false;
499
500 next_output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
501 if (!next_output)
502 return false;
503 DLOG("Next output is %s\n", output_primary_name(next_output));
504
505 /* Find visible workspace on next output */
506 Con *workspace = NULL;
507 GREP_FIRST(workspace, output_get_content(next_output->con), workspace_is_visible(child));
508
509 /* Show next workspace and focus appropriate container if possible. */
510 if (!workspace)
511 return false;
512
513 /* Use descend_focused first to give higher priority to floating or
514 * tiling fullscreen containers. */
515 Con *focus = con_descend_focused(workspace);
516 if (focus->fullscreen_mode == CF_NONE) {
517 Con *focus_tiling = con_descend_tiling_focused(workspace);
518 /* If descend_tiling returned a workspace then focus is either a
519 * floating container or the same workspace. */
520 if (focus_tiling != workspace) {
521 focus = focus_tiling;
522 }
523 }
524
525 workspace_show(workspace);
526 con_activate(focus);
527 x_set_warp_to(&(focus->rect));
528 return true;
529 }
530
531 Con *parent = con->parent;
532
533 if (con->type == CT_FLOATING_CON) {
534 if (orientation != HORIZ)
535 return false;
536
537 /* left/right focuses the previous/next floating container */
538 Con *next;
539 if (way == 'n')
540 next = TAILQ_NEXT(con, floating_windows);
541 else
542 next = TAILQ_PREV(con, floating_head, floating_windows);
543
544 /* If there is no next/previous container, wrap */
545 if (!next) {
546 if (way == 'n')
547 next = TAILQ_FIRST(&(parent->floating_head));
548 else
549 next = TAILQ_LAST(&(parent->floating_head), floating_head);
550 }
551
552 /* Still no next/previous container? bail out */
553 if (!next)
554 return false;
555
556 /* Raise the floating window on top of other windows preserving
557 * relative stack order */
558 while (TAILQ_LAST(&(parent->floating_head), floating_head) != next) {
559 Con *last = TAILQ_LAST(&(parent->floating_head), floating_head);
560 TAILQ_REMOVE(&(parent->floating_head), last, floating_windows);
561 TAILQ_INSERT_HEAD(&(parent->floating_head), last, floating_windows);
562 }
563
564 con_activate(con_descend_focused(next));
565 return true;
566 }
567
568 /* If the orientation does not match or there is no other con to focus, we
569 * need to go higher in the hierarchy */
570 if (con_orientation(parent) != orientation ||
571 con_num_children(parent) == 1)
572 return _tree_next(parent, way, orientation, wrap);
573
574 Con *current = TAILQ_FIRST(&(parent->focus_head));
575 /* TODO: when can the following happen (except for floating windows, which
576 * are handled above)? */
577 if (TAILQ_EMPTY(&(parent->nodes_head))) {
578 DLOG("nothing to focus\n");
579 return false;
580 }
581
582 Con *next;
583 if (way == 'n')
584 next = TAILQ_NEXT(current, nodes);
585 else
586 next = TAILQ_PREV(current, nodes_head, nodes);
587
588 if (!next) {
589 if (config.focus_wrapping != FOCUS_WRAPPING_FORCE) {
590 /* If there is no next/previous container, we check if we can focus one
591 * when going higher (without wrapping, though). If so, we are done, if
592 * not, we wrap */
593 if (_tree_next(parent, way, orientation, false))
594 return true;
595
596 if (!wrap)
597 return false;
598 }
599
600 if (way == 'n')
601 next = TAILQ_FIRST(&(parent->nodes_head));
602 else
603 next = TAILQ_LAST(&(parent->nodes_head), nodes_head);
604 }
605
606 /* Don't violate fullscreen focus restrictions. */
607 if (!con_fullscreen_permits_focusing(next))
608 return false;
609
610 /* 3: focus choice comes in here. at the moment we will go down
611 * until we find a window */
612 /* TODO: check for window, atm we only go down as far as possible */
613 con_activate(con_descend_focused(next));
614 return true;
615 }
616
617 /*
618 * Changes focus in the given way (next/previous) and given orientation
619 * (horizontal/vertical).
620 *
621 */
622 void tree_next(char way, orientation_t orientation) {
623 _tree_next(focused, way, orientation,
624 config.focus_wrapping != FOCUS_WRAPPING_OFF);
625 }
626
627 /*
628 * tree_flatten() removes pairs of redundant split containers, e.g.:
629 * [workspace, horizontal]
630 * [v-split] [child3]
631 * [h-split]
632 * [child1] [child2]
633 * In this example, the v-split and h-split container are redundant.
634 * Such a situation can be created by moving containers in a direction which is
635 * not the orientation of their parent container. i3 needs to create a new
636 * split container then and if you move containers this way multiple times,
637 * redundant chains of split-containers can be the result.
638 *
639 */
640 void tree_flatten(Con *con) {
641 Con *current, *child, *parent = con->parent;
642 DLOG("Checking if I can flatten con = %p / %s\n", con, con->name);
643
644 /* We only consider normal containers without windows */
645 if (con->type != CT_CON ||
646 parent->layout == L_OUTPUT || /* con == "content" */
647 con->window != NULL)
648 goto recurse;
649
650 /* Ensure it got only one child */
651 child = TAILQ_FIRST(&(con->nodes_head));
652 if (child == NULL || TAILQ_NEXT(child, nodes) != NULL)
653 goto recurse;
654
655 DLOG("child = %p, con = %p, parent = %p\n", child, con, parent);
656
657 /* The child must have a different orientation than the con but the same as
658 * the con’s parent to be redundant */
659 if (!con_is_split(con) ||
660 !con_is_split(child) ||
661 (con->layout != L_SPLITH && con->layout != L_SPLITV) ||
662 (child->layout != L_SPLITH && child->layout != L_SPLITV) ||
663 con_orientation(con) == con_orientation(child) ||
664 con_orientation(child) != con_orientation(parent))
665 goto recurse;
666
667 DLOG("Alright, I have to flatten this situation now. Stay calm.\n");
668 /* 1: save focus */
669 Con *focus_next = TAILQ_FIRST(&(child->focus_head));
670
671 DLOG("detaching...\n");
672 /* 2: re-attach the children to the parent before con */
673 while (!TAILQ_EMPTY(&(child->nodes_head))) {
674 current = TAILQ_FIRST(&(child->nodes_head));
675 DLOG("detaching current=%p / %s\n", current, current->name);
676 con_detach(current);
677 DLOG("re-attaching\n");
678 /* We don’t use con_attach() here because for a CT_CON, the special
679 * case handling of con_attach() does not trigger. So all it would do
680 * is calling TAILQ_INSERT_AFTER, but with the wrong container. So we
681 * directly use the TAILQ macros. */
682 current->parent = parent;
683 TAILQ_INSERT_BEFORE(con, current, nodes);
684 DLOG("attaching to focus list\n");
685 TAILQ_INSERT_TAIL(&(parent->focus_head), current, focused);
686 current->percent = con->percent;
687 }
688 DLOG("re-attached all\n");
689
690 /* 3: restore focus, if con was focused */
691 if (focus_next != NULL &&
692 TAILQ_FIRST(&(parent->focus_head)) == con) {
693 DLOG("restoring focus to focus_next=%p\n", focus_next);
694 TAILQ_REMOVE(&(parent->focus_head), focus_next, focused);
695 TAILQ_INSERT_HEAD(&(parent->focus_head), focus_next, focused);
696 DLOG("restored focus.\n");
697 }
698
699 /* 4: close the redundant cons */
700 DLOG("closing redundant cons\n");
701 tree_close_internal(con, DONT_KILL_WINDOW, true);
702
703 /* Well, we got to abort the recursion here because we destroyed the
704 * container. However, if tree_flatten() is called sufficiently often,
705 * there can’t be the situation of having two pairs of redundant containers
706 * at once. Therefore, we can safely abort the recursion on this level
707 * after flattening. */
708 return;
709
710 recurse:
711 /* We cannot use normal foreach here because tree_flatten might close the
712 * current container. */
713 current = TAILQ_FIRST(&(con->nodes_head));
714 while (current != NULL) {
715 Con *next = TAILQ_NEXT(current, nodes);
716 tree_flatten(current);
717 current = next;
718 }
719
720 current = TAILQ_FIRST(&(con->floating_head));
721 while (current != NULL) {
722 Con *next = TAILQ_NEXT(current, floating_windows);
723 tree_flatten(current);
724 current = next;
725 }
726 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * util.c: Utility functions, which can be useful everywhere within i3 (see
7 * also libi3).
8 *
9 */
10 #include "all.h"
11
12 #include <sys/wait.h>
13 #include <stdarg.h>
14 #if defined(__OpenBSD__)
15 #include <sys/cdefs.h>
16 #endif
17 #include <fcntl.h>
18 #include <pwd.h>
19 #include <yajl/yajl_version.h>
20 #include <libgen.h>
21 #include <ctype.h>
22
23 #define SN_API_NOT_YET_FROZEN 1
24 #include <libsn/sn-launcher.h>
25
26 int min(int a, int b) {
27 return (a < b ? a : b);
28 }
29
30 int max(int a, int b) {
31 return (a > b ? a : b);
32 }
33
34 bool rect_contains(Rect rect, uint32_t x, uint32_t y) {
35 return (x >= rect.x &&
36 x <= (rect.x + rect.width) &&
37 y >= rect.y &&
38 y <= (rect.y + rect.height));
39 }
40
41 Rect rect_add(Rect a, Rect b) {
42 return (Rect){a.x + b.x,
43 a.y + b.y,
44 a.width + b.width,
45 a.height + b.height};
46 }
47
48 Rect rect_sub(Rect a, Rect b) {
49 return (Rect){a.x - b.x,
50 a.y - b.y,
51 a.width - b.width,
52 a.height - b.height};
53 }
54
55 /*
56 * Returns true if the name consists of only digits.
57 *
58 */
59 __attribute__((pure)) bool name_is_digits(const char *name) {
60 /* positive integers and zero are interpreted as numbers */
61 for (size_t i = 0; i < strlen(name); i++)
62 if (!isdigit(name[i]))
63 return false;
64
65 return true;
66 }
67
68 /*
69 * Set 'out' to the layout_t value for the given layout. The function
70 * returns true on success or false if the passed string is not a valid
71 * layout name.
72 *
73 */
74 bool layout_from_name(const char *layout_str, layout_t *out) {
75 if (strcmp(layout_str, "default") == 0) {
76 *out = L_DEFAULT;
77 return true;
78 } else if (strcasecmp(layout_str, "stacked") == 0 ||
79 strcasecmp(layout_str, "stacking") == 0) {
80 *out = L_STACKED;
81 return true;
82 } else if (strcasecmp(layout_str, "tabbed") == 0) {
83 *out = L_TABBED;
84 return true;
85 } else if (strcasecmp(layout_str, "splitv") == 0) {
86 *out = L_SPLITV;
87 return true;
88 } else if (strcasecmp(layout_str, "splith") == 0) {
89 *out = L_SPLITH;
90 return true;
91 }
92
93 return false;
94 }
95
96 /*
97 * Parses the workspace name as a number. Returns -1 if the workspace should be
98 * interpreted as a "named workspace".
99 *
100 */
101 long ws_name_to_number(const char *name) {
102 /* positive integers and zero are interpreted as numbers */
103 char *endptr = NULL;
104 long parsed_num = strtol(name, &endptr, 10);
105 if (parsed_num == LONG_MIN ||
106 parsed_num == LONG_MAX ||
107 parsed_num < 0 ||
108 endptr == name) {
109 parsed_num = -1;
110 }
111
112 return parsed_num;
113 }
114
115 /*
116 * Updates *destination with new_value and returns true if it was changed or false
117 * if it was the same
118 *
119 */
120 bool update_if_necessary(uint32_t *destination, const uint32_t new_value) {
121 uint32_t old_value = *destination;
122
123 return ((*destination = new_value) != old_value);
124 }
125
126 /*
127 * exec()s an i3 utility, for example the config file migration script or
128 * i3-nagbar. This function first searches $PATH for the given utility named,
129 * then falls back to the dirname() of the i3 executable path and then falls
130 * back to the dirname() of the target of /proc/self/exe (on linux).
131 *
132 * This function should be called after fork()ing.
133 *
134 * The first argument of the given argv vector will be overwritten with the
135 * executable name, so pass NULL.
136 *
137 * If the utility cannot be found in any of these locations, it exits with
138 * return code 2.
139 *
140 */
141 void exec_i3_utility(char *name, char *argv[]) {
142 /* start the migration script, search PATH first */
143 char *migratepath = name;
144 argv[0] = migratepath;
145 execvp(migratepath, argv);
146
147 /* if the script is not in path, maybe the user installed to a strange
148 * location and runs the i3 binary with an absolute path. We use
149 * argv[0]’s dirname */
150 char *pathbuf = sstrdup(start_argv[0]);
151 char *dir = dirname(pathbuf);
152 sasprintf(&migratepath, "%s/%s", dir, name);
153 argv[0] = migratepath;
154 execvp(migratepath, argv);
155
156 #if defined(__linux__)
157 /* on linux, we have one more fall-back: dirname(/proc/self/exe) */
158 char buffer[BUFSIZ];
159 if (readlink("/proc/self/exe", buffer, BUFSIZ) == -1) {
160 warn("could not read /proc/self/exe");
161 _exit(1);
162 }
163 dir = dirname(buffer);
164 sasprintf(&migratepath, "%s/%s", dir, name);
165 argv[0] = migratepath;
166 execvp(migratepath, argv);
167 #endif
168
169 warn("Could not start %s", name);
170 _exit(2);
171 }
172
173 /*
174 * Checks if the given path exists by calling stat().
175 *
176 */
177 bool path_exists(const char *path) {
178 struct stat buf;
179 return (stat(path, &buf) == 0);
180 }
181
182 /*
183 * Goes through the list of arguments (for exec()) and add/replace the given option,
184 * including the option name, its argument, and the option character.
185 */
186 static char **add_argument(char **original, char *opt_char, char *opt_arg, char *opt_name) {
187 int num_args;
188 for (num_args = 0; original[num_args] != NULL; num_args++)
189 ;
190 char **result = scalloc(num_args + 3, sizeof(char *));
191
192 /* copy the arguments, but skip the ones we'll replace */
193 int write_index = 0;
194 bool skip_next = false;
195 for (int i = 0; i < num_args; ++i) {
196 if (skip_next) {
197 skip_next = false;
198 continue;
199 }
200 if (!strcmp(original[i], opt_char) ||
201 (opt_name && !strcmp(original[i], opt_name))) {
202 if (opt_arg)
203 skip_next = true;
204 continue;
205 }
206 result[write_index++] = original[i];
207 }
208
209 /* add the arguments we'll replace */
210 result[write_index++] = opt_char;
211 result[write_index] = opt_arg;
212
213 return result;
214 }
215
216 #define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
217 #define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
218
219 static char *store_restart_layout(void) {
220 setlocale(LC_NUMERIC, "C");
221 yajl_gen gen = yajl_gen_alloc(NULL);
222
223 dump_node(gen, croot, true);
224
225 setlocale(LC_NUMERIC, "");
226
227 const unsigned char *payload;
228 size_t length;
229 y(get_buf, &payload, &length);
230
231 /* create a temporary file if one hasn't been specified, or just
232 * resolve the tildes in the specified path */
233 char *filename;
234 if (config.restart_state_path == NULL) {
235 filename = get_process_filename("restart-state");
236 if (!filename)
237 return NULL;
238 } else {
239 filename = resolve_tilde(config.restart_state_path);
240 }
241
242 /* create the directory, it could have been cleaned up before restarting or
243 * may not exist at all in case it was user-specified. */
244 char *filenamecopy = sstrdup(filename);
245 char *base = dirname(filenamecopy);
246 DLOG("Creating \"%s\" for storing the restart layout\n", base);
247 if (mkdirp(base, DEFAULT_DIR_MODE) != 0)
248 ELOG("Could not create \"%s\" for storing the restart layout, layout will be lost.\n", base);
249 free(filenamecopy);
250
251 int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
252 if (fd == -1) {
253 perror("open()");
254 free(filename);
255 return NULL;
256 }
257
258 if (writeall(fd, payload, length) == -1) {
259 ELOG("Could not write restart layout to \"%s\", layout will be lost: %s\n", filename, strerror(errno));
260 free(filename);
261 close(fd);
262 return NULL;
263 }
264
265 close(fd);
266
267 if (length > 0) {
268 DLOG("layout: %.*s\n", (int)length, payload);
269 }
270
271 y(free);
272
273 return filename;
274 }
275
276 /*
277 * Restart i3 in-place
278 * appends -a to argument list to disable autostart
279 *
280 */
281 void i3_restart(bool forget_layout) {
282 char *restart_filename = forget_layout ? NULL : store_restart_layout();
283
284 kill_nagbar(&config_error_nagbar_pid, true);
285 kill_nagbar(&command_error_nagbar_pid, true);
286
287 restore_geometry();
288
289 ipc_shutdown(SHUTDOWN_REASON_RESTART);
290
291 LOG("restarting \"%s\"...\n", start_argv[0]);
292 /* make sure -a is in the argument list or add it */
293 start_argv = add_argument(start_argv, "-a", NULL, NULL);
294
295 /* make debuglog-on persist */
296 if (get_debug_logging()) {
297 start_argv = add_argument(start_argv, "-d", "all", NULL);
298 }
299
300 /* replace -r <file> so that the layout is restored */
301 if (restart_filename != NULL) {
302 start_argv = add_argument(start_argv, "--restart", restart_filename, "-r");
303 }
304
305 execvp(start_argv[0], start_argv);
306
307 /* not reached */
308 }
309
310 #if defined(__OpenBSD__) || defined(__APPLE__)
311
312 /*
313 * Taken from FreeBSD
314 * Find the first occurrence of the byte string s in byte string l.
315 *
316 */
317 void *memmem(const void *l, size_t l_len, const void *s, size_t s_len) {
318 register char *cur, *last;
319 const char *cl = (const char *)l;
320 const char *cs = (const char *)s;
321
322 /* we need something to compare */
323 if (l_len == 0 || s_len == 0)
324 return NULL;
325
326 /* "s" must be smaller or equal to "l" */
327 if (l_len < s_len)
328 return NULL;
329
330 /* special case where s_len == 1 */
331 if (s_len == 1)
332 return memchr(l, (int)*cs, l_len);
333
334 /* the last position where its possible to find "s" in "l" */
335 last = (char *)cl + l_len - s_len;
336
337 for (cur = (char *)cl; cur <= last; cur++)
338 if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0)
339 return cur;
340
341 return NULL;
342 }
343
344 #endif
345
346 /*
347 * Escapes the given string if a pango font is currently used.
348 * If the string has to be escaped, the input string will be free'd.
349 *
350 */
351 char *pango_escape_markup(char *input) {
352 if (!font_is_pango())
353 return input;
354
355 char *escaped = g_markup_escape_text(input, -1);
356 FREE(input);
357
358 return escaped;
359 }
360
361 /*
362 * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
363 * it exited (or could not be started, depending on the exit code).
364 *
365 */
366 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
367 ev_child_stop(EV_A_ watcher);
368
369 if (!WIFEXITED(watcher->rstatus)) {
370 ELOG("ERROR: i3-nagbar did not exit normally.\n");
371 return;
372 }
373
374 int exitcode = WEXITSTATUS(watcher->rstatus);
375 DLOG("i3-nagbar process exited with status %d\n", exitcode);
376 if (exitcode == 2) {
377 ELOG("ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
378 }
379
380 *((pid_t *)watcher->data) = -1;
381 }
382
383 /*
384 * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
385 * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
386 *
387 */
388 static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
389 pid_t *nagbar_pid = (pid_t *)watcher->data;
390 if (*nagbar_pid != -1) {
391 LOG("Sending SIGKILL (%d) to i3-nagbar with PID %d\n", SIGKILL, *nagbar_pid);
392 kill(*nagbar_pid, SIGKILL);
393 }
394 }
395
396 /*
397 * Starts an i3-nagbar instance with the given parameters. Takes care of
398 * handling SIGCHLD and killing i3-nagbar when i3 exits.
399 *
400 * The resulting PID will be stored in *nagbar_pid and can be used with
401 * kill_nagbar() to kill the bar later on.
402 *
403 */
404 void start_nagbar(pid_t *nagbar_pid, char *argv[]) {
405 if (*nagbar_pid != -1) {
406 DLOG("i3-nagbar already running (PID %d), not starting again.\n", *nagbar_pid);
407 return;
408 }
409
410 *nagbar_pid = fork();
411 if (*nagbar_pid == -1) {
412 warn("Could not fork()");
413 return;
414 }
415
416 /* child */
417 if (*nagbar_pid == 0)
418 exec_i3_utility("i3-nagbar", argv);
419
420 DLOG("Starting i3-nagbar with PID %d\n", *nagbar_pid);
421
422 /* parent */
423 /* install a child watcher */
424 ev_child *child = smalloc(sizeof(ev_child));
425 ev_child_init(child, &nagbar_exited, *nagbar_pid, 0);
426 child->data = nagbar_pid;
427 ev_child_start(main_loop, child);
428
429 /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
430 * still running) */
431 ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
432 ev_cleanup_init(cleanup, nagbar_cleanup);
433 cleanup->data = nagbar_pid;
434 ev_cleanup_start(main_loop, cleanup);
435 }
436
437 /*
438 * Kills the i3-nagbar process, if *nagbar_pid != -1.
439 *
440 * If wait_for_it is set (restarting i3), this function will waitpid(),
441 * otherwise, ev is assumed to handle it (reloading).
442 *
443 */
444 void kill_nagbar(pid_t *nagbar_pid, bool wait_for_it) {
445 if (*nagbar_pid == -1)
446 return;
447
448 if (kill(*nagbar_pid, SIGTERM) == -1)
449 warn("kill(configerror_nagbar) failed");
450
451 if (!wait_for_it)
452 return;
453
454 /* When restarting, we don’t enter the ev main loop anymore and after the
455 * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD
456 * for us and we would end up with a <defunct> process. Therefore we
457 * waitpid() here. */
458 waitpid(*nagbar_pid, NULL, 0);
459 }
460
461 /*
462 * Converts a string into a long using strtol().
463 * This is a convenience wrapper checking the parsing result. It returns true
464 * if the number could be parsed.
465 */
466 bool parse_long(const char *str, long *out, int base) {
467 char *end;
468 long result = strtol(str, &end, base);
469 if (result == LONG_MIN || result == LONG_MAX || result < 0 || (end != NULL && *end != '\0')) {
470 *out = result;
471 return false;
472 }
473
474 *out = result;
475 return true;
476 }
477
478 /*
479 * Slurp reads path in its entirety into buf, returning the length of the file
480 * or -1 if the file could not be read. buf is set to a buffer of appropriate
481 * size, or NULL if -1 is returned.
482 *
483 */
484 ssize_t slurp(const char *path, char **buf) {
485 FILE *f;
486 if ((f = fopen(path, "r")) == NULL) {
487 ELOG("Cannot open file \"%s\": %s\n", path, strerror(errno));
488 return -1;
489 }
490 struct stat stbuf;
491 if (fstat(fileno(f), &stbuf) != 0) {
492 ELOG("Cannot fstat() \"%s\": %s\n", path, strerror(errno));
493 fclose(f);
494 return -1;
495 }
496 /* Allocate one extra NUL byte to make the buffer usable with C string
497 * functions. yajl doesn’t need this, but this makes slurp safer. */
498 *buf = scalloc(stbuf.st_size + 1, 1);
499 size_t n = fread(*buf, 1, stbuf.st_size, f);
500 fclose(f);
501 if ((ssize_t)n != stbuf.st_size) {
502 ELOG("File \"%s\" could not be read entirely: got %zd, want %" PRIi64 "\n", path, n, (int64_t)stbuf.st_size);
503 FREE(*buf);
504 return -1;
505 }
506 return (ssize_t)n;
507 }
508
509 /*
510 * Convert a direction to its corresponding orientation.
511 *
512 */
513 orientation_t orientation_from_direction(direction_t direction) {
514 return (direction == D_LEFT || direction == D_RIGHT) ? HORIZ : VERT;
515 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * Stores the latest Git commit identifier so that it can be linked into i3
7 * and used dynamically without recompiling every object file.
8 *
9 */
10 #include <config.h>
11
12 const char *i3_version = I3_VERSION;
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * window.c: Updates window attributes (X11 hints/properties).
7 *
8 */
9 #include "all.h"
10
11 /*
12 * Frees an i3Window and all its members.
13 *
14 */
15 void window_free(i3Window *win) {
16 FREE(win->class_class);
17 FREE(win->class_instance);
18 i3string_free(win->name);
19 FREE(win->ran_assignments);
20 FREE(win);
21 }
22
23 /*
24 * Updates the WM_CLASS (consisting of the class and instance) for the
25 * given window.
26 *
27 */
28 void window_update_class(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt) {
29 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
30 DLOG("WM_CLASS not set.\n");
31 FREE(prop);
32 return;
33 }
34
35 /* We cannot use asprintf here since this property contains two
36 * null-terminated strings (for compatibility reasons). Instead, we
37 * use strdup() on both strings */
38 const size_t prop_length = xcb_get_property_value_length(prop);
39 char *new_class = xcb_get_property_value(prop);
40 const size_t class_class_index = strnlen(new_class, prop_length) + 1;
41
42 FREE(win->class_instance);
43 FREE(win->class_class);
44
45 win->class_instance = sstrndup(new_class, prop_length);
46 if (class_class_index < prop_length)
47 win->class_class = sstrndup(new_class + class_class_index, prop_length - class_class_index);
48 else
49 win->class_class = NULL;
50 LOG("WM_CLASS changed to %s (instance), %s (class)\n",
51 win->class_instance, win->class_class);
52
53 if (before_mgmt) {
54 free(prop);
55 return;
56 }
57
58 run_assignments(win);
59
60 free(prop);
61 }
62
63 /*
64 * Updates the name by using _NET_WM_NAME (encoded in UTF-8) for the given
65 * window. Further updates using window_update_name_legacy will be ignored.
66 *
67 */
68 void window_update_name(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt) {
69 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
70 DLOG("_NET_WM_NAME not specified, not changing\n");
71 FREE(prop);
72 return;
73 }
74
75 i3string_free(win->name);
76
77 /* Truncate the name at the first zero byte. See #3515. */
78 const int len = xcb_get_property_value_length(prop);
79 char *name = sstrndup(xcb_get_property_value(prop), len);
80 win->name = i3string_from_utf8(name);
81 free(name);
82
83 Con *con = con_by_window_id(win->id);
84 if (con != NULL && con->title_format != NULL) {
85 i3String *name = con_parse_title_format(con);
86 ewmh_update_visible_name(win->id, i3string_as_utf8(name));
87 I3STRING_FREE(name);
88 }
89 win->name_x_changed = true;
90 LOG("_NET_WM_NAME changed to \"%s\"\n", i3string_as_utf8(win->name));
91
92 win->uses_net_wm_name = true;
93
94 if (before_mgmt) {
95 free(prop);
96 return;
97 }
98
99 run_assignments(win);
100
101 free(prop);
102 }
103
104 /*
105 * Updates the name by using WM_NAME (encoded in COMPOUND_TEXT). We do not
106 * touch what the client sends us but pass it to xcb_image_text_8. To get
107 * proper unicode rendering, the application has to use _NET_WM_NAME (see
108 * window_update_name()).
109 *
110 */
111 void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt) {
112 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
113 DLOG("WM_NAME not set (_NET_WM_NAME is what you want anyways).\n");
114 FREE(prop);
115 return;
116 }
117
118 /* ignore update when the window is known to already have a UTF-8 name */
119 if (win->uses_net_wm_name) {
120 free(prop);
121 return;
122 }
123
124 i3string_free(win->name);
125 const int len = xcb_get_property_value_length(prop);
126 char *name = sstrndup(xcb_get_property_value(prop), len);
127 win->name = i3string_from_utf8(name);
128 free(name);
129
130 Con *con = con_by_window_id(win->id);
131 if (con != NULL && con->title_format != NULL) {
132 i3String *name = con_parse_title_format(con);
133 ewmh_update_visible_name(win->id, i3string_as_utf8(name));
134 I3STRING_FREE(name);
135 }
136
137 LOG("WM_NAME changed to \"%s\"\n", i3string_as_utf8(win->name));
138 LOG("Using legacy window title. Note that in order to get Unicode window "
139 "titles in i3, the application has to set _NET_WM_NAME (UTF-8)\n");
140
141 win->name_x_changed = true;
142
143 if (before_mgmt) {
144 free(prop);
145 return;
146 }
147
148 run_assignments(win);
149
150 free(prop);
151 }
152
153 /*
154 * Updates the CLIENT_LEADER (logical parent window).
155 *
156 */
157 void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
158 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
159 DLOG("CLIENT_LEADER not set on window 0x%08x.\n", win->id);
160 win->leader = XCB_NONE;
161 FREE(prop);
162 return;
163 }
164
165 xcb_window_t *leader = xcb_get_property_value(prop);
166 if (leader == NULL) {
167 free(prop);
168 return;
169 }
170
171 DLOG("Client leader changed to %08x\n", *leader);
172
173 win->leader = *leader;
174
175 free(prop);
176 }
177
178 /*
179 * Updates the TRANSIENT_FOR (logical parent window).
180 *
181 */
182 void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) {
183 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
184 DLOG("TRANSIENT_FOR not set on window 0x%08x.\n", win->id);
185 win->transient_for = XCB_NONE;
186 FREE(prop);
187 return;
188 }
189
190 xcb_window_t transient_for;
191 if (!xcb_icccm_get_wm_transient_for_from_reply(&transient_for, prop)) {
192 free(prop);
193 return;
194 }
195
196 DLOG("Transient for changed to 0x%08x (window 0x%08x)\n", transient_for, win->id);
197
198 win->transient_for = transient_for;
199
200 free(prop);
201 }
202
203 /*
204 * Updates the _NET_WM_STRUT_PARTIAL (reserved pixels at the screen edges)
205 *
206 */
207 void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop) {
208 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
209 DLOG("_NET_WM_STRUT_PARTIAL not set.\n");
210 FREE(prop);
211 return;
212 }
213
214 uint32_t *strut;
215 if (!(strut = xcb_get_property_value(prop))) {
216 free(prop);
217 return;
218 }
219
220 DLOG("Reserved pixels changed to: left = %d, right = %d, top = %d, bottom = %d\n",
221 strut[0], strut[1], strut[2], strut[3]);
222
223 win->reserved = (struct reservedpx){strut[0], strut[1], strut[2], strut[3]};
224
225 free(prop);
226 }
227
228 /*
229 * Updates the WM_WINDOW_ROLE
230 *
231 */
232 void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt) {
233 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
234 DLOG("WM_WINDOW_ROLE not set.\n");
235 FREE(prop);
236 return;
237 }
238
239 char *new_role;
240 sasprintf(&new_role, "%.*s", xcb_get_property_value_length(prop),
241 (char *)xcb_get_property_value(prop));
242 FREE(win->role);
243 win->role = new_role;
244 LOG("WM_WINDOW_ROLE changed to \"%s\"\n", win->role);
245
246 if (before_mgmt) {
247 free(prop);
248 return;
249 }
250
251 run_assignments(win);
252
253 free(prop);
254 }
255
256 /*
257 * Updates the _NET_WM_WINDOW_TYPE property.
258 *
259 */
260 void window_update_type(i3Window *window, xcb_get_property_reply_t *reply) {
261 xcb_atom_t new_type = xcb_get_preferred_window_type(reply);
262 free(reply);
263 if (new_type == XCB_NONE) {
264 DLOG("cannot read _NET_WM_WINDOW_TYPE from window.\n");
265 return;
266 }
267
268 window->window_type = new_type;
269 LOG("_NET_WM_WINDOW_TYPE changed to %i.\n", window->window_type);
270
271 run_assignments(window);
272 }
273
274 /*
275 * Updates the WM_HINTS (we only care about the input focus handling part).
276 *
277 */
278 void window_update_hints(i3Window *win, xcb_get_property_reply_t *prop, bool *urgency_hint) {
279 if (urgency_hint != NULL)
280 *urgency_hint = false;
281
282 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
283 DLOG("WM_HINTS not set.\n");
284 FREE(prop);
285 return;
286 }
287
288 xcb_icccm_wm_hints_t hints;
289
290 if (!xcb_icccm_get_wm_hints_from_reply(&hints, prop)) {
291 DLOG("Could not get WM_HINTS\n");
292 free(prop);
293 return;
294 }
295
296 if (hints.flags & XCB_ICCCM_WM_HINT_INPUT) {
297 win->doesnt_accept_focus = !hints.input;
298 LOG("WM_HINTS.input changed to \"%d\"\n", hints.input);
299 }
300
301 if (urgency_hint != NULL)
302 *urgency_hint = (xcb_icccm_wm_hints_get_urgency(&hints) != 0);
303
304 free(prop);
305 }
306
307 /*
308 * Updates the MOTIF_WM_HINTS. The container's border style should be set to
309 * `motif_border_style' if border style is not BS_NORMAL.
310 *
311 * i3 only uses this hint when it specifies a window should have no
312 * title bar, or no decorations at all, which is how most window managers
313 * handle it.
314 *
315 * The EWMH spec intended to replace Motif hints with _NET_WM_WINDOW_TYPE, but
316 * it is still in use by popular widget toolkits such as GTK+ and Java AWT.
317 *
318 */
319 void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style) {
320 /* This implementation simply mirrors Gnome's Metacity. Official
321 * documentation of this hint is nowhere to be found.
322 * For more information see:
323 * https://people.gnome.org/~tthurman/docs/metacity/xprops_8h-source.html
324 * https://stackoverflow.com/questions/13787553/detect-if-a-x11-window-has-decorations
325 */
326 #define MWM_HINTS_FLAGS_FIELD 0
327 #define MWM_HINTS_DECORATIONS_FIELD 2
328
329 #define MWM_HINTS_DECORATIONS (1 << 1)
330 #define MWM_DECOR_ALL (1 << 0)
331 #define MWM_DECOR_BORDER (1 << 1)
332 #define MWM_DECOR_TITLE (1 << 3)
333
334 if (motif_border_style != NULL)
335 *motif_border_style = BS_NORMAL;
336
337 if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
338 FREE(prop);
339 return;
340 }
341
342 /* The property consists of an array of 5 uint32_t's. The first value is a
343 * bit mask of what properties the hint will specify. We are only interested
344 * in MWM_HINTS_DECORATIONS because it indicates that the third value of the
345 * array tells us which decorations the window should have, each flag being
346 * a particular decoration. Notice that X11 (Xlib) often mentions 32-bit
347 * fields which in reality are implemented using unsigned long variables
348 * (64-bits long on amd64 for example). On the other hand,
349 * xcb_get_property_value() behaves strictly according to documentation,
350 * i.e. returns 32-bit data fields. */
351 uint32_t *motif_hints = (uint32_t *)xcb_get_property_value(prop);
352
353 if (motif_border_style != NULL &&
354 motif_hints[MWM_HINTS_FLAGS_FIELD] & MWM_HINTS_DECORATIONS) {
355 if (motif_hints[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_ALL ||
356 motif_hints[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_TITLE)
357 *motif_border_style = BS_NORMAL;
358 else if (motif_hints[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_BORDER)
359 *motif_border_style = BS_PIXEL;
360 else
361 *motif_border_style = BS_NONE;
362 }
363
364 FREE(prop);
365
366 #undef MWM_HINTS_FLAGS_FIELD
367 #undef MWM_HINTS_DECORATIONS_FIELD
368 #undef MWM_HINTS_DECORATIONS
369 #undef MWM_DECOR_ALL
370 #undef MWM_DECOR_BORDER
371 #undef MWM_DECOR_TITLE
372 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * workspace.c: Modifying workspaces, accessing them, moving containers to
7 * workspaces.
8 *
9 */
10 #include "all.h"
11 #include "yajl_utils.h"
12
13 /* Stores a copy of the name of the last used workspace for the workspace
14 * back-and-forth switching. */
15 static char *previous_workspace_name = NULL;
16
17 /* NULL-terminated list of workspace names (in order) extracted from
18 * keybindings. */
19 static char **binding_workspace_names = NULL;
20
21 /*
22 * Returns the workspace with the given name or NULL if such a workspace does
23 * not exist.
24 *
25 */
26 Con *get_existing_workspace_by_name(const char *name) {
27 Con *output, *workspace = NULL;
28 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
29 GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, name));
30 }
31
32 return workspace;
33 }
34
35 /*
36 * Returns the workspace with the given number or NULL if such a workspace does
37 * not exist.
38 *
39 */
40 Con *get_existing_workspace_by_num(int num) {
41 Con *output, *workspace = NULL;
42 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
43 GREP_FIRST(workspace, output_get_content(output), child->num == num);
44 }
45
46 return workspace;
47 }
48
49 /*
50 * Sets ws->layout to splith/splitv if default_orientation was specified in the
51 * configfile. Otherwise, it uses splith/splitv depending on whether the output
52 * is higher than wide.
53 *
54 */
55 static void _workspace_apply_default_orientation(Con *ws) {
56 /* If default_orientation is set to NO_ORIENTATION we determine
57 * orientation depending on output resolution. */
58 if (config.default_orientation == NO_ORIENTATION) {
59 Con *output = con_get_output(ws);
60 ws->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH;
61 ws->rect = output->rect;
62 DLOG("Auto orientation. Workspace size set to (%d,%d), setting layout to %d.\n",
63 output->rect.width, output->rect.height, ws->layout);
64 } else {
65 ws->layout = (config.default_orientation == HORIZ) ? L_SPLITH : L_SPLITV;
66 }
67 }
68
69 /*
70 * Returns the first output that is assigned to a workspace specified by the
71 * given name or number or NULL if no such output exists. If there is a
72 * workspace with a matching name and another workspace with a matching number,
73 * the output assigned to the first one is returned.
74 * The order of the 'ws_assignments' queue is respected: if multiple assignments
75 * match the specified workspace, the first one is returned.
76 * If 'name' is NULL it will be ignored.
77 * If 'parsed_num' is -1 it will be ignored.
78 *
79 */
80 static Con *get_assigned_output(const char *name, long parsed_num) {
81 Con *output = NULL;
82 struct Workspace_Assignment *assignment;
83 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
84 if (name && strcmp(assignment->name, name) == 0) {
85 DLOG("Found workspace name assignment to output \"%s\"\n", assignment->output);
86 Output *assigned_by_name = get_output_by_name(assignment->output, true);
87 if (assigned_by_name) {
88 /* When the name matches exactly, skip numbered assignments. */
89 return assigned_by_name->con;
90 }
91 } else if (!output && /* Only keep the first numbered assignment. */
92 parsed_num != -1 &&
93 name_is_digits(assignment->name) &&
94 ws_name_to_number(assignment->name) == parsed_num) {
95 DLOG("Found workspace number assignment to output \"%s\"\n", assignment->output);
96 Output *assigned_by_num = get_output_by_name(assignment->output, true);
97 if (assigned_by_num) {
98 output = assigned_by_num->con;
99 }
100 }
101 }
102
103 return output;
104 }
105
106 /*
107 * Returns true if the first output assigned to a workspace with the given
108 * workspace assignment is the same as the given output.
109 */
110 bool output_triggers_assignment(Output *output, struct Workspace_Assignment *assignment) {
111 Con *assigned = get_assigned_output(assignment->name, -1);
112 return assigned && assigned == output->con;
113 }
114
115 /*
116 * Returns a pointer to the workspace with the given number (starting at 0),
117 * creating the workspace if necessary (by allocating the necessary amount of
118 * memory and initializing the data structures correctly).
119 *
120 */
121 Con *workspace_get(const char *num, bool *created) {
122 Con *workspace = get_existing_workspace_by_name(num);
123
124 if (workspace == NULL) {
125 LOG("Creating new workspace \"%s\"\n", num);
126
127 /* We set workspace->num to the number if this workspace’s name begins
128 * with a positive number. Otherwise it’s a named ws and num will be
129 * -1. */
130 long parsed_num = ws_name_to_number(num);
131
132 Con *output = get_assigned_output(num, parsed_num);
133 /* if an assignment is not found, we create this workspace on the current output */
134 if (!output) {
135 output = con_get_output(focused);
136 }
137
138 Con *content = output_get_content(output);
139 LOG("got output %p with content %p\n", output, content);
140 /* We need to attach this container after setting its type. con_attach
141 * will handle CT_WORKSPACEs differently */
142 workspace = con_new(NULL, NULL);
143 char *name;
144 sasprintf(&name, "[i3 con] workspace %s", num);
145 x_set_name(workspace, name);
146 free(name);
147 workspace->type = CT_WORKSPACE;
148 FREE(workspace->name);
149 workspace->name = sstrdup(num);
150 workspace->workspace_layout = config.default_layout;
151 workspace->num = parsed_num;
152 LOG("num = %d\n", workspace->num);
153
154 workspace->parent = content;
155 _workspace_apply_default_orientation(workspace);
156
157 con_attach(workspace, content, false);
158
159 ipc_send_workspace_event("init", workspace, NULL);
160 ewmh_update_number_of_desktops();
161 ewmh_update_desktop_names();
162 ewmh_update_desktop_viewport();
163 ewmh_update_wm_desktop();
164 if (created != NULL)
165 *created = true;
166 } else if (created != NULL) {
167 *created = false;
168 }
169
170 return workspace;
171 }
172
173 /*
174 * Extracts workspace names from keybindings (e.g. “web” from “bindsym $mod+1
175 * workspace web”), so that when an output needs a workspace, i3 can start with
176 * the first configured one. Needs to be called before reorder_bindings() so
177 * that the config-file order is used, not the i3-internal order.
178 *
179 */
180 void extract_workspace_names_from_bindings(void) {
181 Binding *bind;
182 int n = 0;
183 if (binding_workspace_names != NULL) {
184 for (int i = 0; binding_workspace_names[i] != NULL; i++) {
185 free(binding_workspace_names[i]);
186 }
187 FREE(binding_workspace_names);
188 }
189 TAILQ_FOREACH(bind, bindings, bindings) {
190 DLOG("binding with command %s\n", bind->command);
191 if (strlen(bind->command) < strlen("workspace ") ||
192 strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
193 continue;
194 DLOG("relevant command = %s\n", bind->command);
195 const char *target = bind->command + strlen("workspace ");
196 while (*target == ' ' || *target == '\t')
197 target++;
198 /* We check if this is the workspace
199 * next/prev/next_on_output/prev_on_output/back_and_forth/number command.
200 * Beware: The workspace names "next", "prev", "next_on_output",
201 * "prev_on_output", "number", "back_and_forth" and "current" are OK,
202 * so we check before stripping the double quotes */
203 if (strncasecmp(target, "next", strlen("next")) == 0 ||
204 strncasecmp(target, "prev", strlen("prev")) == 0 ||
205 strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 ||
206 strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 ||
207 strncasecmp(target, "number", strlen("number")) == 0 ||
208 strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
209 strncasecmp(target, "current", strlen("current")) == 0)
210 continue;
211 char *target_name = parse_string(&target, false);
212 if (target_name == NULL)
213 continue;
214 if (strncasecmp(target_name, "__", strlen("__")) == 0) {
215 LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target);
216 free(target_name);
217 continue;
218 }
219 DLOG("Saving workspace name \"%s\"\n", target_name);
220
221 binding_workspace_names = srealloc(binding_workspace_names, ++n * sizeof(char *));
222 binding_workspace_names[n - 1] = target_name;
223 }
224 binding_workspace_names = srealloc(binding_workspace_names, ++n * sizeof(char *));
225 binding_workspace_names[n - 1] = NULL;
226 }
227
228 /*
229 * Returns a pointer to a new workspace in the given output. The workspace
230 * is created attached to the tree hierarchy through the given content
231 * container.
232 *
233 */
234 Con *create_workspace_on_output(Output *output, Con *content) {
235 /* add a workspace to this output */
236 char *name;
237 bool exists = true;
238 Con *ws = con_new(NULL, NULL);
239 ws->type = CT_WORKSPACE;
240
241 /* try the configured workspace bindings first to find a free name */
242 for (int n = 0; binding_workspace_names[n] != NULL; n++) {
243 char *target_name = binding_workspace_names[n];
244 /* Ensure that this workspace is not assigned to a different output —
245 * otherwise we would create it, then move it over to its output, then
246 * find a new workspace, etc… */
247 Con *assigned = get_assigned_output(target_name, -1);
248 if (assigned && assigned != output->con) {
249 continue;
250 }
251
252 exists = (get_existing_workspace_by_name(target_name) != NULL);
253 if (!exists) {
254 ws->name = sstrdup(target_name);
255 /* Set ->num to the number of the workspace, if the name actually
256 * is a number or starts with a number */
257 ws->num = ws_name_to_number(ws->name);
258 LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
259
260 break;
261 }
262 }
263
264 if (exists) {
265 /* get the next unused workspace number */
266 DLOG("Getting next unused workspace by number\n");
267 int c = 0;
268 while (exists) {
269 c++;
270 Con *assigned = get_assigned_output(NULL, c);
271 exists = (get_existing_workspace_by_num(c) || (assigned && assigned != output->con));
272 DLOG("result for ws %d: exists = %d\n", c, exists);
273 }
274 ws->num = c;
275 sasprintf(&(ws->name), "%d", c);
276 }
277 con_attach(ws, content, false);
278
279 sasprintf(&name, "[i3 con] workspace %s", ws->name);
280 x_set_name(ws, name);
281 free(name);
282
283 ws->fullscreen_mode = CF_OUTPUT;
284
285 ws->workspace_layout = config.default_layout;
286 _workspace_apply_default_orientation(ws);
287
288 return ws;
289 }
290
291 /*
292 * Returns true if the workspace is currently visible. Especially important for
293 * multi-monitor environments, as they can have multiple currenlty active
294 * workspaces.
295 *
296 */
297 bool workspace_is_visible(Con *ws) {
298 Con *output = con_get_output(ws);
299 if (output == NULL)
300 return false;
301 Con *fs = con_get_fullscreen_con(output, CF_OUTPUT);
302 LOG("workspace visible? fs = %p, ws = %p\n", fs, ws);
303 return (fs == ws);
304 }
305
306 /*
307 * XXX: we need to clean up all this recursive walking code.
308 *
309 */
310 static Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
311 Con *current;
312
313 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
314 if (current != exclude &&
315 current->sticky_group != NULL &&
316 current->window != NULL &&
317 strcmp(current->sticky_group, sticky_group) == 0)
318 return current;
319
320 Con *recurse = _get_sticky(current, sticky_group, exclude);
321 if (recurse != NULL)
322 return recurse;
323 }
324
325 TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
326 if (current != exclude &&
327 current->sticky_group != NULL &&
328 current->window != NULL &&
329 strcmp(current->sticky_group, sticky_group) == 0)
330 return current;
331
332 Con *recurse = _get_sticky(current, sticky_group, exclude);
333 if (recurse != NULL)
334 return recurse;
335 }
336
337 return NULL;
338 }
339
340 /*
341 * Reassigns all child windows in sticky containers. Called when the user
342 * changes workspaces.
343 *
344 * XXX: what about sticky containers which contain containers?
345 *
346 */
347 static void workspace_reassign_sticky(Con *con) {
348 Con *current;
349 /* 1: go through all containers */
350
351 /* handle all children and floating windows of this node */
352 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
353 if (current->sticky_group == NULL) {
354 workspace_reassign_sticky(current);
355 continue;
356 }
357
358 LOG("Ah, this one is sticky: %s / %p\n", current->name, current);
359 /* 2: find a window which we can re-assign */
360 Con *output = con_get_output(current);
361 Con *src = _get_sticky(output, current->sticky_group, current);
362
363 if (src == NULL) {
364 LOG("No window found for this sticky group\n");
365 workspace_reassign_sticky(current);
366 continue;
367 }
368
369 x_move_win(src, current);
370 current->window = src->window;
371 current->mapped = true;
372 src->window = NULL;
373 src->mapped = false;
374
375 x_reparent_child(current, src);
376
377 LOG("re-assigned window from src %p to dest %p\n", src, current);
378 }
379
380 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
381 workspace_reassign_sticky(current);
382 }
383
384 /*
385 * Callback to reset the urgent flag of the given con to false. May be started by
386 * workspace_show to avoid urgency hints being lost by switching to a workspace
387 * focusing the con.
388 *
389 */
390 static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
391 Con *con = w->data;
392
393 ev_timer_stop(main_loop, con->urgency_timer);
394 FREE(con->urgency_timer);
395
396 if (con->urgent) {
397 DLOG("Resetting urgency flag of con %p by timer\n", con);
398 con_set_urgency(con, false);
399 con_update_parents_urgency(con);
400 workspace_update_urgent_flag(con_get_workspace(con));
401 ipc_send_window_event("urgent", con);
402 tree_render();
403 }
404 }
405
406 /*
407 * Switches to the given workspace
408 *
409 */
410 void workspace_show(Con *workspace) {
411 Con *current, *old = NULL;
412
413 /* safe-guard against showing i3-internal workspaces like __i3_scratch */
414 if (con_is_internal(workspace))
415 return;
416
417 /* disable fullscreen for the other workspaces and get the workspace we are
418 * currently on. */
419 TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes) {
420 if (current->fullscreen_mode == CF_OUTPUT)
421 old = current;
422 current->fullscreen_mode = CF_NONE;
423 }
424
425 /* enable fullscreen for the target workspace. If it happens to be the
426 * same one we are currently on anyways, we can stop here. */
427 workspace->fullscreen_mode = CF_OUTPUT;
428 current = con_get_workspace(focused);
429 if (workspace == current) {
430 DLOG("Not switching, already there.\n");
431 return;
432 }
433
434 /* Used to correctly update focus when pushing sticky windows. Holds the
435 * previously focused container in the same output as workspace. For
436 * example, if a sticky window is focused and then we switch focus to a
437 * workspace in another output and then switch to a third workspace in the
438 * first output, the sticky window needs to be refocused. */
439 Con *old_focus = old ? con_descend_focused(old) : NULL;
440
441 /* Remember currently focused workspace for switching back to it later with
442 * the 'workspace back_and_forth' command.
443 * NOTE: We have to duplicate the name as the original will be freed when
444 * the corresponding workspace is cleaned up.
445 * NOTE: Internal cons such as __i3_scratch (when a scratchpad window is
446 * focused) are skipped, see bug #868. */
447 if (current && !con_is_internal(current)) {
448 FREE(previous_workspace_name);
449 previous_workspace_name = sstrdup(current->name);
450 DLOG("Setting previous_workspace_name = %s\n", previous_workspace_name);
451 }
452
453 workspace_reassign_sticky(workspace);
454
455 DLOG("switching to %p / %s\n", workspace, workspace->name);
456 Con *next = con_descend_focused(workspace);
457
458 /* Memorize current output */
459 Con *old_output = con_get_output(focused);
460
461 /* Display urgency hint for a while if the newly visible workspace would
462 * focus and thereby immediately destroy it */
463 if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
464 /* focus for now… */
465 next->urgent = false;
466 con_focus(next);
467
468 /* … but immediately reset urgency flags; they will be set to false by
469 * the timer callback in case the container is focused at the time of
470 * its expiration */
471 focused->urgent = true;
472 workspace->urgent = true;
473
474 if (focused->urgency_timer == NULL) {
475 DLOG("Deferring reset of urgency flag of con %p on newly shown workspace %p\n",
476 focused, workspace);
477 focused->urgency_timer = scalloc(1, sizeof(struct ev_timer));
478 /* use a repeating timer to allow for easy resets */
479 ev_timer_init(focused->urgency_timer, workspace_defer_update_urgent_hint_cb,
480 config.workspace_urgency_timer, config.workspace_urgency_timer);
481 focused->urgency_timer->data = focused;
482 ev_timer_start(main_loop, focused->urgency_timer);
483 } else {
484 DLOG("Resetting urgency timer of con %p on workspace %p\n",
485 focused, workspace);
486 ev_timer_again(main_loop, focused->urgency_timer);
487 }
488 } else
489 con_focus(next);
490
491 ipc_send_workspace_event("focus", workspace, current);
492
493 DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
494 /* Close old workspace if necessary. This must be done *after* doing
495 * urgency handling, because tree_close_internal() will do a con_focus() on the next
496 * client, which will clear the urgency flag too early. Also, there is no
497 * way for con_focus() to know about when to clear urgency immediately and
498 * when to defer it. */
499 if (old && TAILQ_EMPTY(&(old->nodes_head)) && TAILQ_EMPTY(&(old->floating_head))) {
500 /* check if this workspace is currently visible */
501 if (!workspace_is_visible(old)) {
502 LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
503 yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL);
504 tree_close_internal(old, DONT_KILL_WINDOW, false);
505
506 const unsigned char *payload;
507 ylength length;
508 y(get_buf, &payload, &length);
509 ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
510
511 y(free);
512
513 /* Avoid calling output_push_sticky_windows later with a freed container. */
514 if (old == old_focus) {
515 old_focus = NULL;
516 }
517
518 ewmh_update_number_of_desktops();
519 ewmh_update_desktop_names();
520 ewmh_update_desktop_viewport();
521 ewmh_update_wm_desktop();
522 }
523 }
524
525 workspace->fullscreen_mode = CF_OUTPUT;
526 LOG("focused now = %p / %s\n", focused, focused->name);
527
528 /* Set mouse pointer */
529 Con *new_output = con_get_output(focused);
530 if (old_output != new_output) {
531 x_set_warp_to(&next->rect);
532 }
533
534 /* Update the EWMH hints */
535 ewmh_update_current_desktop();
536
537 /* Push any sticky windows to the now visible workspace. */
538 output_push_sticky_windows(old_focus);
539 }
540
541 /*
542 * Looks up the workspace by name and switches to it.
543 *
544 */
545 void workspace_show_by_name(const char *num) {
546 Con *workspace;
547 workspace = workspace_get(num, NULL);
548 workspace_show(workspace);
549 }
550
551 /*
552 * Focuses the next workspace.
553 *
554 */
555 Con *workspace_next(void) {
556 Con *current = con_get_workspace(focused);
557 Con *next = NULL, *first = NULL, *first_opposite = NULL;
558 Con *output;
559
560 if (current->num == -1) {
561 /* If currently a named workspace, find next named workspace. */
562 if ((next = TAILQ_NEXT(current, nodes)) != NULL)
563 return next;
564 bool found_current = false;
565 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
566 /* Skip outputs starting with __, they are internal. */
567 if (con_is_internal(output))
568 continue;
569 NODES_FOREACH(output_get_content(output)) {
570 if (child->type != CT_WORKSPACE)
571 continue;
572 if (!first)
573 first = child;
574 if (!first_opposite || (child->num != -1 && child->num < first_opposite->num))
575 first_opposite = child;
576 if (child == current) {
577 found_current = true;
578 } else if (child->num == -1 && found_current) {
579 next = child;
580 return next;
581 }
582 }
583 }
584 } else {
585 /* If currently a numbered workspace, find next numbered workspace. */
586 TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
587 /* Skip outputs starting with __, they are internal. */
588 if (con_is_internal(output))
589 continue;
590 NODES_FOREACH(output_get_content(output)) {
591 if (child->type != CT_WORKSPACE)
592 continue;
593 if (!first || (child->num != -1 && child->num < first->num))
594 first = child;
595 if (!first_opposite && child->num == -1)
596 first_opposite = child;
597 if (child->num == -1)
598 break;
599 /* Need to check child against current and next because we are
600 * traversing multiple lists and thus are not guaranteed the
601 * relative order between the list of workspaces. */
602 if (current->num < child->num && (!next || child->num < next->num))
603 next = child;
604 }
605 }
606 }
607
608 if (!next)
609 next = first_opposite ? first_opposite : first;
610
611 return next;
612 }
613
614 /*
615 * Focuses the previous workspace.
616 *
617 */
618 Con *workspace_prev(void) {
619 Con *current = con_get_workspace(focused);
620 Con *prev = NULL, *first_opposite = NULL, *last = NULL;
621 Con *output;
622
623 if (current->num == -1) {
624 /* If named workspace, find previous named workspace. */
625 prev = TAILQ_PREV(current, nodes_head, nodes);
626 if (prev && prev->num != -1)
627 prev = NULL;
628 if (!prev) {
629 bool found_current = false;
630 TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
631 /* Skip outputs starting with __, they are internal. */
632 if (con_is_internal(output))
633 continue;
634 NODES_FOREACH_REVERSE(output_get_content(output)) {
635 if (child->type != CT_WORKSPACE)
636 continue;
637 if (!last)
638 last = child;
639 if (!first_opposite || (child->num != -1 && child->num > first_opposite->num))
640 first_opposite = child;
641 if (child == current) {
642 found_current = true;
643 } else if (child->num == -1 && found_current) {
644 prev = child;
645 return prev;
646 }
647 }
648 }
649 }
650 } else {
651 /* If numbered workspace, find previous numbered workspace. */
652 TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
653 /* Skip outputs starting with __, they are internal. */
654 if (con_is_internal(output))
655 continue;
656 NODES_FOREACH_REVERSE(output_get_content(output)) {
657 if (child->type != CT_WORKSPACE)
658 continue;
659 if (!last || (child->num != -1 && last->num < child->num))
660 last = child;
661 if (!first_opposite && child->num == -1)
662 first_opposite = child;
663 if (child->num == -1)
664 continue;
665 /* Need to check child against current and previous because we
666 * are traversing multiple lists and thus are not guaranteed
667 * the relative order between the list of workspaces. */
668 if (current->num > child->num && (!prev || child->num > prev->num))
669 prev = child;
670 }
671 }
672 }
673
674 if (!prev)
675 prev = first_opposite ? first_opposite : last;
676
677 return prev;
678 }
679
680 /*
681 * Focuses the next workspace on the same output.
682 *
683 */
684 Con *workspace_next_on_output(void) {
685 Con *current = con_get_workspace(focused);
686 Con *next = NULL;
687 Con *output = con_get_output(focused);
688
689 if (current->num == -1) {
690 /* If currently a named workspace, find next named workspace. */
691 next = TAILQ_NEXT(current, nodes);
692 } else {
693 /* If currently a numbered workspace, find next numbered workspace. */
694 NODES_FOREACH(output_get_content(output)) {
695 if (child->type != CT_WORKSPACE)
696 continue;
697 if (child->num == -1)
698 break;
699 /* Need to check child against current and next because we are
700 * traversing multiple lists and thus are not guaranteed the
701 * relative order between the list of workspaces. */
702 if (current->num < child->num && (!next || child->num < next->num))
703 next = child;
704 }
705 }
706
707 /* Find next named workspace. */
708 if (!next) {
709 bool found_current = false;
710 NODES_FOREACH(output_get_content(output)) {
711 if (child->type != CT_WORKSPACE)
712 continue;
713 if (child == current) {
714 found_current = true;
715 } else if (child->num == -1 && (current->num != -1 || found_current)) {
716 next = child;
717 goto workspace_next_on_output_end;
718 }
719 }
720 }
721
722 /* Find first workspace. */
723 if (!next) {
724 NODES_FOREACH(output_get_content(output)) {
725 if (child->type != CT_WORKSPACE)
726 continue;
727 if (!next || (child->num != -1 && child->num < next->num))
728 next = child;
729 }
730 }
731 workspace_next_on_output_end:
732 return next;
733 }
734
735 /*
736 * Focuses the previous workspace on same output.
737 *
738 */
739 Con *workspace_prev_on_output(void) {
740 Con *current = con_get_workspace(focused);
741 Con *prev = NULL;
742 Con *output = con_get_output(focused);
743 DLOG("output = %s\n", output->name);
744
745 if (current->num == -1) {
746 /* If named workspace, find previous named workspace. */
747 prev = TAILQ_PREV(current, nodes_head, nodes);
748 if (prev && prev->num != -1)
749 prev = NULL;
750 } else {
751 /* If numbered workspace, find previous numbered workspace. */
752 NODES_FOREACH_REVERSE(output_get_content(output)) {
753 if (child->type != CT_WORKSPACE || child->num == -1)
754 continue;
755 /* Need to check child against current and previous because we
756 * are traversing multiple lists and thus are not guaranteed
757 * the relative order between the list of workspaces. */
758 if (current->num > child->num && (!prev || child->num > prev->num))
759 prev = child;
760 }
761 }
762
763 /* Find previous named workspace. */
764 if (!prev) {
765 bool found_current = false;
766 NODES_FOREACH_REVERSE(output_get_content(output)) {
767 if (child->type != CT_WORKSPACE)
768 continue;
769 if (child == current) {
770 found_current = true;
771 } else if (child->num == -1 && (current->num != -1 || found_current)) {
772 prev = child;
773 goto workspace_prev_on_output_end;
774 }
775 }
776 }
777
778 /* Find last workspace. */
779 if (!prev) {
780 NODES_FOREACH_REVERSE(output_get_content(output)) {
781 if (child->type != CT_WORKSPACE)
782 continue;
783 if (!prev || child->num > prev->num)
784 prev = child;
785 }
786 }
787
788 workspace_prev_on_output_end:
789 return prev;
790 }
791
792 /*
793 * Focuses the previously focused workspace.
794 *
795 */
796 void workspace_back_and_forth(void) {
797 if (!previous_workspace_name) {
798 DLOG("No previous workspace name set. Not switching.\n");
799 return;
800 }
801
802 workspace_show_by_name(previous_workspace_name);
803 }
804
805 /*
806 * Returns the previously focused workspace con, or NULL if unavailable.
807 *
808 */
809 Con *workspace_back_and_forth_get(void) {
810 if (!previous_workspace_name) {
811 DLOG("No previous workspace name set.\n");
812 return NULL;
813 }
814
815 Con *workspace;
816 workspace = workspace_get(previous_workspace_name, NULL);
817
818 return workspace;
819 }
820
821 static bool get_urgency_flag(Con *con) {
822 Con *child;
823 TAILQ_FOREACH(child, &(con->nodes_head), nodes)
824 if (child->urgent || get_urgency_flag(child))
825 return true;
826
827 TAILQ_FOREACH(child, &(con->floating_head), floating_windows)
828 if (child->urgent || get_urgency_flag(child))
829 return true;
830
831 return false;
832 }
833
834 /*
835 * Goes through all clients on the given workspace and updates the workspace’s
836 * urgent flag accordingly.
837 *
838 */
839 void workspace_update_urgent_flag(Con *ws) {
840 bool old_flag = ws->urgent;
841 ws->urgent = get_urgency_flag(ws);
842 DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
843
844 if (old_flag != ws->urgent)
845 ipc_send_workspace_event("urgent", ws, NULL);
846 }
847
848 /*
849 * 'Forces' workspace orientation by moving all cons into a new split-con with
850 * the same layout as the workspace and then changing the workspace layout.
851 *
852 */
853 void ws_force_orientation(Con *ws, orientation_t orientation) {
854 /* 1: create a new split container */
855 Con *split = con_new(NULL, NULL);
856 split->parent = ws;
857
858 /* 2: copy layout from workspace */
859 split->layout = ws->layout;
860
861 /* 3: move the existing cons of this workspace below the new con */
862 Con **focus_order = get_focus_order(ws);
863
864 DLOG("Moving cons\n");
865 while (!TAILQ_EMPTY(&(ws->nodes_head))) {
866 Con *child = TAILQ_FIRST(&(ws->nodes_head));
867 con_detach(child);
868 con_attach(child, split, true);
869 }
870
871 set_focus_order(split, focus_order);
872 free(focus_order);
873
874 /* 4: switch workspace layout */
875 ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV;
876 DLOG("split->layout = %d, ws->layout = %d\n", split->layout, ws->layout);
877
878 /* 5: attach the new split container to the workspace */
879 DLOG("Attaching new split (%p) to ws (%p)\n", split, ws);
880 con_attach(split, ws, false);
881
882 /* 6: fix the percentages */
883 con_fix_percent(ws);
884 }
885
886 /*
887 * Called when a new con (with a window, not an empty or split con) should be
888 * attached to the workspace (for example when managing a new window or when
889 * moving an existing window to the workspace level).
890 *
891 * Depending on the workspace_layout setting, this function either returns the
892 * workspace itself (default layout) or creates a new stacked/tabbed con and
893 * returns that.
894 *
895 */
896 Con *workspace_attach_to(Con *ws) {
897 DLOG("Attaching a window to workspace %p / %s\n", ws, ws->name);
898
899 if (ws->workspace_layout == L_DEFAULT) {
900 DLOG("Default layout, just attaching it to the workspace itself.\n");
901 return ws;
902 }
903
904 DLOG("Non-default layout, creating a new split container\n");
905 /* 1: create a new split container */
906 Con *new = con_new(NULL, NULL);
907 new->parent = ws;
908
909 /* 2: set the requested layout on the split con */
910 new->layout = ws->workspace_layout;
911
912 /* 4: attach the new split container to the workspace */
913 DLOG("Attaching new split %p to workspace %p\n", new, ws);
914 con_attach(new, ws, false);
915
916 /* 5: fix the percentages */
917 con_fix_percent(ws);
918
919 return new;
920 }
921
922 /*
923 * Creates a new container and re-parents all of children from the given
924 * workspace into it.
925 *
926 * The container inherits the layout from the workspace.
927 */
928 Con *workspace_encapsulate(Con *ws) {
929 if (TAILQ_EMPTY(&(ws->nodes_head))) {
930 ELOG("Workspace %p / %s has no children to encapsulate\n", ws, ws->name);
931 return NULL;
932 }
933
934 Con *new = con_new(NULL, NULL);
935 new->parent = ws;
936 new->layout = ws->layout;
937
938 Con **focus_order = get_focus_order(ws);
939
940 DLOG("Moving children of workspace %p / %s into container %p\n",
941 ws, ws->name, new);
942 Con *child;
943 while (!TAILQ_EMPTY(&(ws->nodes_head))) {
944 child = TAILQ_FIRST(&(ws->nodes_head));
945 con_detach(child);
946 con_attach(child, new, true);
947 }
948
949 set_focus_order(new, focus_order);
950 free(focus_order);
951
952 con_attach(new, ws, true);
953
954 return new;
955 }
956
957 /*
958 * Move the given workspace to the specified output.
959 * This returns true if and only if moving the workspace was successful.
960 */
961 bool workspace_move_to_output(Con *ws, Output *output) {
962 LOG("Trying to move workspace %p / %s to output %p / \"%s\".\n", ws, ws->name, output, output_primary_name(output));
963
964 Output *current_output = get_output_for_con(ws);
965 if (current_output == NULL) {
966 ELOG("Cannot get current output. This is a bug in i3.\n");
967 return false;
968 }
969
970 Con *content = output_get_content(output->con);
971 LOG("got output %p with content %p\n", output, content);
972
973 Con *previously_visible_ws = TAILQ_FIRST(&(content->focus_head));
974 LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
975
976 bool workspace_was_visible = workspace_is_visible(ws);
977 if (con_num_children(ws->parent) == 1) {
978 LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
979
980 /* check if we can find a workspace assigned to this output */
981 bool used_assignment = false;
982 struct Workspace_Assignment *assignment;
983 TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
984 if (!output_triggers_assignment(current_output, assignment)) {
985 continue;
986 }
987 /* check if this workspace is already attached to the tree */
988 if (get_existing_workspace_by_name(assignment->name) != NULL) {
989 continue;
990 }
991
992 /* so create the workspace referenced to by this assignment */
993 LOG("Creating workspace from assignment %s.\n", assignment->name);
994 workspace_get(assignment->name, NULL);
995 used_assignment = true;
996 break;
997 }
998
999 /* if we couldn't create the workspace using an assignment, create
1000 * it on the output */
1001 if (!used_assignment)
1002 create_workspace_on_output(current_output, ws->parent);
1003
1004 /* notify the IPC listeners */
1005 ipc_send_workspace_event("init", ws, NULL);
1006 }
1007 DLOG("Detaching\n");
1008
1009 /* detach from the old output and attach to the new output */
1010 Con *old_content = ws->parent;
1011 con_detach(ws);
1012 if (workspace_was_visible) {
1013 /* The workspace which we just detached was visible, so focus
1014 * the next one in the focus-stack. */
1015 Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
1016 LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
1017 workspace_show(focus_ws);
1018 }
1019 con_attach(ws, content, false);
1020
1021 /* fix the coordinates of the floating containers */
1022 Con *floating_con;
1023 TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
1024 floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
1025
1026 ipc_send_workspace_event("move", ws, NULL);
1027 if (workspace_was_visible) {
1028 /* Focus the moved workspace on the destination output. */
1029 workspace_show(ws);
1030 }
1031
1032 /* NB: We cannot simply work with previously_visible_ws since it might
1033 * have been cleaned up by workspace_show() already, depending on the
1034 * focus order/number of other workspaces on the output.
1035 * Instead, we loop through the available workspaces and only work with
1036 * previously_visible_ws if we still find it. */
1037 TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
1038 if (ws != previously_visible_ws)
1039 continue;
1040
1041 /* Call the on_remove_child callback of the workspace which previously
1042 * was visible on the destination output. Since it is no longer
1043 * visible, it might need to get cleaned up. */
1044 CALL(previously_visible_ws, on_remove_child);
1045 break;
1046 }
1047
1048 return true;
1049 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * x.c: Interface to X11, transfers our in-memory state to X11 (see also
7 * render.c). Basically a big state machine.
8 *
9 */
10 #include "all.h"
11
12 #ifndef MAX
13 #define MAX(x, y) ((x) > (y) ? (x) : (y))
14 #endif
15
16 xcb_window_t ewmh_window;
17
18 /* Stores the X11 window ID of the currently focused window */
19 xcb_window_t focused_id = XCB_NONE;
20
21 /* Because 'focused_id' might be reset to force input focus, we separately keep
22 * track of the X11 window ID to be able to always tell whether the focused
23 * window actually changed. */
24 static xcb_window_t last_focused = XCB_NONE;
25
26 /* Stores coordinates to warp mouse pointer to if set */
27 static Rect *warp_to;
28
29 /*
30 * Describes the X11 state we may modify (map state, position, window stack).
31 * There is one entry per container. The state represents the current situation
32 * as X11 sees it (with the exception of the order in the state_head CIRCLEQ,
33 * which represents the order that will be pushed to X11, while old_state_head
34 * represents the current order). It will be updated in x_push_changes().
35 *
36 */
37 typedef struct con_state {
38 xcb_window_t id;
39 bool mapped;
40 bool unmap_now;
41 bool child_mapped;
42 bool is_hidden;
43
44 /* The con for which this state is. */
45 Con *con;
46
47 /* For reparenting, we have a flag (need_reparent) and the X ID of the old
48 * frame this window was in. The latter is necessary because we need to
49 * ignore UnmapNotify events (by changing the window event mask). */
50 bool need_reparent;
51 xcb_window_t old_frame;
52
53 Rect rect;
54 Rect window_rect;
55
56 bool initial;
57
58 char *name;
59
60 CIRCLEQ_ENTRY(con_state)
61 state;
62
63 CIRCLEQ_ENTRY(con_state)
64 old_state;
65
66 TAILQ_ENTRY(con_state)
67 initial_mapping_order;
68 } con_state;
69
70 CIRCLEQ_HEAD(state_head, con_state)
71 state_head =
72 CIRCLEQ_HEAD_INITIALIZER(state_head);
73
74 CIRCLEQ_HEAD(old_state_head, con_state)
75 old_state_head =
76 CIRCLEQ_HEAD_INITIALIZER(old_state_head);
77
78 TAILQ_HEAD(initial_mapping_head, con_state)
79 initial_mapping_head =
80 TAILQ_HEAD_INITIALIZER(initial_mapping_head);
81
82 /*
83 * Returns the container state for the given frame. This function always
84 * returns a container state (otherwise, there is a bug in the code and the
85 * container state of a container for which x_con_init() was not called was
86 * requested).
87 *
88 */
89 static con_state *state_for_frame(xcb_window_t window) {
90 con_state *state;
91 CIRCLEQ_FOREACH(state, &state_head, state)
92 if (state->id == window)
93 return state;
94
95 /* TODO: better error handling? */
96 ELOG("No state found\n");
97 assert(false);
98 return NULL;
99 }
100
101 /*
102 * Changes the atoms on the root window and the windows themselves to properly
103 * reflect the current focus for ewmh compliance.
104 *
105 */
106 static void change_ewmh_focus(xcb_window_t new_focus, xcb_window_t old_focus) {
107 if (new_focus == old_focus) {
108 return;
109 }
110
111 ewmh_update_active_window(new_focus);
112
113 if (new_focus != XCB_WINDOW_NONE) {
114 ewmh_update_focused(new_focus, true);
115 }
116
117 if (old_focus != XCB_WINDOW_NONE) {
118 ewmh_update_focused(old_focus, false);
119 }
120 }
121
122 /*
123 * Initializes the X11 part for the given container. Called exactly once for
124 * every container from con_new().
125 *
126 */
127 void x_con_init(Con *con) {
128 /* TODO: maybe create the window when rendering first? we could then even
129 * get the initial geometry right */
130
131 uint32_t mask = 0;
132 uint32_t values[5];
133
134 xcb_visualid_t visual = get_visualid_by_depth(con->depth);
135 xcb_colormap_t win_colormap;
136 if (con->depth != root_depth) {
137 /* We need to create a custom colormap. */
138 win_colormap = xcb_generate_id(conn);
139 xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, root, visual);
140 con->colormap = win_colormap;
141 } else {
142 /* Use the default colormap. */
143 win_colormap = colormap;
144 con->colormap = XCB_NONE;
145 }
146
147 /* We explicitly set a background color and border color (even though we
148 * don’t even have a border) because the X11 server requires us to when
149 * using 32 bit color depths, see
150 * https://stackoverflow.com/questions/3645632 */
151 mask |= XCB_CW_BACK_PIXEL;
152 values[0] = root_screen->black_pixel;
153
154 mask |= XCB_CW_BORDER_PIXEL;
155 values[1] = root_screen->black_pixel;
156
157 /* our own frames should not be managed */
158 mask |= XCB_CW_OVERRIDE_REDIRECT;
159 values[2] = 1;
160
161 /* see include/xcb.h for the FRAME_EVENT_MASK */
162 mask |= XCB_CW_EVENT_MASK;
163 values[3] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
164
165 mask |= XCB_CW_COLORMAP;
166 values[4] = win_colormap;
167
168 Rect dims = {-15, -15, 10, 10};
169 xcb_window_t frame_id = create_window(conn, dims, con->depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values);
170 draw_util_surface_init(conn, &(con->frame), frame_id, get_visualtype_by_id(visual), dims.width, dims.height);
171 xcb_change_property(conn,
172 XCB_PROP_MODE_REPLACE,
173 con->frame.id,
174 XCB_ATOM_WM_CLASS,
175 XCB_ATOM_STRING,
176 8,
177 (strlen("i3-frame") + 1) * 2,
178 "i3-frame\0i3-frame\0");
179
180 struct con_state *state = scalloc(1, sizeof(struct con_state));
181 state->id = con->frame.id;
182 state->mapped = false;
183 state->initial = true;
184 DLOG("Adding window 0x%08x to lists\n", state->id);
185 CIRCLEQ_INSERT_HEAD(&state_head, state, state);
186 CIRCLEQ_INSERT_HEAD(&old_state_head, state, old_state);
187 TAILQ_INSERT_TAIL(&initial_mapping_head, state, initial_mapping_order);
188 DLOG("adding new state for window id 0x%08x\n", state->id);
189 }
190
191 /*
192 * Re-initializes the associated X window state for this container. You have
193 * to call this when you assign a client to an empty container to ensure that
194 * its state gets updated correctly.
195 *
196 */
197 void x_reinit(Con *con) {
198 struct con_state *state;
199
200 if ((state = state_for_frame(con->frame.id)) == NULL) {
201 ELOG("window state not found\n");
202 return;
203 }
204
205 DLOG("resetting state %p to initial\n", state);
206 state->initial = true;
207 state->child_mapped = false;
208 state->con = con;
209 memset(&(state->window_rect), 0, sizeof(Rect));
210 }
211
212 /*
213 * Reparents the child window of the given container (necessary for sticky
214 * containers). The reparenting happens in the next call of x_push_changes().
215 *
216 */
217 void x_reparent_child(Con *con, Con *old) {
218 struct con_state *state;
219 if ((state = state_for_frame(con->frame.id)) == NULL) {
220 ELOG("window state for con not found\n");
221 return;
222 }
223
224 state->need_reparent = true;
225 state->old_frame = old->frame.id;
226 }
227
228 /*
229 * Moves a child window from Container src to Container dest.
230 *
231 */
232 void x_move_win(Con *src, Con *dest) {
233 struct con_state *state_src, *state_dest;
234
235 if ((state_src = state_for_frame(src->frame.id)) == NULL) {
236 ELOG("window state for src not found\n");
237 return;
238 }
239
240 if ((state_dest = state_for_frame(dest->frame.id)) == NULL) {
241 ELOG("window state for dest not found\n");
242 return;
243 }
244
245 state_dest->con = state_src->con;
246 state_src->con = NULL;
247
248 Rect zero = {0, 0, 0, 0};
249 if (memcmp(&(state_dest->window_rect), &(zero), sizeof(Rect)) == 0) {
250 memcpy(&(state_dest->window_rect), &(state_src->window_rect), sizeof(Rect));
251 DLOG("COPYING RECT\n");
252 }
253 }
254
255 static void _x_con_kill(Con *con) {
256 con_state *state;
257
258 if (con->colormap != XCB_NONE) {
259 xcb_free_colormap(conn, con->colormap);
260 }
261
262 draw_util_surface_free(conn, &(con->frame));
263 draw_util_surface_free(conn, &(con->frame_buffer));
264 xcb_free_pixmap(conn, con->frame_buffer.id);
265 state = state_for_frame(con->frame.id);
266 CIRCLEQ_REMOVE(&state_head, state, state);
267 CIRCLEQ_REMOVE(&old_state_head, state, old_state);
268 TAILQ_REMOVE(&initial_mapping_head, state, initial_mapping_order);
269 FREE(state->name);
270 free(state);
271
272 /* Invalidate focused_id to correctly focus new windows with the same ID */
273 focused_id = last_focused = XCB_NONE;
274 }
275
276 /*
277 * Kills the window decoration associated with the given container.
278 *
279 */
280 void x_con_kill(Con *con) {
281 _x_con_kill(con);
282 xcb_destroy_window(conn, con->frame.id);
283 }
284
285 /*
286 * Completely reinitializes the container's frame, without destroying the old window.
287 *
288 */
289 void x_con_reframe(Con *con) {
290 _x_con_kill(con);
291 x_con_init(con);
292 }
293
294 /*
295 * Returns true if the client supports the given protocol atom (like WM_DELETE_WINDOW)
296 *
297 */
298 bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom) {
299 xcb_get_property_cookie_t cookie;
300 xcb_icccm_get_wm_protocols_reply_t protocols;
301 bool result = false;
302
303 cookie = xcb_icccm_get_wm_protocols(conn, window, A_WM_PROTOCOLS);
304 if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &protocols, NULL) != 1)
305 return false;
306
307 /* Check if the client’s protocols have the requested atom set */
308 for (uint32_t i = 0; i < protocols.atoms_len; i++)
309 if (protocols.atoms[i] == atom)
310 result = true;
311
312 xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
313
314 return result;
315 }
316
317 /*
318 * Kills the given X11 window using WM_DELETE_WINDOW (if supported).
319 *
320 */
321 void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
322 /* if this window does not support WM_DELETE_WINDOW, we kill it the hard way */
323 if (!window_supports_protocol(window, A_WM_DELETE_WINDOW)) {
324 if (kill_window == KILL_WINDOW) {
325 LOG("Killing specific window 0x%08x\n", window);
326 xcb_destroy_window(conn, window);
327 } else {
328 LOG("Killing the X11 client which owns window 0x%08x\n", window);
329 xcb_kill_client(conn, window);
330 }
331 return;
332 }
333
334 /* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
335 * In order to properly initialize these bytes, we allocate 32 bytes even
336 * though we only need less for an xcb_configure_notify_event_t */
337 void *event = scalloc(32, 1);
338 xcb_client_message_event_t *ev = event;
339
340 ev->response_type = XCB_CLIENT_MESSAGE;
341 ev->window = window;
342 ev->type = A_WM_PROTOCOLS;
343 ev->format = 32;
344 ev->data.data32[0] = A_WM_DELETE_WINDOW;
345 ev->data.data32[1] = XCB_CURRENT_TIME;
346
347 LOG("Sending WM_DELETE to the client\n");
348 xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev);
349 xcb_flush(conn);
350 free(event);
351 }
352
353 static void x_draw_title_border(Con *con, struct deco_render_params *p) {
354 assert(con->parent != NULL);
355
356 Rect *dr = &(con->deco_rect);
357 adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
358 int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
359 int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width;
360 if (con->parent->layout == L_TABBED ||
361 (con->parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
362 deco_diff_l = 0;
363 deco_diff_r = 0;
364 }
365
366 draw_util_rectangle(&(con->parent->frame_buffer), p->color->border,
367 dr->x, dr->y, dr->width, 1);
368
369 draw_util_rectangle(&(con->parent->frame_buffer), p->color->border,
370 dr->x + deco_diff_l, dr->y + dr->height - 1, dr->width - (deco_diff_l + deco_diff_r), 1);
371 }
372
373 static void x_draw_decoration_after_title(Con *con, struct deco_render_params *p) {
374 assert(con->parent != NULL);
375
376 Rect *dr = &(con->deco_rect);
377
378 /* Redraw the right border to cut off any text that went past it.
379 * This is necessary when the text was drawn using XCB since cutting text off
380 * automatically does not work there. For pango rendering, this isn't necessary. */
381 if (!font_is_pango()) {
382 /* We actually only redraw the far right two pixels as that is the
383 * distance we keep from the edge (not the entire border width).
384 * Redrawing the entire border would cause text to be cut off. */
385 draw_util_rectangle(&(con->parent->frame_buffer), p->color->background,
386 dr->x + dr->width - 2 * logical_px(1),
387 dr->y,
388 2 * logical_px(1),
389 dr->height);
390 }
391
392 /* Draw a 1px separator line before and after every tab, so that tabs can
393 * be easily distinguished. */
394 if (con->parent->layout == L_TABBED) {
395 /* Left side */
396 draw_util_rectangle(&(con->parent->frame_buffer), p->color->border,
397 dr->x, dr->y, 1, dr->height);
398
399 /* Right side */
400 draw_util_rectangle(&(con->parent->frame_buffer), p->color->border,
401 dr->x + dr->width - 1, dr->y, 1, dr->height);
402 }
403
404 /* Redraw the border. */
405 x_draw_title_border(con, p);
406 }
407
408 /*
409 * Draws the decoration of the given container onto its parent.
410 *
411 */
412 void x_draw_decoration(Con *con) {
413 Con *parent = con->parent;
414 bool leaf = con_is_leaf(con);
415
416 /* This code needs to run for:
417 * • leaf containers
418 * • non-leaf containers which are in a stacked/tabbed container
419 *
420 * It does not need to run for:
421 * • direct children of outputs or dockareas
422 * • floating containers (they don’t have a decoration)
423 */
424 if ((!leaf &&
425 parent->layout != L_STACKED &&
426 parent->layout != L_TABBED) ||
427 parent->type == CT_OUTPUT ||
428 parent->type == CT_DOCKAREA ||
429 con->type == CT_FLOATING_CON)
430 return;
431
432 /* Skip containers whose height is 0 (for example empty dockareas) */
433 if (con->rect.height == 0)
434 return;
435
436 /* Skip containers whose pixmap has not yet been created (can happen when
437 * decoration rendering happens recursively for a window for which
438 * x_push_node() was not yet called) */
439 if (leaf && con->frame_buffer.id == XCB_NONE)
440 return;
441
442 /* 1: build deco_params and compare with cache */
443 struct deco_render_params *p = scalloc(1, sizeof(struct deco_render_params));
444
445 /* find out which colors to use */
446 if (con->urgent)
447 p->color = &config.client.urgent;
448 else if (con == focused || con_inside_focused(con))
449 p->color = &config.client.focused;
450 else if (con == TAILQ_FIRST(&(parent->focus_head)))
451 p->color = &config.client.focused_inactive;
452 else
453 p->color = &config.client.unfocused;
454
455 p->border_style = con_border_style(con);
456
457 Rect *r = &(con->rect);
458 Rect *w = &(con->window_rect);
459 p->con_rect = (struct width_height){r->width, r->height};
460 p->con_window_rect = (struct width_height){w->width, w->height};
461 p->con_deco_rect = con->deco_rect;
462 p->background = config.client.background;
463 p->con_is_leaf = con_is_leaf(con);
464 p->parent_layout = con->parent->layout;
465
466 if (con->deco_render_params != NULL &&
467 (con->window == NULL || !con->window->name_x_changed) &&
468 !parent->pixmap_recreated &&
469 !con->pixmap_recreated &&
470 !con->mark_changed &&
471 memcmp(p, con->deco_render_params, sizeof(struct deco_render_params)) == 0) {
472 free(p);
473 goto copy_pixmaps;
474 }
475
476 Con *next = con;
477 while ((next = TAILQ_NEXT(next, nodes))) {
478 FREE(next->deco_render_params);
479 }
480
481 FREE(con->deco_render_params);
482 con->deco_render_params = p;
483
484 if (con->window != NULL && con->window->name_x_changed)
485 con->window->name_x_changed = false;
486
487 parent->pixmap_recreated = false;
488 con->pixmap_recreated = false;
489 con->mark_changed = false;
490
491 /* 2: draw the client.background, but only for the parts around the window_rect */
492 if (con->window != NULL) {
493 /* top area */
494 draw_util_rectangle(&(con->frame_buffer), config.client.background,
495 0, 0, r->width, w->y);
496 /* bottom area */
497 draw_util_rectangle(&(con->frame_buffer), config.client.background,
498 0, w->y + w->height, r->width, r->height - (w->y + w->height));
499 /* left area */
500 draw_util_rectangle(&(con->frame_buffer), config.client.background,
501 0, 0, w->x, r->height);
502 /* right area */
503 draw_util_rectangle(&(con->frame_buffer), config.client.background,
504 w->x + w->width, 0, r->width - (w->x + w->width), r->height);
505 }
506
507 /* 3: draw a rectangle in border color around the client */
508 if (p->border_style != BS_NONE && p->con_is_leaf) {
509 /* We might hide some borders adjacent to the screen-edge */
510 adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
511 Rect br = con_border_style_rect(con);
512
513 /* These rectangles represent the border around the child window
514 * (left, bottom and right part). We don’t just fill the whole
515 * rectangle because some children are not freely resizable and we want
516 * their background color to "shine through". */
517 if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
518 draw_util_rectangle(&(con->frame_buffer), p->color->child_border, 0, 0, br.x, r->height);
519 }
520 if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
521 draw_util_rectangle(&(con->frame_buffer),
522 p->color->child_border, r->width + (br.width + br.x), 0,
523 -(br.width + br.x), r->height);
524 }
525 if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
526 draw_util_rectangle(&(con->frame_buffer),
527 p->color->child_border, br.x, r->height + (br.height + br.y),
528 r->width + br.width, -(br.height + br.y));
529 }
530 /* pixel border needs an additional line at the top */
531 if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
532 draw_util_rectangle(&(con->frame_buffer),
533 p->color->child_border, br.x, 0, r->width + br.width, br.y);
534 }
535
536 /* Highlight the side of the border at which the next window will be
537 * opened if we are rendering a single window within a split container
538 * (which is undistinguishable from a single window outside a split
539 * container otherwise. */
540 if (TAILQ_NEXT(con, nodes) == NULL &&
541 TAILQ_PREV(con, nodes_head, nodes) == NULL &&
542 con->parent->type != CT_FLOATING_CON) {
543 if (p->parent_layout == L_SPLITH) {
544 draw_util_rectangle(&(con->frame_buffer), p->color->indicator,
545 r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height);
546 } else if (p->parent_layout == L_SPLITV) {
547 draw_util_rectangle(&(con->frame_buffer), p->color->indicator,
548 br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y));
549 }
550 }
551 }
552
553 /* if this is a borderless/1pixel window, we don’t need to render the
554 * decoration. */
555 if (p->border_style != BS_NORMAL)
556 goto copy_pixmaps;
557
558 /* If the parent hasn't been set up yet, skip the decoration rendering
559 * for now. */
560 if (parent->frame_buffer.id == XCB_NONE)
561 goto copy_pixmaps;
562
563 /* For the first child, we clear the parent pixmap to ensure there's no
564 * garbage left on there. This is important to avoid tearing when using
565 * transparency. */
566 if (con == TAILQ_FIRST(&(con->parent->nodes_head))) {
567 draw_util_clear_surface(&(con->parent->frame_buffer), COLOR_TRANSPARENT);
568 FREE(con->parent->deco_render_params);
569 }
570
571 /* 4: paint the bar */
572 draw_util_rectangle(&(parent->frame_buffer), p->color->background,
573 con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height);
574
575 /* 5: draw two unconnected horizontal lines in border color */
576 x_draw_title_border(con, p);
577
578 /* 6: draw the title */
579 int text_offset_y = (con->deco_rect.height - config.font.height) / 2;
580
581 const int title_padding = logical_px(2);
582 const int deco_width = (int)con->deco_rect.width;
583 int mark_width = 0;
584 if (config.show_marks && !TAILQ_EMPTY(&(con->marks_head))) {
585 char *formatted_mark = sstrdup("");
586 bool had_visible_mark = false;
587
588 mark_t *mark;
589 TAILQ_FOREACH(mark, &(con->marks_head), marks) {
590 if (mark->name[0] == '_')
591 continue;
592 had_visible_mark = true;
593
594 char *buf;
595 sasprintf(&buf, "%s[%s]", formatted_mark, mark->name);
596 free(formatted_mark);
597 formatted_mark = buf;
598 }
599
600 if (had_visible_mark) {
601 i3String *mark = i3string_from_utf8(formatted_mark);
602 mark_width = predict_text_width(mark);
603
604 int mark_offset_x = (config.title_align == ALIGN_RIGHT)
605 ? title_padding
606 : deco_width - mark_width - title_padding;
607
608 draw_util_text(mark, &(parent->frame_buffer),
609 p->color->text, p->color->background,
610 con->deco_rect.x + mark_offset_x,
611 con->deco_rect.y + text_offset_y, mark_width);
612 I3STRING_FREE(mark);
613
614 mark_width += title_padding;
615 }
616
617 FREE(formatted_mark);
618 }
619
620 i3String *title = NULL;
621 struct Window *win = con->window;
622 if (win == NULL) {
623 if (con->title_format == NULL) {
624 char *_title;
625 char *tree = con_get_tree_representation(con);
626 sasprintf(&_title, "i3: %s", tree);
627 free(tree);
628
629 title = i3string_from_utf8(_title);
630 FREE(_title);
631 } else {
632 title = con_parse_title_format(con);
633 }
634 } else {
635 title = con->title_format == NULL ? win->name : con_parse_title_format(con);
636 }
637 if (title == NULL) {
638 goto copy_pixmaps;
639 }
640
641 int title_offset_x;
642 switch (config.title_align) {
643 case ALIGN_LEFT:
644 /* (pad)[text ](pad)[mark + its pad) */
645 title_offset_x = title_padding;
646 break;
647 case ALIGN_CENTER:
648 /* (pad)[ text ](pad)[mark + its pad)
649 * To center the text inside its allocated space, the surface
650 * between the brackets, we use the formula
651 * (surface_width - predict_text_width) / 2
652 * where surface_width = deco_width - 2 * pad - mark_width
653 * so, offset = pad + (surface_width - predict_text_width) / 2 =
654 * = … = (deco_width - mark_width - predict_text_width) / 2 */
655 title_offset_x = max(title_padding, (deco_width - mark_width - predict_text_width(title)) / 2);
656 break;
657 case ALIGN_RIGHT:
658 /* [mark + its pad](pad)[ text](pad) */
659 title_offset_x = max(title_padding + mark_width, deco_width - title_padding - predict_text_width(title));
660 break;
661 }
662
663 draw_util_text(title, &(parent->frame_buffer),
664 p->color->text, p->color->background,
665 con->deco_rect.x + title_offset_x,
666 con->deco_rect.y + text_offset_y,
667 deco_width - mark_width - 2 * title_padding);
668
669 if (win == NULL || con->title_format != NULL) {
670 I3STRING_FREE(title);
671 }
672
673 x_draw_decoration_after_title(con, p);
674 copy_pixmaps:
675 draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
676 }
677
678 /*
679 * Recursively calls x_draw_decoration. This cannot be done in x_push_node
680 * because x_push_node uses focus order to recurse (see the comment above)
681 * while drawing the decoration needs to happen in the actual order.
682 *
683 */
684 void x_deco_recurse(Con *con) {
685 Con *current;
686 bool leaf = TAILQ_EMPTY(&(con->nodes_head)) &&
687 TAILQ_EMPTY(&(con->floating_head));
688 con_state *state = state_for_frame(con->frame.id);
689
690 if (!leaf) {
691 TAILQ_FOREACH(current, &(con->nodes_head), nodes)
692 x_deco_recurse(current);
693
694 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
695 x_deco_recurse(current);
696
697 if (state->mapped) {
698 draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
699 }
700 }
701
702 if ((con->type != CT_ROOT && con->type != CT_OUTPUT) &&
703 (!leaf || con->mapped))
704 x_draw_decoration(con);
705 }
706
707 /*
708 * Sets or removes the _NET_WM_STATE_HIDDEN property on con if necessary.
709 *
710 */
711 static void set_hidden_state(Con *con) {
712 if (con->window == NULL) {
713 return;
714 }
715
716 con_state *state = state_for_frame(con->frame.id);
717 bool should_be_hidden = con_is_hidden(con);
718 if (should_be_hidden == state->is_hidden)
719 return;
720
721 if (should_be_hidden) {
722 DLOG("setting _NET_WM_STATE_HIDDEN for con = %p\n", con);
723 xcb_add_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_HIDDEN);
724 } else {
725 DLOG("removing _NET_WM_STATE_HIDDEN for con = %p\n", con);
726 xcb_remove_property_atom(conn, con->window->id, A__NET_WM_STATE, A__NET_WM_STATE_HIDDEN);
727 }
728
729 state->is_hidden = should_be_hidden;
730 }
731
732 /*
733 * This function pushes the properties of each node of the layout tree to
734 * X11 if they have changed (like the map state, position of the window, …).
735 * It recursively traverses all children of the given node.
736 *
737 */
738 void x_push_node(Con *con) {
739 Con *current;
740 con_state *state;
741 Rect rect = con->rect;
742
743 //DLOG("Pushing changes for node %p / %s\n", con, con->name);
744 state = state_for_frame(con->frame.id);
745
746 if (state->name != NULL) {
747 DLOG("pushing name %s for con %p\n", state->name, con);
748
749 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->frame.id,
750 XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(state->name), state->name);
751 FREE(state->name);
752 }
753
754 if (con->window == NULL) {
755 /* Calculate the height of all window decorations which will be drawn on to
756 * this frame. */
757 uint32_t max_y = 0, max_height = 0;
758 TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
759 Rect *dr = &(current->deco_rect);
760 if (dr->y >= max_y && dr->height >= max_height) {
761 max_y = dr->y;
762 max_height = dr->height;
763 }
764 }
765 rect.height = max_y + max_height;
766 if (rect.height == 0)
767 con->mapped = false;
768 }
769
770 /* reparent the child window (when the window was moved due to a sticky
771 * container) */
772 if (state->need_reparent && con->window != NULL) {
773 DLOG("Reparenting child window\n");
774
775 /* Temporarily set the event masks to XCB_NONE so that we won’t get
776 * UnmapNotify events (otherwise the handler would close the container).
777 * These events are generated automatically when reparenting. */
778 uint32_t values[] = {XCB_NONE};
779 xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values);
780 xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values);
781
782 xcb_reparent_window(conn, con->window->id, con->frame.id, 0, 0);
783
784 values[0] = FRAME_EVENT_MASK;
785 xcb_change_window_attributes(conn, state->old_frame, XCB_CW_EVENT_MASK, values);
786 values[0] = CHILD_EVENT_MASK;
787 xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values);
788
789 state->old_frame = XCB_NONE;
790 state->need_reparent = false;
791
792 con->ignore_unmap++;
793 DLOG("ignore_unmap for reparenting of con %p (win 0x%08x) is now %d\n",
794 con, con->window->id, con->ignore_unmap);
795 }
796
797 /* The pixmap of a borderless leaf container will not be used except
798 * for the titlebar in a stack or tabs (issue #1013). */
799 bool is_pixmap_needed = (con->border_style != BS_NONE ||
800 !con_is_leaf(con) ||
801 con->parent->layout == L_STACKED ||
802 con->parent->layout == L_TABBED);
803
804 /* The root con and output cons will never require a pixmap. In particular for the
805 * __i3 output, this will likely not work anyway because it might be ridiculously
806 * large, causing an XCB_ALLOC error. */
807 if (con->type == CT_ROOT || con->type == CT_OUTPUT)
808 is_pixmap_needed = false;
809
810 bool fake_notify = false;
811 /* Set new position if rect changed (and if height > 0) or if the pixmap
812 * needs to be recreated */
813 if ((is_pixmap_needed && con->frame_buffer.id == XCB_NONE) || (memcmp(&(state->rect), &rect, sizeof(Rect)) != 0 &&
814 rect.height > 0)) {
815 /* We first create the new pixmap, then render to it, set it as the
816 * background and only afterwards change the window size. This reduces
817 * flickering. */
818
819 bool has_rect_changed = (state->rect.x != rect.x || state->rect.y != rect.y ||
820 state->rect.width != rect.width || state->rect.height != rect.height);
821
822 /* Check if the container has an unneeded pixmap left over from
823 * previously having a border or titlebar. */
824 if (!is_pixmap_needed && con->frame_buffer.id != XCB_NONE) {
825 draw_util_surface_free(conn, &(con->frame_buffer));
826 xcb_free_pixmap(conn, con->frame_buffer.id);
827 con->frame_buffer.id = XCB_NONE;
828 }
829
830 if (is_pixmap_needed && (has_rect_changed || con->frame_buffer.id == XCB_NONE)) {
831 if (con->frame_buffer.id == XCB_NONE) {
832 con->frame_buffer.id = xcb_generate_id(conn);
833 } else {
834 draw_util_surface_free(conn, &(con->frame_buffer));
835 xcb_free_pixmap(conn, con->frame_buffer.id);
836 }
837
838 uint16_t win_depth = root_depth;
839 if (con->window)
840 win_depth = con->window->depth;
841
842 /* Ensure we have valid dimensions for our surface. */
843 // TODO This is probably a bug in the condition above as we should never enter this path
844 // for height == 0. Also, we should probably handle width == 0 the same way.
845 int width = MAX((int32_t)rect.width, 1);
846 int height = MAX((int32_t)rect.height, 1);
847
848 xcb_create_pixmap(conn, win_depth, con->frame_buffer.id, con->frame.id, width, height);
849 draw_util_surface_init(conn, &(con->frame_buffer), con->frame_buffer.id,
850 get_visualtype_by_id(get_visualid_by_depth(win_depth)), width, height);
851
852 /* For the graphics context, we disable GraphicsExposure events.
853 * Those will be sent when a CopyArea request cannot be fulfilled
854 * properly due to parts of the source being unmapped or otherwise
855 * unavailable. Since we always copy from pixmaps to windows, this
856 * is not a concern for us. */
857 xcb_change_gc(conn, con->frame_buffer.gc, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
858
859 draw_util_surface_set_size(&(con->frame), width, height);
860 con->pixmap_recreated = true;
861
862 /* Don’t render the decoration for windows inside a stack which are
863 * not visible right now */
864 // TODO Should this work the same way for L_TABBED?
865 if (!con->parent ||
866 con->parent->layout != L_STACKED ||
867 TAILQ_FIRST(&(con->parent->focus_head)) == con)
868 /* Render the decoration now to make the correct decoration visible
869 * from the very first moment. Later calls will be cached, so this
870 * doesn’t hurt performance. */
871 x_deco_recurse(con);
872 }
873
874 DLOG("setting rect (%d, %d, %d, %d)\n", rect.x, rect.y, rect.width, rect.height);
875 /* flush to ensure that the following commands are sent in a single
876 * buffer and will be processed directly afterwards (the contents of a
877 * window get lost when resizing it, therefore we want to provide it as
878 * fast as possible) */
879 xcb_flush(conn);
880 xcb_set_window_rect(conn, con->frame.id, rect);
881 if (con->frame_buffer.id != XCB_NONE) {
882 draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
883 }
884 xcb_flush(conn);
885
886 memcpy(&(state->rect), &rect, sizeof(Rect));
887 fake_notify = true;
888 }
889
890 /* dito, but for child windows */
891 if (con->window != NULL &&
892 memcmp(&(state->window_rect), &(con->window_rect), sizeof(Rect)) != 0) {
893 DLOG("setting window rect (%d, %d, %d, %d)\n",
894 con->window_rect.x, con->window_rect.y, con->window_rect.width, con->window_rect.height);
895 xcb_set_window_rect(conn, con->window->id, con->window_rect);
896 memcpy(&(state->window_rect), &(con->window_rect), sizeof(Rect));
897 fake_notify = true;
898 }
899
900 /* Map if map state changed, also ensure that the child window
901 * is changed if we are mapped and there is a new, unmapped child window.
902 * Unmaps are handled in x_push_node_unmaps(). */
903 if ((state->mapped != con->mapped || (con->window != NULL && !state->child_mapped)) &&
904 con->mapped) {
905 xcb_void_cookie_t cookie;
906
907 if (con->window != NULL) {
908 /* Set WM_STATE_NORMAL because GTK applications don’t want to
909 * drag & drop if we don’t. Also, xprop(1) needs it. */
910 long data[] = {XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE};
911 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
912 A_WM_STATE, A_WM_STATE, 32, 2, data);
913 }
914
915 uint32_t values[1];
916 if (!state->child_mapped && con->window != NULL) {
917 cookie = xcb_map_window(conn, con->window->id);
918
919 /* We are interested in EnterNotifys as soon as the window is
920 * mapped */
921 values[0] = CHILD_EVENT_MASK;
922 xcb_change_window_attributes(conn, con->window->id, XCB_CW_EVENT_MASK, values);
923 DLOG("mapping child window (serial %d)\n", cookie.sequence);
924 state->child_mapped = true;
925 }
926
927 cookie = xcb_map_window(conn, con->frame.id);
928
929 values[0] = FRAME_EVENT_MASK;
930 xcb_change_window_attributes(conn, con->frame.id, XCB_CW_EVENT_MASK, values);
931
932 /* copy the pixmap contents to the frame window immediately after mapping */
933 if (con->frame_buffer.id != XCB_NONE) {
934 draw_util_copy_surface(&(con->frame_buffer), &(con->frame), 0, 0, 0, 0, con->rect.width, con->rect.height);
935 }
936 xcb_flush(conn);
937
938 DLOG("mapping container %08x (serial %d)\n", con->frame.id, cookie.sequence);
939 state->mapped = con->mapped;
940 }
941
942 state->unmap_now = (state->mapped != con->mapped) && !con->mapped;
943
944 if (fake_notify) {
945 DLOG("Sending fake configure notify\n");
946 fake_absolute_configure_notify(con);
947 }
948
949 set_hidden_state(con);
950
951 /* Handle all children and floating windows of this node. We recurse
952 * in focus order to display the focused client in a stack first when
953 * switching workspaces (reduces flickering). */
954 TAILQ_FOREACH(current, &(con->focus_head), focused) {
955 x_push_node(current);
956 }
957 }
958
959 /*
960 * Same idea as in x_push_node(), but this function only unmaps windows. It is
961 * necessary to split this up to handle new fullscreen clients properly: The
962 * new window needs to be mapped and focus needs to be set *before* the
963 * underlying windows are unmapped. Otherwise, focus will revert to the
964 * PointerRoot and will then be set to the new window, generating unnecessary
965 * FocusIn/FocusOut events.
966 *
967 */
968 static void x_push_node_unmaps(Con *con) {
969 Con *current;
970 con_state *state;
971
972 //DLOG("Pushing changes (with unmaps) for node %p / %s\n", con, con->name);
973 state = state_for_frame(con->frame.id);
974
975 /* map/unmap if map state changed, also ensure that the child window
976 * is changed if we are mapped *and* in initial state (meaning the
977 * container was empty before, but now got a child) */
978 if (state->unmap_now) {
979 xcb_void_cookie_t cookie;
980 if (con->window != NULL) {
981 /* Set WM_STATE_WITHDRAWN, it seems like Java apps need it */
982 long data[] = {XCB_ICCCM_WM_STATE_WITHDRAWN, XCB_NONE};
983 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id,
984 A_WM_STATE, A_WM_STATE, 32, 2, data);
985 }
986
987 cookie = xcb_unmap_window(conn, con->frame.id);
988 DLOG("unmapping container %p / %s (serial %d)\n", con, con->name, cookie.sequence);
989 /* we need to increase ignore_unmap for this container (if it
990 * contains a window) and for every window "under" this one which
991 * contains a window */
992 if (con->window != NULL) {
993 con->ignore_unmap++;
994 DLOG("ignore_unmap for con %p (frame 0x%08x) now %d\n", con, con->frame.id, con->ignore_unmap);
995 }
996 state->mapped = con->mapped;
997 }
998
999 /* handle all children and floating windows of this node */
1000 TAILQ_FOREACH(current, &(con->nodes_head), nodes)
1001 x_push_node_unmaps(current);
1002
1003 TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
1004 x_push_node_unmaps(current);
1005 }
1006
1007 /*
1008 * Returns true if the given container is currently attached to its parent.
1009 *
1010 * TODO: Remove once #1185 has been fixed
1011 */
1012 static bool is_con_attached(Con *con) {
1013 if (con->parent == NULL)
1014 return false;
1015
1016 Con *current;
1017 TAILQ_FOREACH(current, &(con->parent->nodes_head), nodes) {
1018 if (current == con)
1019 return true;
1020 }
1021
1022 return false;
1023 }
1024
1025 /*
1026 * Pushes all changes (state of each node, see x_push_node() and the window
1027 * stack) to X11.
1028 *
1029 * NOTE: We need to push the stack first so that the windows have the correct
1030 * stacking order. This is relevant for workspace switching where we map the
1031 * windows because mapping may generate EnterNotify events. When they are
1032 * generated in the wrong order, this will cause focus problems when switching
1033 * workspaces.
1034 *
1035 */
1036 void x_push_changes(Con *con) {
1037 con_state *state;
1038 xcb_query_pointer_cookie_t pointercookie;
1039
1040 /* If we need to warp later, we request the pointer position as soon as possible */
1041 if (warp_to) {
1042 pointercookie = xcb_query_pointer(conn, root);
1043 }
1044
1045 DLOG("-- PUSHING WINDOW STACK --\n");
1046 //DLOG("Disabling EnterNotify\n");
1047 /* We need to keep SubstructureRedirect around, otherwise clients can send
1048 * ConfigureWindow requests and get them applied directly instead of having
1049 * them become ConfigureRequests that i3 handles. */
1050 uint32_t values[1] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
1051 CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
1052 if (state->mapped)
1053 xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
1054 }
1055 //DLOG("Done, EnterNotify disabled\n");
1056 bool order_changed = false;
1057 bool stacking_changed = false;
1058
1059 /* count first, necessary to (re)allocate memory for the bottom-to-top
1060 * stack afterwards */
1061 int cnt = 0;
1062 CIRCLEQ_FOREACH_REVERSE(state, &state_head, state)
1063 if (con_has_managed_window(state->con))
1064 cnt++;
1065
1066 /* The bottom-to-top window stack of all windows which are managed by i3.
1067 * Used for x_get_window_stack(). */
1068 static xcb_window_t *client_list_windows = NULL;
1069 static int client_list_count = 0;
1070
1071 if (cnt != client_list_count) {
1072 client_list_windows = srealloc(client_list_windows, sizeof(xcb_window_t) * cnt);
1073 client_list_count = cnt;
1074 }
1075
1076 xcb_window_t *walk = client_list_windows;
1077
1078 /* X11 correctly represents the stack if we push it from bottom to top */
1079 CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
1080 if (con_has_managed_window(state->con))
1081 memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t));
1082
1083 //DLOG("stack: 0x%08x\n", state->id);
1084 con_state *prev = CIRCLEQ_PREV(state, state);
1085 con_state *old_prev = CIRCLEQ_PREV(state, old_state);
1086 if (prev != old_prev)
1087 order_changed = true;
1088 if ((state->initial || order_changed) && prev != CIRCLEQ_END(&state_head)) {
1089 stacking_changed = true;
1090 //DLOG("Stacking 0x%08x above 0x%08x\n", prev->id, state->id);
1091 uint32_t mask = 0;
1092 mask |= XCB_CONFIG_WINDOW_SIBLING;
1093 mask |= XCB_CONFIG_WINDOW_STACK_MODE;
1094 uint32_t values[] = {state->id, XCB_STACK_MODE_ABOVE};
1095
1096 xcb_configure_window(conn, prev->id, mask, values);
1097 }
1098 state->initial = false;
1099 }
1100
1101 /* If we re-stacked something (or a new window appeared), we need to update
1102 * the _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING hints */
1103 if (stacking_changed) {
1104 DLOG("Client list changed (%i clients)\n", cnt);
1105 ewmh_update_client_list_stacking(client_list_windows, client_list_count);
1106
1107 walk = client_list_windows;
1108
1109 /* reorder by initial mapping */
1110 TAILQ_FOREACH(state, &initial_mapping_head, initial_mapping_order) {
1111 if (con_has_managed_window(state->con))
1112 *walk++ = state->con->window->id;
1113 }
1114
1115 ewmh_update_client_list(client_list_windows, client_list_count);
1116 }
1117
1118 DLOG("PUSHING CHANGES\n");
1119 x_push_node(con);
1120
1121 if (warp_to) {
1122 xcb_query_pointer_reply_t *pointerreply = xcb_query_pointer_reply(conn, pointercookie, NULL);
1123 if (!pointerreply) {
1124 ELOG("Could not query pointer position, not warping pointer\n");
1125 } else {
1126 int mid_x = warp_to->x + (warp_to->width / 2);
1127 int mid_y = warp_to->y + (warp_to->height / 2);
1128
1129 Output *current = get_output_containing(pointerreply->root_x, pointerreply->root_y);
1130 Output *target = get_output_containing(mid_x, mid_y);
1131 if (current != target) {
1132 /* Ignore MotionNotify events generated by warping */
1133 xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
1134 xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, mid_x, mid_y);
1135 xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
1136 }
1137
1138 free(pointerreply);
1139 }
1140 warp_to = NULL;
1141 }
1142
1143 //DLOG("Re-enabling EnterNotify\n");
1144 values[0] = FRAME_EVENT_MASK;
1145 CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
1146 if (state->mapped)
1147 xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
1148 }
1149 //DLOG("Done, EnterNotify re-enabled\n");
1150
1151 x_deco_recurse(con);
1152
1153 xcb_window_t to_focus = focused->frame.id;
1154 if (focused->window != NULL)
1155 to_focus = focused->window->id;
1156
1157 if (focused_id != to_focus) {
1158 if (!focused->mapped) {
1159 DLOG("Not updating focus (to %p / %s), focused window is not mapped.\n", focused, focused->name);
1160 /* Invalidate focused_id to correctly focus new windows with the same ID */
1161 focused_id = XCB_NONE;
1162 } else {
1163 if (focused->window != NULL &&
1164 focused->window->needs_take_focus &&
1165 focused->window->doesnt_accept_focus) {
1166 DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x (focused: %p / %s)\n",
1167 to_focus, focused, focused->name);
1168 send_take_focus(to_focus, last_timestamp);
1169
1170 change_ewmh_focus((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE), last_focused);
1171
1172 if (to_focus != last_focused && is_con_attached(focused))
1173 ipc_send_window_event("focus", focused);
1174 } else {
1175 DLOG("Updating focus (focused: %p / %s) to X11 window 0x%08x\n", focused, focused->name, to_focus);
1176 /* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get
1177 * no focus change events for our own focus changes. We only want
1178 * these generated by the clients. */
1179 if (focused->window != NULL) {
1180 values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE);
1181 xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
1182 }
1183 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp);
1184 if (focused->window != NULL) {
1185 values[0] = CHILD_EVENT_MASK;
1186 xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
1187 }
1188
1189 change_ewmh_focus((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE), last_focused);
1190
1191 if (to_focus != XCB_NONE && to_focus != last_focused && focused->window != NULL && is_con_attached(focused))
1192 ipc_send_window_event("focus", focused);
1193 }
1194
1195 focused_id = last_focused = to_focus;
1196 }
1197 }
1198
1199 if (focused_id == XCB_NONE) {
1200 /* If we still have no window to focus, we focus the EWMH window instead. We use this rather than the
1201 * root window in order to avoid an X11 fallback mechanism causing a ghosting effect (see #1378). */
1202 DLOG("Still no window focused, better set focus to the EWMH support window (%d)\n", ewmh_window);
1203 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, last_timestamp);
1204 change_ewmh_focus(XCB_WINDOW_NONE, last_focused);
1205
1206 focused_id = ewmh_window;
1207 }
1208
1209 xcb_flush(conn);
1210 DLOG("ENDING CHANGES\n");
1211
1212 /* Disable EnterWindow events for windows which will be unmapped in
1213 * x_push_node_unmaps() now. Unmapping windows happens when switching
1214 * workspaces. We want to avoid getting EnterNotifies during that phase
1215 * because they would screw up our focus. One of these cases is having a
1216 * stack with two windows. If the first window is focused and gets
1217 * unmapped, the second one appears under the cursor and therefore gets an
1218 * EnterNotify event. */
1219 values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
1220 CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
1221 if (!state->unmap_now)
1222 continue;
1223 xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
1224 }
1225
1226 /* Push all pending unmaps */
1227 x_push_node_unmaps(con);
1228
1229 /* save the current stack as old stack */
1230 CIRCLEQ_FOREACH(state, &state_head, state) {
1231 CIRCLEQ_REMOVE(&old_state_head, state, old_state);
1232 CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state);
1233 }
1234 //CIRCLEQ_FOREACH(state, &old_state_head, old_state) {
1235 // DLOG("old stack: 0x%08x\n", state->id);
1236 //}
1237
1238 xcb_flush(conn);
1239 }
1240
1241 /*
1242 * Raises the specified container in the internal stack of X windows. The
1243 * next call to x_push_changes() will make the change visible in X11.
1244 *
1245 */
1246 void x_raise_con(Con *con) {
1247 con_state *state;
1248 state = state_for_frame(con->frame.id);
1249 //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id);
1250
1251 CIRCLEQ_REMOVE(&state_head, state, state);
1252 CIRCLEQ_INSERT_HEAD(&state_head, state, state);
1253 }
1254
1255 /*
1256 * Sets the WM_NAME property (so, no UTF8, but used only for debugging anyways)
1257 * of the given name. Used for properly tagging the windows for easily spotting
1258 * i3 windows in xwininfo -root -all.
1259 *
1260 */
1261 void x_set_name(Con *con, const char *name) {
1262 struct con_state *state;
1263
1264 if ((state = state_for_frame(con->frame.id)) == NULL) {
1265 ELOG("window state not found\n");
1266 return;
1267 }
1268
1269 FREE(state->name);
1270 state->name = sstrdup(name);
1271 }
1272
1273 /*
1274 * Set up the I3_SHMLOG_PATH atom.
1275 *
1276 */
1277 void update_shmlog_atom(void) {
1278 if (*shmlogname == '\0') {
1279 xcb_delete_property(conn, root, A_I3_SHMLOG_PATH);
1280 } else {
1281 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
1282 A_I3_SHMLOG_PATH, A_UTF8_STRING, 8,
1283 strlen(shmlogname), shmlogname);
1284 }
1285 }
1286
1287 /*
1288 * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH)
1289 *
1290 */
1291 void x_set_i3_atoms(void) {
1292 pid_t pid = getpid();
1293 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SOCKET_PATH, A_UTF8_STRING, 8,
1294 (current_socketpath == NULL ? 0 : strlen(current_socketpath)),
1295 current_socketpath);
1296 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
1297 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8,
1298 strlen(current_configpath), current_configpath);
1299 update_shmlog_atom();
1300 }
1301
1302 /*
1303 * Set warp_to coordinates. This will trigger on the next call to
1304 * x_push_changes().
1305 *
1306 */
1307 void x_set_warp_to(Rect *rect) {
1308 if (config.mouse_warping != POINTER_WARPING_NONE)
1309 warp_to = rect;
1310 }
1311
1312 /*
1313 * Applies the given mask to the event mask of every i3 window decoration X11
1314 * window. This is useful to disable EnterNotify while resizing so that focus
1315 * is untouched.
1316 *
1317 */
1318 void x_mask_event_mask(uint32_t mask) {
1319 uint32_t values[] = {FRAME_EVENT_MASK & mask};
1320
1321 con_state *state;
1322 CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
1323 if (state->mapped)
1324 xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
1325 }
1326 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * xcb.c: Helper functions for easier usage of XCB
7 *
8 */
9 #include "all.h"
10
11 unsigned int xcb_numlock_mask;
12
13 /*
14 * Convenience wrapper around xcb_create_window which takes care of depth, generating an ID and checking
15 * for errors.
16 *
17 */
18 xcb_window_t create_window(xcb_connection_t *conn, Rect dims,
19 uint16_t depth, xcb_visualid_t visual, uint16_t window_class,
20 enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values) {
21 xcb_window_t result = xcb_generate_id(conn);
22
23 /* If the window class is XCB_WINDOW_CLASS_INPUT_ONLY, we copy depth and
24 * visual id from the parent window. */
25 if (window_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
26 depth = XCB_COPY_FROM_PARENT;
27 visual = XCB_COPY_FROM_PARENT;
28 }
29
30 xcb_void_cookie_t gc_cookie = xcb_create_window(conn,
31 depth,
32 result, /* the window id */
33 root, /* parent == root */
34 dims.x, dims.y, dims.width, dims.height, /* dimensions */
35 0, /* border = 0, we draw our own */
36 window_class,
37 visual,
38 mask,
39 values);
40
41 xcb_generic_error_t *error = xcb_request_check(conn, gc_cookie);
42 if (error != NULL) {
43 ELOG("Could not create window. Error code: %d.\n", error->error_code);
44 }
45
46 /* Set the cursor */
47 if (xcursor_supported) {
48 mask = XCB_CW_CURSOR;
49 values[0] = xcursor_get_cursor(cursor);
50 xcb_change_window_attributes(conn, result, mask, values);
51 } else {
52 xcb_cursor_t cursor_id = xcb_generate_id(conn);
53 i3Font cursor_font = load_font("cursor", false);
54 int xcb_cursor = xcursor_get_xcb_cursor(cursor);
55 xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id,
56 cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0,
57 65535, 65535, 65535);
58 xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, &cursor_id);
59 xcb_free_cursor(conn, cursor_id);
60 }
61
62 /* Map the window (= make it visible) */
63 if (map)
64 xcb_map_window(conn, result);
65
66 return result;
67 }
68
69 /*
70 * Generates a configure_notify_event with absolute coordinates (relative to the X root
71 * window, not to the client’s frame) for the given client.
72 *
73 */
74 void fake_absolute_configure_notify(Con *con) {
75 xcb_rectangle_t absolute;
76 if (con->window == NULL)
77 return;
78
79 absolute.x = con->rect.x + con->window_rect.x;
80 absolute.y = con->rect.y + con->window_rect.y;
81 absolute.width = con->window_rect.width;
82 absolute.height = con->window_rect.height;
83
84 DLOG("fake rect = (%d, %d, %d, %d)\n", absolute.x, absolute.y, absolute.width, absolute.height);
85
86 fake_configure_notify(conn, absolute, con->window->id, con->border_width);
87 }
88
89 /*
90 * Sends the WM_TAKE_FOCUS ClientMessage to the given window
91 *
92 */
93 void send_take_focus(xcb_window_t window, xcb_timestamp_t timestamp) {
94 /* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
95 * In order to properly initialize these bytes, we allocate 32 bytes even
96 * though we only need less for an xcb_configure_notify_event_t */
97 void *event = scalloc(32, 1);
98 xcb_client_message_event_t *ev = event;
99
100 ev->response_type = XCB_CLIENT_MESSAGE;
101 ev->window = window;
102 ev->type = A_WM_PROTOCOLS;
103 ev->format = 32;
104 ev->data.data32[0] = A_WM_TAKE_FOCUS;
105 ev->data.data32[1] = timestamp;
106
107 DLOG("Sending WM_TAKE_FOCUS to the client\n");
108 xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)ev);
109 free(event);
110 }
111
112 /*
113 * Configures the given window to have the size/position specified by given rect
114 *
115 */
116 void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r) {
117 xcb_void_cookie_t cookie;
118 cookie = xcb_configure_window(conn, window,
119 XCB_CONFIG_WINDOW_X |
120 XCB_CONFIG_WINDOW_Y |
121 XCB_CONFIG_WINDOW_WIDTH |
122 XCB_CONFIG_WINDOW_HEIGHT,
123 &(r.x));
124 /* ignore events which are generated because we configured a window */
125 add_ignore_event(cookie.sequence, -1);
126 }
127
128 /*
129 * Returns the first supported _NET_WM_WINDOW_TYPE atom.
130 *
131 */
132 xcb_atom_t xcb_get_preferred_window_type(xcb_get_property_reply_t *reply) {
133 if (reply == NULL || xcb_get_property_value_length(reply) == 0)
134 return XCB_NONE;
135
136 xcb_atom_t *atoms;
137 if ((atoms = xcb_get_property_value(reply)) == NULL)
138 return XCB_NONE;
139
140 for (int i = 0; i < xcb_get_property_value_length(reply) / (reply->format / 8); i++) {
141 if (atoms[i] == A__NET_WM_WINDOW_TYPE_NORMAL ||
142 atoms[i] == A__NET_WM_WINDOW_TYPE_DIALOG ||
143 atoms[i] == A__NET_WM_WINDOW_TYPE_UTILITY ||
144 atoms[i] == A__NET_WM_WINDOW_TYPE_TOOLBAR ||
145 atoms[i] == A__NET_WM_WINDOW_TYPE_SPLASH ||
146 atoms[i] == A__NET_WM_WINDOW_TYPE_MENU ||
147 atoms[i] == A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU ||
148 atoms[i] == A__NET_WM_WINDOW_TYPE_POPUP_MENU ||
149 atoms[i] == A__NET_WM_WINDOW_TYPE_TOOLTIP ||
150 atoms[i] == A__NET_WM_WINDOW_TYPE_NOTIFICATION) {
151 return atoms[i];
152 }
153 }
154
155 return XCB_NONE;
156 }
157
158 /*
159 * Returns true if the given reply contains the given atom.
160 *
161 */
162 bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom) {
163 if (prop == NULL || xcb_get_property_value_length(prop) == 0)
164 return false;
165
166 xcb_atom_t *atoms;
167 if ((atoms = xcb_get_property_value(prop)) == NULL)
168 return false;
169
170 for (int i = 0; i < xcb_get_property_value_length(prop) / (prop->format / 8); i++)
171 if (atoms[i] == atom)
172 return true;
173
174 return false;
175 }
176
177 /*
178 * Set the cursor of the root window to the given cursor id.
179 * This function should only be used if xcursor_supported == false.
180 * Otherwise, use xcursor_set_root_cursor().
181 *
182 */
183 void xcb_set_root_cursor(int cursor) {
184 xcb_cursor_t cursor_id = xcb_generate_id(conn);
185 i3Font cursor_font = load_font("cursor", false);
186 int xcb_cursor = xcursor_get_xcb_cursor(cursor);
187 xcb_create_glyph_cursor(conn, cursor_id, cursor_font.specific.xcb.id,
188 cursor_font.specific.xcb.id, xcb_cursor, xcb_cursor + 1, 0, 0, 0,
189 65535, 65535, 65535);
190 xcb_change_window_attributes(conn, root, XCB_CW_CURSOR, &cursor_id);
191 xcb_free_cursor(conn, cursor_id);
192 xcb_flush(conn);
193 }
194
195 /*
196 * Get depth of visual specified by visualid
197 *
198 */
199 uint16_t get_visual_depth(xcb_visualid_t visual_id) {
200 xcb_depth_iterator_t depth_iter;
201
202 depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
203 for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
204 xcb_visualtype_iterator_t visual_iter;
205
206 visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
207 for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
208 if (visual_id == visual_iter.data->visual_id) {
209 return depth_iter.data->depth;
210 }
211 }
212 }
213 return 0;
214 }
215
216 /*
217 * Get visual type specified by visualid
218 *
219 */
220 xcb_visualtype_t *get_visualtype_by_id(xcb_visualid_t visual_id) {
221 xcb_depth_iterator_t depth_iter;
222
223 depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
224 for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
225 xcb_visualtype_iterator_t visual_iter;
226
227 visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
228 for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
229 if (visual_id == visual_iter.data->visual_id) {
230 return visual_iter.data;
231 }
232 }
233 }
234 return 0;
235 }
236
237 /*
238 * Get visualid with specified depth
239 *
240 */
241 xcb_visualid_t get_visualid_by_depth(uint16_t depth) {
242 xcb_depth_iterator_t depth_iter;
243
244 depth_iter = xcb_screen_allowed_depths_iterator(root_screen);
245 for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
246 if (depth_iter.data->depth != depth)
247 continue;
248
249 xcb_visualtype_iterator_t visual_iter;
250
251 visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
252 if (!visual_iter.rem)
253 continue;
254 return visual_iter.data->visual_id;
255 }
256 return 0;
257 }
258
259 /*
260 * Add an atom to a list of atoms the given property defines.
261 * This is useful, for example, for manipulating _NET_WM_STATE.
262 *
263 */
264 void xcb_add_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom) {
265 xcb_change_property(conn, XCB_PROP_MODE_APPEND, window, property, XCB_ATOM_ATOM, 32, 1, (uint32_t[]){atom});
266 }
267
268 /*
269 * Remove an atom from a list of atoms the given property defines without
270 * removing any other potentially set atoms. This is useful, for example, for
271 * manipulating _NET_WM_STATE.
272 *
273 */
274 void xcb_remove_property_atom(xcb_connection_t *conn, xcb_window_t window, xcb_atom_t property, xcb_atom_t atom) {
275 xcb_grab_server(conn);
276
277 xcb_get_property_reply_t *reply =
278 xcb_get_property_reply(conn,
279 xcb_get_property(conn, false, window, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 4096), NULL);
280 if (reply == NULL || xcb_get_property_value_length(reply) == 0)
281 goto release_grab;
282 xcb_atom_t *atoms = xcb_get_property_value(reply);
283 if (atoms == NULL) {
284 goto release_grab;
285 }
286
287 {
288 int num = 0;
289 const int current_size = xcb_get_property_value_length(reply) / (reply->format / 8);
290 xcb_atom_t values[current_size];
291 for (int i = 0; i < current_size; i++) {
292 if (atoms[i] != atom)
293 values[num++] = atoms[i];
294 }
295
296 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, property, XCB_ATOM_ATOM, 32, num, values);
297 }
298
299 release_grab:
300 FREE(reply);
301 xcb_ungrab_server(conn);
302 }
303
304 /*
305 * Grab the specified buttons on a window when managing it.
306 *
307 */
308 void xcb_grab_buttons(xcb_connection_t *conn, xcb_window_t window, int *buttons) {
309 int i = 0;
310 while (buttons[i] > 0) {
311 xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC,
312 XCB_GRAB_MODE_ASYNC, root, XCB_NONE, buttons[i], XCB_BUTTON_MASK_ANY);
313
314 i++;
315 }
316 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * xcursor.c: xcursor support for themed cursors.
7 *
8 */
9 #include <config.h>
10
11 #include <assert.h>
12 #include <xcb/xcb_cursor.h>
13
14 #include "i3.h"
15 #include "xcb.h"
16 #include "xcursor.h"
17
18 static xcb_cursor_context_t *ctx;
19 static xcb_cursor_t cursors[XCURSOR_CURSOR_MAX];
20
21 static const int xcb_cursors[XCURSOR_CURSOR_MAX] = {
22 XCB_CURSOR_LEFT_PTR,
23 XCB_CURSOR_SB_H_DOUBLE_ARROW,
24 XCB_CURSOR_SB_V_DOUBLE_ARROW,
25 XCB_CURSOR_WATCH};
26
27 void xcursor_load_cursors(void) {
28 if (xcb_cursor_context_new(conn, root_screen, &ctx) < 0) {
29 ELOG("xcursor support unavailable\n");
30 xcursor_supported = false;
31 return;
32 }
33 #define LOAD_CURSOR(constant, name) \
34 do { \
35 cursors[constant] = xcb_cursor_load_cursor(ctx, name); \
36 } while (0)
37 LOAD_CURSOR(XCURSOR_CURSOR_POINTER, "left_ptr");
38 LOAD_CURSOR(XCURSOR_CURSOR_RESIZE_HORIZONTAL, "sb_h_double_arrow");
39 LOAD_CURSOR(XCURSOR_CURSOR_RESIZE_VERTICAL, "sb_v_double_arrow");
40 LOAD_CURSOR(XCURSOR_CURSOR_WATCH, "watch");
41 LOAD_CURSOR(XCURSOR_CURSOR_MOVE, "fleur");
42 LOAD_CURSOR(XCURSOR_CURSOR_TOP_LEFT_CORNER, "top_left_corner");
43 LOAD_CURSOR(XCURSOR_CURSOR_TOP_RIGHT_CORNER, "top_right_corner");
44 LOAD_CURSOR(XCURSOR_CURSOR_BOTTOM_LEFT_CORNER, "bottom_left_corner");
45 LOAD_CURSOR(XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER, "bottom_right_corner");
46 #undef LOAD_CURSOR
47 }
48
49 /*
50 * Sets the cursor of the root window to the 'pointer' cursor.
51 *
52 * This function is called when i3 is initialized, because with some login
53 * managers, the root window will not have a cursor otherwise.
54 *
55 */
56 void xcursor_set_root_cursor(int cursor_id) {
57 xcb_change_window_attributes(conn, root, XCB_CW_CURSOR,
58 (uint32_t[]){xcursor_get_cursor(cursor_id)});
59 }
60
61 xcb_cursor_t xcursor_get_cursor(enum xcursor_cursor_t c) {
62 assert(c < XCURSOR_CURSOR_MAX);
63 return cursors[c];
64 }
65
66 int xcursor_get_xcb_cursor(enum xcursor_cursor_t c) {
67 assert(c < XCURSOR_CURSOR_MAX);
68 return xcb_cursors[c];
69 }
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * This is LEGACY code (we support RandR, which can do much more than
7 * Xinerama), but necessary for the poor users of the nVidia binary
8 * driver which does not support RandR in 2011 *sigh*.
9 *
10 */
11 #include "all.h"
12
13 #include <xcb/xinerama.h>
14
15 static int num_screens;
16
17 /*
18 * Looks in outputs for the Output whose start coordinates are x, y
19 *
20 */
21 static Output *get_screen_at(unsigned int x, unsigned int y) {
22 Output *output;
23 TAILQ_FOREACH(output, &outputs, outputs)
24 if (output->rect.x == x && output->rect.y == y)
25 return output;
26
27 return NULL;
28 }
29
30 /*
31 * Gets the Xinerama screens and converts them to virtual Outputs (only one screen for two
32 * Xinerama screen which are configured in clone mode) in the given screenlist
33 *
34 */
35 static void query_screens(xcb_connection_t *conn) {
36 xcb_xinerama_query_screens_reply_t *reply;
37 xcb_xinerama_screen_info_t *screen_info;
38
39 reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL);
40 if (!reply) {
41 ELOG("Couldn't get Xinerama screens\n");
42 return;
43 }
44 screen_info = xcb_xinerama_query_screens_screen_info(reply);
45 int screens = xcb_xinerama_query_screens_screen_info_length(reply);
46
47 for (int screen = 0; screen < screens; screen++) {
48 Output *s = get_screen_at(screen_info[screen].x_org, screen_info[screen].y_org);
49 if (s != NULL) {
50 DLOG("Re-used old Xinerama screen %p\n", s);
51 /* This screen already exists. We use the littlest screen so that the user
52 can always see the complete workspace */
53 s->rect.width = min(s->rect.width, screen_info[screen].width);
54 s->rect.height = min(s->rect.height, screen_info[screen].height);
55 } else {
56 s = scalloc(1, sizeof(Output));
57 struct output_name *output_name = scalloc(1, sizeof(struct output_name));
58 sasprintf(&output_name->name, "xinerama-%d", num_screens);
59 SLIST_INIT(&s->names_head);
60 SLIST_INSERT_HEAD(&s->names_head, output_name, names);
61 DLOG("Created new Xinerama screen %s (%p)\n", output_primary_name(s), s);
62 s->active = true;
63 s->rect.x = screen_info[screen].x_org;
64 s->rect.y = screen_info[screen].y_org;
65 s->rect.width = screen_info[screen].width;
66 s->rect.height = screen_info[screen].height;
67 /* We always treat the screen at 0x0 as the primary screen */
68 if (s->rect.x == 0 && s->rect.y == 0)
69 TAILQ_INSERT_HEAD(&outputs, s, outputs);
70 else
71 TAILQ_INSERT_TAIL(&outputs, s, outputs);
72 output_init_con(s);
73 init_ws_for_output(s, output_get_content(s->con));
74 num_screens++;
75 }
76
77 DLOG("found Xinerama screen: %d x %d at %d x %d\n",
78 screen_info[screen].width, screen_info[screen].height,
79 screen_info[screen].x_org, screen_info[screen].y_org);
80 }
81
82 free(reply);
83
84 if (num_screens == 0) {
85 ELOG("No screens found. Please fix your setup. i3 will exit now.\n");
86 exit(0);
87 }
88 }
89
90 /*
91 * This creates the root_output (borrowed from randr.c) and uses it
92 * as the sole output for this session.
93 *
94 */
95 static void use_root_output(xcb_connection_t *conn) {
96 Output *s = create_root_output(conn);
97 s->active = true;
98 TAILQ_INSERT_TAIL(&outputs, s, outputs);
99 output_init_con(s);
100 init_ws_for_output(s, output_get_content(s->con));
101 }
102
103 /*
104 * We have just established a connection to the X server and need the initial Xinerama
105 * information to setup workspaces for each screen.
106 *
107 */
108 void xinerama_init(void) {
109 if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) {
110 DLOG("Xinerama extension not found, using root output.\n");
111 use_root_output(conn);
112 } else {
113 xcb_xinerama_is_active_reply_t *reply;
114 reply = xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL);
115
116 if (reply == NULL || !reply->state) {
117 DLOG("Xinerama is not active (in your X-Server), using root output.\n");
118 use_root_output(conn);
119 } else
120 query_screens(conn);
121
122 FREE(reply);
123 }
124 }
0 #! /bin/sh
1 # test-driver - basic testsuite driver script.
2
3 scriptversion=2018-03-07.03; # UTC
4
5 # Copyright (C) 2011-2018 Free Software Foundation, Inc.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <https://www.gnu.org/licenses/>.
19
20 # As a special exception to the GNU General Public License, if you
21 # distribute this file as part of a program that contains a
22 # configuration script generated by Autoconf, you may include it under
23 # the same distribution terms that you use for the rest of that program.
24
25 # This file is maintained in Automake, please report
26 # bugs to <bug-automake@gnu.org> or send patches to
27 # <automake-patches@gnu.org>.
28
29 # Make unconditional expansion of undefined variables an error. This
30 # helps a lot in preventing typo-related bugs.
31 set -u
32
33 usage_error ()
34 {
35 echo "$0: $*" >&2
36 print_usage >&2
37 exit 2
38 }
39
40 print_usage ()
41 {
42 cat <<END
43 Usage:
44 test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
45 [--expect-failure={yes|no}] [--color-tests={yes|no}]
46 [--enable-hard-errors={yes|no}] [--]
47 TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
48 The '--test-name', '--log-file' and '--trs-file' options are mandatory.
49 END
50 }
51
52 test_name= # Used for reporting.
53 log_file= # Where to save the output of the test script.
54 trs_file= # Where to save the metadata of the test run.
55 expect_failure=no
56 color_tests=no
57 enable_hard_errors=yes
58 while test $# -gt 0; do
59 case $1 in
60 --help) print_usage; exit $?;;
61 --version) echo "test-driver $scriptversion"; exit $?;;
62 --test-name) test_name=$2; shift;;
63 --log-file) log_file=$2; shift;;
64 --trs-file) trs_file=$2; shift;;
65 --color-tests) color_tests=$2; shift;;
66 --expect-failure) expect_failure=$2; shift;;
67 --enable-hard-errors) enable_hard_errors=$2; shift;;
68 --) shift; break;;
69 -*) usage_error "invalid option: '$1'";;
70 *) break;;
71 esac
72 shift
73 done
74
75 missing_opts=
76 test x"$test_name" = x && missing_opts="$missing_opts --test-name"
77 test x"$log_file" = x && missing_opts="$missing_opts --log-file"
78 test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
79 if test x"$missing_opts" != x; then
80 usage_error "the following mandatory options are missing:$missing_opts"
81 fi
82
83 if test $# -eq 0; then
84 usage_error "missing argument"
85 fi
86
87 if test $color_tests = yes; then
88 # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
89 red='' # Red.
90 grn='' # Green.
91 lgn='' # Light green.
92 blu='' # Blue.
93 mgn='' # Magenta.
94 std='' # No color.
95 else
96 red= grn= lgn= blu= mgn= std=
97 fi
98
99 do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
100 trap "st=129; $do_exit" 1
101 trap "st=130; $do_exit" 2
102 trap "st=141; $do_exit" 13
103 trap "st=143; $do_exit" 15
104
105 # Test script is run here.
106 "$@" >$log_file 2>&1
107 estatus=$?
108
109 if test $enable_hard_errors = no && test $estatus -eq 99; then
110 tweaked_estatus=1
111 else
112 tweaked_estatus=$estatus
113 fi
114
115 case $tweaked_estatus:$expect_failure in
116 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
117 0:*) col=$grn res=PASS recheck=no gcopy=no;;
118 77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
119 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
120 *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
121 *:*) col=$red res=FAIL recheck=yes gcopy=yes;;
122 esac
123
124 # Report the test outcome and exit status in the logs, so that one can
125 # know whether the test passed or failed simply by looking at the '.log'
126 # file, without the need of also peaking into the corresponding '.trs'
127 # file (automake bug#11814).
128 echo "$res $test_name (exit status: $estatus)" >>$log_file
129
130 # Report outcome to console.
131 echo "${col}${res}${std}: $test_name"
132
133 # Register the test result, and other relevant metadata.
134 echo ":test-result: $res" > $trs_file
135 echo ":global-test-result: $res" >> $trs_file
136 echo ":recheck: $recheck" >> $trs_file
137 echo ":copy-in-global-log: $gcopy" >> $trs_file
138
139 # Local Variables:
140 # mode: shell-script
141 # sh-indentation: 2
142 # eval: (add-hook 'before-save-hook 'time-stamp)
143 # time-stamp-start: "scriptversion="
144 # time-stamp-format: "%:y-%02m-%02d.%02H"
145 # time-stamp-time-zone: "UTC0"
146 # time-stamp-end: "; # UTC"
147 # End:
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 use strict; use warnings;
3 use ExtUtils::MakeMaker;
4
5 WriteMakefile(
6 NAME => 'i3-testsuite',
7 MIN_PERL_VERSION => '5.010000', # 5.10.0
8 PREREQ_PM => {
9 'AnyEvent' => 0,
10 'X11::XCB' => '0.12',
11 'Inline' => 0,
12 'Inline::C' => 0,
13 'ExtUtils::PkgConfig' => 0,
14 'Test::More' => '0.94',
15 'IPC::Run' => 0,
16 },
17 PM => {}, # do not install any files from this directory
18 clean => {
19 FILES => 'testsuite-* latest i3-cfg-for-*',
20 },
21 # This is a pure-Perl distribution:
22 linkext => {
23 LINKTYPE => '',
24 },
25 );
26
27 package MY;
28 sub test { } # do not run the tests while installing
29
30 # do not rename the Makefile
31 sub clean {
32 my $section = shift->SUPER::clean(@_);
33 $section =~ s/^\t\Q$_\E\n$//m for
34 '- $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL)';
35 $section;
36 }
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 # © 2010 Michael Stapelberg and contributors
3 package complete_run;
4 use strict;
5 use warnings;
6 use v5.10;
7 use utf8;
8 # the following are modules which ship with Perl (>= 5.10):
9 use Pod::Usage;
10 use File::Temp qw(tempfile tempdir);
11 use Getopt::Long;
12 use POSIX ();
13 use TAP::Harness;
14 use TAP::Parser;
15 use TAP::Parser::Aggregator;
16 use Time::HiRes qw(time);
17 use IO::Handle;
18
19 # these are shipped with the testsuite
20 use lib qw(@abs_top_builddir@/testcases/lib @abs_top_srcdir@/testcases/lib @abs_top_srcdir@/AnyEvent-I3/blib/lib);
21 use i3test::Util qw(slurp);
22 use StartXServer;
23 use StatusLine;
24 use TestWorker;
25 # the following modules are not shipped with Perl
26 use AnyEvent;
27 use AnyEvent::Util;
28 use AnyEvent::Handle;
29 use AnyEvent::I3 qw(:all);
30 use X11::XCB::Connection;
31 use JSON::XS; # AnyEvent::I3 depends on it, too.
32
33 binmode STDOUT, ':utf8';
34 binmode STDERR, ':utf8';
35
36 # Close superfluous file descriptors which were passed by running in a VIM
37 # subshell or situations like that.
38 AnyEvent::Util::close_all_fds_except(0, 1, 2);
39
40 our @CLEANUP;
41
42 # convenience wrapper to write to the log file
43 my $log;
44 sub Log { say $log "@_" }
45
46 my %timings;
47 my $help = 0;
48 # Number of tests to run in parallel. Important to know how many Xephyr
49 # instances we need to start (unless @displays are given). Defaults to
50 # num_cores * 2.
51 my $parallel = undef;
52 my @displays = ();
53 my %options = (
54 valgrind => 0,
55 strace => 0,
56 xtrace => 0,
57 coverage => 0,
58 restart => 0,
59 xvfb => 1,
60 );
61 my $keep_xserver_output = 0;
62
63 my $result = GetOptions(
64 "coverage-testing" => \$options{coverage},
65 "keep-xserver-output" => \$keep_xserver_output,
66 "valgrind" => \$options{valgrind},
67 "strace" => \$options{strace},
68 "xtrace" => \$options{xtrace},
69 "xvfb!" => \$options{xvfb},
70 "display=s" => \@displays,
71 "parallel=i" => \$parallel,
72 "help|?" => \$help,
73 );
74
75 pod2usage(-verbose => 2, -exitcode => 0) if $help;
76
77 # Check for missing executables
78 my @binaries = qw(
79 @abs_top_builddir@/i3
80 @abs_top_builddir@/i3bar/i3bar
81 @abs_top_builddir@/i3-config-wizard/i3-config-wizard
82 @abs_top_builddir@/i3-dump-log/i3-dump-log
83 @abs_top_builddir@/i3-input/i3-input
84 @abs_top_builddir@/i3-msg/i3-msg
85 @abs_top_builddir@/i3-nagbar/i3-nagbar
86 );
87
88 foreach my $binary (@binaries) {
89 die "$binary executable not found, did you run “make”?" unless -e $binary;
90 die "$binary is not an executable" unless -x $binary;
91 }
92
93 my @test_binaries = qw(
94 @abs_top_builddir@/test.commands_parser
95 @abs_top_builddir@/test.config_parser
96 @abs_top_builddir@/test.inject_randr15
97 );
98
99 foreach my $binary (@test_binaries) {
100 die "$binary executable not found, did you run “make check”?" unless -e $binary;
101 die "$binary is not an executable" unless -x $binary;
102 }
103
104 $ENV{PATH} = join(':',
105 '@abs_top_builddir@/i3-nagbar',
106 '@abs_top_builddir@/i3-msg',
107 '@abs_top_builddir@/i3-input',
108 '@abs_top_builddir@/i3-dump-log',
109 '@abs_top_builddir@/i3-config-wizard',
110 '@abs_top_builddir@/i3bar',
111 '@abs_top_builddir@',
112 '@abs_top_srcdir@',
113 $ENV{PATH});
114
115 qx(Xephyr -help 2>&1);
116 die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?;
117
118 qx(xvfb-run --help 2>&1);
119 if ($? && $options{xvfb}) {
120 say "xvfb-run not found, not running tests under xvfb. Install the xvfb package to speed up tests";
121 $options{xvfb} = 0;
122 }
123
124 if ($options{xvfb}) {
125 for (my $n = 99; $n < 120; $n++) {
126 my $path = File::Temp::tmpnam($ENV{TMPDIR} // "/tmp", "i3-testsXXXXXX");
127 if (!defined(POSIX::mkfifo($path, 0600))) {
128 die "mkfifo: $!";
129 }
130 my $pid = fork // die "fork: $!";
131 if ($pid == 0) {
132 # Child
133
134 # Xvfb checks whether the parent ignores USR1 and sends USR1 to the
135 # parent when ready, so that the wait call will be interrupted. We
136 # can’t implement this in Perl, as Perl’s waitpid transparently
137 # handles -EINTR.
138 exec('/bin/sh', '-c', qq|trap "exit" INT; trap : USR1; (trap '' USR1; exec Xvfb :$n -screen 0 640x480x8 -nolisten tcp) & PID=\$!; wait; if ! kill -0 \$PID 2>/dev/null; then echo 1:\$PID > $path; else echo 0:\$PID > $path; wait \$PID; fi|);
139 die "exec: $!";
140 }
141 chomp(my $kill = slurp($path));
142 unlink($path);
143 my ($code, $xvfbpid) = ($kill =~ m,^([0-1]):(.*)$,);
144 next unless $code eq '0';
145
146 $ENV{DISPLAY} = ":$n";
147 say "Running tests under Xvfb display $ENV{DISPLAY}";
148
149 push(@CLEANUP, sub {
150 kill(15, $xvfbpid);
151 });
152 last;
153 }
154 }
155
156 @displays = split(/,/, join(',', @displays));
157 @displays = map { s/ //g; $_ } @displays;
158
159 # 2: get a list of all testcases
160 my @testfiles = @ARGV;
161
162 # if no files were passed on command line, run all tests from t/
163 if (scalar @testfiles == 0) {
164 @testfiles = <@abs_top_srcdir@/testcases/t/*.t> if @testfiles == 0;
165 } else {
166 @testfiles = map {
167 # Fully qualify each specified file if necessary
168 if (! -e $_) {
169 $_ = "@abs_top_srcdir@/testcases/$_";
170 }
171 $_
172 } @testfiles;
173 }
174
175 my $numtests = scalar @testfiles;
176
177 # No displays specified, let’s start some Xephyr instances.
178 if (@displays == 0) {
179 @displays = start_xserver($parallel, $numtests, $keep_xserver_output);
180 }
181
182 # 1: create an output directory for this test-run
183 my $outdir = "testsuite-";
184 $outdir .= POSIX::strftime("%Y-%m-%d-%H-%M-%S-", localtime());
185 $outdir .= `git describe --tags`;
186 chomp($outdir);
187 mkdir($outdir) or die "Could not create $outdir";
188 unlink("latest") if -l "latest";
189 symlink("$outdir", "latest") or die "Could not symlink latest to $outdir";
190
191
192 # connect to all displays for two reasons:
193 # 1: check if the display actually works
194 # 2: keep the connection open so that i3 is not the only client. this prevents
195 # the X server from exiting
196 my @single_worker;
197 for my $display (@displays) {
198 my $screen;
199 my $x = X11::XCB::Connection->new(display => $display);
200 if ($x->has_error) {
201 die "Could not connect to display $display\n";
202 } else {
203 # start a TestWorker for each display
204 push @single_worker, worker($display, $x, $outdir, \%options);
205 }
206 }
207
208 # Read previous timing information, if available. We will be able to roughly
209 # predict the test duration and schedule a good order for the tests.
210 my $timingsjson = slurp('.last_run_timings.json') if -e '.last_run_timings.json';
211 %timings = %{decode_json($timingsjson)} if length($timingsjson) > 0;
212
213 # Re-order the files so that those which took the longest time in the previous
214 # run will be started at the beginning to not delay the whole run longer than
215 # necessary.
216 @testfiles = map { $_->[0] }
217 sort { $b->[1] <=> $a->[1] }
218 map { [$_, $timings{$_} // 999] } @testfiles;
219
220 # Run 000-load-deps.t first to bail out early when dependencies are missing.
221 my ($loadtest) = grep { $_ =~ m,t/000-load-deps.t$, } @testfiles;
222 if (defined($loadtest)) {
223 @testfiles = ($loadtest, grep { $_ ne $loadtest } @testfiles);
224 }
225
226 # Run 533-randr15.t last because it destructively modifies the RandR
227 # configuration of the X session, interfering with any test started afterwards.
228 my ($randrtest) = grep { $_ =~ m,t/533-randr15.t$, } @testfiles;
229 if (defined($randrtest)) {
230 @testfiles = ((grep { $_ ne $randrtest } @testfiles), $randrtest);
231 }
232
233 printf("\nRough time estimate for this run: %.2f seconds\n\n", $timings{GLOBAL})
234 if exists($timings{GLOBAL});
235
236 # Forget the old timings, we don’t necessarily run the same set of tests as
237 # before. Otherwise we would end up with left-overs.
238 %timings = (GLOBAL => time());
239
240 my $logfile = "$outdir/complete-run.log";
241 open $log, '>', $logfile or die "Could not create '$logfile': $!";
242 $log->autoflush(1);
243 say "Writing logfile to '$logfile'...";
244
245 # 3: run all tests
246 my @done;
247 my $num = @testfiles;
248 my $harness = TAP::Harness->new({ });
249
250 my $aggregator = TAP::Parser::Aggregator->new();
251 $aggregator->start();
252
253 status_init(displays => \@displays, tests => $num);
254
255 my $single_cv = AE::cv;
256
257 # We start tests concurrently: For each display, one test gets started. Every
258 # test starts another test after completing.
259 for (@single_worker) {
260 $single_cv->begin;
261 take_job($_, $single_cv, \@testfiles);
262 }
263
264 $single_cv->recv;
265
266 $aggregator->stop();
267
268 # print empty lines to separate failed tests from statuslines
269 print "\n\n";
270
271 for (@done) {
272 my ($test, $output) = @$_;
273 say "no output for $test" unless $output;
274 Log "output for $test:";
275 Log $output;
276 # print error messages of failed tests
277 say for $output =~ /^not ok.+\n+((?:^#.+\n)+)/mg
278 }
279
280 # 4: print summary
281 $harness->summary($aggregator);
282
283 close $log;
284
285 # 5: Save the timings for better scheduling/prediction next run.
286 $timings{GLOBAL} = time() - $timings{GLOBAL};
287 open(my $fh, '>', '.last_run_timings.json');
288 print $fh encode_json(\%timings);
289 close($fh);
290
291 # 6: Print the slowest test files.
292 my @slowest = map { $_->[0] }
293 sort { $b->[1] <=> $a->[1] }
294 map { [$_, $timings{$_}] }
295 grep { !/^GLOBAL$/ } keys %timings;
296 say '';
297 say 'The slowest tests are:';
298 printf("\t%s with %.2f seconds\n", $_, $timings{$_})
299 for @slowest[0..($#slowest > 4 ? 4 : $#slowest)];
300
301 # When we are running precisely one test, print the output. Makes developing
302 # with a single testcase easier.
303 if ($numtests == 1) {
304 say '';
305 say 'Test output:';
306 say slurp($logfile);
307 }
308
309 END { cleanup() }
310
311 # Report logfiles that match “(Leak|Address)Sanitizer:”.
312 my @logs_with_leaks;
313 for my $log (<$outdir/i3-log-for-*>) {
314 if (slurp($log) =~ /(Leak|Address)Sanitizer:/) {
315 push @logs_with_leaks, $log;
316 }
317 }
318 if (scalar @logs_with_leaks > 0) {
319 say "\nThe following test logfiles contain AddressSanitizer or LeakSanitizer reports:";
320 for my $log (sort @logs_with_leaks) {
321 say "\t$log";
322 }
323 }
324
325 exit ($aggregator->failed > 0);
326
327 #
328 # Takes a test from the beginning of @testfiles and runs it.
329 #
330 # The TAP::Parser (which reads the test output) will get called as soon as
331 # there is some activity on the stdout file descriptor of the test process
332 # (using an AnyEvent->io watcher).
333 #
334 # When a test completes and @done contains $num entries, the $cv condvar gets
335 # triggered to finish testing.
336 #
337 sub take_job {
338 my ($worker, $cv, $tests) = @_;
339
340 my $test = shift @$tests
341 or return $cv->end;
342
343 my $display = $worker->{display};
344
345 Log status($display, "$test: starting");
346 $timings{$test} = time();
347 worker_next($worker, $test);
348
349 # create a TAP::Parser with an in-memory fh
350 my $output;
351 my $parser = TAP::Parser->new({
352 source => do { open(my $fh, '<', \$output); $fh },
353 });
354
355 my $ipc = $worker->{ipc};
356
357 my $w;
358 $w = AnyEvent->io(
359 fh => $ipc,
360 poll => 'r',
361 cb => sub {
362 state $tests_completed = 0;
363 state $partial = '';
364
365 sysread($ipc, my $buf, 4096) or die "sysread: $!";
366
367 if ($partial) {
368 $buf = $partial . $buf;
369 $partial = '';
370 }
371
372 # make sure we feed TAP::Parser complete lines so it doesn't blow up
373 if (substr($buf, -1, 1) ne "\n") {
374 my $nl = rindex($buf, "\n");
375 if ($nl == -1) {
376 $partial = $buf;
377 return;
378 }
379
380 # strip partial from buffer
381 $partial = substr($buf, $nl + 1, '');
382 }
383
384 # count lines before stripping eof-marker otherwise we might
385 # end up with for (1 .. 0) { } which would effectivly skip the loop
386 my $lines = $buf =~ tr/\n//;
387 my $t_eof = $buf =~ s/^$TestWorker::EOF$//m;
388
389 $output .= $buf;
390
391 for (1 .. $lines) {
392 my $result = $parser->next;
393 next unless defined($result);
394 if ($result->is_test) {
395 $tests_completed++;
396 status($display, "$test: [$tests_completed/??] ");
397 } elsif ($result->is_bailout) {
398 Log status($display, "$test: BAILOUT");
399 status_completed(scalar @done);
400 say "";
401 say "test $test bailed out: " . $result->explanation;
402 exit 1;
403 }
404 }
405
406 return unless $t_eof;
407
408 Log status($display, "$test: finished");
409 $timings{$test} = time() - $timings{$test};
410 status_completed(scalar @done);
411
412 $aggregator->add($test, $parser);
413 push @done, [ $test, $output ];
414
415 undef $w;
416 take_job($worker, $cv, $tests);
417 }
418 );
419 }
420
421 sub cleanup {
422 my $exitcode = $?;
423 $_->() for @CLEANUP;
424 exit $exitcode;
425 }
426
427 # must be in a begin block because we C<exit 0> above
428 BEGIN {
429 $SIG{$_} = sub {
430 require Carp; Carp::cluck("Caught SIG$_[0]\n");
431 cleanup();
432 } for qw(INT TERM QUIT KILL PIPE)
433 }
434
435 __END__
436
437 =head1 NAME
438
439 complete-run.pl - Run the i3 testsuite
440
441 =head1 SYNOPSIS
442
443 complete-run.pl [files...]
444
445 =head1 EXAMPLE
446
447 To run the whole testsuite on a reasonable number of Xephyr instances (your
448 running X11 will not be touched), run:
449 ./complete-run.pl
450
451 To run only a specific test (useful when developing a new feature), run:
452 ./complete-run t/100-fullscreen.t
453
454 =head1 OPTIONS
455
456 =over 8
457
458 =item B<--display>
459
460 Specifies which X11 display should be used. Can be specified multiple times and
461 will parallelize the tests:
462
463 # Run tests on the second X server
464 ./complete-run.pl -d :1
465
466 # Run four tests in parallel on some Xephyr servers
467 ./complete-run.pl -d :1,:2,:3,:4
468
469 Note that it is not necessary to specify this anymore. If omitted,
470 complete-run.pl will start (num_cores * 2) Xephyr instances.
471
472 =item B<--valgrind>
473
474 Runs i3 under valgrind to find memory problems. The output will be available in
475 C<latest/valgrind-for-$test.log>.
476
477 =item B<--strace>
478
479 Runs i3 under strace to trace system calls. The output will be available in
480 C<latest/strace-for-$test.log>.
481
482 =item B<--xtrace>
483
484 Runs i3 under xtrace to trace X11 requests/replies. The output will be
485 available in C<latest/xtrace-for-$test.log>.
486
487 =item B<--xvfb>
488
489 =item B<--no-xvfb>
490
491 Enable or disable running tests under Xvfb. Enabled by default.
492
493 =item B<--coverage-testing>
494
495 Generates a test coverage report at C<latest/i3-coverage>. Exits i3 cleanly
496 during tests (instead of kill -9) to make coverage testing work properly.
497
498 =item B<--parallel>
499
500 Number of Xephyr instances to start (if you don't want to start num_cores * 2
501 instances for some reason).
502
503 # Run all tests on a single Xephyr instance
504 ./complete-run.pl -p 1
505
506 =back
0 # ISO 10646 = Unicode
1 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
2
3 # Use Mouse+Mod1 to drag floating windows to their wanted position
4 floating_modifier Mod1
5
6 # Open empty container
7 bindsym Mod1+Shift+Return open
8
9 # Start terminal (Mod1+Enter)
10 bindsym Mod1+Return exec /usr/bin/urxvt
11
12 # Start dmenu (Mod1+p)
13 bindsym Mod1+p exec /usr/bin/dmenu_run
14
15 # Horizontal orientation
16 bindsym Mod1+h split h
17
18 # Vertical orientation
19 bindsym Mod1+v split v
20
21 # Fullscreen (Mod1+f)
22 bindsym Mod1+f fullscreen toggle
23
24 # Stacking (Mod1+s)
25 bindsym Mod1+s layout stacking
26
27 # Tabbed (Mod1+w)
28 bindsym Mod1+w layout tabbed
29
30 # Default (Mod1+l)
31 bindsym Mod1+l layout default
32
33 # toggle tiling / floating
34 bindsym Mod1+Shift+space mode toggle
35
36 bindsym Mod1+u level up
37 #bindsym Mod1+d level down
38
39 # Kill current client (Mod1+c)
40 bindsym Mod1+c kill
41
42 # Restore saved JSON layout
43 bindsym Mod1+y restore /home/michael/i3/layout.json
44
45 # Restart i3
46 bindsym Mod1+Shift+c restart
47 # Reload i3
48 bindsym Mod1+Shift+j reload
49 # Exit i3
50 bindsym Mod1+Shift+l exit
51
52 # Focus (Mod1+n/r/t/d)
53 bindsym Mod1+n prev h
54 bindsym Mod1+r next v
55 bindsym Mod1+t prev v
56 bindsym Mod1+d next h
57
58 # alternatively, you can use the cursor keys:
59 bindsym Mod1+Left prev h
60 bindsym Mod1+Right next h
61 bindsym Mod1+Down next v
62 bindsym Mod1+Up prev v
63
64 # Move
65 bindsym Mod1+Shift+n move left
66 bindsym Mod1+Shift+r move down
67 bindsym Mod1+Shift+t move up
68 bindsym Mod1+Shift+d move right
69
70 # alternatively, you can use the cursor keys:
71 bindsym Mod1+Shift+Left move left
72 bindsym Mod1+Shift+Right move right
73 bindsym Mod1+Shift+Down move down
74 bindsym Mod1+Shift+Up move up
75
76 # Workspaces (Mod1+1/2/…)
77 bindsym Mod1+1 workspace 1
78 bindsym Mod1+2 workspace 2
79 bindsym Mod1+3 workspace 3
80 bindsym Mod1+4 workspace 4
81 bindsym Mod1+5 workspace 5
82 bindsym Mod1+6 workspace 6
83 bindsym Mod1+7 workspace 7
84 bindsym Mod1+8 workspace 8
85 bindsym Mod1+9 workspace 9
86 bindsym Mod1+0 workspace 10
87
88 # Move to Workspaces
89 bindsym Mod1+Shift+1 move workspace 1
90 bindsym Mod1+Shift+2 move workspace 2
91 bindsym Mod1+Shift+3 move workspace 3
92 bindsym Mod1+Shift+4 move workspace 4
93 bindsym Mod1+Shift+5 move workspace 5
94 bindsym Mod1+Shift+6 move workspace 6
95 bindsym Mod1+Shift+7 move workspace 7
96 bindsym Mod1+Shift+8 move workspace 8
97 bindsym Mod1+Shift+9 move workspace 9
98 bindsym Mod1+Shift+0 move workspace 10
0 /*
1 * vim:ts=4:sw=4:expandtab
2 *
3 * i3 - an improved dynamic tiling window manager
4 * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
5 *
6 * inject_randr1.5.c: An X11 proxy which interprets RandR 1.5 GetMonitors
7 * requests and overwrites their reply with a custom reply.
8 *
9 * This tool can be refactored as necessary in order to perform the same
10 * purpose for other request types. The RandR 1.5 specific portions of the code
11 * have been marked as such to make such a refactoring easier.
12 *
13 */
14 #include "all.h"
15
16 #include <ev.h>
17 #include <fcntl.h>
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 #include <sys/time.h>
22 #include <sys/resource.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <libgen.h>
27
28 static void uds_connection_cb(EV_P_ ev_io *w, int revents);
29 static void read_client_setup_request_cb(EV_P_ ev_io *w, int revents);
30 static void read_server_setup_reply_cb(EV_P_ ev_io *w, int revents);
31 static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents);
32 static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents);
33
34 static char *sun_path = NULL;
35
36 static void cleanup_socket(void) {
37 if (sun_path != NULL) {
38 unlink(sun_path);
39 free(sun_path);
40 sun_path = NULL;
41 }
42 }
43
44 struct injected_reply {
45 void *buf;
46 off_t len;
47 };
48
49 /* BEGIN RandR 1.5 specific */
50 static struct injected_reply getmonitors_reply = {NULL, 0};
51 static struct injected_reply getoutputinfo_reply = {NULL, 0};
52 /* END RandR 1.5 specific */
53
54 #define XCB_PAD(i) (-(i)&3)
55
56 struct connstate {
57 /* clientw is a libev watcher for the connection which we accept()ed. */
58 ev_io *clientw;
59
60 /* serverw is a libev watcher for the connection to X11 which we initiated
61 * on behalf of the client. */
62 ev_io *serverw;
63
64 /* sequence is the client-side sequence number counter. In X11’s wire
65 * encoding, sequence counters are not included in requests, only in
66 * replies. */
67 int sequence;
68
69 /* BEGIN RandR 1.5 specific */
70 /* sequence number of the most recent GetExtension request for RANDR */
71 int getext_randr;
72 /* sequence number of the most recent RRGetMonitors request */
73 int getmonitors;
74 /* sequence number of the most recent RRGetOutputInfo request */
75 int getoutputinfo;
76
77 int randr_major_opcode;
78 /* END RandR 1.5 specific */
79 };
80
81 /*
82 * Returns 0 on EOF
83 * Returns -1 on error (with errno from read() untouched)
84 *
85 */
86 static size_t readall_into(void *buffer, const size_t len, int fd) {
87 size_t read_bytes = 0;
88 while (read_bytes < len) {
89 ssize_t n = read(fd, buffer + read_bytes, len - read_bytes);
90 if (n <= 0) {
91 return n;
92 }
93 read_bytes += (size_t)n;
94 }
95 return read_bytes;
96 }
97
98 /*
99 * Exits the program with an error if the read failed.
100 *
101 */
102 static void must_read(int n) {
103 if (n == -1) {
104 err(EXIT_FAILURE, "read()");
105 }
106 if (n == 0) {
107 errx(EXIT_FAILURE, "EOF");
108 }
109 }
110
111 /*
112 * Exits the program with an error if the write failed.
113 *
114 */
115 static void must_write(int n) {
116 if (n == -1) {
117 err(EXIT_FAILURE, "write()");
118 }
119 }
120
121 static void uds_connection_cb(EV_P_ ev_io *w, int revents) {
122 struct sockaddr_un addr;
123 socklen_t addrlen = sizeof(addr);
124 const int clientfd = accept(w->fd, (struct sockaddr *)&addr, &addrlen);
125 if (clientfd == -1) {
126 if (errno == EINTR) {
127 return;
128 }
129 err(EXIT_FAILURE, "accept()");
130 }
131
132 struct connstate *connstate = scalloc(1, sizeof(struct connstate));
133
134 ev_io *clientw = scalloc(1, sizeof(ev_io));
135 connstate->clientw = clientw;
136 clientw->data = connstate;
137 ev_io_init(clientw, read_client_setup_request_cb, clientfd, EV_READ);
138 ev_io_start(EV_A_ clientw);
139 }
140
141 // https://www.x.org/releases/current/doc/xproto/x11protocol.html#Encoding::Connection_Setup
142 static void read_client_setup_request_cb(EV_P_ ev_io *w, int revents) {
143 ev_io_stop(EV_A_ w);
144 struct connstate *connstate = (struct connstate *)w->data;
145
146 /* Read X11 setup request in its entirety. */
147 xcb_setup_request_t setup_request;
148 must_read(readall_into(&setup_request, sizeof(setup_request), w->fd));
149
150 /* Establish a connection to X11. */
151 int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
152 if (fd == -1) {
153 err(EXIT_FAILURE, "socket()");
154 }
155
156 char *host;
157 int displayp;
158 if (xcb_parse_display(getenv("DISPLAY"), &host, &displayp, NULL) == 0) {
159 errx(EXIT_FAILURE, "Could not parse DISPLAY=%s", getenv("DISPLAY"));
160 }
161 free(host);
162
163 struct sockaddr_un addr;
164 memset(&addr, 0, sizeof(addr));
165 addr.sun_family = AF_LOCAL;
166 snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/.X11-unix/X%d", displayp);
167 if (connect(fd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
168 err(EXIT_FAILURE, "connect(%s)", addr.sun_path);
169 }
170
171 /* Relay setup request. */
172 must_write(writeall(fd, &setup_request, sizeof(setup_request)));
173
174 if (setup_request.authorization_protocol_name_len > 0 ||
175 setup_request.authorization_protocol_data_len > 0) {
176 const size_t authlen = setup_request.authorization_protocol_name_len +
177 XCB_PAD(setup_request.authorization_protocol_name_len) +
178 setup_request.authorization_protocol_data_len +
179 XCB_PAD(setup_request.authorization_protocol_data_len);
180 void *buf = smalloc(authlen);
181 must_read(readall_into(buf, authlen, w->fd));
182 must_write(writeall(fd, buf, authlen));
183 free(buf);
184 }
185
186 /* Wait for a response from the X11 server. */
187 ev_io *serverw = scalloc(1, sizeof(ev_io));
188 connstate->serverw = serverw;
189 serverw->data = connstate;
190 ev_io_init(serverw, read_server_setup_reply_cb, fd, EV_READ);
191 ev_io_start(EV_A_ serverw);
192 }
193
194 static void read_server_setup_reply_cb(EV_P_ ev_io *w, int revents) {
195 struct connstate *connstate = (struct connstate *)w->data;
196 xcb_setup_failed_t setup_failed;
197 must_read(readall_into(&setup_failed, sizeof(setup_failed), w->fd));
198
199 switch (setup_failed.status) {
200 case 0:
201 errx(EXIT_FAILURE, "error authenticating at the X11 server");
202
203 case 2:
204 errx(EXIT_FAILURE, "two-factor auth not implemented");
205
206 case 1:
207 must_write(writeall(connstate->clientw->fd, &setup_failed, sizeof(xcb_setup_failed_t)));
208 const size_t len = (setup_failed.length * 4);
209 void *buf = smalloc(len);
210 must_read(readall_into(buf, len, w->fd));
211 must_write(writeall(connstate->clientw->fd, buf, len));
212 free(buf);
213
214 ev_set_cb(connstate->clientw, read_client_x11_packet_cb);
215 ev_set_cb(connstate->serverw, read_server_x11_packet_cb);
216 ev_io_start(EV_A_ connstate->clientw);
217 break;
218
219 default:
220 errx(EXIT_FAILURE, "X11 protocol error: expected setup_failed.status in [0..2], got %d", setup_failed.status);
221 }
222 }
223
224 // https://www.x.org/releases/current/doc/xproto/x11protocol.html#request_format
225 typedef struct {
226 uint8_t opcode;
227 uint8_t pad0;
228 uint16_t length;
229 } generic_x11_request_t;
230
231 // https://www.x.org/releases/current/doc/xproto/x11protocol.html#reply_format
232 typedef struct {
233 uint8_t code; /* if 1, this is a reply. if 0, this is an error. else, an event */
234 uint8_t pad0;
235 uint16_t sequence;
236 uint32_t length;
237 } generic_x11_reply_t;
238
239 static void read_client_x11_packet_cb(EV_P_ ev_io *w, int revents) {
240 struct connstate *connstate = (struct connstate *)w->data;
241
242 void *request = smalloc(sizeof(generic_x11_request_t));
243 must_read(readall_into(request, sizeof(generic_x11_request_t), connstate->clientw->fd));
244 const size_t len = (((generic_x11_request_t *)request)->length * 4);
245 if (len > sizeof(generic_x11_request_t)) {
246 request = srealloc(request, len);
247 must_read(readall_into(request + sizeof(generic_x11_request_t),
248 len - sizeof(generic_x11_request_t),
249 connstate->clientw->fd));
250 }
251
252 // XXX: sequence counter wrapping is not implemented, but should not be
253 // necessary given that this tool is scoped for test cases.
254 connstate->sequence++;
255
256 /* BEGIN RandR 1.5 specific */
257 const uint8_t opcode = ((generic_x11_request_t *)request)->opcode;
258 if (opcode == XCB_QUERY_EXTENSION) {
259 xcb_query_extension_request_t *req = request;
260 const char *name = request + sizeof(xcb_query_extension_request_t);
261 if (req->name_len == strlen("RANDR") &&
262 strncmp(name, "RANDR", strlen("RANDR")) == 0) {
263 connstate->getext_randr = connstate->sequence;
264 }
265 } else if (opcode == connstate->randr_major_opcode) {
266 const uint8_t randr_opcode = ((generic_x11_request_t *)request)->pad0;
267 if (randr_opcode == XCB_RANDR_GET_MONITORS) {
268 connstate->getmonitors = connstate->sequence;
269 } else if (randr_opcode == XCB_RANDR_GET_OUTPUT_INFO) {
270 connstate->getoutputinfo = connstate->sequence;
271 }
272 }
273 /* END RandR 1.5 specific */
274
275 must_write(writeall(connstate->serverw->fd, request, len));
276 free(request);
277 }
278
279 static bool handle_sequence(struct connstate *connstate, uint16_t sequence) {
280 /* BEGIN RandR 1.5 specific */
281 if (sequence == connstate->getmonitors) {
282 printf("RRGetMonitors reply!\n");
283 if (getmonitors_reply.buf != NULL) {
284 printf("injecting reply\n");
285 ((generic_x11_reply_t *)getmonitors_reply.buf)->sequence = sequence;
286 must_write(writeall(connstate->clientw->fd, getmonitors_reply.buf, getmonitors_reply.len));
287 return true;
288 }
289 }
290
291 if (sequence == connstate->getoutputinfo) {
292 printf("RRGetOutputInfo reply!\n");
293 if (getoutputinfo_reply.buf != NULL) {
294 printf("injecting reply\n");
295 ((generic_x11_reply_t *)getoutputinfo_reply.buf)->sequence = sequence;
296 must_write(writeall(connstate->clientw->fd, getoutputinfo_reply.buf, getoutputinfo_reply.len));
297 return true;
298 }
299 }
300 /* END RandR 1.5 specific */
301
302 return false;
303 }
304
305 static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents) {
306 struct connstate *connstate = (struct connstate *)w->data;
307 // all packets from the server are at least 32 bytes in length
308 size_t len = 32;
309 void *packet = smalloc(len);
310 must_read(readall_into(packet, len, connstate->serverw->fd));
311 switch (((generic_x11_reply_t *)packet)->code) {
312 case 0: { // error
313 const uint16_t sequence = ((xcb_request_error_t *)packet)->sequence;
314 if (handle_sequence(connstate, sequence)) {
315 free(packet);
316 return;
317 }
318 break;
319 }
320 case 1: // reply
321 len += ((generic_x11_reply_t *)packet)->length * 4;
322 if (len > 32) {
323 packet = srealloc(packet, len);
324 must_read(readall_into(packet + 32, len - 32, connstate->serverw->fd));
325 }
326
327 /* BEGIN RandR 1.5 specific */
328 const uint16_t sequence = ((generic_x11_reply_t *)packet)->sequence;
329
330 if (sequence == connstate->getext_randr) {
331 xcb_query_extension_reply_t *reply = packet;
332 connstate->randr_major_opcode = reply->major_opcode;
333 }
334 /* END RandR 1.5 specific */
335
336 if (handle_sequence(connstate, sequence)) {
337 free(packet);
338 return;
339 }
340
341 break;
342
343 default: // event
344 break;
345 }
346 must_write(writeall(connstate->clientw->fd, packet, len));
347 free(packet);
348 }
349
350 static void child_cb(EV_P_ ev_child *w, int revents) {
351 ev_child_stop(EV_A_ w);
352 if (WIFEXITED(w->rstatus)) {
353 exit(WEXITSTATUS(w->rstatus));
354 } else {
355 exit(WTERMSIG(w->rstatus) + 128);
356 }
357 }
358
359 static void must_read_reply(const char *filename, struct injected_reply *reply) {
360 FILE *f;
361 if ((f = fopen(filename, "r")) == NULL) {
362 err(EXIT_FAILURE, "fopen(%s)", filename);
363 }
364 struct stat stbuf;
365 if (fstat(fileno(f), &stbuf) != 0) {
366 err(EXIT_FAILURE, "fstat(%s)", filename);
367 }
368 reply->len = stbuf.st_size;
369 reply->buf = smalloc(stbuf.st_size);
370 int n = fread(reply->buf, 1, stbuf.st_size, f);
371 if (n != stbuf.st_size) {
372 err(EXIT_FAILURE, "fread(%s)", filename);
373 }
374 fclose(f);
375 }
376
377 int main(int argc, char *argv[]) {
378 static struct option long_options[] = {
379 {"getmonitors_reply", required_argument, 0, 0},
380 {"getoutputinfo_reply", required_argument, 0, 0},
381 {0, 0, 0, 0},
382 };
383 char *options_string = "";
384 int opt;
385 int option_index = 0;
386
387 while ((opt = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
388 switch (opt) {
389 case 0: {
390 const char *option_name = long_options[option_index].name;
391 if (strcmp(option_name, "getmonitors_reply") == 0) {
392 must_read_reply(optarg, &getmonitors_reply);
393 } else if (strcmp(option_name, "getoutputinfo_reply") == 0) {
394 must_read_reply(optarg, &getoutputinfo_reply);
395 }
396 break;
397 }
398 default:
399 exit(EXIT_FAILURE);
400 }
401 }
402
403 if (optind >= argc) {
404 errx(EXIT_FAILURE, "syntax: %s [options] <command>\n", argv[0]);
405 }
406
407 int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
408 if (fd == -1) {
409 err(EXIT_FAILURE, "socket(AF_UNIX)");
410 }
411
412 if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
413 warn("Could not set FD_CLOEXEC");
414 }
415
416 struct sockaddr_un addr;
417 memset(&addr, 0, sizeof(struct sockaddr_un));
418 addr.sun_family = AF_UNIX;
419 int i;
420 bool bound = false;
421 for (i = 0; i < 100; i++) {
422 /* XXX: The path to X11 sockets differs on some platforms (e.g. Trusted
423 * Solaris, HPUX), but since libxcb doesn’t provide a function to
424 * generate the path, we’ll just have to hard-code it for now. */
425 snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/.X11-unix/X%d", i);
426
427 if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
428 warn("bind(%s)", addr.sun_path);
429 } else {
430 bound = true;
431 /* Let the user know bind() was successful, so that they know the
432 * error messages can be disregarded. */
433 fprintf(stderr, "Successfuly bound to %s\n", addr.sun_path);
434 sun_path = sstrdup(addr.sun_path);
435 break;
436 }
437 }
438
439 if (!bound) {
440 err(EXIT_FAILURE, "bind()");
441 }
442
443 atexit(cleanup_socket);
444
445 /* This program will be started for each testcase which requires it, so we
446 * expect precisely one connection. */
447 if (listen(fd, 1) == -1) {
448 err(EXIT_FAILURE, "listen()");
449 }
450
451 pid_t child = fork();
452 if (child == -1) {
453 err(EXIT_FAILURE, "fork()");
454 }
455 if (child == 0) {
456 char *display;
457 sasprintf(&display, ":%d", i);
458 setenv("DISPLAY", display, 1);
459 free(display);
460
461 char **child_args = argv + optind;
462 execvp(child_args[0], child_args);
463 err(EXIT_FAILURE, "exec()");
464 }
465
466 struct ev_loop *loop = ev_default_loop(0);
467
468 ev_child cw;
469 ev_child_init(&cw, child_cb, child, 0);
470 ev_child_start(loop, &cw);
471
472 ev_io watcher;
473 ev_io_init(&watcher, uds_connection_cb, fd, EV_READ);
474 ev_io_start(loop, &watcher);
475
476 ev_run(loop, 0);
477 }
0 package SocketActivation;
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use IO::Socket::UNIX; # core
6 use Cwd qw(abs_path); # core
7 use POSIX qw(:fcntl_h); # core
8 use AnyEvent::Handle; # not core
9 use AnyEvent::Util; # not core
10 use Exporter 'import';
11 use v5.10;
12
13 our @EXPORT = qw(activate_i3);
14
15 #
16 # Starts i3 using socket activation. Creates a listening socket (with bind +
17 # listen) which is then passed to i3, who in turn calls accept and handles the
18 # requests.
19 #
20 # Since the kernel buffers the connect, the parent process can connect to the
21 # socket immediately after forking. It then sends a request and waits until it
22 # gets an answer. Obviously, i3 has to be initialized to actually answer the
23 # request.
24 #
25 # This way, we can wait *precisely* the amount of time which i3 waits to get
26 # ready, which is a *HUGE* speed gain (and a lot more robust) in comparison to
27 # using sleep() with a fixed amount of time.
28 #
29 # unix_socket_path: Location of the socket to use for the activation
30 # display: X11 $ENV{DISPLAY}
31 # configfile: path to the configuration file to use
32 # logpath: path to the logfile to which i3 will append
33 # cv: an AnyEvent->condvar which will be triggered once i3 is ready
34 #
35 sub activate_i3 {
36 my %args = @_;
37
38 # remove the old unix socket
39 unlink($args{unix_socket_path});
40
41 my $socket = IO::Socket::UNIX->new(
42 Listen => 1,
43 Local => $args{unix_socket_path},
44 );
45
46 my $pid = fork;
47 if (!defined($pid)) {
48 die "could not fork()";
49 }
50 if ($pid == 0) {
51 # Start a process group so that in the parent, we can kill the entire
52 # process group and immediately kill i3bar and any other child
53 # processes.
54 setpgrp;
55
56 $ENV{LISTEN_PID} = $$;
57 $ENV{LISTEN_FDS} = 1;
58 delete $ENV{DESKTOP_STARTUP_ID};
59 delete $ENV{I3SOCK};
60 # $SHELL could be set to fish, which will horribly break running shell
61 # commands via i3’s exec feature. This happened e.g. when having
62 # “set-option -g default-shell "/usr/bin/fish"” in ~/.tmux.conf
63 delete $ENV{SHELL};
64 unless ($args{dont_create_temp_dir}) {
65 $ENV{XDG_RUNTIME_DIR} = '/tmp/i3-testsuite/';
66 mkdir $ENV{XDG_RUNTIME_DIR};
67 }
68 $ENV{DISPLAY} = $args{display};
69
70 # We are about to exec, but we did not modify $^F to include $socket
71 # when creating the socket (because the file descriptor could have a
72 # number != 3 which would lead to i3 leaking a file descriptor). This
73 # caused Perl to set the FD_CLOEXEC flag, which would close $socket on
74 # exec(), effectively *NOT* passing $socket to the new process.
75 # Therefore, we explicitly clear FD_CLOEXEC (the only flag right now)
76 # by setting the flags to 0.
77 POSIX::fcntl($socket, F_SETFD, 0) or die "Could not clear fd flags: $!";
78
79 # If the socket does not use file descriptor 3 by chance already, we
80 # close fd 3 and dup2() the socket to 3.
81 if (fileno($socket) != 3) {
82 POSIX::close(3);
83 POSIX::dup2(fileno($socket), 3);
84 POSIX::close(fileno($socket));
85 }
86
87 # Make sure no file descriptors are open. Strangely, I got an open file
88 # descriptor pointing to AnyEvent/Impl/EV.pm when testing.
89 AnyEvent::Util::close_all_fds_except(0, 1, 2, 3);
90
91 # Construct the command to launch i3. Use maximum debug level, disable
92 # the interactive signalhandler to make it crash immediately instead.
93 # Also disable logging to SHM since we redirect the logs anyways.
94 # Force Xinerama because we use Xdmx for multi-monitor tests.
95 my $i3cmd = q|i3 --shmlog-size=0 --disable-signalhandler|;
96 if (!defined($args{inject_randr15})) {
97 $i3cmd .= q| --force-xinerama|;
98 }
99 if (!$args{validate_config}) {
100 # We only set logging if i3 is actually started, but not if we only
101 # validate the config file. This is to keep logging to a minimum as
102 # such a test will likely want to inspect the log file.
103 $i3cmd .= q| -V -d all|;
104 }
105
106 # For convenience:
107 my $outdir = $args{outdir};
108 my $test = $args{testname};
109
110 if ($args{restart}) {
111 $i3cmd .= ' -L ' . abs_path('restart-state.golden');
112 }
113
114 if ($args{validate_config}) {
115 $i3cmd .= ' -C';
116 }
117
118 if ($args{valgrind}) {
119 $i3cmd =
120 qq|valgrind --log-file="$outdir/valgrind-for-$test.log" | .
121 qq|--suppressions="./valgrind.supp" | .
122 qq|--leak-check=full --track-origins=yes --num-callers=20 | .
123 qq|--tool=memcheck -- $i3cmd|;
124 }
125
126 my $logfile = "$outdir/i3-log-for-$test";
127 # Append to $logfile instead of overwriting because i3 might be
128 # run multiple times in one testcase.
129 my $cmd = "exec $i3cmd -c $args{configfile} >>$logfile 2>&1";
130
131 if ($args{strace}) {
132 my $out = "$outdir/strace-for-$test.log";
133
134 # We overwrite LISTEN_PID with the correct process ID to make
135 # socket activation work (LISTEN_PID has to match getpid(),
136 # otherwise the LISTEN_FDS will be treated as a left-over).
137 $cmd = qq|strace -fF -s2048 -v -o "$out" -- | .
138 'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
139 }
140
141 if ($args{xtrace}) {
142 my $out = "$outdir/xtrace-for-$test.log";
143
144 # See comment in $args{strace} branch.
145 $cmd = qq|xtrace -n -o "$out" -- | .
146 'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
147 }
148
149 if ($args{inject_randr15}) {
150 # See comment in $args{strace} branch.
151 $cmd = 'test.inject_randr15 --getmonitors_reply="' .
152 $args{inject_randr15} . '" ' .
153 ($args{inject_randr15_outputinfo}
154 ? '--getoutputinfo_reply="' .
155 $args{inject_randr15_outputinfo} . '" '
156 : '') .
157 '-- ' .
158 'sh -c "export LISTEN_PID=\$\$; ' . $cmd . '"';
159 }
160
161 # We need to use the shell due to using output redirections.
162 exec '/bin/sh', '-c', $cmd;
163
164 # if we are still here, i3 could not be found or exec failed. bail out.
165 exit 1;
166 }
167
168 # close the socket, the child process should be the only one which keeps a file
169 # descriptor on the listening socket.
170 $socket->close;
171
172 if ($args{validate_config}) {
173 $args{cv}->send(1);
174 return $pid;
175 }
176
177 # We now connect (will succeed immediately) and send a request afterwards.
178 # As soon as the reply is there, i3 is considered ready.
179 my $cl = IO::Socket::UNIX->new(Peer => $args{unix_socket_path});
180 my $hdl;
181 $hdl = AnyEvent::Handle->new(
182 fh => $cl,
183 on_error => sub {
184 $hdl->destroy;
185 $args{cv}->send(0);
186 });
187
188 # send a get_tree message without payload
189 $hdl->push_write('i3-ipc' . pack("LL", 0, 4));
190
191 # wait for the reply
192 $hdl->push_read(chunk => 1, => sub {
193 my ($h, $line) = @_;
194 $args{cv}->send(1);
195 undef $hdl;
196 });
197
198 return $pid;
199 }
200
201 1
0 package StartXServer;
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use Exporter 'import';
6 use Time::HiRes qw(sleep);
7 use i3test::Util qw(slurp);
8 use v5.10;
9
10 our @EXPORT = qw(start_xserver);
11
12 my @pids;
13 my $x_socketpath = '/tmp/.X11-unix/X';
14
15 # forks an X server process
16 sub fork_xserver {
17 my $keep_xserver_output = shift;
18 my $displaynum = shift;
19 my $pid = fork();
20 die "Could not fork: $!" unless defined($pid);
21 if ($pid == 0) {
22 # Child, close stdout/stderr, then start Xephyr
23 if (!$keep_xserver_output) {
24 close STDOUT;
25 close STDERR;
26 }
27
28 exec @_;
29 exit 1;
30 }
31 push(@complete_run::CLEANUP, sub {
32 kill(15, $pid);
33 # Unlink the X11 socket, Xdmx seems to leave it there.
34 unlink($x_socketpath . $displaynum);
35 });
36
37 push @pids, $pid;
38
39 return $x_socketpath . $displaynum;
40 }
41
42 # Blocks until the socket paths specified in the given array reference actually
43 # exist.
44 sub wait_for_x {
45 my ($sockets_waiting) = @_;
46
47 # Wait until Xdmx actually runs. Pretty ugly solution, but as long as we
48 # can’t socket-activate X11…
49 while (1) {
50 @$sockets_waiting = grep { ! -S $_ } @$sockets_waiting;
51 last unless @$sockets_waiting;
52 sleep 0.1;
53 }
54 }
55
56 =head2 start_xserver($parallel)
57
58 Starts C<$parallel> (or number of cores * 2 if undef) Xephyr processes (see
59 https://www.freedesktop.org/wiki/Software/Xephyr/) and returns two arrayrefs: a
60 list of X11 display numbers to the Xephyr processes and a list of PIDs of the
61 processes.
62
63 =cut
64
65 sub start_xserver {
66 my ($parallel, $numtests, $keep_xserver_output) = @_;
67
68 my @displays = ();
69 my @childpids = ();
70
71 # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have
72 # _SC_NPROCESSORS_CONF.
73 my $num_cores;
74 if (-e '/proc/cpuinfo') {
75 my $cpuinfo = slurp('/proc/cpuinfo');
76 $num_cores = scalar grep { /model name/ } split("\n", $cpuinfo);
77 }
78 # If /proc/cpuinfo does not exist, we fall back to 2 cores.
79 $num_cores ||= 2;
80
81 # If unset, we use num_cores * 2.
82 $parallel ||= ($num_cores * 2);
83
84 # If we are running a small number of tests, don’t over-parallelize.
85 $parallel = $numtests if $numtests < $parallel;
86
87 # First get the last used display number, then increment it by one.
88 # Effectively falls back to 1 if no X server is running.
89 my ($displaynum) = reverse sort { $a <=> $b } map{ /(\d+)$/ } glob($x_socketpath . '*');
90 $displaynum++;
91
92 say "Starting $parallel Xephyr instances, starting at :$displaynum...";
93
94 $SIG{CHLD} = sub {
95 my $child = waitpid -1, POSIX::WNOHANG;
96 @pids = grep { $_ != $child } @pids;
97 return unless @pids == 0;
98 print STDERR "All X server processes died.\n";
99 print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n";
100 exit 1;
101 };
102
103 my @sockets_waiting;
104 for (1 .. $parallel) {
105 my $socket = fork_xserver($keep_xserver_output, $displaynum,
106 'Xephyr', ":$displaynum", '-screen', '1280x800',
107 '-nolisten', 'tcp', '-name', "i3test");
108 push(@displays, ":$displaynum");
109 push(@sockets_waiting, $socket);
110 $displaynum++;
111 }
112
113 wait_for_x(\@sockets_waiting);
114
115 return @displays;
116 }
117
118 1
0 package StatusLine;
1 use strict; use warnings;
2
3 # enable autoflush on STDOUT.
4 # this is essential, because we print our statuslines without a newline
5 $| = 1;
6
7 use Exporter 'import';
8 our @EXPORT = qw/status_init status status_completed/;
9
10 my $ansi_clear_line = "\033[2K";
11 my $ansi_save_cursor = "\0337";
12 my $ansi_restore_cursor = "\0338";
13 my %ansi_line_upwards;
14
15 my $tests_total;
16
17 sub noninteractive {
18 # CONTINUOUS_INTEGRATION gets set when running under Travis, see
19 # https://docs.travis-ci.com/user/ci-environment/ and
20 # https://github.com/travis-ci/travis-ci/issues/1337
21 return (! -t STDOUT) || (
22 defined($ENV{CONTINUOUS_INTEGRATION}) &&
23 $ENV{CONTINUOUS_INTEGRATION} eq 'true');
24 }
25
26 # setup %ansi_line_upwards to map all working displays to the
27 # specific movement commands and initialize all status lines
28 sub status_init {
29 my %args = @_;
30 my $displays = $args{displays};
31 $tests_total = $args{tests};
32
33 return if noninteractive();
34
35 for my $n (1 .. @$displays) {
36 # since we are moving upwards, get $display in reverse order
37 my $display = $displays->[-$n];
38
39 $ansi_line_upwards{$display} = "\033[$n\101";
40
41 # print an empty line for this status line
42 print "\n";
43 }
44
45 status_completed(0);
46 }
47
48 # generates the status text, prints it in the appropriate line
49 # and returns it, so it can be used in conjunction with C<Log()>
50 sub status {
51 my ($display, $msg) = @_;
52 my $status = "[$display] $msg";
53
54 return $status if noninteractive();
55
56 print
57 $ansi_save_cursor,
58 $ansi_line_upwards{$display},
59 $ansi_clear_line,
60 $status,
61 $ansi_restore_cursor;
62
63 return $status;
64 }
65
66 sub status_completed {
67 my $num = shift;
68
69 return if noninteractive();
70
71 print
72 $ansi_save_cursor,
73 $ansi_clear_line,
74 "completed $num of $tests_total tests",
75 $ansi_restore_cursor;
76 }
77
78
79 __PACKAGE__ __END__
0 # vim:ts=4:sw=4:sts=4:expandtab
1 package TestWorker;
2 use strict; use warnings;
3 use v5.10;
4
5 use Socket qw(AF_UNIX SOCK_DGRAM PF_UNSPEC);
6 use IO::Handle; # for ->autoflush
7
8 use POSIX ();
9
10 use Errno qw(EAGAIN);
11
12 use Exporter 'import';
13 our @EXPORT = qw(worker worker_next);
14
15 use File::Basename qw(basename);
16 my @x;
17 my $options;
18
19 sub worker {
20 my ($display, $x, $outdir, $optref) = @_;
21
22 # make sure $x hangs around
23 push @x, $x;
24
25 # store the options hashref
26 $options = $optref;
27
28 socketpair(my $ipc_child, my $ipc, AF_UNIX, SOCK_DGRAM, PF_UNSPEC)
29 or die "socketpair: $!";
30
31 $ipc->autoflush(1);
32 $ipc_child->autoflush(1);
33
34 my $worker = {
35 display => $display,
36 ipc => $ipc,
37 };
38
39 my $pid = fork // die "could not fork: $!";
40
41 if ($pid == 0) {
42 close $ipc;
43 undef @complete_run::CLEANUP;
44 # reap dead test children
45 $SIG{CHLD} = sub { waitpid -1, POSIX::WNOHANG };
46
47 $worker->{ipc} = $ipc_child;
48
49 # Preload the i3test module: reduces user CPU from 25s to 18s
50 require i3test;
51
52 worker_wait($worker, $outdir);
53 exit 23;
54
55 }
56
57 close $ipc_child;
58 push @complete_run::CLEANUP, sub {
59 # signal via empty line to exit itself
60 syswrite($ipc, "\n") or kill('TERM', $pid);
61 waitpid $pid, 0;
62 };
63
64 return $worker;
65
66 }
67
68 our $EOF = "# end of file\n";
69 sub worker_wait {
70 my ($self, $outdir) = @_;
71
72 my $ipc = $self->{ipc};
73 my $ipc_fd = fileno($ipc);
74
75 while (1) {
76 my $file = $ipc->getline;
77 if (!defined($file)) {
78 next if $! == EAGAIN;
79 last;
80 }
81 chomp $file;
82
83 exit unless $file;
84
85 die "tried to launch nonexistent testfile $file: $!\n"
86 unless -e $file;
87
88 # start a new and self contained process:
89 # whatever happens in the testfile should *NOT* affect us.
90
91 my $pid = fork // die "could not fork: $!";
92 if ($pid == 0) {
93 undef @complete_run::CLEANUP;
94 local $SIG{CHLD};
95
96 $0 = $file;
97
98 # Re-seed rand() so that File::Temp’s tempnam produces different
99 # results, making a TOCTOU between e.g. t/175-startup-notification.t
100 # and t/180-fd-leaks.t less likely.
101 srand(time ^ $$);
102
103 POSIX::dup2($ipc_fd, 0);
104 POSIX::dup2($ipc_fd, 1);
105 POSIX::dup2(1, 2);
106
107 # get Test::Builder singleton
108 my $test = Test::Builder->new;
109
110 # Test::Builder dups stdout/stderr while loading.
111 # we need to reset them here to point to $ipc
112 $test->output(\*STDOUT);
113 $test->failure_output(\*STDERR);
114 $test->todo_output(\*STDOUT);
115
116 @ENV{qw(HOME DISPLAY TESTNAME OUTDIR VALGRIND STRACE XTRACE COVERAGE RESTART)}
117 = ($outdir,
118 $self->{display},
119 basename($file),
120 $outdir,
121 $options->{valgrind},
122 $options->{strace},
123 $options->{xtrace},
124 $options->{coverage},
125 $options->{restart});
126
127 package main;
128 local $@;
129 do $file;
130 $test->ok(undef, "$@") if $@;
131
132 # XXX hack, we need to trigger the read watcher once more
133 # to signal eof to TAP::Parser
134 print $EOF;
135
136 exit 0;
137 }
138 }
139 }
140
141 sub worker_next {
142 my ($self, $file) = @_;
143
144 my $ipc = $self->{ipc};
145 syswrite $ipc, "$file\n" or die "syswrite: $!";
146 }
147
148 __PACKAGE__ __END__
0 package i3test::Test;
1 # vim:ts=4:sw=4:expandtab
2
3 use base 'Test::Builder::Module';
4
5 our @EXPORT = qw(
6 is_num_children
7 is_num_fullscreen
8 cmp_float
9 does_i3_live
10 );
11
12 my $CLASS = __PACKAGE__;
13
14 =head1 NAME
15
16 i3test::Test - Additional test instructions for use in i3 testcases
17
18 =head1 SYNOPSIS
19
20 use i3test;
21
22 my $ws = fresh_workspace;
23 is_num_children($ws, 0, 'no containers on this workspace yet');
24 cmd 'open';
25 is_num_children($ws, 1, 'one container after "open"');
26
27 done_testing;
28
29 =head1 DESCRIPTION
30
31 This module provides convenience methods for i3 testcases. If you notice that a
32 certain pattern is present in 5 or more test cases, it should most likely be
33 moved into this module.
34
35 =head1 EXPORT
36
37 =head2 is_num_children($workspace, $expected, $test_name)
38
39 Gets the number of children on the given workspace and verifies that they match
40 the expected amount of children.
41
42 is_num_children('1', 0, 'no containers on workspace 1 at startup');
43
44 =cut
45
46 sub is_num_children {
47 my ($workspace, $num_children, $name) = @_;
48 my $tb = $CLASS->builder;
49
50 my $con = i3test::get_ws($workspace);
51 $tb->ok(defined($con), "Workspace $workspace exists");
52 if (!defined($con)) {
53 $tb->skip("Workspace does not exist, skipping is_num_children");
54 return;
55 }
56
57 my $got_num_children = scalar @{$con->{nodes}};
58
59 $tb->is_num($got_num_children, $num_children, $name);
60 }
61
62 =head2 is_num_fullscreen($workspace, $expected, $test_name)
63
64 Gets the number of fullscreen containers on the given workspace and verifies that
65 they match the expected amount.
66
67 is_num_fullscreen('1', 0, 'no fullscreen containers on workspace 1');
68
69 =cut
70 sub is_num_fullscreen {
71 my ($workspace, $num_fullscreen, $name) = @_;
72 my $workspace_content = i3test::get_ws($workspace);
73 my $tb = $CLASS->builder;
74
75 my $nodes = scalar grep { $_->{fullscreen_mode} != 0 } @{$workspace_content->{nodes}->[0]->{nodes}};
76 my $cons = scalar grep { $_->{fullscreen_mode} != 0 } @{$workspace_content->{nodes}};
77 my $floating = scalar grep { $_->{fullscreen_mode} != 0 } @{$workspace_content->{floating_nodes}->[0]->{nodes}};
78 $tb->is_num($nodes + $cons + $floating, $num_fullscreen, $name);
79 }
80
81 =head2 cmp_float($a, $b)
82
83 Compares floating point numbers C<$a> and C<$b> and returns true if they differ
84 less then 1e-6.
85
86 $tmp = fresh_workspace;
87
88 open_window for (1..4);
89
90 cmd 'resize grow width 10 px or 25 ppt';
91
92 ($nodes, $focus) = get_ws_content($tmp);
93 ok(cmp_float($nodes->[0]->{percent}, 0.166666666666667), 'first window got 16%');
94 ok(cmp_float($nodes->[1]->{percent}, 0.166666666666667), 'second window got 16%');
95 ok(cmp_float($nodes->[2]->{percent}, 0.166666666666667), 'third window got 16%');
96 ok(cmp_float($nodes->[3]->{percent}, 0.50), 'fourth window got 50%');
97
98 =cut
99 sub cmp_float {
100 my ($a, $b, $name) = @_;
101 my $tb = $CLASS->builder;
102
103 $tb->cmp_ok(abs($a - $b), '<', 1e-6, $name);
104 }
105
106 =head2 does_i3_live
107
108 Returns true if the layout tree can still be received from i3.
109
110 # i3 used to crash on invalid commands in revision X
111 cmd 'invalid command';
112 does_i3_live;
113
114 =cut
115 sub does_i3_live {
116 my $tree = i3test::i3(i3test::get_socket_path())->get_tree->recv;
117 my @nodes = @{$tree->{nodes}};
118 my $tb = $CLASS->builder;
119 $tb->ok((@nodes > 0), 'i3 still lives');
120 }
121
122 =head1 AUTHOR
123
124 Michael Stapelberg <michael@i3wm.org>
125
126 =cut
127
128 1
0 package i3test::Util;
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use v5.10;
6
7 use X11::XCB qw(GET_PROPERTY_TYPE_ANY);
8 use X11::XCB::Connection;
9
10 use Exporter qw(import);
11 our @EXPORT = qw(
12 slurp
13 get_socket_path
14 );
15
16 =encoding utf-8
17
18 =head1 NAME
19
20 i3test::Util - General utility functions
21
22 =cut
23
24 =head1 EXPORT
25
26 =cut
27
28 =head2 slurp($fn)
29
30 Reads the entire file specified in the arguments and returns the content.
31
32 =cut
33 sub slurp {
34 my ($file) = @_;
35 my $content = do {
36 local $/ = undef;
37 open my $fh, "<", $file or die "could not open $file: $!";
38 <$fh>;
39 };
40
41 return $content;
42 }
43
44 =head2 get_socket_path([X11::XCB::Connection])
45
46 Gets the socket path from the C<I3_SOCKET_PATH> atom stored on the X11 root
47 window.
48
49 =cut
50 sub get_socket_path {
51 my ($x) = @_;
52 $x //= X11::XCB::Connection->new();
53 my $atom = $x->atom(name => 'I3_SOCKET_PATH');
54 my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256);
55 my $reply = $x->get_property_reply($cookie->{sequence});
56 my $socketpath = $reply->{value};
57 if ($socketpath eq "/tmp/nested-$ENV{DISPLAY}") {
58 $socketpath .= '-activation';
59 }
60 return $socketpath;
61 }
62
63 =head1 AUTHOR
64
65 Michael Stapelberg <michael@i3wm.org>
66
67 =cut
68
69 1
0 package i3test::XTEST;
1 # vim:ts=4:sw=4:expandtab
2
3 use strict;
4 use warnings;
5 use v5.10;
6
7 use Test::More;
8 use i3test::Util qw(get_socket_path);
9 use lib qw(@abs_top_srcdir@/AnyEvent-I3/blib/lib);
10 use AnyEvent::I3;
11 use ExtUtils::PkgConfig;
12
13 use Exporter ();
14 our @EXPORT = qw(
15 inlinec_connect
16 xtest_sync_with
17 xtest_sync_with_i3
18 set_xkb_group
19 xtest_key_press
20 xtest_key_release
21 xtest_button_press
22 xtest_button_release
23 binding_events
24 );
25
26 =encoding utf-8
27
28 =head1 NAME
29
30 i3test::XTEST - Inline::C wrappers for xcb-xtest and xcb-xkb
31
32 =cut
33
34 # We need to use libxcb-xkb because xdotool cannot trigger ISO_Next_Group
35 # anymore: it contains code to set the XKB group to 1 and then restore the
36 # previous group, effectively rendering any keys that switch groups
37 # ineffective.
38 my %sn_config;
39 BEGIN {
40 %sn_config = ExtUtils::PkgConfig->find('xcb-xkb xcb-xtest xcb-util');
41 }
42
43 use Inline C => Config => LIBS => $sn_config{libs}, CCFLAGS => $sn_config{cflags};
44 use Inline C => <<'END_OF_C_CODE';
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <stdbool.h>
50 #include <stdint.h>
51
52 #include <xcb/xcb.h>
53 #include <xcb/xkb.h>
54 #include <xcb/xtest.h>
55 #include <xcb/xcb_aux.h>
56
57 static xcb_connection_t *conn = NULL;
58 static xcb_window_t sync_window;
59 static xcb_window_t root_window;
60 static xcb_atom_t i3_sync_atom;
61
62 bool inlinec_connect() {
63 int screen;
64
65 if ((conn = xcb_connect(NULL, &screen)) == NULL ||
66 xcb_connection_has_error(conn)) {
67 if (conn != NULL) {
68 xcb_disconnect(conn);
69 }
70 fprintf(stderr, "Could not connect to X11\n");
71 return false;
72 }
73
74 if (!xcb_get_extension_data(conn, &xcb_xkb_id)->present) {
75 fprintf(stderr, "XKB not present\n");
76 return false;
77 }
78
79 if (!xcb_get_extension_data(conn, &xcb_test_id)->present) {
80 fprintf(stderr, "XTEST not present\n");
81 return false;
82 }
83
84 xcb_generic_error_t *err = NULL;
85 xcb_xkb_use_extension_reply_t *usereply;
86 usereply = xcb_xkb_use_extension_reply(
87 conn, xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION), &err);
88 if (err != NULL || usereply == NULL) {
89 fprintf(stderr, "xcb_xkb_use_extension() failed\n");
90 free(err);
91 return false;
92 }
93 free(usereply);
94
95 xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, xcb_intern_atom(conn, 0, strlen("I3_SYNC"), "I3_SYNC"), NULL);
96 i3_sync_atom = reply->atom;
97 free(reply);
98
99 xcb_screen_t *root_screen = xcb_aux_get_screen(conn, screen);
100 root_window = root_screen->root;
101 sync_window = xcb_generate_id(conn);
102 xcb_create_window(conn,
103 XCB_COPY_FROM_PARENT, // depth
104 sync_window, // window
105 root_window, // parent
106 -15, // x
107 -15, // y
108 1, // width
109 1, // height
110 0, // border_width
111 XCB_WINDOW_CLASS_INPUT_OUTPUT, // class
112 XCB_COPY_FROM_PARENT, // visual
113 XCB_CW_OVERRIDE_REDIRECT, // value_mask
114 (uint32_t[]){
115 1, // override_redirect
116 }); // value_list
117
118 return true;
119 }
120
121 void xtest_sync_with(int window) {
122 xcb_client_message_event_t ev;
123 memset(&ev, '\0', sizeof(xcb_client_message_event_t));
124
125 const int nonce = rand() % 255;
126
127 ev.response_type = XCB_CLIENT_MESSAGE;
128 ev.window = sync_window;
129 ev.type = i3_sync_atom;
130 ev.format = 32;
131 ev.data.data32[0] = sync_window;
132 ev.data.data32[1] = nonce;
133
134 xcb_send_event(conn, false, (xcb_window_t)window, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char *)&ev);
135 xcb_flush(conn);
136
137 xcb_generic_event_t *event = NULL;
138 while (1) {
139 free(event);
140 if ((event = xcb_wait_for_event(conn)) == NULL) {
141 break;
142 }
143 if (event->response_type == 0) {
144 fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
145 continue;
146 }
147
148 /* Strip off the highest bit (set if the event is generated) */
149 const int type = (event->response_type & 0x7F);
150 switch (type) {
151 case XCB_CLIENT_MESSAGE: {
152 xcb_client_message_event_t *ev = (xcb_client_message_event_t *)event;
153 {
154 const uint32_t got = ev->data.data32[0];
155 const uint32_t want = sync_window;
156 if (got != want) {
157 fprintf(stderr, "Ignoring ClientMessage: unknown window: got %d, want %d\n", got, want);
158 continue;
159 }
160 }
161 {
162 const uint32_t got = ev->data.data32[1];
163 const uint32_t want = nonce;
164 if (got != want) {
165 fprintf(stderr, "Ignoring ClientMessage: unknown nonce: got %d, want %d\n", got, want);
166 continue;
167 }
168 }
169 return;
170 }
171 default:
172 fprintf(stderr, "Unexpected X11 event of type %d received (XCB_CLIENT_MESSAGE = %d)\n", type, XCB_CLIENT_MESSAGE);
173 break;
174 }
175 }
176 free(event);
177 }
178
179 void xtest_sync_with_i3() {
180 xtest_sync_with((int)root_window);
181 }
182
183 // NOTE: while |group| should be a uint8_t, Inline::C will not define the
184 // function unless we use an int.
185 bool set_xkb_group(int group) {
186 xcb_generic_error_t *err = NULL;
187 // Needs libxcb ≥ 1.11 so that we have the following bug fix:
188 // https://cgit.freedesktop.org/xcb/proto/commit/src/xkb.xml?id=8d7ee5b6ba4cf343f7df70372a3e1f85b82aeed7
189 xcb_void_cookie_t cookie = xcb_xkb_latch_lock_state_checked(
190 conn,
191 XCB_XKB_ID_USE_CORE_KBD, /* deviceSpec */
192 0, /* affectModLocks */
193 0, /* modLocks */
194 1, /* lockGroup */
195 group, /* groupLock */
196 0, /* affectModLatches */
197 0, /* latchGroup */
198 0); /* groupLatch */
199 if ((err = xcb_request_check(conn, cookie)) != NULL) {
200 fprintf(stderr, "X error code %d\n", err->error_code);
201 free(err);
202 return false;
203 }
204 return true;
205 }
206
207 bool xtest_input(int type, int detail, int x, int y) {
208 xcb_generic_error_t *err;
209 xcb_void_cookie_t cookie;
210
211 cookie = xcb_test_fake_input_checked(
212 conn,
213 type, /* type */
214 detail, /* detail */
215 XCB_CURRENT_TIME, /* time */
216 XCB_NONE, /* root */
217 x, /* rootX */
218 y, /* rootY */
219 XCB_NONE); /* deviceid */
220 if ((err = xcb_request_check(conn, cookie)) != NULL) {
221 fprintf(stderr, "X error code %d\n", err->error_code);
222 free(err);
223 return false;
224 }
225
226 return true;
227 }
228
229 bool xtest_key(int type, int detail) {
230 return xtest_input(type, detail, 0, 0);
231 }
232
233 bool xtest_key_press(int detail) {
234 return xtest_key(XCB_KEY_PRESS, detail);
235 }
236
237 bool xtest_key_release(int detail) {
238 return xtest_key(XCB_KEY_RELEASE, detail);
239 }
240
241 bool xtest_button_press(int button, int x, int y) {
242 return xtest_input(XCB_BUTTON_PRESS, button, x, y);
243 }
244
245 bool xtest_button_release(int button, int x, int y) {
246 return xtest_input(XCB_BUTTON_RELEASE, button, x, y);
247 }
248
249 END_OF_C_CODE
250
251 sub import {
252 my ($class, %args) = @_;
253 ok(inlinec_connect(), 'Connect to X11, verify XKB and XTEST are present (via Inline::C)');
254 goto \&Exporter::import;
255 }
256
257 =head1 EXPORT
258
259 =cut
260
261 =head2 set_xkb_group($group)
262
263 Changes the current XKB group from the default of 1 to C<$group>, which must be
264 one of 1, 2, 3, 4.
265
266 Returns false when there was an X11 error changing the group, true otherwise.
267
268 =head2 xtest_key_press($detail)
269
270 Sends a KeyPress event via XTEST, with the specified C<$detail>, i.e. key code.
271 Use C<xev(1)> to find key codes.
272
273 Returns false when there was an X11 error, true otherwise.
274
275 =head2 xtest_key_release($detail)
276
277 Sends a KeyRelease event via XTEST, with the specified C<$detail>, i.e. key code.
278 Use C<xev(1)> to find key codes.
279
280 Returns false when there was an X11 error, true otherwise.
281
282 =head2 xtest_button_press($button, $x, $y)
283
284 Sends a ButtonPress event via XTEST, with the specified C<$button>.
285
286 Returns false when there was an X11 error, true otherwise.
287
288 =head2 xtest_button_release($button, $x, $y)
289
290 Sends a ButtonRelease event via XTEST, with the specified C<$button>.
291
292 Returns false when there was an X11 error, true otherwise.
293
294 =head2 xtest_sync_with($window)
295
296 Ensures the specified window has processed all X11 events which were triggered
297 by this module, provided the window response to the i3 sync protocol.
298
299 =head2 xtest_sync_with_i3()
300
301 Ensures i3 has processed all X11 events which were triggered by this module.
302
303 =head1 AUTHOR
304
305 Michael Stapelberg <michael@i3wm.org>
306
307 =cut
308
309 1
0 package i3test;
1 # vim:ts=4:sw=4:expandtab
2 use strict; use warnings;
3
4 use File::Temp qw(tmpnam tempfile tempdir);
5 use Test::Builder;
6 use X11::XCB::Rect;
7 use X11::XCB::Window;
8 use X11::XCB qw(:all);
9 use lib qw(@abs_top_srcdir@/AnyEvent-I3/blib/lib);
10 use AnyEvent::I3;
11 use List::Util qw(first);
12 use Time::HiRes qw(sleep);
13 use Cwd qw(abs_path);
14 use POSIX ':sys_wait_h';
15 use Scalar::Util qw(blessed);
16 use SocketActivation;
17 use i3test::Util qw(slurp);
18
19 use v5.10;
20
21 # preload
22 use Test::More ();
23 use Data::Dumper ();
24
25 use Exporter ();
26 our @EXPORT = qw(
27 get_workspace_names
28 get_output_for_workspace
29 get_unused_workspace
30 fresh_workspace
31 get_ws_content
32 get_ws
33 get_focused
34 open_empty_con
35 open_window
36 open_floating_window
37 get_dock_clients
38 cmd
39 sync_with_i3
40 exit_gracefully
41 exit_forcefully
42 workspace_exists
43 focused_ws
44 get_socket_path
45 launch_with_config
46 get_i3_log
47 wait_for_event
48 wait_for_map
49 wait_for_unmap
50 $x
51 kill_all_windows
52 events_for
53 listen_for_binding
54 is_net_wm_state_focused
55 );
56
57 =head1 NAME
58
59 i3test - Testcase setup module
60
61 =encoding utf-8
62
63 =head1 SYNOPSIS
64
65 use i3test;
66
67 my $ws = fresh_workspace;
68 is_num_children($ws, 0, 'no containers on this workspace yet');
69 cmd 'open';
70 is_num_children($ws, 1, 'one container after "open"');
71
72 done_testing;
73
74 =head1 DESCRIPTION
75
76 This module is used in every i3 testcase and takes care of automatically
77 starting i3 before any test instructions run. It also saves you typing of lots
78 of boilerplate in every test file.
79
80
81 i3test automatically "use"s C<Test::More>, C<Data::Dumper>, C<AnyEvent::I3>,
82 C<Time::HiRes>’s C<sleep> and C<i3test::Test> so that all of them are available
83 to you in your testcase.
84
85 See also C<i3test::Test> (L<https://build.i3wm.org/docs/lib-i3test-test.html>)
86 which provides additional test instructions (like C<ok> or C<is>).
87
88 =cut
89
90 my $tester = Test::Builder->new();
91 my $_cached_socket_path = undef;
92 my $_sync_window = undef;
93 my $tmp_socket_path = undef;
94
95 our $x;
96
97 BEGIN {
98 my $window_count = 0;
99 sub counter_window {
100 return $window_count++;
101 }
102 }
103
104 my $i3_pid;
105 my $i3_autostart;
106
107 END {
108 # Skip the remaining cleanup for testcases which set i3_autostart => 0:
109 return if !defined($i3_pid) && !$i3_autostart;
110
111 # don't trigger SIGCHLD handler
112 local $SIG{CHLD};
113
114 # From perldoc -v '$?':
115 # Inside an "END" subroutine $? contains the value
116 # that is going to be given to "exit()".
117 #
118 # Since waitpid sets $?, we need to localize it,
119 # otherwise TAP would be misinterpreted our return status
120 local $?;
121
122 # When measuring code coverage, try to exit i3 cleanly (otherwise, .gcda
123 # files are not written)
124 if ($ENV{COVERAGE} || $ENV{VALGRIND}) {
125 exit_gracefully($i3_pid, "/tmp/nested-$ENV{DISPLAY}");
126
127 } else {
128 kill(-9, $i3_pid)
129 or $tester->BAIL_OUT("could not kill i3: $!");
130
131 waitpid $i3_pid, 0;
132 }
133 }
134
135 sub import {
136 my ($class, %args) = @_;
137 my $pkg = caller;
138
139 $x ||= i3test::X11->new;
140 # set the pointer to a predictable position in case a previous test has
141 # disturbed it
142 $x->warp_pointer(
143 0, # src_window (None)
144 $x->get_root_window(), # dst_window (None)
145 0, # src_x
146 0, # src_y
147 0, # src_width
148 0, # src_height
149 0, # dst_x
150 0); # dst_y
151 # Synchronize with X11 to ensure the pointer has been warped before i3
152 # starts up.
153 $x->get_input_focus_reply($x->get_input_focus()->{sequence});
154
155 $i3_autostart = delete($args{i3_autostart}) // 1;
156 my $i3_config = delete($args{i3_config}) // '-default';
157
158 my $cv = launch_with_config($i3_config, dont_block => 1)
159 if $i3_autostart;
160
161 my $test_more_args = '';
162 $test_more_args = join(' ', 'qw(', %args, ')') if keys %args;
163 local $@;
164 eval << "__";
165 package $pkg;
166 use Test::More $test_more_args;
167 use Data::Dumper;
168 use AnyEvent::I3;
169 use Time::HiRes qw(sleep);
170 use i3test::Test;
171 __
172 $tester->BAIL_OUT("$@") if $@;
173 feature->import(":5.10");
174 strict->import;
175 warnings->import;
176
177 $cv->recv if $i3_autostart;
178
179 @_ = ($class);
180 goto \&Exporter::import;
181 }
182
183 =head1 EXPORT
184
185 =head2 wait_for_event($timeout, $callback)
186
187 Waits for the next event and calls the given callback for every event to
188 determine if this is the event we are waiting for.
189
190 Can be used to wait until a window is mapped, until a ClientMessage is
191 received, etc.
192
193 wait_for_event 0.25, sub { $_[0]->{response_type} == MAP_NOTIFY };
194
195 =cut
196 sub wait_for_event {
197 my ($timeout, $cb) = @_;
198
199 $x->flush;
200
201 while (defined(my $event = $x->wait_for_event)) {
202 return 1 if $cb->($event);
203 }
204 }
205
206 =head2 wait_for_map($window)
207
208 Thin wrapper around wait_for_event which waits for MAP_NOTIFY.
209 Make sure to include 'structure_notify' in the window’s event_mask attribute.
210
211 This function is called by C<open_window>, so in most cases, you don’t need to
212 call it on your own. If you need special setup of the window before mapping,
213 you might have to map it on your own and use this function:
214
215 my $window = open_window(dont_map => 1);
216 # Do something special with the window first
217 # …
218
219 # Now map it and wait until it’s been mapped
220 $window->map;
221 wait_for_map($window);
222
223 =cut
224 sub wait_for_map {
225 my ($win) = @_;
226 my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
227 wait_for_event 4, sub {
228 $_[0]->{response_type} == MAP_NOTIFY and $_[0]->{window} == $id
229 };
230 }
231
232 =head2 wait_for_unmap($window)
233
234 Wrapper around C<wait_for_event> which waits for UNMAP_NOTIFY. Also calls
235 C<sync_with_i3> to make sure i3 also picked up and processed the UnmapNotify
236 event.
237
238 my $ws = fresh_workspace;
239 my $window = open_window;
240 is_num_children($ws, 1, 'one window on workspace');
241 $window->unmap;
242 wait_for_unmap;
243 is_num_children($ws, 0, 'no more windows on this workspace');
244
245 =cut
246 sub wait_for_unmap {
247 my ($win) = @_;
248 # my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
249 wait_for_event 4, sub {
250 $_[0]->{response_type} == UNMAP_NOTIFY # and $_[0]->{window} == $id
251 };
252 sync_with_i3();
253 }
254
255 =head2 open_window([ $args ])
256
257 Opens a new window (see C<X11::XCB::Window>), maps it, waits until it got mapped
258 and synchronizes with i3.
259
260 The following arguments can be passed:
261
262 =over 4
263
264 =item class
265
266 The X11 window class (e.g. WINDOW_CLASS_INPUT_OUTPUT), not to be confused with
267 the WM_CLASS!
268
269 =item rect
270
271 An arrayref with 4 members specifying the initial geometry (position and size)
272 of the window, e.g. C<< [ 0, 100, 70, 50 ] >> for a window appearing at x=0, y=100
273 with width=70 and height=50.
274
275 Note that this is entirely irrelevant for tiling windows.
276
277 =item background_color
278
279 The background pixel color of the window, formatted as "#rrggbb", like HTML
280 color codes (e.g. #c0c0c0). This is useful to tell windows apart when actually
281 watching the testcases.
282
283 =item event_mask
284
285 An arrayref containing strings which describe the X11 event mask we use for that
286 window. The default is C<< [ 'structure_notify' ] >>.
287
288 =item name
289
290 The window’s C<_NET_WM_NAME> (UTF-8 window title). By default, this is "Window
291 n" with n being replaced by a counter to keep windows apart.
292
293 =item dont_map
294
295 Set to a true value to avoid mapping the window (making it visible).
296
297 =item before_map
298
299 A coderef which is called before the window is mapped (unless C<dont_map> is
300 true). The freshly created C<$window> is passed as C<$_> and as the first
301 argument.
302
303 =back
304
305 The default values are equivalent to this call:
306
307 open_window(
308 class => WINDOW_CLASS_INPUT_OUTPUT
309 rect => [ 0, 0, 30, 30 ]
310 background_color => '#c0c0c0'
311 event_mask => [ 'structure_notify' ]
312 name => 'Window <n>'
313 );
314
315 Usually, though, calls are simpler:
316
317 my $top_window = open_window;
318
319 To identify the resulting window object in i3 commands, use the id property:
320
321 my $top_window = open_window;
322 cmd '[id="' . $top_window->id . '"] kill';
323
324 =cut
325 sub open_window {
326 my %args = @_ == 1 ? %{$_[0]} : @_;
327
328 my $dont_map = delete $args{dont_map};
329 my $before_map = delete $args{before_map};
330
331 $args{class} //= WINDOW_CLASS_INPUT_OUTPUT;
332 $args{rect} //= [ 0, 0, 30, 30 ];
333 $args{background_color} //= '#c0c0c0';
334 $args{event_mask} //= [ 'structure_notify' ];
335 $args{name} //= 'Window ' . counter_window();
336
337 my $window = $x->root->create_child(%args);
338 $window->add_hint('input');
339
340 if ($before_map) {
341 # TODO: investigate why _create is not needed
342 $window->_create;
343 $before_map->($window) for $window;
344 }
345
346 return $window if $dont_map;
347
348 $window->map;
349 wait_for_map($window);
350
351 # MapWindow is sent before i3 even starts rendering: the window is placed at
352 # temporary off-screen coordinates first, and x_push_changes() sends further
353 # X11 requests to set focus etc. Hence, we sync with i3 before continuing.
354 sync_with_i3();
355
356 return $window;
357 }
358
359 =head2 open_floating_window([ $args ])
360
361 Thin wrapper around open_window which sets window_type to
362 C<_NET_WM_WINDOW_TYPE_UTILITY> to make the window floating.
363
364 The arguments are the same as those of C<open_window>.
365
366 =cut
367 sub open_floating_window {
368 my %args = @_ == 1 ? %{$_[0]} : @_;
369
370 $args{window_type} = $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY');
371
372 return open_window(\%args);
373 }
374
375 sub open_empty_con {
376 my ($i3) = @_;
377
378 my $reply = $i3->command('open')->recv;
379 return $reply->[0]->{id};
380 }
381
382 =head2 get_workspace_names()
383
384 Returns an arrayref containing the name of every workspace (regardless of its
385 output) which currently exists.
386
387 my $workspace_names = get_workspace_names;
388 is(scalar @$workspace_names, 3, 'three workspaces exist currently');
389
390 =cut
391 sub get_workspace_names {
392 my $i3 = i3(get_socket_path());
393 my $tree = $i3->get_tree->recv;
394 my @outputs = @{$tree->{nodes}};
395 my @cons;
396 for my $output (@outputs) {
397 next if $output->{name} eq '__i3';
398 # get the first CT_CON of each output
399 my $content = first { $_->{type} eq 'con' } @{$output->{nodes}};
400 @cons = (@cons, @{$content->{nodes}});
401 }
402 [ map { $_->{name} } @cons ]
403 }
404
405 =head2 get_output_for_workspace()
406
407 Returns the name of the output on which this workspace resides
408
409 cmd 'focus output fake-1';
410 cmd 'workspace 1';
411 is(get_output_for_workspace('1', 'fake-0', 'Workspace 1 in output fake-0');
412
413 =cut
414 sub get_output_for_workspace {
415 my $ws_name = shift @_;
416 my $i3 = i3(get_socket_path());
417 my $tree = $i3->get_tree->recv;
418 my @outputs = @{$tree->{nodes}};
419
420 foreach (grep { not $_->{name} =~ /^__/ } @outputs) {
421 my $output = $_->{name};
422 foreach (grep { $_->{name} =~ "content" } @{$_->{nodes}}) {
423 return $output if $_->{nodes}[0]->{name} =~ $ws_name;
424 }
425 }
426 }
427
428 =head2 get_unused_workspace
429
430 Returns a workspace name which has not yet been used. See also
431 C<fresh_workspace> which directly switches to an unused workspace.
432
433 my $ws = get_unused_workspace;
434 cmd "workspace $ws";
435
436 =cut
437 sub get_unused_workspace {
438 my @names = get_workspace_names();
439 my $tmp;
440 do { $tmp = tmpnam() } while ((scalar grep { $_ eq $tmp } @names) > 0);
441 $tmp
442 }
443
444 =head2 fresh_workspace([ $args ])
445
446 Switches to an unused workspace and returns the name of that workspace.
447
448 Optionally switches to the specified output first.
449
450 my $ws = fresh_workspace;
451
452 # Get a fresh workspace on the second output.
453 my $ws = fresh_workspace(output => 1);
454
455 =cut
456 sub fresh_workspace {
457 my %args = @_;
458 if (exists($args{output})) {
459 my $i3 = i3(get_socket_path());
460 my $tree = $i3->get_tree->recv;
461 my $output = first { $_->{name} eq "fake-$args{output}" }
462 @{$tree->{nodes}};
463 die "BUG: Could not find output $args{output}" unless defined($output);
464 # Get the focused workspace on that output and switch to it.
465 my $content = first { $_->{type} eq 'con' } @{$output->{nodes}};
466 my $focused = $content->{focus}->[0];
467 my $workspace = first { $_->{id} == $focused } @{$content->{nodes}};
468 $workspace = $workspace->{name};
469 cmd("workspace $workspace");
470 }
471
472 my $unused = get_unused_workspace;
473 cmd("workspace $unused");
474 $unused
475 }
476
477 =head2 get_ws($workspace)
478
479 Returns the container (from the i3 layout tree) which represents C<$workspace>.
480
481 my $ws = fresh_workspace;
482 my $ws_con = get_ws($ws);
483 ok(!$ws_con->{urgent}, 'fresh workspace not marked urgent');
484
485 Here is an example which counts the number of urgent containers recursively,
486 starting from the workspace container:
487
488 sub count_urgent {
489 my ($con) = @_;
490
491 my @children = (@{$con->{nodes}}, @{$con->{floating_nodes}});
492 my $urgent = grep { $_->{urgent} } @children;
493 $urgent += count_urgent($_) for @children;
494 return $urgent;
495 }
496 my $urgent = count_urgent(get_ws($ws));
497 is($urgent, 3, "three urgent windows on workspace $ws");
498
499
500 =cut
501 sub get_ws {
502 my ($name) = @_;
503 my $i3 = i3(get_socket_path());
504 my $tree = $i3->get_tree->recv;
505
506 my @outputs = @{$tree->{nodes}};
507 my @workspaces;
508 for my $output (@outputs) {
509 # get the first CT_CON of each output
510 my $content = first { $_->{type} eq 'con' } @{$output->{nodes}};
511 @workspaces = (@workspaces, @{$content->{nodes}});
512 }
513
514 # as there can only be one workspace with this name, we can safely
515 # return the first entry
516 return first { $_->{name} eq $name } @workspaces;
517 }
518
519 =head2 get_ws_content($workspace)
520
521 Returns the content (== tree, starting from the node of a workspace)
522 of a workspace. If called in array context, also includes the focus
523 stack of the workspace.
524
525 my $nodes = get_ws_content($ws);
526 is(scalar @$nodes, 4, 'there are four containers at workspace-level');
527
528 Or, in array context:
529
530 my $window = open_window;
531 my ($nodes, $focus) = get_ws_content($ws);
532 is($focus->[0], $window->id, 'newly opened window focused');
533
534 Note that this function does not do recursion for you! It only returns the
535 containers B<on workspace level>. If you want to work with all containers (even
536 nested ones) on a workspace, you have to use recursion:
537
538 # NB: This function does not count floating windows
539 sub count_urgent {
540 my ($nodes) = @_;
541
542 my $urgent = 0;
543 for my $con (@$nodes) {
544 $urgent++ if $con->{urgent};
545 $urgent += count_urgent($con->{nodes});
546 }
547
548 return $urgent;
549 }
550 my $nodes = get_ws_content($ws);
551 my $urgent = count_urgent($nodes);
552 is($urgent, 3, "three urgent windows on workspace $ws");
553
554 If you also want to deal with floating windows, you have to use C<get_ws>
555 instead and access C<< ->{nodes} >> and C<< ->{floating_nodes} >> on your own.
556
557 =cut
558 sub get_ws_content {
559 my ($name) = @_;
560 my $con = get_ws($name);
561 return wantarray ? ($con->{nodes}, $con->{focus}) : $con->{nodes};
562 }
563
564 =head2 get_focused($workspace)
565
566 Returns the container ID of the currently focused container on C<$workspace>.
567
568 Note that the container ID is B<not> the X11 window ID, so comparing the result
569 of C<get_focused> with a window's C<< ->{id} >> property does B<not> work.
570
571 my $ws = fresh_workspace;
572 my $first_window = open_window;
573 my $first_id = get_focused();
574
575 my $second_window = open_window;
576 my $second_id = get_focused();
577
578 cmd 'focus left';
579
580 is(get_focused($ws), $first_id, 'second window focused');
581
582 =cut
583 sub get_focused {
584 my ($ws) = @_;
585 my $con = get_ws($ws);
586
587 my @focused = @{$con->{focus}};
588 my $lf;
589 while (@focused > 0) {
590 $lf = $focused[0];
591 last unless defined($con->{focus});
592 @focused = @{$con->{focus}};
593 my @cons = grep { $_->{id} == $lf } (@{$con->{nodes}}, @{$con->{'floating_nodes'}});
594 $con = $cons[0];
595 }
596
597 return $lf;
598 }
599
600 =head2 get_dock_clients([ $dockarea ])
601
602 Returns an array of all dock containers in C<$dockarea> (one of "top" or
603 "bottom"). If C<$dockarea> is not specified, returns an array of all dock
604 containers in any dockarea.
605
606 my @docked = get_dock_clients;
607 is(scalar @docked, 0, 'no dock clients yet');
608
609 =cut
610 sub get_dock_clients {
611 my $which = shift;
612
613 my $tree = i3(get_socket_path())->get_tree->recv;
614 my @outputs = @{$tree->{nodes}};
615 # Children of all dockareas
616 my @docked;
617 for my $output (@outputs) {
618 if (!defined($which)) {
619 @docked = (@docked, map { @{$_->{nodes}} }
620 grep { $_->{type} eq 'dockarea' }
621 @{$output->{nodes}});
622 } elsif ($which eq 'top') {
623 my $first = first { $_->{type} eq 'dockarea' } @{$output->{nodes}};
624 @docked = (@docked, @{$first->{nodes}}) if defined($first);
625 } elsif ($which eq 'bottom') {
626 my @matching = grep { $_->{type} eq 'dockarea' } @{$output->{nodes}};
627 my $last = $matching[-1];
628 @docked = (@docked, @{$last->{nodes}}) if defined($last);
629 }
630 }
631 return @docked;
632 }
633
634 =head2 cmd($command)
635
636 Sends the specified command to i3 and returns the output.
637
638 my $ws = unused_workspace;
639 cmd "workspace $ws";
640 cmd 'focus right';
641
642 =cut
643 sub cmd {
644 i3(get_socket_path())->command(@_)->recv
645 }
646
647 =head2 workspace_exists($workspace)
648
649 Returns true if C<$workspace> is the name of an existing workspace.
650
651 my $old_ws = focused_ws;
652 # switch away from where we currently are
653 fresh_workspace;
654
655 ok(workspace_exists($old_ws), 'old workspace still exists');
656
657 =cut
658 sub workspace_exists {
659 my ($name) = @_;
660 (scalar grep { $_ eq $name } @{get_workspace_names()}) > 0;
661 }
662
663 =head2 focused_ws
664
665 Returns the name of the currently focused workspace.
666
667 my $ws = focused_ws;
668 is($ws, '1', 'i3 starts on workspace 1');
669
670 =cut
671 sub focused_ws {
672 my $i3 = i3(get_socket_path());
673 my $tree = $i3->get_tree->recv;
674 my $focused = $tree->{focus}->[0];
675 my $output = first { $_->{id} == $focused } @{$tree->{nodes}};
676 my $content = first { $_->{type} eq 'con' } @{$output->{nodes}};
677 my $first = first { $_->{fullscreen_mode} == 1 } @{$content->{nodes}};
678 return $first->{name}
679 }
680
681 =head2 sync_with_i3([ $args ])
682
683 Sends an I3_SYNC ClientMessage with a random value to the root window.
684 i3 will reply with the same value, but, due to the order of events it
685 processes, only after all other events are done.
686
687 This can be used to ensure the results of a cmd 'focus left' are pushed to
688 X11 and that C<< $x->input_focus >> returns the correct value afterwards.
689
690 See also L<https://build.i3wm.org/docs/testsuite.html> for a longer explanation.
691
692 my $window = open_window;
693 $window->add_hint('urgency');
694 # Ensure i3 picked up the change
695 sync_with_i3;
696
697 The only time when you need to use the C<no_cache> argument is when you just
698 killed your own X11 connection:
699
700 cmd 'kill client';
701 # We need to re-establish the X11 connection which we just killed :).
702 $x = i3test::X11->new;
703 sync_with_i3(no_cache => 1);
704
705 =cut
706 sub sync_with_i3 {
707 my %args = @_ == 1 ? %{$_[0]} : @_;
708
709 # Since we need a (mapped) window for receiving a ClientMessage, we create
710 # one on the first call of sync_with_i3. It will be re-used in all
711 # subsequent calls.
712 if (!exists($args{window_id}) &&
713 (!defined($_sync_window) || exists($args{no_cache}))) {
714 $_sync_window = open_window(
715 rect => [ -15, -15, 10, 10 ],
716 override_redirect => 1,
717 dont_map => 1,
718 );
719 }
720
721 my $window_id = delete $args{window_id};
722 $window_id //= $_sync_window->id;
723
724 my $root = $x->get_root_window();
725 # Generate a random number to identify this particular ClientMessage.
726 my $myrnd = int(rand(255)) + 1;
727
728 # Generate a ClientMessage, see xcb_client_message_t
729 my $msg = pack "CCSLLLLLLL",
730 CLIENT_MESSAGE, # response_type
731 32, # format
732 0, # sequence
733 $root, # destination window
734 $x->atom(name => 'I3_SYNC')->id,
735
736 $window_id, # data[0]: our own window id
737 $myrnd, # data[1]: a random value to identify the request
738 0,
739 0,
740 0;
741
742 # Send it to the root window -- since i3 uses the SubstructureRedirect
743 # event mask, it will get the ClientMessage.
744 $x->send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
745
746 return $myrnd if $args{dont_wait_for_event};
747
748 # now wait until the reply is here
749 return wait_for_event 4, sub {
750 my ($event) = @_;
751 # TODO: const
752 return 0 unless $event->{response_type} == 161;
753
754 my ($win, $rnd) = unpack "LL", $event->{data};
755 return ($rnd == $myrnd);
756 };
757 }
758
759 =head2 exit_gracefully($pid, [ $socketpath ])
760
761 Tries to exit i3 gracefully (with the 'exit' cmd) or kills the PID if that fails.
762
763 If C<$socketpath> is not specified, C<get_socket_path()> will be called.
764
765 You only need to use this function if you have launched i3 on your own with
766 C<launch_with_config>. Otherwise, it will be automatically called when the
767 testcase ends.
768
769 use i3test i3_autostart => 0;
770 my $pid = launch_with_config($config);
771 # …
772 exit_gracefully($pid);
773
774 =cut
775 sub exit_gracefully {
776 my ($pid, $socketpath) = @_;
777 $socketpath ||= get_socket_path();
778
779 my $exited = 0;
780 eval {
781 say "Exiting i3 cleanly...";
782 i3($socketpath)->command('exit')->recv;
783 $exited = 1;
784 };
785
786 if (!$exited) {
787 kill(9, $pid)
788 or $tester->BAIL_OUT("could not kill i3: $!");
789 }
790
791 if ($socketpath =~ m,^/tmp/i3-test-socket-,) {
792 unlink($socketpath);
793 }
794
795 waitpid $pid, 0;
796 undef $i3_pid;
797 }
798
799 =head2 exit_forcefully($pid, [ $signal ])
800
801 Tries to exit i3 forcefully by sending a signal (defaults to SIGTERM).
802
803 You only need to use this function if you want to test signal handling
804 (in which case you must have launched i3 on your own with
805 C<launch_with_config>).
806
807 use i3test i3_autostart => 0;
808 my $pid = launch_with_config($config);
809 # …
810 exit_forcefully($pid);
811
812 =cut
813 sub exit_forcefully {
814 my ($pid, $signal) = @_;
815 $signal ||= 'TERM';
816
817 # Send the given signal to the i3 instance and wait for up to 10s
818 # for it to terminate.
819 kill($signal, $pid)
820 or $tester->BAIL_OUT("could not kill i3: $!");
821 my $status;
822 my $timeout = 10;
823 do {
824 $status = waitpid $pid, WNOHANG;
825
826 if ($status <= 0) {
827 sleep(1);
828 $timeout--;
829 }
830 } while ($status <= 0 && $timeout > 0);
831
832 if ($status <= 0) {
833 kill('KILL', $pid)
834 or $tester->BAIL_OUT("could not kill i3: $!");
835 waitpid $pid, 0;
836 }
837 undef $i3_pid;
838 }
839
840 =head2 get_socket_path([ $cache ])
841
842 Gets the socket path from the C<I3_SOCKET_PATH> atom stored on the X11 root
843 window. After the first call, this function will return a cached version of the
844 socket path unless you specify a false value for C<$cache>.
845
846 my $i3 = i3(get_socket_path());
847 $i3->command('nop test example')->recv;
848
849 To avoid caching:
850
851 my $i3 = i3(get_socket_path(0));
852
853 =cut
854 sub get_socket_path {
855 my ($cache) = @_;
856 $cache //= 1;
857
858 if ($cache && defined($_cached_socket_path)) {
859 return $_cached_socket_path;
860 }
861 my $socketpath = i3test::Util::get_socket_path($x);
862 $_cached_socket_path = $socketpath;
863 return $socketpath;
864 }
865
866 =head2 launch_with_config($config, [ $args ])
867
868 Launches a new i3 process with C<$config> as configuration file. Useful for
869 tests which test specific config file directives.
870
871 use i3test i3_autostart => 0;
872
873 my $config = <<EOT;
874 # i3 config file (v4)
875 for_window [class="borderless"] border none
876 for_window [title="special borderless title"] border none
877 EOT
878
879 my $pid = launch_with_config($config);
880
881 # …
882
883 exit_gracefully($pid);
884
885 =cut
886 sub launch_with_config {
887 my ($config, %args) = @_;
888
889 $tmp_socket_path = "/tmp/nested-$ENV{DISPLAY}";
890
891 $args{dont_create_temp_dir} //= 0;
892 $args{validate_config} //= 0;
893
894 my ($fh, $tmpfile) = tempfile("i3-cfg-for-$ENV{TESTNAME}-XXXXX", UNLINK => 1);
895
896 say $fh "ipc-socket $tmp_socket_path"
897 unless $args{dont_add_socket_path};
898
899 if ($config ne '-default') {
900 print $fh $config;
901 } else {
902 open(my $conf_fh, '<', '@abs_top_srcdir@/testcases/i3-test.config')
903 or $tester->BAIL_OUT("could not open default config: $!");
904 local $/;
905 say $fh scalar <$conf_fh>;
906 }
907
908 close($fh);
909
910 my $cv = AnyEvent->condvar;
911 $i3_pid = activate_i3(
912 unix_socket_path => "$tmp_socket_path-activation",
913 display => $ENV{DISPLAY},
914 configfile => $tmpfile,
915 outdir => $ENV{OUTDIR},
916 testname => $ENV{TESTNAME},
917 valgrind => $ENV{VALGRIND},
918 strace => $ENV{STRACE},
919 xtrace => $ENV{XTRACE},
920 restart => $ENV{RESTART},
921 cv => $cv,
922 dont_create_temp_dir => $args{dont_create_temp_dir},
923 validate_config => $args{validate_config},
924 inject_randr15 => $args{inject_randr15},
925 inject_randr15_outputinfo => $args{inject_randr15_outputinfo},
926 );
927
928 # If we called i3 with -C, we wait for it to exit and then return as
929 # there's nothing else we need to do.
930 if ($args{validate_config}) {
931 $cv->recv;
932 waitpid $i3_pid, 0;
933
934 # We need this since exit_gracefully will not be called in this case.
935 undef $i3_pid;
936
937 return ${^CHILD_ERROR_NATIVE};
938 }
939
940 # force update of the cached socket path in lib/i3test
941 # as soon as i3 has started
942 $cv->cb(sub { get_socket_path(0) });
943
944 return $cv if $args{dont_block};
945
946 # blockingly wait until i3 is ready
947 $cv->recv;
948
949 return $i3_pid;
950 }
951
952 =head2 get_i3_log
953
954 Returns the content of the log file for the current test.
955
956 =cut
957 sub get_i3_log {
958 my $logfile = "$ENV{OUTDIR}/i3-log-for-$ENV{TESTNAME}";
959 return slurp($logfile);
960 }
961
962 =head2 kill_all_windows
963
964 Kills all windows to clean up between tests.
965
966 =cut
967 sub kill_all_windows {
968 # Sync in case not all windows are managed by i3 just yet.
969 sync_with_i3;
970 cmd '[title=".*"] kill';
971 }
972
973 =head2 events_for($subscribecb, [ $rettype ], [ $eventcbs ])
974
975 Helper function which returns an array containing all events of type $rettype
976 which were generated by i3 while $subscribecb was running.
977
978 Set $eventcbs to subscribe to multiple event types and/or perform your own event
979 aggregation.
980
981 =cut
982 sub events_for {
983 my ($subscribecb, $rettype, $eventcbs) = @_;
984
985 my @events;
986 $eventcbs //= {};
987 if (defined($rettype)) {
988 $eventcbs->{$rettype} = sub { push @events, shift };
989 }
990 my $subscribed = AnyEvent->condvar;
991 my $flushed = AnyEvent->condvar;
992 $eventcbs->{tick} = sub {
993 my ($event) = @_;
994 if ($event->{first}) {
995 $subscribed->send($event);
996 } else {
997 $flushed->send($event);
998 }
999 };
1000 my $i3 = i3(get_socket_path(0));
1001 $i3->connect->recv;
1002 $i3->subscribe($eventcbs)->recv;
1003 $subscribed->recv;
1004 # Subscription established, run the callback.
1005 $subscribecb->();
1006 # Now generate a tick event, which we know we’ll receive (and at which point
1007 # all other events have been received).
1008 my $nonce = int(rand(255)) + 1;
1009 $i3->send_tick($nonce);
1010
1011 my $tick = $flushed->recv;
1012 $tester->is_eq($tick->{payload}, $nonce, 'tick nonce received');
1013 return @events;
1014 }
1015
1016 =head2 listen_for_binding($cb)
1017
1018 Helper function to evaluate whether sending KeyPress/KeyRelease events via XTEST
1019 triggers an i3 key binding or not. Expects key bindings to be configured in the
1020 form “bindsym <binding> nop <binding>”, e.g. “bindsym Mod4+Return nop
1021 Mod4+Return”.
1022
1023 is(listen_for_binding(
1024 sub {
1025 xtest_key_press(133); # Super_L
1026 xtest_key_press(36); # Return
1027 xtest_key_release(36); # Return
1028 xtest_key_release(133); # Super_L
1029 xtest_sync_with_i3;
1030 },
1031 ),
1032 'Mod4+Return',
1033 'triggered the "Mod4+Return" keybinding');
1034
1035 =cut
1036
1037 sub listen_for_binding {
1038 my ($cb) = @_;
1039 my $triggered = AnyEvent->condvar;
1040 my @events = events_for(
1041 $cb,
1042 'binding');
1043
1044 $tester->is_eq(scalar @events, 1, 'Received precisely one event');
1045 $tester->is_eq($events[0]->{change}, 'run', 'change is "run"');
1046 # We look at the command (which is “nop <binding>”) because that is easier
1047 # than re-assembling the string representation of $event->{binding}.
1048 my $command = $events[0]->{binding}->{command};
1049 $command =~ s/^nop //g;
1050 return $command;
1051 }
1052
1053 =head2 is_net_wm_state_focused
1054
1055 Returns true if the given window has the _NET_WM_STATE_FOCUSED atom.
1056
1057 ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
1058
1059 =cut
1060 sub is_net_wm_state_focused {
1061 my ($window) = @_;
1062
1063 sync_with_i3;
1064 my $atom = $x->atom(name => '_NET_WM_STATE_FOCUSED');
1065 my $cookie = $x->get_property(
1066 0,
1067 $window->{id},
1068 $x->atom(name => '_NET_WM_STATE')->id,
1069 GET_PROPERTY_TYPE_ANY,
1070 0,
1071 4096
1072 );
1073
1074 my $reply = $x->get_property_reply($cookie->{sequence});
1075 my $len = $reply->{length};
1076 return 0 if $len == 0;
1077
1078 my @atoms = unpack("L$len", $reply->{value});
1079 for (my $i = 0; $i < $len; $i++) {
1080 return 1 if $atoms[$i] == $atom->id;
1081 }
1082
1083 return 0;
1084 }
1085
1086
1087 =head1 AUTHOR
1088
1089 Michael Stapelberg <michael@i3wm.org>
1090
1091 =cut
1092
1093 package i3test::X11;
1094 use parent 'X11::XCB::Connection';
1095
1096 sub input_focus {
1097 my $self = shift;
1098 i3test::sync_with_i3();
1099
1100 return $self->SUPER::input_focus(@_);
1101 }
1102
1103 1
0 #!/usr/bin/env perl
1 # vim:ts=4:sw=4:expandtab
2 # © 2012 Michael Stapelberg and contributors
3 # Script to create a new testcase from a template.
4 #
5 # # Create (and edit) a new test for moving floating windows
6 # ./new-test floating move
7 #
8 # # Create (and edit) a multi-monitor test for moving workspaces
9 # ./new-test -m move workspaces
10
11 use strict;
12 use warnings;
13 use File::Basename qw(basename);
14 use Getopt::Long;
15 use v5.10;
16
17 my $usage = <<'EOF';
18 Script to create a new testcase from a template.
19
20 # Create (and edit) a new test for moving floating windows
21 ./new-test floating move
22
23 # Create (and edit) a multi-monitor test for moving workspaces
24 ./new-test -m move workspaces
25 EOF
26
27 my $multi_monitor;
28
29 my $result = GetOptions(
30 'multi-monitor|mm' => \$multi_monitor
31 );
32
33 my $testname = join(' ', @ARGV);
34 $testname =~ s/ /-/g;
35
36 unless (length $testname) {
37 say $usage;
38 exit(0);
39 }
40
41 my $header = <<'EOF';
42 #!perl
43 # vim:ts=4:sw=4:expandtab
44 #
45 # Please read the following documents before working on tests:
46 # • https://build.i3wm.org/docs/testsuite.html
47 # (or docs/testsuite)
48 #
49 # • https://build.i3wm.org/docs/lib-i3test.html
50 # (alternatively: perldoc ./testcases/lib/i3test.pm)
51 #
52 # • https://build.i3wm.org/docs/ipc.html
53 # (or docs/ipc)
54 #
55 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
56 # (unless you are already familiar with Perl)
57 #
58 # TODO: Description of this file.
59 # Ticket: #999
60 # Bug still in: #GITREV#
61 EOF
62
63 # Figure out the next test filename.
64 my @files;
65 if ($multi_monitor) {
66 @files = grep { basename($_) =~ /^5/ } <t/*.t>;
67 } else {
68 @files = grep { basename($_) !~ /^5/ } <t/*.t>;
69 }
70 my ($latest) = sort { $b cmp $a } @files;
71 my ($num) = (basename($latest) =~ /^([0-9]+)/);
72 my $filename = "t/" . ($num+1) . "-" . lc($testname) . ".t";
73
74 # Get the current git revision.
75 chomp(my $gitrev = qx(git describe --tags));
76 $header =~ s/#GITREV#/$gitrev/g;
77
78 # Create the file based on our template.
79 open(my $fh, '>', $filename);
80 print $fh $header;
81 if ($multi_monitor) {
82 print $fh <<'EOF';
83 use i3test i3_autostart => 0;
84
85 my $config = <<EOT;
86 # i3 config file (v4)
87 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
88
89 fake-outputs 1024x768+0+0,1024x768+1024+0
90 EOT
91
92 my $pid = launch_with_config($config);
93
94
95 exit_gracefully($pid);
96
97 done_testing;
98 EOF
99 } else {
100 print $fh <<'EOF';
101 use i3test;
102
103
104 done_testing;
105 EOF
106 }
107 close($fh);
108
109 my $editor = $ENV{EDITOR};
110 $editor ||= 'vi';
111 exec $editor, $filename;
0 {"id":6752976,"type":0,"orientation":"none","percent":null,"urgent":false,"focused":false,"layout":"default","border":"none","rect":{"x":0,"y":0,"width":1280,"height":1024},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"root","window":null,"nodes":[{"id":6809472,"type":1,"orientation":"none","percent":1,"urgent":false,"focused":false,"layout":"output","border":"none","rect":{"x":0,"y":0,"width":1280,"height":1024},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"xroot-0","window":null,"nodes":[{"id":6810064,"type":5,"orientation":"vertical","percent":null,"urgent":false,"focused":false,"layout":"dockarea","border":"none","rect":{"x":0,"y":0,"width":1280,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"topdock","window":null,"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":0,"swallows":[{"dock":2,"insert_where":2}]},{"id":6831664,"type":2,"orientation":"none","percent":null,"urgent":false,"focused":false,"layout":"default","border":"none","rect":{"x":0,"y":0,"width":1280,"height":1024},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"content","window":null,"nodes":[{"id":6832880,"type":4,"orientation":"horizontal","percent":null,"urgent":false,"focused":true,"layout":"default","border":"none","rect":{"x":0,"y":0,"width":1280,"height":1024},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"1","num":1,"window":null,"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":1,"swallows":[]}],"floating_nodes":[],"focus":[6832880],"fullscreen_mode":0,"swallows":[]},{"id":6832224,"type":5,"orientation":"vertical","percent":null,"urgent":false,"focused":false,"layout":"dockarea","border":"none","rect":{"x":0,"y":1024,"width":1280,"height":0},"window_rect":{"x":0,"y":0,"width":0,"height":0},"geometry":{"x":0,"y":0,"width":0,"height":0},"name":"bottomdock","window":null,"nodes":[],"floating_nodes":[],"focus":[],"fullscreen_mode":0,"swallows":[{"dock":3,"insert_where":2}]}],"floating_nodes":[],"focus":[6831664,6810064,6832224],"fullscreen_mode":0,"swallows":[]}],"floating_nodes":[],"focus":[6809472],"fullscreen_mode":0,"swallows":[]}
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2
3 use Test::More;
4
5 BEGIN {
6 my @deps = qw(
7 X11::XCB::Connection
8 X11::XCB::Window
9 AnyEvent
10 IPC::Run
11 ExtUtils::PkgConfig
12 Inline
13 );
14 for my $dep (@deps) {
15 use_ok($dep) or BAIL_OUT(qq|The Perl module "$dep" could not be loaded. Please see https://build.i3wm.org/docs/testsuite.html#_installing_the_dependencies|);
16 }
17 }
18
19 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
19
20 my $window = open_window(rect => $original_rect, dont_map => 1);
21 isa_ok($window, 'X11::XCB::Window');
22
23 is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
24
25 $window->map;
26 wait_for_map $window;
27
28 my $new_rect = $window->rect;
29 ok(!eq_hash($new_rect, $original_rect), "Window got repositioned");
30
31 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # checks if i3 supports I3_SYNC
17 #
18 use i3test;
19
20 my $result = sync_with_i3;
21 ok($result, 'syncing was successful');
22
23 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 fresh_workspace;
19
20 #####################################################################
21 # Ensure IPC works by switching workspaces
22 #####################################################################
23
24 # Create a window so we can get a focus different from NULL
25 my $window = open_window;
26
27 my $focus = $x->input_focus;
28
29 # Switch to another workspace
30 fresh_workspace;
31
32 sync_with_i3;
33 my $new_focus = $x->input_focus;
34 isnt($focus, $new_focus, "Focus changed");
35
36 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
19
20 my $window = open_window(
21 rect => $original_rect,
22 override_redirect => 1,
23 dont_map => 1,
24 );
25
26 isa_ok($window, 'X11::XCB::Window');
27
28 is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
29
30 $window->map;
31
32 my $new_rect = $window->rect;
33 isa_ok($new_rect, 'X11::XCB::Rect');
34
35 is_deeply($new_rect, $original_rect, "window untouched");
36
37 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 # Create a floating window which is smaller than the minimum enforced size of i3
19 my $window = open_floating_window;
20
21 isa_ok($window, 'X11::XCB::Window');
22
23 my ($absolute, $top) = $window->rect;
24
25 ok($window->mapped, 'Window is mapped');
26 cmp_ok($absolute->{width}, '>=', 75, 'i3 raised the width to 75');
27 cmp_ok($absolute->{height}, '>=', 50, 'i3 raised the height to 50');
28
29 ok($absolute->{x} != 0 && $absolute->{y} != 0, 'i3 did not map it to (0x0)');
30
31 $window->unmap;
32
33 $window = open_floating_window(rect => [ 20, 20, 80, 90 ]);
34
35 isa_ok($window, 'X11::XCB::Window');
36
37 ($absolute, $top) = $window->rect;
38
39 cmp_ok($absolute->{width}, '==', 80, "i3 let the width at 80");
40 cmp_ok($absolute->{height}, '==', 90, "i3 let the height at 90");
41
42 cmp_ok($top->{x}, '==', 20, 'i3 mapped it to x=20');
43 cmp_ok($top->{y}, '==', 20, 'i3 mapped it to y=20');
44
45 $window->unmap;
46
47 #####################################################################
48 # check that a tiling window which is then made floating still has
49 # at least the size of its initial geometry
50 #####################################################################
51
52 $window = open_window(rect => [ 1, 1, 80, 90 ]);
53
54 isa_ok($window, 'X11::XCB::Window');
55
56 cmd 'floating enable';
57 sync_with_i3;
58
59 ($absolute, $top) = $window->rect;
60
61 cmp_ok($absolute->{width}, '==', 80, "i3 let the width at 80");
62 cmp_ok($absolute->{height}, '==', 90, "i3 let the height at 90");
63
64 $window->unmap;
65
66 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17 use List::Util qw(first);
18
19 my $i3 = i3(get_socket_path());
20
21 my $tmp = fresh_workspace;
22
23 # get the output of this workspace
24 my $tree = $i3->get_tree->recv;
25 my @outputs = @{$tree->{nodes}};
26 my $output;
27 for my $o (@outputs) {
28 # get the first CT_CON of each output
29 my $content = first { $_->{type} eq 'con' } @{$o->{nodes}};
30 if (defined(first { $_->{name} eq $tmp } @{$content->{nodes}})) {
31 $output = $o;
32 last;
33 }
34 }
35
36 ##################################
37 # map a window, then fullscreen it
38 ##################################
39
40 my $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
41
42 my $window = open_window(
43 rect => $original_rect,
44 dont_map => 1,
45 );
46
47 isa_ok($window, 'X11::XCB::Window');
48
49 is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
50
51 $window->map;
52
53 wait_for_map $window;
54
55 # open another container to make the window get only half of the screen
56 cmd 'open';
57
58 my $new_rect = $window->rect;
59 ok(!eq_hash($new_rect, $original_rect), "Window got repositioned");
60 $original_rect = $new_rect;
61
62 $window->fullscreen(1);
63
64 sync_with_i3;
65
66 $new_rect = $window->rect;
67 ok(!eq_hash($new_rect, $original_rect), "Window got repositioned after fullscreen");
68
69 my $orect = $output->{rect};
70 my $wrect = $new_rect;
71
72 # see if the window really is fullscreen. 20 px for borders are allowed
73 my $threshold = 20;
74 ok(($wrect->{x} - $orect->{x}) < $threshold, 'x coordinate fullscreen');
75 ok(($wrect->{y} - $orect->{y}) < $threshold, 'y coordinate fullscreen');
76 ok(abs($wrect->{width} - $orect->{width}) < $threshold, 'width coordinate fullscreen');
77 ok(abs($wrect->{height} - $orect->{height}) < $threshold, 'height coordinate fullscreen');
78
79
80 $window->unmap;
81
82 #########################################################
83 # test with a window which is fullscreened before mapping
84 #########################################################
85
86 # open another container because the empty one will swallow the window we
87 # map in a second
88 cmd 'open';
89
90 $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
91 $window = open_window(
92 rect => $original_rect,
93 dont_map => 1,
94 );
95
96 is_deeply($window->rect, $original_rect, "rect unmodified before mapping");
97
98 $window->fullscreen(1);
99 $window->map;
100
101 wait_for_map $window;
102
103 $new_rect = $window->rect;
104 ok(!eq_hash($new_rect, $original_rect), "Window got repositioned after fullscreen");
105 ok($window->mapped, "Window is mapped after opening it in fullscreen mode");
106
107 $wrect = $new_rect;
108
109 # see if the window really is fullscreen. 20 px for borders are allowed
110 ok(($wrect->{x} - $orect->{x}) < $threshold, 'x coordinate fullscreen');
111 ok(($wrect->{y} - $orect->{y}) < $threshold, 'y coordinate fullscreen');
112 ok(abs($wrect->{width} - $orect->{width}) < $threshold, 'width coordinate fullscreen');
113 ok(abs($wrect->{height} - $orect->{height}) < $threshold, 'height coordinate fullscreen');
114
115 ################################################################################
116 # Verify that when one window wants to go into fullscreen mode, the old
117 # fullscreen window will be replaced.
118 ################################################################################
119
120 $original_rect = X11::XCB::Rect->new(x => 0, y => 0, width => 30, height => 30);
121 my $swindow = open_window(
122 rect => $original_rect,
123 dont_map => 1,
124 );
125
126 $swindow->map;
127
128 sync_with_i3;
129
130 ok(!$swindow->mapped, 'window not mapped while fullscreen window active');
131
132 $new_rect = $swindow->rect;
133 ok(!eq_hash($new_rect, $original_rect), "Window got repositioned");
134
135 $swindow->fullscreen(1);
136 sync_with_i3;
137
138 is_num_fullscreen($tmp, 1, 'amount of fullscreen windows');
139
140 $window->fullscreen(0);
141 sync_with_i3;
142 is_num_fullscreen($tmp, 1, 'amount of fullscreen windows');
143
144 ok($swindow->mapped, 'window mapped after other fullscreen ended');
145
146 ###########################################################################
147 # as $swindow is out of state at the moment (it requested to be fullscreen,
148 # but the WM denied), we check what happens if we go out of fullscreen now
149 # (nothing should happen)
150 ###########################################################################
151
152 $swindow->fullscreen(0);
153 sync_with_i3;
154
155 is_num_fullscreen($tmp, 0, 'amount of fullscreen windows after disabling');
156
157 cmd 'fullscreen';
158
159 is_num_fullscreen($tmp, 1, 'amount of fullscreen windows after fullscreen command');
160
161 cmd 'fullscreen';
162
163 is_num_fullscreen($tmp, 0, 'amount of fullscreen windows after fullscreen command');
164
165 # clean up the workspace so that it will be cleaned when switching away
166 cmd 'kill' for (@{get_ws_content($tmp)});
167
168 ################################################################################
169 # Verify that changing focus while in fullscreen does not work.
170 ################################################################################
171
172 $tmp = fresh_workspace;
173
174 my $other = open_window;
175 is($x->input_focus, $other->id, 'other window focused');
176
177 $window = open_window;
178 is($x->input_focus, $window->id, 'window focused');
179
180 cmd 'fullscreen';
181 is($x->input_focus, $window->id, 'fullscreen window focused');
182
183 cmd 'focus left';
184 is($x->input_focus, $window->id, 'fullscreen window still focused');
185
186 ################################################################################
187 # Verify that changing workspace while in global fullscreen does not work.
188 ################################################################################
189
190 $tmp = fresh_workspace;
191 $window = open_window;
192
193 cmd 'fullscreen global';
194 is($x->input_focus, $window->id, 'window focused');
195 is(focused_ws(), $tmp, 'workspace selected');
196
197 $other = get_unused_workspace;
198 cmd "workspace $other";
199 is($x->input_focus, $window->id, 'window still focused');
200 is(focused_ws(), $tmp, 'workspace still selected');
201
202 # leave global fullscreen so that is does not interfere with the other tests
203 $window->fullscreen(0);
204 sync_with_i3;
205
206 ################################################################################
207 # Verify that fullscreening a window on a second workspace and moving it onto
208 # the first workspace unfullscreens the first window.
209 ################################################################################
210
211 my $tmp2 = fresh_workspace;
212 $swindow = open_window;
213
214 cmd 'fullscreen';
215
216 is_num_fullscreen($tmp2, 1, 'one fullscreen window on second ws');
217
218 cmd "move workspace $tmp";
219
220 is_num_fullscreen($tmp2, 0, 'no fullscreen windows on second ws');
221 is_num_fullscreen($tmp, 1, 'one fullscreen window on first ws');
222
223 $swindow->fullscreen(0);
224 sync_with_i3;
225
226 # Verify that $swindow was the one that initially remained fullscreen.
227 is_num_fullscreen($tmp, 0, 'no fullscreen windows on first ws');
228
229 ################################################################################
230 # Verify that opening a window with _NET_WM_STATE_FULLSCREEN unfullscreens any
231 # existing container on the workspace and fullscreens the newly opened window.
232 ################################################################################
233
234 $tmp = fresh_workspace;
235
236 $window = open_window();
237
238 cmd "fullscreen";
239
240 is_num_fullscreen($tmp, 1, 'one fullscreen window on ws');
241 is($x->input_focus, $window->id, 'fullscreen window focused');
242
243 $swindow = open_window({
244 fullscreen => 1
245 });
246
247 is_num_fullscreen($tmp, 1, 'one fullscreen window on ws');
248 is($x->input_focus, $swindow->id, 'fullscreen window focused');
249
250 ################################################################################
251 # Verify that command ‘fullscreen enable’ works and is idempotent.
252 ################################################################################
253
254 $tmp = fresh_workspace;
255
256 $window = open_window;
257 is($x->input_focus, $window->id, 'window focused');
258 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
259
260 cmd 'fullscreen enable';
261 is($x->input_focus, $window->id, 'window still focused');
262 is_num_fullscreen($tmp, 1, 'one fullscreen window on workspace');
263
264 cmd 'fullscreen enable';
265 is($x->input_focus, $window->id, 'window still focused');
266 is_num_fullscreen($tmp, 1, 'still one fullscreen window on workspace');
267
268 $window->fullscreen(0);
269 sync_with_i3;
270 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
271
272 ################################################################################
273 # Verify that command ‘fullscreen enable global’ works and is idempotent.
274 ################################################################################
275
276 $tmp = fresh_workspace;
277
278 $window = open_window;
279 is($x->input_focus, $window->id, 'window focused');
280 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
281
282 cmd 'fullscreen enable global';
283 is($x->input_focus, $window->id, 'window still focused');
284 is_num_fullscreen($tmp, 1, 'one fullscreen window on workspace');
285
286 cmd 'fullscreen enable global';
287 is($x->input_focus, $window->id, 'window still focused');
288 is_num_fullscreen($tmp, 1, 'still one fullscreen window on workspace');
289
290 $window->fullscreen(0);
291 sync_with_i3;
292 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
293
294 ################################################################################
295 # Verify that command ‘fullscreen disable’ works and is idempotent.
296 ################################################################################
297
298 $tmp = fresh_workspace;
299
300 $window = open_window;
301 is($x->input_focus, $window->id, 'window focused');
302 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
303
304 $window->fullscreen(1);
305 sync_with_i3;
306 is_num_fullscreen($tmp, 1, 'one fullscreen window on workspace');
307
308 cmd 'fullscreen disable';
309 is($x->input_focus, $window->id, 'window still focused');
310 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
311
312 cmd 'fullscreen disable';
313 is($x->input_focus, $window->id, 'window still focused');
314 is_num_fullscreen($tmp, 0, 'still no fullscreen window on workspace');
315
316 ################################################################################
317 # Verify that command ‘fullscreen toggle’ works.
318 ################################################################################
319
320 $tmp = fresh_workspace;
321
322 $window = open_window;
323 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
324
325 cmd 'fullscreen toggle';
326 is($x->input_focus, $window->id, 'window still focused');
327 is_num_fullscreen($tmp, 1, 'one fullscreen window on workspace');
328
329 cmd 'fullscreen toggle';
330 is($x->input_focus, $window->id, 'window still focused');
331 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
332
333 ################################################################################
334 # Verify that a window’s fullscreen is disabled when another one is enabled
335 # on the same workspace. The new fullscreen window should be focused.
336 ################################################################################
337
338 $tmp = fresh_workspace;
339
340 $window = open_window;
341 $other = open_window;
342
343 is($x->input_focus, $other->id, 'other window focused');
344 is_num_fullscreen($tmp, 0, 'no fullscreen window on workspace');
345
346 cmd 'fullscreen enable';
347 is($x->input_focus, $other->id, 'other window focused');
348 is_num_fullscreen($tmp, 1, 'one fullscreen window on workspace');
349
350 cmd '[id="' . $window->id . '"] fullscreen enable';
351 is($x->input_focus, $window->id, 'window focused');
352 is_num_fullscreen($tmp, 1, 'one fullscreen window on workspace');
353
354 ################################################################################
355 # Verify that when a global fullscreen is enabled the window is focused and
356 # its workspace is selected, so that disabling the fullscreen keeps the window
357 # focused and visible.
358 ################################################################################
359
360 $tmp = fresh_workspace;
361
362 $window = open_window;
363
364 is($x->input_focus, $window->id, 'window focused');
365
366 cmd 'workspace ' . get_unused_workspace;
367 isnt($x->input_focus, $window->id, 'window not focused');
368 isnt(focused_ws(), $tmp, 'workspace not selected');
369
370 cmd '[id="' . $window->id . '"] fullscreen enable global';
371 is($x->input_focus, $window->id, 'window focused');
372
373 cmd 'fullscreen disable';
374 is($x->input_focus, $window->id, 'window still focused');
375 is(focused_ws(), $tmp, 'workspace selected');
376
377 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $tmp = fresh_workspace;
19
20 #####################################################################
21 # Create two windows and make sure focus switching works
22 #####################################################################
23
24 # Change mode of the container to "default" for following tests
25 cmd 'layout default';
26 cmd 'split v';
27
28 my $top = open_window;
29 my $mid = open_window;
30 my $bottom = open_window;
31
32 #
33 # Returns the input focus after sending the given command to i3 via IPC
34 # end sleeping for half a second to make sure i3 reacted
35 #
36 sub focus_after {
37 my $msg = shift;
38
39 cmd $msg;
40 return $x->input_focus;
41 }
42
43 my $focus = $x->input_focus;
44 is($focus, $bottom->id, "Latest window focused");
45
46 $focus = focus_after('focus up');
47 is($focus, $mid->id, "Middle window focused");
48
49 $focus = focus_after('focus up');
50 is($focus, $top->id, "Top window focused");
51
52 #####################################################################
53 # Test focus wrapping
54 #####################################################################
55
56 $focus = focus_after('focus up');
57 is($focus, $bottom->id, "Bottom window focused (wrapping to the top works)");
58
59 $focus = focus_after('focus down');
60 is($focus, $top->id, "Top window focused (wrapping to the bottom works)");
61
62 #####################################################################
63 # Test focus is only successful if there was a window that could be
64 # matched.
65 #####################################################################
66
67 my $result = cmd '[con_mark=__does_not_exist] focus';
68 is($result->[0]->{success}, 0, 'focus is unsuccessful if no window was matched');
69
70 #####################################################################
71
72 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17 use X11::XCB 'PROP_MODE_REPLACE';
18 use List::Util qw(first);
19
20 #####################################################################
21 # verify that there is no dock window yet
22 #####################################################################
23
24 # Children of all dockareas
25 my @docked = get_dock_clients;
26 is(@docked, 0, 'no dock clients yet');
27
28 #####################################################################
29 # Create a dock window and see if it gets managed
30 #####################################################################
31
32 my $screens = $x->screens;
33
34 # Get the primary screen
35 my $primary = first { $_->primary } @{$screens};
36
37 # TODO: focus the primary screen before
38 my $window = open_window({
39 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
40 });
41
42 my $rect = $window->rect;
43 is($rect->width, $primary->rect->width, 'dock client is as wide as the screen');
44 is($rect->height, 30, 'height is unchanged');
45
46 #####################################################################
47 # check that we can find it in the layout tree at the expected position
48 #####################################################################
49
50 @docked = get_dock_clients('top');
51 is(@docked, 1, 'one dock client found');
52
53 # verify the position/size
54 my $docknode = $docked[0];
55
56 is($docknode->{rect}->{x}, 0, 'dock node placed at x=0');
57 is($docknode->{rect}->{y}, 0, 'dock node placed at y=0');
58 is($docknode->{rect}->{width}, $primary->rect->width, 'dock node as wide as the screen');
59 is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
60
61 #####################################################################
62 # check that re-configuring the height works
63 #####################################################################
64
65 $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 50, height => 40));
66
67 sync_with_i3;
68
69 @docked = get_dock_clients('top');
70 is(@docked, 1, 'one dock client found');
71
72 # verify the position/size
73 $docknode = $docked[0];
74
75 is($docknode->{rect}->{x}, 0, 'dock node placed at x=0');
76 is($docknode->{rect}->{y}, 0, 'dock node placed at y=0');
77 is($docknode->{rect}->{width}, $primary->rect->width, 'dock node as wide as the screen');
78 is($docknode->{rect}->{height}, 40, 'dock height changed');
79
80 $window->destroy;
81
82 wait_for_unmap $window;
83
84 @docked = get_dock_clients();
85 is(@docked, 0, 'no more dock clients');
86
87 #####################################################################
88 # check if it gets placed on bottom (by coordinates)
89 #####################################################################
90
91 $window = open_window({
92 rect => [ 0, 1000, 30, 30 ],
93 background_color => '#FF0000',
94 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
95 });
96
97 $rect = $window->rect;
98 is($rect->width, $primary->rect->width, 'dock client is as wide as the screen');
99 is($rect->height, 30, 'height is unchanged');
100
101 @docked = get_dock_clients('bottom');
102 is(@docked, 1, 'dock client on bottom');
103
104 $window->destroy;
105
106 wait_for_unmap $window;
107
108 @docked = get_dock_clients();
109 is(@docked, 0, 'no more dock clients');
110
111 #####################################################################
112 # check if it gets placed on bottom (by hint)
113 #####################################################################
114
115 $window = open_window({
116 dont_map => 1,
117 rect => [ 0, 1000, 30, 30 ],
118 background_color => '#FF0000',
119 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
120 });
121
122 $window->_create();
123
124 # Add a _NET_WM_STRUT_PARTIAL hint
125 my $atomname = $x->atom(name => '_NET_WM_STRUT_PARTIAL');
126 my $atomtype = $x->atom(name => 'CARDINAL');
127
128 $x->change_property(
129 PROP_MODE_REPLACE,
130 $window->id,
131 $atomname->id,
132 $atomtype->id,
133 32, # 32 bit integer
134 12,
135 pack('L12', 0, 0, 16, 0, 0, 0, 0, 0, 0, 1280, 0, 0)
136 );
137
138 $window->map;
139
140 wait_for_map $window;
141
142 @docked = get_dock_clients('top');
143 is(@docked, 1, 'dock client on top');
144
145 # now change strut_partial to reserve space on the bottom and the dock should
146 # be moved to the bottom dock area
147 $x->change_property(
148 PROP_MODE_REPLACE,
149 $window->id,
150 $atomname->id,
151 $atomtype->id,
152 32, # 32 bit integer
153 12,
154 pack('L12', 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 1280, 0)
155 );
156
157 sync_with_i3;
158 @docked = get_dock_clients('bottom');
159 is(@docked, 1, 'dock client on bottom');
160
161 $window->destroy;
162
163 wait_for_unmap $window;
164
165 @docked = get_dock_clients();
166 is(@docked, 0, 'no more dock clients');
167
168 $window = open_window({
169 dont_map => 1,
170 rect => [ 0, 1000, 30, 30 ],
171 background_color => '#FF0000',
172 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
173 });
174
175 $window->_create();
176
177 # Add a _NET_WM_STRUT_PARTIAL hint
178 $atomname = $x->atom(name => '_NET_WM_STRUT_PARTIAL');
179 $atomtype = $x->atom(name => 'CARDINAL');
180
181 $x->change_property(
182 PROP_MODE_REPLACE,
183 $window->id,
184 $atomname->id,
185 $atomtype->id,
186 32, # 32 bit integer
187 12,
188 pack('L12', 0, 0, 0, 16, 0, 0, 0, 0, 0, 1280, 0, 0)
189 );
190
191 $window->map;
192
193 wait_for_map $window;
194
195 @docked = get_dock_clients('bottom');
196 is(@docked, 1, 'dock client on bottom');
197
198 $window->destroy;
199
200
201 #####################################################################
202 # regression test: transient dock client
203 #####################################################################
204
205 my $fwindow = open_window({
206 dont_map => 1,
207 background_color => '#FF0000',
208 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
209 });
210
211 $fwindow->transient_for($window);
212 $fwindow->map;
213
214 wait_for_map $fwindow;
215
216 does_i3_live;
217
218 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks if the focus is correctly restored, when creating a floating client
17 # over an unfocused tiling client and destroying the floating one again.
18
19 use i3test;
20
21 fresh_workspace;
22
23 cmd 'split h';
24 my $tiled_left = open_window;
25 my $tiled_right = open_window;
26
27 # Get input focus before creating the floating window
28 my $focus = $x->input_focus;
29
30 # Create a floating window which is smaller than the minimum enforced size of i3
31 my $window = open_floating_window;
32
33 is($x->input_focus, $window->id, 'floating window focused');
34
35 $window->unmap;
36
37 wait_for_unmap $window;
38
39 is($x->input_focus, $focus, 'Focus correctly restored');
40
41 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17 use File::Temp;
18
19 my $tmp = fresh_workspace;
20
21 cmd 'split h';
22
23 #####################################################################
24 # Create two windows and make sure focus switching works
25 #####################################################################
26
27 my $top = open_window;
28 my $mid = open_window;
29 my $bottom = open_window;
30
31 #
32 # Returns the input focus after sending the given command to i3 via IPC
33 # and syncing with i3
34 #
35 sub focus_after {
36 my $msg = shift;
37
38 cmd $msg;
39 return $x->input_focus;
40 }
41
42 my $focus = $x->input_focus;
43 is($focus, $bottom->id, "Latest window focused");
44
45 $focus = focus_after('focus left');
46 is($focus, $mid->id, "Middle window focused");
47
48 #####################################################################
49 # Now goto a mark which does not exist
50 #####################################################################
51
52 my $random_mark = mktemp('mark.XXXXXX');
53
54 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
55 is($focus, $mid->id, "focus unchanged");
56
57 cmd "mark $random_mark";
58
59 $focus = focus_after('focus left');
60 is($focus, $top->id, "Top window focused");
61
62 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
63 is($focus, $mid->id, "goto worked");
64
65 # check that we can specify multiple criteria
66
67 $focus = focus_after('focus left');
68 is($focus, $top->id, "Top window focused");
69
70 $focus = focus_after(qq|[con_mark="$random_mark" con_mark="$random_mark"] focus|);
71 is($focus, $mid->id, "goto worked");
72
73 #####################################################################
74 # Set the same mark multiple times and see if focus works correctly
75 #####################################################################
76
77 $focus = focus_after('focus left');
78 is($focus, $top->id, "Top window focused");
79
80 cmd "mark $random_mark";
81
82 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
83 is($focus, $top->id, "focus unchanged after goto");
84
85 $focus = focus_after('focus right');
86 is($focus, $mid->id, "mid window focused");
87
88 $focus = focus_after(qq|[con_mark="$random_mark"] focus|);
89 is($focus, $top->id, "goto worked");
90
91 #####################################################################
92 # Check whether the focus command will switch to a different
93 # workspace if necessary
94 #####################################################################
95
96 my $tmp2 = fresh_workspace;
97
98 is(focused_ws(), $tmp2, 'tmp2 now focused');
99
100 cmd qq|[con_mark="$random_mark"] focus|;
101
102 is(focused_ws(), $tmp, 'tmp now focused');
103
104 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 fresh_workspace;
19
20 #####################################################################
21 # Create a floating window and see if resizing works
22 #####################################################################
23
24 my $window = open_floating_window;
25
26 # See if configurerequests cause window movements (they should not)
27 my ($a, $t) = $window->rect;
28 $window->rect(X11::XCB::Rect->new(x => $a->x, y => $a->y, width => $a->width, height => $a->height));
29
30 sync_with_i3;
31
32 my ($na, $nt) = $window->rect;
33 is_deeply($na, $a, 'Rects are equal after configurerequest');
34
35 sub test_resize {
36 $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 100, height => 100));
37
38 sync_with_i3;
39
40 my ($absolute, $top) = $window->rect;
41
42 # Make sure the width/height are different from what we’re gonna test, so
43 # that the test will work.
44 isnt($absolute->width, 300, 'width != 300');
45 isnt($absolute->height, 500, 'height != 500');
46
47 $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => 300, height => 500));
48
49 sync_with_i3;
50
51 ($absolute, $top) = $window->rect;
52
53 is($absolute->width, 300, 'width = 300');
54 is($absolute->height, 500, 'height = 500');
55 }
56
57 # Test with default border
58 test_resize;
59
60 # Test borderless
61 cmd 'border none';
62
63 test_resize;
64
65 # Test with 1-px-border
66 cmd 'border 1pixel';
67
68 test_resize;
69
70 ################################################################################
71 # Check if we can position a floating window out of bounds. The Xephyr screen
72 # is 1280x1024, so x=2864, y=893 is out of bounds.
73 ################################################################################
74
75 ($a, $t) = $window->rect;
76 $window->rect(X11::XCB::Rect->new(
77 x => 2864,
78 y => 893,
79 width => $a->width,
80 height => $a->height));
81
82 sync_with_i3;
83
84 ($a, $t) = $window->rect;
85 cmp_ok($a->x, '<', 1280, 'X not moved out of bounds');
86 cmp_ok($a->y, '<', 1024, 'Y not moved out of bounds');
87
88 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test i3_autostart => 0;
17 use List::Util qw(first);
18
19 my $_NET_WM_STATE_REMOVE = 0;
20 my $_NET_WM_STATE_ADD = 1;
21 my $_NET_WM_STATE_TOGGLE = 2;
22
23 sub set_urgency {
24 my ($win, $urgent_flag, $type) = @_;
25 if ($type == 1) {
26 # Because X11::XCB does not keep track of clearing the urgency hint
27 # when receiving focus, we just delete it in all cases and then re-set
28 # it if appropriate.
29 $win->delete_hint('urgency');
30 $win->add_hint('urgency') if ($urgent_flag);
31 } elsif ($type == 2) {
32 my $msg = pack "CCSLLLLLL",
33 X11::XCB::CLIENT_MESSAGE, # response_type
34 32, # format
35 0, # sequence
36 $win->id, # window
37 $x->atom(name => '_NET_WM_STATE')->id, # message type
38 ($urgent_flag ? $_NET_WM_STATE_ADD : $_NET_WM_STATE_REMOVE), # data32[0]
39 $x->atom(name => '_NET_WM_STATE_DEMANDS_ATTENTION')->id, # data32[1]
40 0, # data32[2]
41 0, # data32[3]
42 0; # data32[4]
43
44 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
45 }
46 }
47
48 my $config = <<EOT;
49 # i3 config file (v4)
50 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
51
52 force_display_urgency_hint 0ms
53 EOT
54
55 my ($type, $tmp, $w1, $w2);
56 for ($type = 1; $type <= 2; $type++) {
57 my $pid = launch_with_config($config);
58 $tmp = fresh_workspace;
59
60 #####################################################################
61 # Create two windows and put them in stacking mode
62 #####################################################################
63
64 cmd 'split v';
65
66 my $top = open_window;
67 my $bottom = open_window;
68
69 my @urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
70 is(@urgent, 0, 'no window got the urgent flag');
71
72 # cmd 'layout stacking';
73
74 #####################################################################
75 # Add the urgency hint, switch to a different workspace and back again
76 #####################################################################
77 set_urgency($top, 1, $type);
78 sync_with_i3;
79
80 my @content = @{get_ws_content($tmp)};
81 @urgent = grep { $_->{urgent} } @content;
82 my $top_info = first { $_->{window} == $top->id } @content;
83 my $bottom_info = first { $_->{window} == $bottom->id } @content;
84
85 ok($top_info->{urgent}, 'top window is marked urgent');
86 ok(!$bottom_info->{urgent}, 'bottom window is not marked urgent');
87 is(@urgent, 1, 'exactly one window got the urgent flag');
88
89 cmd '[id="' . $top->id . '"] focus';
90
91 @urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
92 is(@urgent, 0, 'no window got the urgent flag after focusing');
93
94 set_urgency($top, 1, $type);
95 sync_with_i3;
96
97 @urgent = grep { $_->{urgent} } @{get_ws_content($tmp)};
98 is(@urgent, 0, 'no window got the urgent flag after re-setting urgency hint');
99
100 #####################################################################
101 # Check if the workspace urgency hint gets set/cleared correctly
102 #####################################################################
103
104 my $ws = get_ws($tmp);
105 ok(!$ws->{urgent}, 'urgent flag not set on workspace');
106
107 my $otmp = fresh_workspace;
108
109 set_urgency($top, 1, $type);
110 sync_with_i3;
111
112 $ws = get_ws($tmp);
113 ok($ws->{urgent}, 'urgent flag set on workspace');
114
115 cmd "workspace $tmp";
116
117 $ws = get_ws($tmp);
118 ok(!$ws->{urgent}, 'urgent flag not set on workspace after switching');
119
120 ################################################################################
121 # Use the 'urgent' criteria to switch to windows which have the urgency hint set.
122 ################################################################################
123
124 # Go to a new workspace, open a different window, verify focus is on it.
125 $otmp = fresh_workspace;
126 my $different_window = open_window;
127 is($x->input_focus, $different_window->id, 'new window focused');
128
129 # Add the urgency hint on the other window.
130 set_urgency($top, 1, $type);
131 sync_with_i3;
132
133 # Now try to switch to that window and see if focus changes.
134 cmd '[urgent=latest] focus';
135 isnt($x->input_focus, $different_window->id, 'window no longer focused');
136 is($x->input_focus, $top->id, 'urgent window focused');
137
138 ################################################################################
139 # Same thing, but with multiple windows and using the 'urgency=latest' criteria
140 # (verify that it works in the correct order).
141 ################################################################################
142
143 cmd "workspace $otmp";
144 is($x->input_focus, $different_window->id, 'new window focused again');
145
146 set_urgency($top, 1, $type);
147 sync_with_i3;
148
149 set_urgency($bottom, 1, $type);
150 sync_with_i3;
151
152 cmd '[urgent=latest] focus';
153 is($x->input_focus, $bottom->id, 'latest urgent window focused');
154 set_urgency($bottom, 0, $type);
155 sync_with_i3;
156
157 cmd '[urgent=latest] focus';
158 is($x->input_focus, $top->id, 'second urgent window focused');
159 set_urgency($top, 0, $type);
160 sync_with_i3;
161
162 ################################################################################
163 # Same thing, but with multiple windows and using the 'urgency=oldest' criteria
164 # (verify that it works in the correct order).
165 ################################################################################
166
167 cmd "workspace $otmp";
168 is($x->input_focus, $different_window->id, 'new window focused again');
169
170 set_urgency($top, 1, $type);
171 sync_with_i3;
172
173 set_urgency($bottom, 1, $type);
174 sync_with_i3;
175
176 cmd '[urgent=oldest] focus';
177 is($x->input_focus, $top->id, 'oldest urgent window focused');
178 set_urgency($top, 0, $type);
179 sync_with_i3;
180
181 cmd '[urgent=oldest] focus';
182 is($x->input_focus, $bottom->id, 'oldest urgent window focused');
183 set_urgency($bottom, 0, $type);
184 sync_with_i3;
185
186 ################################################################################
187 # Check if urgent flag gets propagated to parent containers
188 ################################################################################
189
190 cmd 'split v';
191
192
193
194 sub count_urgent {
195 my ($con) = @_;
196
197 my @children = (@{$con->{nodes}}, @{$con->{floating_nodes}});
198 my $urgent = grep { $_->{urgent} } @children;
199 $urgent += count_urgent($_) for @children;
200 return $urgent;
201 }
202
203 $tmp = fresh_workspace;
204
205 my $win1 = open_window;
206 my $win2 = open_window;
207 cmd 'layout stacked';
208 cmd 'split vertical';
209 my $win3 = open_window;
210 my $win4 = open_window;
211 cmd 'split horizontal' ;
212 my $win5 = open_window;
213 my $win6 = open_window;
214
215 sync_with_i3;
216
217
218 my $urgent = count_urgent(get_ws($tmp));
219 is($urgent, 0, 'no window got the urgent flag');
220
221 cmd '[id="' . $win2->id . '"] focus';
222 sync_with_i3;
223 set_urgency($win5, 1, $type);
224 set_urgency($win6, 1, $type);
225 sync_with_i3;
226
227 # we should have 5 urgent cons. win5, win6 and their 3 split parents.
228
229 $urgent = count_urgent(get_ws($tmp));
230 is($urgent, 5, '2 windows and 3 split containers got the urgent flag');
231
232 cmd '[id="' . $win5->id . '"] focus';
233 sync_with_i3;
234
235 # now win5 and still the split parents should be urgent.
236 $urgent = count_urgent(get_ws($tmp));
237 is($urgent, 4, '1 window and 3 split containers got the urgent flag');
238
239 cmd '[id="' . $win6->id . '"] focus';
240 sync_with_i3;
241
242 # now now window should be urgent.
243 $urgent = count_urgent(get_ws($tmp));
244 is($urgent, 0, 'All urgent flags got cleared');
245
246 ################################################################################
247 # Regression test: Check that urgent floating containers work properly (ticket
248 # #821)
249 ################################################################################
250
251 $tmp = fresh_workspace;
252 my $floating_win = open_floating_window;
253
254 # switch away
255 fresh_workspace;
256
257 set_urgency($floating_win, 1, $type);
258 sync_with_i3;
259
260 cmd "workspace $tmp";
261
262 does_i3_live;
263
264 ###############################################################################
265 # Check if the urgency hint is still set when the urgent window is killed
266 ###############################################################################
267
268 my $ws1 = fresh_workspace;
269 my $ws2 = fresh_workspace;
270 cmd "workspace $ws1";
271 $w1 = open_window;
272 $w2 = open_window;
273 cmd "workspace $ws2";
274 sync_with_i3;
275 set_urgency($w1, 1, $type);
276 sync_with_i3;
277 cmd '[id="' . $w1->id . '"] kill';
278 sync_with_i3;
279 my $w = get_ws($ws1);
280 is($w->{urgent}, 0, 'Urgent flag no longer set after killing the window ' .
281 'from another workspace');
282
283 ##############################################################################
284 # Check if urgent flag can be unset if we move the window out of the container
285 ##############################################################################
286 $tmp = fresh_workspace;
287 cmd 'layout tabbed';
288 $w1 = open_window;
289 $w2 = open_window;
290 sync_with_i3;
291 cmd '[id="' . $w2->id . '"] focus';
292 sync_with_i3;
293 cmd 'split v';
294 cmd 'layout stacked';
295 my $w3 = open_window;
296 sync_with_i3;
297 cmd '[id="' . $w2->id . '"] focus';
298 sync_with_i3;
299 set_urgency($w3, 1, $type);
300 sync_with_i3;
301 cmd 'focus parent';
302 sync_with_i3;
303 cmd 'move right';
304 cmd '[id="' . $w3->id . '"] focus';
305 sync_with_i3;
306 $ws = get_ws($tmp);
307 ok(!$ws->{urgent}, 'urgent flag not set on workspace');
308
309 ##############################################################################
310 # Regression test for #1187: Urgency hint moves to new workspace when moving
311 # a container to another workspace.
312 ##############################################################################
313
314 my $tmp_source = fresh_workspace;
315 my $tmp_target = fresh_workspace;
316 cmd 'workspace ' . $tmp_source;
317 sync_with_i3;
318 $w1 = open_window;
319 $w2 = open_window;
320 sync_with_i3;
321 cmd '[id="' . $w1->id . '"] focus';
322 sync_with_i3;
323 cmd 'mark urgent_con';
324 cmd '[id="' . $w2->id . '"] focus';
325 set_urgency($w1, 1, $type);
326 sync_with_i3;
327 cmd '[con_mark="urgent_con"] move container to workspace ' . $tmp_target;
328 sync_with_i3;
329 my $source_ws = get_ws($tmp_source);
330 my $target_ws = get_ws($tmp_target);
331 ok(!$source_ws->{urgent}, 'Source workspace is no longer marked urgent');
332 is($target_ws->{urgent}, 1, 'Target workspace is now marked urgent');
333
334 ##############################################################################
335 # Test that moving an unfocused container doesn't reset its urgency hint.
336 ##############################################################################
337 $tmp = fresh_workspace;
338 $win1 = open_window;
339 $win2 = open_window;
340 cmd 'split v';
341 $win3 = open_window;
342 set_urgency($win1, 1, $type);
343 sync_with_i3;
344
345 my $win1_info;
346
347 @content = @{get_ws_content($tmp)};
348 $win1_info = first { $_->{window} == $win1->id } @content;
349 ok($win1_info->{urgent}, 'win1 window is marked urgent');
350
351 cmd '[id="' . $win1->id . '"] move right';
352 cmd '[id="' . $win1->id . '"] move right';
353 @content = @{get_ws_content($tmp)};
354 $win1_info = first { $_->{window} == $win1->id } @content;
355 ok($win1_info->{urgent}, 'win1 window is still marked urgent after moving');
356
357 cmd '[id="' . $win1->id . '"] focus';
358 @content = @{get_ws_content($tmp)};
359 $win1_info = first { $_->{window} == $win1->id } @content;
360 ok(!$win1_info->{urgent}, 'win1 window is not marked urgent after focusing');
361
362 ##############################################################################
363
364 exit_gracefully($pid);
365 }
366
367 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $tmp = fresh_workspace;
19
20 ####################################################################################
21 # first part: test if a floating window will be correctly positioned above its leader
22 #
23 # This is verified by opening two windows, then opening a floating window above the
24 # right one, then above the left one. If the floating windows are all positioned alike,
25 # one of both (depending on your screen resolution) will be positioned wrong.
26 ####################################################################################
27
28 my $left = open_window({ name => 'Left' });
29 my $right = open_window({ name => 'Right' });
30
31 my ($abs, $rgeom) = $right->rect;
32
33 my $child = open_floating_window({
34 dont_map => 1,
35 name => 'Child window',
36 });
37 $child->client_leader($right);
38 $child->map;
39
40 ok(wait_for_map($child), 'child window mapped');
41
42 my $cgeom;
43 ($abs, $cgeom) = $child->rect;
44 cmp_ok($cgeom->x, '>=', $rgeom->x, 'Child X >= right container X');
45
46 my $child2 = open_floating_window({
47 dont_map => 1,
48 name => 'Child window 2',
49 });
50 $child2->client_leader($left);
51 $child2->map;
52
53 ok(wait_for_map($child2), 'second child window mapped');
54
55 ($abs, $cgeom) = $child2->rect;
56 cmp_ok(($cgeom->x + $cgeom->width), '<', $rgeom->x, 'child above left window');
57
58 # check wm_transient_for
59 my $fwindow = open_window({ dont_map => 1 });
60 $fwindow->transient_for($right);
61 $fwindow->map;
62
63 ok(wait_for_map($fwindow), 'transient window mapped');
64
65 my ($absolute, $top) = $fwindow->rect;
66 ok($absolute->{x} != 0 && $absolute->{y} != 0, 'i3 did not map it to (0x0)');
67
68 SKIP: {
69 skip "(workspace placement by client_leader not yet implemented)", 3;
70
71 #####################################################################
72 # Create a parent window
73 #####################################################################
74
75 my $window = open_window({ dont_map => 1, name => 'Parent window' });
76 $window->map;
77
78 ok(wait_for_map($window), 'parent window mapped');
79
80 #########################################################################
81 # Switch to a different workspace and open a child window. It should be opened
82 # on the old workspace.
83 #########################################################################
84 fresh_workspace;
85
86 my $child = open_window({ dont_map => 1, name => 'Child window' });
87 $child->client_leader($window);
88 $child->map;
89
90 ok(wait_for_map($child), 'child window mapped');
91
92 isnt($x->input_focus, $child->id, "Child window focused");
93
94 # Switch back
95 cmd "workspace $tmp";
96
97 is($x->input_focus, $child->id, "Child window focused");
98
99 }
100
101 ################################################################################
102 # Verify that transient_for can be set and unset.
103 ################################################################################
104
105 $tmp = fresh_workspace;
106
107 $fwindow = open_window({ dont_map => 1 });
108 $fwindow->transient_for($right);
109 $fwindow->map;
110
111 wait_for_map($fwindow);
112
113 my $floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0];
114 is($floating_con->{window_properties}->{transient_for}, $right->id, 'WM_TRANSIENT_FOR properly parsed');
115
116 $x->delete_property($fwindow->id, $x->atom(name => 'WM_TRANSIENT_FOR')->id);
117 $x->flush;
118
119 sync_with_i3;
120
121 $floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0];
122 is($floating_con->{window_properties}->{transient_for}, undef, 'WM_TRANSIENT_FOR properly removed');
123
124 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $old_ws = get_ws(focused_ws());
19
20 # We are switching to an empty workpspace from an empty workspace, so we expect
21 # to receive "init", "focus", and "empty".
22 my @events = events_for(
23 sub { cmd 'workspace 2' },
24 'workspace');
25
26 my $current_ws = get_ws(focused_ws());
27
28 is(scalar @events, 3, 'Received 3 events');
29 is($events[0]->{change}, 'init', 'First event has change = init');
30 is($events[0]->{current}->{id}, $current_ws->{id}, 'the "current" property contains the initted workspace con');
31
32 is($events[1]->{change}, 'focus', 'Second event has change = focus');
33 is($events[1]->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con');
34 is($events[1]->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last');
35
36 is($events[2]->{change}, 'empty', 'Third event has change = empty');
37 is($events[2]->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con');
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17 use List::Util qw(first);
18
19 # to not depend on List::MoreUtils
20 sub all (&@) {
21 my $cb = shift;
22 for (@_) {
23 return 0 unless $cb->();
24 }
25 return 1;
26 }
27
28 sub none (&@) {
29 my $cb = shift;
30 for (@_) {
31 return 0 if $cb->();
32 }
33 return 1;
34 }
35
36 my $i3 = i3(get_socket_path());
37
38 ####################
39 # Request tree
40 ####################
41
42 my $tree = $i3->get_tree->recv;
43
44 # a unique value
45 my $ignore = \"";
46
47 my $expected = {
48 fullscreen_mode => 0,
49 sticky => $ignore,
50 nodes => $ignore,
51 window => undef,
52 name => 'root',
53 orientation => $ignore,
54 type => 'root',
55 id => $ignore,
56 rect => $ignore,
57 deco_rect => $ignore,
58 window_rect => $ignore,
59 geometry => $ignore,
60 swallows => $ignore,
61 percent => undef,
62 layout => 'splith',
63 floating => 'auto_off',
64 last_split_layout => 'splith',
65 scratchpad_state => 'none',
66 focus => $ignore,
67 focused => JSON::XS::false,
68 urgent => JSON::XS::false,
69 border => 'normal',
70 'floating_nodes' => $ignore,
71 workspace_layout => 'default',
72 current_border_width => -1,
73 };
74
75 # a shallow copy is sufficient, since we only ignore values at the root
76 my $tree_copy = { %$tree };
77
78 for (keys %$expected) {
79 my $val = $expected->{$_};
80
81 # delete unwanted keys, so we can use is_deeply()
82 if (ref($val) eq 'SCALAR' and $val == $ignore) {
83 delete $tree_copy->{$_};
84 delete $expected->{$_};
85 }
86 }
87
88 is_deeply($tree_copy, $expected, 'root node OK');
89
90 my @nodes = @{$tree->{nodes}};
91
92 ok(@nodes > 0, 'root node has at least one leaf');
93
94 ok((all { $_->{type} eq 'output' } @nodes), 'all nodes are of type CT_OUTPUT');
95 ok((none { defined($_->{window}) } @nodes), 'no CT_OUTPUT contains a window');
96 ok((all { @{$_->{nodes}} > 0 } @nodes), 'all nodes have at least one leaf (workspace)');
97 my @workspaces;
98 for my $ws (@nodes) {
99 my $content = first { $_->{type} eq 'con' } @{$ws->{nodes}};
100 @workspaces = (@workspaces, @{$content->{nodes}});
101 }
102
103 ok((all { $_->{type} eq 'workspace' } @workspaces), 'all workspaces are of type CT_WORKSPACE');
104 #ok((all { @{$_->{nodes}} == 0 } @workspaces), 'all workspaces are empty yet');
105 ok((none { defined($_->{window}) } @workspaces), 'no CT_OUTPUT contains a window');
106
107 # TODO: get the focused container
108
109 $i3->command('open')->recv;
110
111 # TODO: get the focused container, check if it changed.
112 # TODO: get the old focused container, check if there is a new child
113
114 #diag(Dumper(\@workspaces));
115
116 #diag(Dumper($tree));
117
118
119 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether we can switch to a non-existant workspace
17 # (necessary for further tests)
18 #
19 use List::Util qw(first);
20 use i3test;
21
22 # to ensure that workspace 1 stays open
23 cmd 'open';
24
25 my $tmp = fresh_workspace;
26 ok(workspace_exists($tmp), 'workspace created');
27 # if the workspace could not be created, we cannot run any other test
28 # (every test starts by creating its workspace)
29 if (!workspace_exists($tmp)) {
30 BAIL_OUT('Cannot create workspace, further tests make no sense');
31 }
32
33 my $otmp = fresh_workspace;
34 diag("Other temporary workspace name: $otmp\n");
35
36 # As the old workspace was empty, it should get
37 # cleaned up as we switch away from it
38 cmd "workspace $otmp";
39 ok(!workspace_exists($tmp), 'old workspace cleaned up');
40
41 # Switch to the same workspace again to make sure it doesn’t get cleaned up
42 cmd "workspace $otmp";
43 cmd "workspace $otmp";
44 ok(workspace_exists($otmp), 'other workspace still exists');
45
46
47 #####################################################################
48 # check if the workspace next / prev commands work
49 #####################################################################
50
51 cmd 'workspace next';
52
53 ok(!workspace_exists('next'), 'workspace "next" does not exist');
54
55 cmd "workspace $tmp";
56 cmd 'open';
57
58 ok(workspace_exists($tmp), 'workspace created');
59
60 cmd "workspace $otmp";
61 cmd 'open';
62
63 ok(workspace_exists($tmp), 'workspace tmp still exists');
64 ok(workspace_exists($otmp), 'workspace otmp created');
65
66 is(focused_ws(), $otmp, 'focused workspace is otmp');
67
68 cmd 'workspace prev';
69 is(focused_ws(), $tmp, 'focused workspace is tmp after workspace prev');
70
71 cmd 'workspace next';
72 is(focused_ws(), $otmp, 'focused workspace is otmp after workspace next');
73
74
75 #####################################################################
76 # check that wrapping works
77 #####################################################################
78
79 cmd 'workspace next';
80 is(focused_ws(), '1', 'focused workspace is 1 after workspace next');
81
82 cmd 'workspace next';
83 is(focused_ws(), $tmp, 'focused workspace is tmp after workspace next');
84
85 cmd 'workspace next';
86 is(focused_ws(), $otmp, 'focused workspace is otmp after workspace next');
87
88
89 cmd 'workspace prev';
90 is(focused_ws(), $tmp, 'focused workspace is tmp after workspace prev');
91
92 cmd 'workspace prev';
93 is(focused_ws(), '1', 'focused workspace is tmp after workspace prev');
94
95 cmd 'workspace prev';
96 is(focused_ws(), $otmp, 'focused workspace is otmp after workspace prev');
97
98
99 #####################################################################
100 # check if we can change to "next" / "prev"
101 #####################################################################
102
103 cmd 'workspace "next"';
104
105 ok(workspace_exists('next'), 'workspace "next" exists');
106 is(focused_ws(), 'next', 'now on workspace next');
107
108 cmd 'workspace "prev"';
109
110 ok(workspace_exists('prev'), 'workspace "prev" exists');
111 is(focused_ws(), 'prev', 'now on workspace prev');
112
113 #####################################################################
114 # check that the numbers are assigned/recognized correctly
115 #####################################################################
116
117 cmd "workspace 3: $tmp";
118 my $ws = get_ws("3: $tmp");
119 ok(defined($ws), "workspace 3: $tmp was created");
120 is($ws->{num}, 3, 'workspace number is 3');
121
122 cmd "workspace 0: $tmp";
123 $ws = get_ws("0: $tmp");
124 ok(defined($ws), "workspace 0: $tmp was created");
125 is($ws->{num}, 0, 'workspace number is 0');
126
127 cmd "workspace aa: $tmp";
128 $ws = get_ws("aa: $tmp");
129 ok(defined($ws), "workspace aa: $tmp was created");
130 is($ws->{num}, -1, 'workspace number is -1');
131
132 ################################################################################
133 # Check that we can go to workspace "4: foo" with the command
134 # "workspace number 4".
135 ################################################################################
136
137 ok(!workspace_exists('4'), 'workspace 4 does not exist');
138 ok(!workspace_exists('4: foo'), 'workspace 4: foo does not exist yet');
139 cmd 'workspace 4: foo';
140 ok(workspace_exists('4: foo'), 'workspace 4: foo was created');
141 cmd 'open';
142
143 cmd 'workspace 3';
144 ok(workspace_exists('4: foo'), 'workspace 4: foo still open');
145 cmd 'workspace number 4';
146 is(focused_ws(), '4: foo', 'now on workspace 4: foo');
147 ok(!workspace_exists('4'), 'workspace 4 still does not exist');
148
149 ################################################################################
150 # Check that we "workspace number 5" will create workspace 5 if it does not yet
151 # exist.
152 ################################################################################
153
154 ok(!workspace_exists('5'), 'workspace 5 does not exist');
155 cmd 'workspace number 5';
156 ok(workspace_exists('5'), 'workspace 5 was created');
157
158 ################################################################################
159 # Check that we can go to workspace "7: foo" with the command
160 # "workspace number 7: bar", i.e. the additional workspace name is ignored.
161 ################################################################################
162
163 ok(!workspace_exists('7'), 'workspace 7 does not exist');
164 ok(!workspace_exists('7: bar'), 'workspace 7: bar does not exist');
165 ok(!workspace_exists('7: foo'), 'workspace 7: foo does not exist yet');
166 cmd 'workspace 7: foo';
167 ok(workspace_exists('7: foo'), 'workspace 7: foo was created');
168 cmd 'open';
169
170 cmd 'workspace 6';
171 ok(workspace_exists('7: foo'), 'workspace 7: foo still open');
172 cmd 'workspace number 7: bar';
173 is(focused_ws(), '7: foo', 'now on workspace 7: foo');
174 ok(!workspace_exists('7'), 'workspace 7 still does not exist');
175 ok(!workspace_exists('7: bar'), 'workspace 7: bar still does not exist');
176
177 ################################################################################
178 # Check that "workspace number 8: foo" will create workspace "8: foo" if it
179 # does not yet exist (just like "workspace 8: foo" would).
180 ################################################################################
181
182 ok(!workspace_exists('8: foo'), 'workspace 8: foo does not exist');
183 cmd 'workspace number 8: foo';
184 ok(workspace_exists('8: foo'), 'workspace 8: foo was created');
185
186 ################################################################################
187 # Verify that renaming workspaces works.
188 ################################################################################
189
190 sub workspace_numbers_sorted {
191 my ($name) = @_;
192 my $i3 = i3(get_socket_path());
193 my $tree = $i3->get_tree->recv;
194
195 my @outputs = @{$tree->{nodes}};
196 my @workspaces;
197 for my $output (@outputs) {
198 my $content = first { $_->{type} eq 'con' } @{$output->{nodes}};
199 @workspaces = (@workspaces, @{$content->{nodes}});
200 }
201
202 my @numbers = grep { $_ != -1 } map { $_->{num} } @workspaces;
203 is_deeply(
204 [ sort { $a <=> $b } @numbers ],
205 \@numbers,
206 'workspace numbers sorted');
207 }
208
209 # 1: numbered workspace
210 cmd 'workspace 10';
211 cmd 'open';
212 cmd 'workspace 13';
213 cmd 'open';
214
215 workspace_numbers_sorted();
216
217 cmd 'workspace 9';
218 is(focused_ws(), '9', 'now on workspace 9');
219
220 ok(!workspace_exists('12'), 'workspace 12 does not exist yet');
221 cmd 'rename workspace 9 to 12';
222 ok(!workspace_exists('9'), 'workspace 9 does not exist anymore');
223 is(focused_ws(), '12', 'now on workspace 12');
224 $ws = get_ws('12');
225 is($ws->{num}, 12, 'number correctly changed');
226
227 workspace_numbers_sorted();
228
229 # 2: numbered + named workspace
230 cmd 'workspace 9: foo';
231 is(focused_ws(), '9: foo', 'now on workspace 9: foo');
232
233 ok(!workspace_exists('11: bar'), 'workspace 11: bar does not exist yet');
234 cmd 'rename workspace "9: foo" to "11: bar"';
235 ok(!workspace_exists('9: foo'), 'workspace 9 does not exist anymore');
236 is(focused_ws(), '11: bar', 'now on workspace 10');
237 $ws = get_ws('11: bar');
238 is($ws->{num}, 11, 'number correctly changed');
239 workspace_numbers_sorted();
240 # keep that one open, we need it later
241 cmd 'open';
242
243 # 3: named workspace
244 cmd 'workspace bleh';
245 is(focused_ws(), 'bleh', 'now on workspace bleh');
246
247 ok(!workspace_exists('qux'), 'workspace qux does not exist yet');
248 cmd 'rename workspace bleh to qux';
249 ok(!workspace_exists('bleh'), 'workspace 9 does not exist anymore');
250 is(focused_ws(), 'qux', 'now on workspace qux');
251 $ws = get_ws('qux');
252 is($ws->{num}, -1, 'number correctly changed');
253 workspace_numbers_sorted();
254
255 # 4: rename current workspace
256 cmd 'workspace 4711';
257 is(focused_ws(), '4711', 'now on workspace 4711');
258
259 ok(!workspace_exists('42'), 'workspace 42 does not exist yet');
260 cmd 'rename workspace to 42';
261 ok(!workspace_exists('4711'), 'workspace 4711 does not exist anymore');
262 is(focused_ws(), '42', 'now on workspace 42');
263 $ws = get_ws('42');
264 is($ws->{num}, 42, 'number correctly changed');
265 workspace_numbers_sorted();
266
267 # 5: special cases
268 cmd 'workspace bla';
269 is(focused_ws(), 'bla', 'now on workspace to');
270
271 ok(!workspace_exists('to'), 'workspace to does not exist yet');
272 cmd 'rename workspace bla to to';
273 ok(!workspace_exists('bla'), 'workspace bla does not exist anymore');
274 is(focused_ws(), 'to', 'now on workspace to');
275 cmd 'rename workspace to to bla';
276 ok(!workspace_exists('to'), 'workspace to does not exist anymore');
277 is(focused_ws(), 'bla', 'now on workspace bla');
278 cmd 'rename workspace to to';
279 ok(!workspace_exists('bla'), 'workspace bla does not exist anymore');
280 is(focused_ws(), 'to', 'now on workspace to');
281 cmd 'rename workspace to bla';
282 ok(!workspace_exists('to'), 'workspace to does not exist anymore');
283 is(focused_ws(), 'bla', 'now on workspace bla');
284 cmd 'rename workspace to tosomething';
285 ok(!workspace_exists('bla'), 'workspace bla does not exist anymore');
286 is(focused_ws(), 'tosomething', 'now on workspace tosomething');
287
288 # 6: already existing workspace
289 my $result = cmd 'rename workspace qux to 11: bar';
290 ok(!$result->[0]->{success}, 'renaming workspace to an already existing one failed');
291
292 # 7: non-existing old workspace (verify command result)
293 $result = cmd 'rename workspace notexistant to bleh';
294 ok(!$result->[0]->{success}, 'renaming workspace which does not exist failed');
295
296 # 8: change case
297 ok(!workspace_exists('11: BAR'), 'workspace 11: BAR does not exist yet');
298 $result = cmd 'rename workspace "11: bar" to "11: BAR"';
299 ok($result->[0]->{success}, 'renaming workspace from 11: bar to 11: BAR worked');
300 ok(workspace_exists('11: BAR'), 'workspace 11: BAR now exists');
301
302 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether opening an empty container and killing it again works
17 #
18 use List::Util qw(first);
19 use i3test;
20
21 my $tmp = fresh_workspace;
22
23 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
24
25 # Open a new container
26 cmd 'open';
27
28 ok(@{get_ws_content($tmp)} == 1, 'container opened');
29
30 cmd 'kill';
31 ok(@{get_ws_content($tmp)} == 0, 'container killed');
32
33 ##############################################################
34 # open two containers and kill the one which is not focused
35 # by its ID to test if the parser correctly matches the window
36 ##############################################################
37
38 cmd 'open';
39 cmd 'open';
40 ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
41
42 my $content = get_ws_content($tmp);
43 my $not_focused = first { !$_->{focused} } @{$content};
44 my $id = $not_focused->{id};
45
46 cmd "[con_id=\"$id\"] kill";
47
48 $content = get_ws_content($tmp);
49 ok(@{$content} == 1, 'one container killed');
50 ok($content->[0]->{id} != $id, 'correct window killed');
51
52 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests all kinds of matching methods
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
23
24 # Open a new window
25 my $window = open_window;
26 my $content = get_ws_content($tmp);
27 ok(@{$content} == 1, 'window mapped');
28 my $win = $content->[0];
29
30 ######################################################################
31 # first test that matches which should not match this window really do
32 # not match it
33 ######################################################################
34 # TODO: specify more match types
35 # we can match on any (non-empty) class here since that window does not have
36 # WM_CLASS set
37 cmd q|[class=".*"] kill|;
38 cmd q|[con_id="99999"] kill|;
39
40 is_num_children($tmp, 1, 'window still there');
41
42 # now kill the window
43 cmd 'nop now killing the window';
44 my $id = $win->{id};
45 cmd qq|[con_id="$id"] kill|;
46
47 wait_for_unmap $window;
48
49 cmd 'nop checking if its gone';
50 is_num_children($tmp, 0, 'window killed');
51
52 # TODO: same test, but with pcre expressions
53
54 ######################################################################
55 # check that multiple criteria work are checked with a logical AND,
56 # not a logical OR (that is, matching is not cancelled after the first
57 # criterion matches).
58 ######################################################################
59
60 $tmp = fresh_workspace;
61
62 my $left = open_window(wm_class => 'special', name => 'left');
63 ok($left->mapped, 'left window mapped');
64
65 my $right = open_window(wm_class => 'special', name => 'right');
66 ok($right->mapped, 'right window mapped');
67
68 # two windows should be here
69 is_num_children($tmp, 2, 'two windows opened');
70
71 cmd '[class="special" title="left"] kill';
72
73 sync_with_i3;
74
75 is_num_children($tmp, 1, 'one window still there');
76
77 ######################################################################
78 # check that regular expressions work
79 ######################################################################
80
81 $tmp = fresh_workspace;
82
83 $left = open_window(name => 'left', wm_class => 'special7');
84 ok($left->mapped, 'left window mapped');
85 is_num_children($tmp, 1, 'window opened');
86
87 cmd '[class="^special[0-9]$"] kill';
88 wait_for_unmap $left;
89 is_num_children($tmp, 0, 'window killed');
90
91 ######################################################################
92 # check that UTF-8 works when matching
93 ######################################################################
94
95 $tmp = fresh_workspace;
96
97 $left = open_window(name => 'ä 3', wm_class => 'special7');
98 ok($left->mapped, 'left window mapped');
99 is_num_children($tmp, 1, 'window opened');
100
101 cmd '[title="^\w [3]$"] kill';
102 wait_for_unmap $left;
103 is_num_children($tmp, 0, 'window killed');
104
105 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests multiple commands (using ';') and multiple operations (using ',')
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 sub multiple_cmds {
23 my ($cmd) = @_;
24
25 cmd 'open';
26 cmd 'open';
27 ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
28
29 cmd $cmd;
30 ok(@{get_ws_content($tmp)} == 0, "both containers killed (cmd = $cmd)");
31 }
32 multiple_cmds('kill;kill');
33 multiple_cmds('kill; kill');
34 multiple_cmds('kill ; kill');
35 multiple_cmds('kill ;kill');
36 multiple_cmds('kill ;kill');
37 multiple_cmds('kill ; kill');
38 multiple_cmds("kill;\tkill");
39 multiple_cmds("kill\t;kill");
40 multiple_cmds("kill\t;\tkill");
41 multiple_cmds("kill\t ;\tkill");
42 multiple_cmds("kill\t ;\t kill");
43 multiple_cmds("kill \t ; \t kill");
44
45 #####################################################################
46 # test if un-quoted strings are handled correctly
47 #####################################################################
48
49 $tmp = fresh_workspace;
50 cmd 'open';
51 my $unused = get_unused_workspace;
52 ok(!($unused ~~ @{get_workspace_names()}), 'workspace does not exist yet');
53 cmd "move workspace $unused; nop parser test";
54 ok(($unused ~~ @{get_workspace_names()}), 'workspace exists after moving');
55
56 #####################################################################
57 # quote the workspace name and use a ; (command separator) in its name
58 #####################################################################
59
60 cmd 'open';
61 $unused = get_unused_workspace;
62 $unused .= ';a';
63 ok(!($unused ~~ @{get_workspace_names()}), 'workspace does not exist yet');
64 cmd qq|move workspace "$unused"; nop parser test|;
65 ok(($unused ~~ @{get_workspace_names()}), 'workspace exists after moving');
66
67 # TODO: need a non-invasive command before implementing a test which uses ','
68
69 ################################################################################
70 # regression test: 10 invalid commands should not crash i3 (10 is the stack
71 # depth)
72 ################################################################################
73
74 cmd 'move gibberish' for (0 .. 10);
75
76 does_i3_live;
77
78 ################################################################################
79 # regression test: an invalid command should come back with an error.
80 ################################################################################
81
82 my $reply = cmd 'bullshit-command-which-we-never-implement meh';
83 is(scalar @$reply, 1, 'got one command reply');
84 ok(!$reply->[0]->{success}, 'reply has success == false');
85
86 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests focus switching (next/prev)
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 ######################################################################
23 # Open one container, verify that 'focus down' and 'focus right' do nothing
24 ######################################################################
25 cmd 'open';
26
27 my ($nodes, $focus) = get_ws_content($tmp);
28 my $old_focused = $focus->[0];
29
30 cmd 'focus down';
31 ($nodes, $focus) = get_ws_content($tmp);
32 is($focus->[0], $old_focused, 'focus did not change with only one con');
33
34 cmd 'focus right';
35 ($nodes, $focus) = get_ws_content($tmp);
36 is($focus->[0], $old_focused, 'focus did not change with only one con');
37
38 ######################################################################
39 # Open another container, verify that 'focus right' switches
40 ######################################################################
41 my $left = $old_focused;
42
43 cmd 'open';
44 ($nodes, $focus) = get_ws_content($tmp);
45 isnt($old_focused, $focus->[0], 'new container is focused');
46 my $mid = $focus->[0];
47
48 cmd 'open';
49 ($nodes, $focus) = get_ws_content($tmp);
50 isnt($old_focused, $focus->[0], 'new container is focused');
51 my $right = $focus->[0];
52
53 cmd 'focus right';
54 ($nodes, $focus) = get_ws_content($tmp);
55 isnt($focus->[0], $right, 'focus did change');
56 is($focus->[0], $left, 'left container focused (wrapping)');
57
58 cmd 'focus right';
59 ($nodes, $focus) = get_ws_content($tmp);
60 is($focus->[0], $mid, 'middle container focused');
61
62 cmd 'focus right';
63 ($nodes, $focus) = get_ws_content($tmp);
64 is($focus->[0], $right, 'right container focused');
65
66 cmd 'focus left';
67 ($nodes, $focus) = get_ws_content($tmp);
68 is($focus->[0], $mid, 'middle container focused');
69
70 cmd 'focus left';
71 ($nodes, $focus) = get_ws_content($tmp);
72 is($focus->[0], $left, 'left container focused');
73
74 cmd 'focus left';
75 ($nodes, $focus) = get_ws_content($tmp);
76 is($focus->[0], $right, 'right container focused');
77
78
79 ######################################################################
80 # Test focus command
81 ######################################################################
82
83 cmd qq|[con_id="$mid"] focus|;
84 ($nodes, $focus) = get_ws_content($tmp);
85 is($focus->[0], $mid, 'middle container focused');
86
87
88 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests splitting
17 #
18 use i3test;
19 use List::Util qw(first);
20
21 my $tmp;
22 my $ws;
23
24 ################################################################################
25 # Open two containers, split, open another container. Then verify
26 # the layout is like we expect it to be
27 ################################################################################
28
29 sub verify_split_layout {
30 my (%args) = @_;
31
32 $tmp = fresh_workspace;
33
34 $ws = get_ws($tmp);
35 is($ws->{layout}, 'splith', 'orientation horizontal by default');
36 cmd 'split v';
37 $ws = get_ws($tmp);
38 is($ws->{layout}, 'splitv', 'split v changes workspace orientation');
39
40 cmd 'open';
41 cmd 'open';
42 my $content = get_ws_content($tmp);
43
44 is(@{$content}, 2, 'two containers on workspace level');
45 my $first = $content->[0];
46 my $second = $content->[1];
47
48 is(@{$first->{nodes}}, 0, 'first container has no children');
49 is(@{$second->{nodes}}, 0, 'second container has no children (yet)');
50 my $old_id = $second->{id};
51
52 cmd $args{split_command};
53 cmd 'open';
54
55 $content = get_ws_content($tmp);
56
57 is(@{$content}, 2, 'two containers on workspace level');
58 $first = $content->[0];
59 $second = $content->[1];
60
61 is(@{$first->{nodes}}, 0, 'first container has no children');
62 isnt($second->{id}, $old_id, 'second container was replaced');
63 is($second->{layout}, 'splith', 'orientation is horizontal');
64 is(@{$second->{nodes}}, 2, 'second container has 2 children');
65 is($second->{nodes}->[0]->{id}, $old_id, 'found old second container');
66 }
67
68 verify_split_layout(split_command => 'split h');
69 verify_split_layout(split_command => 'split horizontal');
70
71 # TODO: extend this test-case (test next/prev)
72 # - wrapping (no horizontal switch possible, goes level-up)
73 # - going level-up "manually"
74
75 ######################################################################
76 # Test splitting multiple times without actually creating windows
77 ######################################################################
78
79 $tmp = fresh_workspace;
80
81 $ws = get_ws($tmp);
82 is($ws->{layout}, 'splith', 'orientation horizontal by default');
83 cmd 'split v';
84 $ws = get_ws($tmp);
85 is($ws->{layout}, 'splitv', 'split v changes workspace orientation');
86
87 cmd 'open';
88 my @content = @{get_ws_content($tmp)};
89
90 # recursively sums up all nodes and their children
91 sub sum_nodes {
92 my ($nodes) = @_;
93
94 return 0 if !@{$nodes};
95
96 my @children = (map { @{$_->{nodes}} } @{$nodes},
97 map { @{$_->{'floating_nodes'}} } @{$nodes});
98
99 return @{$nodes} + sum_nodes(\@children);
100 }
101
102 my $old_count = sum_nodes(\@content);
103 cmd 'split v';
104
105 @content = @{get_ws_content($tmp)};
106 $old_count = sum_nodes(\@content);
107
108 cmd 'split v';
109
110 @content = @{get_ws_content($tmp)};
111 my $count = sum_nodes(\@content);
112 is($count, $old_count, 'not more windows after splitting again');
113
114 ######################################################################
115 # In the special case of being inside a stacked or tabbed container, we don’t
116 # want this to happen.
117 ######################################################################
118
119 $tmp = fresh_workspace;
120
121 cmd 'open';
122 @content = @{get_ws_content($tmp)};
123 is(scalar @content, 1, 'Precisely one container on this ws');
124 cmd 'layout stacked';
125 @content = @{get_ws_content($tmp)};
126 is(scalar @content, 1, 'Still one container on this ws');
127 is(scalar @{$content[0]->{nodes}}, 1, 'Stacked con has one child node');
128
129 cmd 'split h';
130 cmd 'open';
131 @content = @{get_ws_content($tmp)};
132 is(scalar @content, 1, 'Still one container on this ws');
133 is(scalar @{$content[0]->{nodes}}, 1, 'Stacked con still has one child node');
134
135 ################################################################################
136 # When focusing the workspace, changing the layout should have an effect on the
137 # workspace, not on the parent (CT_CONTENT) container.
138 ################################################################################
139
140 sub get_output_content {
141 my $tree = i3(get_socket_path())->get_tree->recv;
142
143 my @outputs = grep { $_->{name} !~ /^__/ } @{$tree->{nodes}};
144 is(scalar @outputs, 1, 'exactly one output (testcase not multi-monitor capable)');
145 my $output = $outputs[0];
146 # get the first (and only) CT_CON
147 return first { $_->{type} eq 'con' } @{$output->{nodes}};
148 }
149
150 $tmp = fresh_workspace;
151
152 cmd 'open';
153 cmd 'split v';
154 cmd 'open';
155 cmd 'focus parent';
156 is(get_output_content()->{layout}, 'splith', 'content container layout ok');
157 cmd 'layout stacked';
158 is(get_output_content()->{layout}, 'splith', 'content container layout still ok');
159
160 ######################################################################
161 # Splitting a workspace that has more than one child
162 ######################################################################
163
164 $tmp = fresh_workspace;
165
166 cmd 'open';
167 cmd 'open';
168 cmd 'focus parent';
169 cmd 'split v';
170 cmd 'open';
171
172 my $content = get_ws_content($tmp);
173 my $fst = $content->[0];
174 my $snd = $content->[1];
175
176 is(@{$content}, 2, 'two containers on workspace');
177 is(@{$fst->{nodes}}, 2, 'first child has two children');
178 is(@{$snd->{nodes}}, 0, 'second child has no children');
179
180 ######################################################################
181 # Test split toggle
182 ######################################################################
183
184 $tmp = fresh_workspace;
185 cmd 'split h';
186 cmd 'split toggle';
187 $ws = get_ws($tmp);
188 is($ws->{layout}, 'splitv', 'toggled workspace split');
189
190 $tmp = fresh_workspace;
191 cmd 'split h';
192 cmd 'split toggle';
193 cmd 'split toggle';
194 $ws = get_ws($tmp);
195 is($ws->{layout}, 'splith', 'toggled workspace back and forth');
196
197 cmd 'open';
198 cmd 'open';
199 cmd 'split toggle';
200
201 $content = get_ws_content($tmp);
202 my $second = $content->[1];
203 is($second->{layout}, 'splitv', 'toggled container orientation to vertical');
204
205 cmd 'split toggle';
206 $content = get_ws_content($tmp);
207 $second = $content->[1];
208 is($second->{layout}, 'splith', 'toggled container orientation back to horizontal');
209
210 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests moving. Basically, there are four different code-paths:
17 # 1) move a container which cannot be moved (single container on a workspace)
18 # 2) move a container before another single container
19 # 3) move a container inside another container
20 # 4) move a container in a different direction so that we need to go up in tree
21 #
22 use i3test;
23
24 my $i3 = i3(get_socket_path());
25
26 my $tmp = fresh_workspace;
27
28 ######################################################################
29 # 1) move a container which cannot be moved
30 ######################################################################
31
32 cmd 'open';
33
34 my $old_content = get_ws_content($tmp);
35 is(@{$old_content}, 1, 'one container on this workspace');
36
37 my $first = $old_content->[0]->{id};
38
39 cmd 'move left';
40 cmd 'move right';
41 cmd 'move up';
42 cmd 'move down';
43
44 my $content = get_ws_content($tmp);
45 is_deeply($old_content, $content, 'workspace unmodified after useless moves');
46
47 ######################################################################
48 # 2) move a container before another single container
49 ######################################################################
50
51 cmd 'open';
52 $content = get_ws_content($tmp);
53 is(@{$content}, 2, 'two containers on this workspace');
54 my $second = $content->[1]->{id};
55
56 is($content->[0]->{id}, $first, 'first container unmodified');
57
58 # Move the second container before the first one (→ swap them)
59 cmd 'move left';
60 $content = get_ws_content($tmp);
61 is($content->[0]->{id}, $second, 'first container modified');
62
63 # We should not be able to move any further
64 cmd 'move left';
65 $content = get_ws_content($tmp);
66 is($content->[0]->{id}, $second, 'first container unmodified');
67
68 # Now move in the other direction
69 cmd 'move right';
70 $content = get_ws_content($tmp);
71 is($content->[0]->{id}, $first, 'first container modified');
72
73 # We should not be able to move any further
74 cmd 'move right';
75 $content = get_ws_content($tmp);
76 is($content->[0]->{id}, $first, 'first container unmodified');
77
78 ######################################################################
79 # 3) move a container inside another container
80 ######################################################################
81
82 # Split the current (second) container and create a new container on workspace
83 # level. Our layout looks like this now:
84 # --------------------------
85 # | | second | |
86 # | first | ------ | third |
87 # | | | |
88 # --------------------------
89 cmd 'split v';
90 cmd 'focus parent';
91 cmd 'open';
92
93 $content = get_ws_content($tmp);
94 is(@{$content}, 3, 'three containers on this workspace');
95 my $third = $content->[2]->{id};
96
97 cmd 'move left';
98 $content = get_ws_content($tmp);
99 is(@{$content}, 2, 'only two containers on this workspace');
100 my $nodes = $content->[1]->{nodes};
101 is($nodes->[0]->{id}, $second, 'second container on top');
102 is($nodes->[1]->{id}, $third, 'third container on bottom');
103
104 ######################################################################
105 # move it inside the split container
106 ######################################################################
107
108 cmd 'move up';
109 $nodes = get_ws_content($tmp)->[1]->{nodes};
110 is($nodes->[0]->{id}, $third, 'third container on top');
111 is($nodes->[1]->{id}, $second, 'second container on bottom');
112
113 # move it outside again
114 cmd 'move left';
115 is_num_children($tmp, 3, 'three containers after moving left');
116
117 # due to automatic flattening/cleanup, the remaining split container
118 # will be replaced by the con itself, so we will still have 3 nodes
119 cmd 'move right';
120 is_num_children($tmp, 2, 'two containers after moving right (flattening)');
121
122 ######################################################################
123 # 4) We create two v-split containers on the workspace, then we move
124 # all Cons from the left v-split to the right one. The old vsplit
125 # container needs to be closed. Verify that it will be closed.
126 ######################################################################
127
128 my $otmp = fresh_workspace;
129
130 cmd "open";
131 cmd "open";
132 cmd "split v";
133 cmd "open";
134 cmd 'focus left';
135 cmd "split v";
136 cmd "open";
137 cmd "move right";
138 cmd 'focus left';
139 cmd "move right";
140
141 is_num_children($otmp, 1, 'only one node on this workspace');
142
143 ######################################################################
144 # 5) test moving floating containers.
145 ######################################################################
146
147 $tmp = fresh_workspace;
148 my $floatwin = open_floating_window;
149 my ($absolute_before, $top_before) = $floatwin->rect;
150
151 cmd 'move left';
152
153 sync_with_i3;
154
155 my ($absolute, $top) = $floatwin->rect;
156
157 is($absolute->x, ($absolute_before->x - 10), 'moved 10 px to the left');
158 is($absolute->y, $absolute_before->y, 'y not changed');
159 is($absolute->width, $absolute_before->width, 'width not changed');
160 is($absolute->height, $absolute_before->height, 'height not changed');
161
162 $absolute_before = $absolute;
163 $top_before = $top;
164
165 cmd 'move right';
166
167 sync_with_i3;
168
169 ($absolute, $top) = $floatwin->rect;
170
171 is($absolute->x, ($absolute_before->x + 10), 'moved 10 px to the right');
172 is($absolute->y, $absolute_before->y, 'y not changed');
173 is($absolute->width, $absolute_before->width, 'width not changed');
174 is($absolute->height, $absolute_before->height, 'height not changed');
175
176 $absolute_before = $absolute;
177 $top_before = $top;
178
179 cmd 'move up';
180
181 sync_with_i3;
182
183 ($absolute, $top) = $floatwin->rect;
184
185 is($absolute->x, $absolute_before->x, 'x not changed');
186 is($absolute->y, ($absolute_before->y - 10), 'moved 10 px up');
187 is($absolute->width, $absolute_before->width, 'width not changed');
188 is($absolute->height, $absolute_before->height, 'height not changed');
189
190 $absolute_before = $absolute;
191 $top_before = $top;
192
193 cmd 'move down';
194
195 sync_with_i3;
196
197 ($absolute, $top) = $floatwin->rect;
198
199 is($absolute->x, $absolute_before->x, 'x not changed');
200 is($absolute->y, ($absolute_before->y + 10), 'moved 10 px up');
201 is($absolute->width, $absolute_before->width, 'width not changed');
202 is($absolute->height, $absolute_before->height, 'height not changed');
203
204 $absolute_before = $absolute;
205 $top_before = $top;
206
207 ######################################################################
208 # 6) test moving floating containers with a specific amount of px
209 ######################################################################
210
211 cmd 'move left 20 px';
212
213 sync_with_i3;
214
215 ($absolute, $top) = $floatwin->rect;
216
217 is($absolute->x, ($absolute_before->x - 20), 'moved 20 px to the left');
218 is($absolute->y, $absolute_before->y, 'y not changed');
219 is($absolute->width, $absolute_before->width, 'width not changed');
220 is($absolute->height, $absolute_before->height, 'height not changed');
221
222 ######################################################################
223 # 6) test moving floating window to a specified position
224 # and to absolute center
225 ######################################################################
226
227 $tmp = fresh_workspace;
228 open_floating_window; my @floatcon;
229
230 cmd 'move position 5 px 15 px';
231
232 @floatcon = @{get_ws($tmp)->{floating_nodes}};
233
234 is($floatcon[0]->{rect}->{x}, 5, 'moved to position 5 x');
235 is($floatcon[0]->{rect}->{y}, 15, 'moved to position 15 y');
236
237 cmd 'move absolute position center';
238
239 @floatcon = @{get_ws($tmp)->{floating_nodes}};
240
241 my $center_x = int($x->root->rect->width/2) - int($floatcon[0]->{rect}->{width}/2);
242 my $center_y = int($x->root->rect->height/2) - int($floatcon[0]->{rect}->{height}/2);
243
244 is($floatcon[0]->{rect}->{x}, $center_x, "moved to center at position $center_x x");
245 is($floatcon[0]->{rect}->{y}, $center_y, "moved to center at position $center_y y");
246
247 # Make sure the command works with criteria
248 open_floating_window;
249
250 @floatcon = @{get_ws($tmp)->{floating_nodes}};
251
252 cmd '[con_id="' . $floatcon[0]->{nodes}[0]->{id} . '"] move position 25 px 30 px';
253 cmd '[con_id="' . $floatcon[1]->{nodes}[0]->{id} . '"] move position 35 px 40 px';
254
255 @floatcon = @{get_ws($tmp)->{floating_nodes}};
256
257 is($floatcon[0]->{rect}->{x}, 25, 'moved to position 25 x with criteria');
258 is($floatcon[0]->{rect}->{y}, 30, 'moved to position 30 y with criteria');
259
260 is($floatcon[1]->{rect}->{x}, 35, 'moved to position 35 x with criteria');
261 is($floatcon[1]->{rect}->{y}, 40, 'moved to position 40 y with criteria');
262
263 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: closing of floating clients did crash i3 when closing the
17 # container which contained this client.
18 #
19 use i3test;
20
21 fresh_workspace;
22
23 cmd 'open';
24 cmd 'mode toggle';
25 cmd 'kill';
26 cmd 'kill';
27
28 does_i3_live;
29
30 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: make a container floating, kill its parent, make it tiling again
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 cmd 'open';
23 my $left = get_focused($tmp);
24 cmd 'open';
25 my $old = get_focused($tmp);
26 cmd 'split v';
27 cmd 'open';
28 my $floating = get_focused($tmp);
29 diag("focused floating: " . get_focused($tmp));
30 cmd 'mode toggle';
31 sync_with_i3;
32
33 # kill old container
34 cmd qq|[con_id="$old"] focus|;
35 is(get_focused($tmp), $old, 'old container focused');
36 cmd 'kill';
37
38 # kill left container
39 cmd qq|[con_id="$left"] focus|;
40 is(get_focused($tmp), $left, 'old container focused');
41 cmd 'kill';
42
43 # focus floating window, make it tiling again
44 cmd qq|[con_id="$floating"] focus|;
45 is(get_focused($tmp), $floating, 'floating window focused');
46
47 sync_with_i3;
48 cmd 'mode toggle';
49
50 does_i3_live;
51
52 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Check if new containers are opened after the currently focused one instead
17 # of always at the end
18 use List::Util qw(first);
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22
23 my $tmp = fresh_workspace;
24
25 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
26
27 # Open two new container
28 my $first = open_empty_con($i3);
29
30 ok(@{get_ws_content($tmp)} == 1, 'containers opened');
31
32 my $second = open_empty_con($i3);
33
34 isnt($first, $second, 'different container focused');
35
36 ##############################################################
37 # see if new containers open after the currently focused
38 ##############################################################
39
40 cmd qq|[con_id="$first"] focus|;
41 cmd 'open';
42 my $content = get_ws_content($tmp);
43 ok(@{$content} == 3, 'three containers opened');
44
45 is($content->[0]->{id}, $first, 'first container unmodified');
46 isnt($content->[1]->{id}, $second, 'second container replaced');
47 is($content->[2]->{id}, $second, 'third container unmodified');
48
49 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Check if the focus is correctly restored after closing windows.
17 #
18 use i3test;
19 use List::Util qw(first);
20
21 my $i3 = i3(get_socket_path());
22
23 my $tmp = fresh_workspace;
24
25 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
26
27 my $first = open_empty_con($i3);
28 my $second = open_empty_con($i3);
29
30 cmd 'split v';
31
32 my ($nodes, $focus) = get_ws_content($tmp);
33
34 ok(!$nodes->[1]->{focused}, 'split container not focused');
35 cmd 'focus parent';
36 ($nodes, $focus) = get_ws_content($tmp);
37 ok($nodes->[1]->{focused}, 'split container focused after focus parent');
38
39 my $third = open_empty_con($i3);
40
41 isnt(get_focused($tmp), $second, 'different container focused');
42
43 # We have the following layout now (con is focused):
44 # .----------------.
45 # | split | |
46 # | .----. | con |
47 # | | cn | | |
48 # | `----' | |
49 # `----------------'
50
51 ##############################################################
52 # see if the focus goes down to $first (not to its split parent)
53 # when closing $second
54 ##############################################################
55
56 cmd 'kill';
57 sync_with_i3;
58
59 ($nodes, $focus) = get_ws_content($tmp);
60 is($nodes->[1]->{nodes}->[0]->{id}, $second, 'second container found');
61 ok($nodes->[1]->{nodes}->[0]->{focused}, 'second container focused');
62
63 ##############################################################
64 # another case, using a slightly different layout (regression)
65 ##############################################################
66
67 $tmp = fresh_workspace;
68
69 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
70
71 cmd 'split v';
72 $first = open_empty_con($i3);
73 my $bottom = open_empty_con($i3);
74
75 cmd 'focus up';
76 cmd 'split h';
77 my $middle = open_empty_con($i3);
78 my $right = open_empty_con($i3);
79 cmd 'focus down';
80
81 # We have the following layout now (second is focused):
82 # .----------------------------.
83 # | .------------------------. |
84 # | | first | middle | right | |
85 # | `------------------------' |
86 # |----------------------------|
87 # | |
88 # | second |
89 # | |
90 # `----------------------------'
91
92 # Check if the focus is restored to $right when we close $second
93 cmd 'kill';
94
95 is(get_focused($tmp), $right, 'top right container focused (in focus stack)');
96
97 ($nodes, $focus) = get_ws_content($tmp);
98 my $tr = first { $_->{id} eq $right } @{$nodes->[0]->{nodes}};
99 is($tr->{focused}, 1, 'top right container really has focus');
100
101 ##############################################################
102 # check if focus is correct after closing an unfocused window
103 ##############################################################
104
105 $tmp = fresh_workspace;
106
107 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
108
109 $first = open_empty_con($i3);
110 $middle = open_empty_con($i3);
111 # XXX: the $right empty con will be filled with the x11 window we are creating afterwards
112 $right = open_empty_con($i3);
113 my $win = open_window({ background_color => '#00ff00' });
114
115 cmd qq|[con_id="$middle"] focus|;
116 $win->destroy;
117 sync_with_i3;
118
119 is(get_focused($tmp), $middle, 'middle container focused');
120
121 ##############################################################
122 # check if the workspace container can be closed
123 ##############################################################
124
125 $tmp = fresh_workspace;
126
127 my $window = open_window();
128
129 # one window opened on the current workspace
130 ($nodes, $focus) = get_ws_content($tmp);
131 is(scalar @$nodes, 1, 'workspace contains one node');
132
133 # focus the workspace
134 cmd "focus parent";
135 cmd "focus parent";
136
137 # try to kill the workspace
138 cmd "kill";
139 sync_with_i3;
140
141 # the workspace should now be empty
142 ($nodes, $focus) = get_ws_content($tmp);
143 is(scalar @$nodes, 0, 'workspace is empty');
144
145 ################################################################################
146 # check if killing a workspace also closes floating windows.
147 ################################################################################
148
149 $tmp = fresh_workspace;
150
151 $window = open_window;
152 my $floating_window = open_floating_window;
153
154 # one window opened on the current workspace
155 ($nodes, $focus) = get_ws_content($tmp);
156 is(scalar @$focus, 2, 'workspace contains two nodes');
157
158 # focus the workspace
159 cmd "focus parent";
160 cmd "focus parent";
161
162 # try to kill the workspace
163 cmd "kill";
164 sync_with_i3;
165
166 # the workspace should now be empty
167 ($nodes, $focus) = get_ws_content($tmp);
168 is(scalar @$focus, 0, 'workspace is empty');
169
170 ##############################################################
171 # and now for something completely different:
172 # check if the pointer position is relevant when restoring focus
173 # (it should not be relevant, of course)
174 ##############################################################
175
176 # TODO: add test code as soon as I can reproduce it
177
178 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Check if empty split containers are automatically closed.
17 #
18 use i3test;
19
20 my $i3 = i3(get_socket_path());
21
22 my $tmp = fresh_workspace;
23
24 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
25
26 my $first = open_empty_con($i3);
27 my $second = open_empty_con($i3);
28 cmd qq|[con_id="$first"] focus|;
29
30 cmd 'split v';
31
32 my ($nodes, $focus) = get_ws_content($tmp);
33
34 is($nodes->[0]->{focused}, 0, 'split container not focused');
35
36 # focus the split container
37 cmd 'focus parent';
38 ($nodes, $focus) = get_ws_content($tmp);
39 my $split = $focus->[0];
40 cmd 'focus child';
41
42 $second = open_empty_con($i3);
43
44 isnt($first, $second, 'different container focused');
45
46 ##############################################################
47 # close both windows and see if the split container still exists
48 ##############################################################
49
50 cmd 'kill';
51 cmd 'kill';
52 ($nodes, $focus) = get_ws_content($tmp);
53 isnt($nodes->[0]->{id}, $split, 'split container closed');
54
55 # clean up the remaining containers to ensure this workspace will be garbage
56 # collected.
57 cmd 'kill';
58 cmd 'kill';
59
60 ##############################################################
61 # same thing but this time we are moving the cons away instead
62 # of killing them
63 ##############################################################
64
65 $tmp = fresh_workspace;
66
67 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
68
69 $first = open_empty_con($i3);
70 $second = open_empty_con($i3);
71 cmd qq|[con_id="$first"] focus|;
72
73 cmd 'split v';
74
75 ($nodes, $focus) = get_ws_content($tmp);
76
77 is($nodes->[0]->{focused}, 0, 'split container not focused');
78
79 # focus the split container
80 cmd 'focus parent';
81 ($nodes, $focus) = get_ws_content($tmp);
82 $split = $focus->[0];
83 cmd 'focus child';
84
85 $second = open_empty_con($i3);
86
87 isnt($first, $second, 'different container focused');
88
89 ##############################################################
90 # close both windows and see if the split container still exists
91 ##############################################################
92
93 my $otmp = get_unused_workspace();
94 cmd "move workspace $otmp";
95 cmd "move workspace $otmp";
96 ($nodes, $focus) = get_ws_content($tmp);
97 isnt($nodes->[0]->{id}, $split, 'split container closed');
98
99 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Check if stacking containers can be used independently of
17 # the split mode (horizontal/vertical) of the underlying
18 # container.
19 #
20 use i3test;
21
22 my $i3 = i3(get_socket_path());
23
24 my $tmp = fresh_workspace;
25
26 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
27
28 # Enforce vertical split mode
29 cmd 'split v';
30
31 my $first = open_empty_con($i3);
32 my $second = open_empty_con($i3);
33
34 isnt($first, $second, 'two different containers opened');
35
36 ##############################################################
37 # change mode to stacking and cycle through the containers
38 ##############################################################
39
40 cmd 'layout stacking';
41 is(get_focused($tmp), $second, 'second container still focused');
42
43 cmd 'focus down';
44 is(get_focused($tmp), $first, 'first container focused');
45
46 cmd 'focus up';
47 is(get_focused($tmp), $second, 'second container focused again');
48
49 ##############################################################
50 # now change the orientation to horizontal and cycle
51 ##############################################################
52
53 cmd 'focus parent';
54 cmd 'split h';
55 cmd 'focus child';
56
57 cmd 'focus down';
58 is(get_focused($tmp), $first, 'first container focused');
59
60 cmd 'focus up';
61 is(get_focused($tmp), $second, 'second container focused again');
62
63
64 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks if the 'move [window/container] to workspace' command works correctly
17 #
18 use i3test;
19
20 my $i3 = i3(get_socket_path());
21
22 # We move the pointer out of our way to avoid a bug where the focus will
23 # be set to the window under the cursor
24 sync_with_i3;
25 $x->root->warp_pointer(0, 0);
26 sync_with_i3;
27
28 sub move_workspace_test {
29 my ($movecmd) = @_;
30
31 my $tmp = get_unused_workspace();
32 my $tmp2 = get_unused_workspace();
33 cmd "workspace $tmp";
34
35 is_num_children($tmp, 0, 'no containers yet');
36
37 my $first = open_empty_con($i3);
38 my $second = open_empty_con($i3);
39 is_num_children($tmp, 2, 'two containers on first ws');
40
41 cmd "workspace $tmp2";
42 is_num_children($tmp2, 0, 'no containers on second ws yet');
43
44 cmd "workspace $tmp";
45
46 cmd "$movecmd $tmp2";
47 is_num_children($tmp, 1, 'one container on first ws anymore');
48 is_num_children($tmp2, 1, 'one container on second ws');
49 my ($nodes, $focus) = get_ws_content($tmp2);
50
51 is($focus->[0], $second, 'same container on different ws');
52
53 ($nodes, $focus) = get_ws_content($tmp);
54 ok($nodes->[0]->{focused}, 'first container focused on first ws');
55 }
56
57 move_workspace_test('move workspace'); # supported for legacy reasons
58 move_workspace_test('move to workspace');
59 # Those are just synonyms and more verbose ways of saying the same thing:
60 move_workspace_test('move window to workspace');
61 move_workspace_test('move container to workspace');
62
63 ################################################################################
64 # Check that 'move to workspace number <number>' works to move a window to
65 # named workspaces which start with <number>.
66 ################################################################################
67
68 cmd 'workspace 13: meh';
69 cmd 'open';
70 is_num_children('13: meh', 1, 'one container on 13: meh');
71
72 ok(!workspace_exists('13'), 'workspace 13 does not exist yet');
73
74 cmd 'workspace 12';
75 cmd 'open';
76
77 cmd 'move to workspace number 13';
78 is_num_children('13: meh', 2, 'one container on 13: meh');
79 is_num_children('12', 0, 'no container on 12 anymore');
80
81 ok(!workspace_exists('13'), 'workspace 13 does still not exist');
82
83 ################################################################################
84 # Check that 'move to workspace number <number><name>' works to move a window to
85 # named workspaces which start with <number>.
86 ################################################################################
87
88 cmd 'workspace 15: meh';
89 cmd 'open';
90 is_num_children('15: meh', 1, 'one container on 15: meh');
91
92 ok(!workspace_exists('15'), 'workspace 15 does not exist yet');
93 ok(!workspace_exists('15: duh'), 'workspace 15: duh does not exist yet');
94
95 cmd 'workspace 14';
96 cmd 'open';
97
98 cmd 'move to workspace number 15: duh';
99 is_num_children('15: meh', 2, 'two containers on 15: meh');
100 is_num_children('14', 0, 'no container on 14 anymore');
101
102 ok(!workspace_exists('15'), 'workspace 15 does still not exist');
103 ok(!workspace_exists('15: duh'), 'workspace 15 does still not exist');
104
105 ###################################################################
106 # check if 'move workspace next' and 'move workspace prev' work
107 ###################################################################
108
109 # Open two containers on the first workspace, one container on the second
110 # workspace. Because the workspaces are named, they will be sorted by order of
111 # creation.
112 my $tmp = get_unused_workspace();
113 my $tmp2 = get_unused_workspace();
114 cmd "workspace $tmp";
115 is_num_children($tmp, 0, 'no containers yet');
116 my $first = open_empty_con($i3);
117 my $second = open_empty_con($i3);
118 is_num_children($tmp, 2, 'two containers');
119
120 cmd "workspace $tmp2";
121 is_num_children($tmp2, 0, 'no containers yet');
122 my $third = open_empty_con($i3);
123 is_num_children($tmp2, 1, 'one container on second ws');
124
125 # go back to the first workspace, move one of the containers to the next one
126 cmd "workspace $tmp";
127 cmd 'move workspace next';
128 is_num_children($tmp, 1, 'one container on first ws');
129 is_num_children($tmp2, 2, 'two containers on second ws');
130
131 # go to the second workspace and move two containers to the first one
132 cmd "workspace $tmp2";
133 cmd 'move workspace prev';
134 cmd 'move workspace prev';
135 is_num_children($tmp, 3, 'three containers on first ws');
136 is_num_children($tmp2, 0, 'no containers on second ws');
137
138 ###################################################################
139 # check if 'move workspace current' works
140 ###################################################################
141
142 $tmp = get_unused_workspace();
143 $tmp2 = get_unused_workspace();
144
145 cmd "workspace $tmp";
146 $first = open_window(name => 'win-name');
147 is_num_children($tmp, 1, 'one container on first ws');
148
149 cmd "workspace $tmp2";
150 is_num_children($tmp2, 0, 'no containers yet');
151
152 cmd qq|[title="win-name"] move workspace $tmp2|;
153 is_num_children($tmp2, 1, 'one container on second ws');
154
155 cmd qq|[title="win-name"] move workspace $tmp|;
156 is_num_children($tmp2, 0, 'no containers on second ws');
157
158 ###################################################################
159 # check if floating cons are moved to new workspaces properly
160 # (that is, if they are floating on the target ws, too)
161 ###################################################################
162
163 $tmp = get_unused_workspace();
164 $tmp2 = get_unused_workspace();
165 cmd "workspace $tmp";
166
167 cmd "open";
168 cmd "floating toggle";
169
170 my $ws = get_ws($tmp);
171 is(@{$ws->{nodes}}, 0, 'no nodes on workspace');
172 is(@{$ws->{floating_nodes}}, 1, 'one floating node on workspace');
173
174 cmd "move workspace $tmp2";
175
176 $ws = get_ws($tmp2);
177 is(@{$ws->{nodes}}, 0, 'no nodes on workspace');
178 is(@{$ws->{floating_nodes}}, 1, 'one floating node on workspace');
179
180 ################################################################################
181 # Check that 'move workspace number' works correctly.
182 ################################################################################
183
184 $tmp = get_unused_workspace();
185 cmd 'open';
186
187 cmd 'workspace 16';
188 cmd 'open';
189 is_num_children('16', 1, 'one node on ws 16');
190
191 cmd "workspace $tmp";
192 cmd 'open';
193 cmd 'move workspace number 16';
194 is_num_children('16', 2, 'two nodes on ws 16');
195
196 ok(!workspace_exists('17'), 'workspace 17 does not exist yet');
197 cmd 'open';
198 cmd 'move workspace number 17';
199 ok(workspace_exists('17'), 'workspace 17 created by moving');
200 is(@{get_ws('17')->{nodes}}, 1, 'one node on ws 16');
201
202 ################################################################################
203 # The following four tests verify the various 'move workspace' commands when
204 # the selection is itself a workspace.
205 ################################################################################
206
207 # borrowed from 122-split.t
208 # recursively sums up all nodes and their children
209 sub sum_nodes {
210 my ($nodes) = @_;
211
212 return 0 if !@{$nodes};
213
214 my @children = (map { @{$_->{nodes}} } @{$nodes},
215 map { @{$_->{'floating_nodes'}} } @{$nodes});
216
217 return @{$nodes} + sum_nodes(\@children);
218 }
219
220 ############################################################
221 # move workspace 'next|prev'
222 ############################################################
223 $tmp = get_unused_workspace();
224 $tmp2 = get_unused_workspace();
225
226 cmd "workspace $tmp";
227 cmd 'open';
228 is_num_children($tmp, 1, 'one container on first ws');
229
230 cmd "workspace $tmp2";
231 cmd 'open';
232 is_num_children($tmp2, 1, 'one container on second ws');
233 cmd 'open';
234 is_num_children($tmp2, 2, 'two containers on second ws');
235
236 cmd 'focus parent';
237 cmd 'move workspace prev';
238
239 is_num_children($tmp, 2, 'two child containers on first ws');
240 is(sum_nodes(get_ws_content($tmp)), 4, 'four total containers on first ws');
241 is_num_children($tmp2, 0, 'no containers on second ws');
242
243 ############################################################
244 # move workspace current
245 # This is a special case that should be a no-op.
246 ############################################################
247 $tmp = fresh_workspace();
248
249 cmd 'open';
250 is_num_children($tmp, 1, 'one container on first ws');
251 my $tmpcount = sum_nodes(get_ws_content($tmp));
252
253 cmd 'focus parent';
254 cmd "move workspace $tmp";
255
256 is(sum_nodes(get_ws_content($tmp)), $tmpcount, 'number of containers in first ws unchanged');
257
258 ############################################################
259 # move workspace '<name>'
260 ############################################################
261 $tmp2 = get_unused_workspace();
262 $tmp = fresh_workspace();
263
264 cmd 'open';
265 is_num_children($tmp, 1, 'one container on first ws');
266
267 cmd "workspace $tmp2";
268 cmd 'open';
269 is_num_children($tmp2, 1, 'one container on second ws');
270 cmd 'open';
271 is_num_children($tmp2, 2, 'two containers on second ws');
272
273 cmd 'focus parent';
274 cmd "move workspace $tmp";
275
276 is_num_children($tmp, 2, 'two child containers on first ws');
277 is(sum_nodes(get_ws_content($tmp)), 4, 'four total containers on first ws');
278 is_num_children($tmp2, 0, 'no containers on second ws');
279
280 ############################################################
281 # move workspace number '<number>'
282 ############################################################
283 cmd 'workspace 18';
284 cmd 'open';
285 is_num_children('18', 1, 'one container on ws 18');
286
287 cmd 'workspace 19';
288 cmd 'open';
289 is_num_children('19', 1, 'one container on ws 19');
290 cmd 'open';
291 is_num_children('19', 2, 'two containers on ws 19');
292
293 cmd 'focus parent';
294 cmd 'move workspace number 18';
295
296 is_num_children('18', 2, 'two child containers on ws 18');
297 is(sum_nodes(get_ws_content('18')), 4, 'four total containers on ws 18');
298 is_num_children('19', 0, 'no containers on ws 19');
299
300 ###################################################################
301 # move workspace '<name>' with a floating child
302 ###################################################################
303 $tmp2 = get_unused_workspace();
304 $tmp = fresh_workspace();
305 cmd 'open';
306 cmd 'floating toggle';
307 cmd 'open';
308 cmd 'floating toggle';
309 cmd 'open';
310
311 $ws = get_ws($tmp);
312 is_num_children($tmp, 1, 'one container on first workspace');
313 is(@{$ws->{floating_nodes}}, 2, 'two floating nodes on first workspace');
314
315 cmd 'focus parent';
316 cmd "move workspace $tmp2";
317
318 $ws = get_ws($tmp2);
319 is_num_children($tmp2, 1, 'one container on second workspace');
320 is(@{$ws->{floating_nodes}}, 2, 'two floating nodes on second workspace');
321
322 ###################################################################
323 # same as the above, but with only floating children
324 ###################################################################
325 $tmp2 = get_unused_workspace();
326 $tmp = fresh_workspace();
327 cmd 'open';
328 cmd 'floating toggle';
329
330 $ws = get_ws($tmp);
331 is_num_children($tmp, 0, 'no regular nodes on first workspace');
332 is(@{$ws->{floating_nodes}}, 1, 'one floating node on first workspace');
333
334 cmd 'focus parent';
335 cmd "move workspace $tmp2";
336
337 $ws = get_ws($tmp2);
338 is_num_children($tmp2, 0, 'no regular nodes on second workspace');
339 is(@{$ws->{floating_nodes}}, 1, 'one floating node on second workspace');
340
341 ###################################################################
342 # Check that moving an empty workspace using criteria doesn't
343 # create unfocused empty workspace.
344 ###################################################################
345 $tmp2 = get_unused_workspace();
346 $tmp = fresh_workspace();
347 cmd 'mark a';
348 cmd "[con_mark=a] move to workspace $tmp2";
349
350 is (get_ws($tmp2), undef, 'No empty workspace created');
351
352 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks if size hints are interpreted correctly.
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
23
24 my $win = open_window({ dont_map => 1 });
25 # XXX: we should check screen size. in screens with an AR of 2.0,
26 # this is not a good idea.
27 my $aspect = X11::XCB::Sizehints::Aspect->new;
28 $aspect->min_num(600);
29 $aspect->min_den(300);
30 $aspect->max_num(600);
31 $aspect->max_den(300);
32 $win->_create;
33 $win->map;
34 wait_for_map $win;
35 $win->hints->aspect($aspect);
36 $x->flush;
37
38 sync_with_i3;
39
40 my $rect = $win->rect;
41 my $ar = $rect->width / $rect->height;
42 diag("Aspect ratio = $ar");
43 ok(($ar > 1.90) && ($ar < 2.10), 'Aspect ratio about 2.0');
44
45 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 #
4 # Please read the following documents before working on tests:
5 # • https://build.i3wm.org/docs/testsuite.html
6 # (or docs/testsuite)
7 #
8 # • https://build.i3wm.org/docs/lib-i3test.html
9 # (alternatively: perldoc ./testcases/lib/i3test.pm)
10 #
11 # • https://build.i3wm.org/docs/ipc.html
12 # (or docs/ipc)
13 #
14 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
15 # (unless you are already familiar with Perl)
16 #
17 #
18 use i3test;
19
20 cmd 'blargh!';
21
22 does_i3_live;
23
24 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $tmp = fresh_workspace;
19
20 #############################################################################
21 # 1: see if focus stays the same when toggling tiling/floating mode
22 #############################################################################
23
24 my $first = open_window;
25 my $second = open_window;
26
27 is($x->input_focus, $second->id, 'second window focused');
28
29 cmd 'floating enable';
30 cmd 'floating disable';
31
32 is($x->input_focus, $second->id, 'second window still focused after mode toggle');
33
34 #############################################################################
35 # 2: see if focus stays on the current floating window if killing another
36 # floating window
37 #############################################################################
38
39 $tmp = fresh_workspace;
40
41 $first = open_window; # window 2
42 $second = open_window; # window 3
43 my $third = open_window; # window 4
44
45 is($x->input_focus, $third->id, 'last container focused');
46
47 cmd 'floating enable';
48
49 cmd '[id="' . $second->id . '"] focus';
50
51 is($x->input_focus, $second->id, 'second con focused');
52
53 cmd 'floating enable';
54
55 # now kill the third one (it's floating). focus should stay unchanged
56 cmd '[id="' . $third->id . '"] kill';
57
58 wait_for_unmap($third);
59
60 is($x->input_focus, $second->id, 'second con still focused after killing third');
61
62
63 #############################################################################
64 # 3: see if the focus gets reverted correctly when closing floating clients
65 # (first to the next floating client, then to the last focused tiling client)
66 #############################################################################
67
68 $tmp = fresh_workspace;
69
70 $first = open_window({ background_color => '#ff0000' }); # window 5
71 $second = open_window({ background_color => '#00ff00' }); # window 6
72 $third = open_window({ background_color => '#0000ff' }); # window 7
73
74 is($x->input_focus, $third->id, 'last container focused');
75
76 cmd 'floating enable';
77
78 cmd '[id="' . $second->id . '"] focus';
79
80 is($x->input_focus, $second->id, 'second con focused');
81
82 cmd 'floating enable';
83
84 # now kill the second one. focus should fall back to the third one, which is
85 # also floating
86 cmd 'kill';
87 wait_for_unmap($second);
88
89 is($x->input_focus, $third->id, 'third con focused');
90
91 cmd 'kill';
92 wait_for_unmap($third);
93
94 is($x->input_focus, $first->id, 'first con focused after killing all floating cons');
95
96 #############################################################################
97 # 4: same test as 3, but with another split con
98 #############################################################################
99
100 $tmp = fresh_workspace;
101
102 $first = open_window({ background_color => '#ff0000' }); # window 5
103 cmd 'split v';
104 cmd 'layout stacked';
105 $second = open_window({ background_color => '#00ff00' }); # window 6
106 $third = open_window({ background_color => '#0000ff' }); # window 7
107 is($x->input_focus, $third->id, 'last container focused');
108
109 cmd '[id="' . $second->id . '"] focus';
110 cmd 'floating enable';
111 cmd '[id="' . $third->id . '"] floating enable';
112
113 sync_with_i3;
114 is($x->input_focus, $second->id, 'second con focused');
115
116 # now kill the second one. focus should fall back to the third one, which is
117 # also floating
118 cmd 'kill';
119 wait_for_unmap($second);
120
121 is($x->input_focus, $third->id, 'third con focused');
122
123 cmd 'kill';
124 wait_for_unmap($third);
125
126 is($x->input_focus, $first->id, 'first con focused after killing all floating cons');
127
128 #############################################################################
129 # 5: see if the 'focus tiling' and 'focus floating' commands work
130 #############################################################################
131
132 $tmp = fresh_workspace;
133
134 $first = open_window({ background_color => '#ff0000' }); # window 8
135 $second = open_window({ background_color => '#00ff00' }); # window 9
136
137 is($x->input_focus, $second->id, 'second container focused');
138
139 cmd 'floating enable';
140
141 is($x->input_focus, $second->id, 'second container focused');
142
143 cmd 'focus tiling';
144
145 is($x->input_focus, $first->id, 'first (tiling) container focused');
146
147 cmd 'focus floating';
148
149 is($x->input_focus, $second->id, 'second (floating) container focused');
150
151 cmd 'focus floating';
152
153 is($x->input_focus, $second->id, 'second (floating) container still focused');
154
155 cmd 'focus mode_toggle';
156
157 is($x->input_focus, $first->id, 'first (tiling) container focused');
158
159 cmd 'focus mode_toggle';
160
161 is($x->input_focus, $second->id, 'second (floating) container focused');
162
163 #############################################################################
164 # 6: see if switching floating focus using the focus left/right command works
165 #############################################################################
166
167 $tmp = fresh_workspace;
168
169 $first = open_floating_window({ background_color => '#ff0000' });# window 10
170 $second = open_floating_window({ background_color => '#00ff00' }); # window 11
171 $third = open_floating_window({ background_color => '#0000ff' }); # window 12
172
173 is($x->input_focus, $third->id, 'third container focused');
174
175 cmd 'focus left';
176
177 is($x->input_focus, $second->id, 'second container focused');
178
179 cmd 'focus left';
180
181 is($x->input_focus, $first->id, 'first container focused');
182
183 cmd 'focus left';
184
185 is($x->input_focus, $third->id, 'focus wrapped to third container');
186
187 cmd 'focus right';
188
189 is($x->input_focus, $first->id, 'focus wrapped to first container');
190
191 cmd 'focus right';
192
193 is($x->input_focus, $second->id, 'focus on second container');
194
195 #############################################################################
196 # 7: verify that focusing the parent of a window inside a floating con goes
197 # up to the grandparent (workspace) and that focusing child from the ws
198 # goes back down to the child of the floating con
199 #############################################################################
200
201 $tmp = fresh_workspace;
202
203 my $tiled = open_window;
204 my $floating = open_floating_window;
205 is($x->input_focus, $floating->id, 'floating window focused');
206
207 cmd 'focus parent';
208
209 is(get_ws($tmp)->{focused}, 1, 'workspace is focused');
210 cmd 'focus child';
211
212 is($x->input_focus, $floating->id, 'floating window focused');
213
214 #############################################################################
215 # 8: verify that focusing a floating window raises it to the top.
216 # This test can't verify that the floating container is visually on top, just
217 # that it is placed on the tail of the floating_head.
218 # See issue: 2572
219 #############################################################################
220
221 $tmp = fresh_workspace;
222
223 $first = open_floating_window;
224 $second = open_floating_window;
225
226 is($x->input_focus, $second->id, 'second floating window focused');
227 my $ws = get_ws($tmp);
228 is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $second->id, 'second on top');
229 is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first->id, 'first behind');
230
231 cmd '[id=' . $first->id . '] focus';
232
233 is($x->input_focus, $first->id, 'first floating window focused');
234 $ws = get_ws($tmp);
235 is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $first->id, 'first on top');
236 is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $second->id, 'second behind');
237
238 #############################################################################
239 # 9: verify that disabling / enabling floating for a window from a different
240 # workspace maintains the correct focus order.
241 #############################################################################
242
243 sub open_window_helper {
244 my $floating = shift if @_;
245 if ($floating){
246 return open_floating_window;
247 }
248 else {
249 return open_window;
250 }
251 }
252
253 for my $floating (0, 1){
254 $tmp = fresh_workspace;
255 $first = open_window;
256 $second = open_window_helper($floating);
257 is($x->input_focus, $second->id, "second window focused");
258
259 fresh_workspace;
260 cmd "[id=" . $second->id . "] floating toggle";
261 cmd "workspace $tmp";
262 sync_with_i3;
263
264 my $workspace = get_ws($tmp);
265 is($workspace->{floating_nodes}->[0]->{nodes}->[0]->{window}, $second->id, 'second window on first workspace, floating') unless $floating;
266 is($workspace->{nodes}->[1]->{window}, $second->id, 'second window on first workspace, right') unless !$floating;
267 is($x->input_focus, $second->id, 'second window still focused');
268 }
269
270 #############################################################################
271 # 10: verify that toggling floating for an unfocused window on another
272 # workspace doesn't make it focused.
273 #############################################################################
274
275 for my $floating (0, 1){
276 $tmp = fresh_workspace;
277 $first = open_window_helper($floating);
278 $second = open_window;
279 is($x->input_focus, $second->id, 'second (tiling) window focused');
280
281 fresh_workspace;
282 cmd "[id=" . $first->id . "] floating toggle";
283 cmd "workspace $tmp";
284 sync_with_i3;
285
286 my $workspace = get_ws($tmp);
287 is($workspace->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first->id, 'first window on first workspace, floating') unless $floating;
288 is($workspace->{nodes}->[1]->{window}, $first->id, 'first window on first workspace, right') unless !$floating;
289 is($x->input_focus, $second->id, 'second window still focused');
290 }
291
292 #############################################################################
293 # 11: verify that toggling floating for a focused window on another workspace
294 # which has another, unfocused floating window maintains the focus of the
295 # first window.
296 #############################################################################
297 for my $floating (0, 1){
298 $tmp = fresh_workspace;
299 $first = open_window;
300 $second = open_floating_window;
301 is($x->input_focus, $second->id, 'second (floating) window focused');
302 $third = open_window_helper($floating);
303 is($x->input_focus, $third->id, "third (floating = $floating) window focused");
304
305 fresh_workspace;
306 cmd "[id=" . $third->id . "] floating toggle";
307 cmd "workspace $tmp";
308 sync_with_i3;
309
310 my $workspace = get_ws($tmp);
311 is($workspace->{floating_nodes}->[0]->{nodes}->[0]->{window}, $third->id, 'third window on first workspace, floating') unless $floating;
312 is($workspace->{nodes}->[1]->{window}, $third->id, 'third window on first workspace, right') unless !$floating;
313 is($x->input_focus, $third->id, 'third window still focused');
314 }
315
316 #############################################################################
317 # 12: verify that toggling floating for an unfocused window on another
318 # workspace which has another, focused floating window doesn't change focus.
319 #############################################################################
320
321 for my $floating (0, 1){
322 $tmp = fresh_workspace;
323 $first = open_window;
324 $second = open_window_helper($floating);
325 is($x->input_focus, $second->id, "second (floating = $floating) window focused");
326 $third = open_floating_window;
327 is($x->input_focus, $third->id, 'third (floating) window focused');
328
329 fresh_workspace;
330 cmd "[id=" . $second->id . "] floating toggle";
331 cmd "workspace $tmp";
332 sync_with_i3;
333
334 my $workspace = get_ws($tmp);
335 is($workspace->{floating_nodes}->[0]->{nodes}->[0]->{window}, $second->id, 'second window on first workspace, floating') unless $floating;
336 is($workspace->{nodes}->[1]->{window}, $second->id, 'second window on first workspace, right') unless !$floating;
337 is($x->input_focus, $third->id, 'third window still focused');
338 }
339
340 #############################################################################
341 # 13: For layout [H1 [A V1[ B F ] ] ] verify that toggling F's floating
342 # mode maintains its focus.
343 #############################################################################
344
345 for my $floating (0, 1){
346 $tmp = fresh_workspace;
347 $first = open_window;
348 $second = open_window;
349 cmd "split v";
350 sync_with_i3;
351 is($x->input_focus, $second->id, "second (floating = $floating) window focused");
352 $third = open_window_helper($floating);
353 is($x->input_focus, $third->id, 'third (floating) window focused');
354
355 fresh_workspace;
356 cmd "[id=" . $third->id . "] floating toggle";
357 cmd "workspace $tmp";
358 sync_with_i3;
359
360 my $workspace = get_ws($tmp);
361 is($workspace->{floating_nodes}->[0]->{nodes}->[0]->{window}, $third->id, 'third window on first workspace, floating') unless $floating;
362 is($workspace->{nodes}->[1]->{nodes}->[1]->{window}, $third->id, 'third window on first workspace') unless !$floating;
363 is($x->input_focus, $third->id, 'third window still focused');
364 }
365
366 #############################################################################
367 # 14: For layout [H1 [A V1[ H2 [B H2 [ C V2 [ F D ] ] ] ] ] ] verify that
368 # toggling F's floating mode maintains its focus.
369 #############################################################################
370
371 sub kill_and_confirm_focus {
372 my $focus = shift;
373 my $msg = shift;
374 cmd "kill";
375 sync_with_i3;
376 is($x->input_focus, $focus, $msg);
377 }
378
379 $tmp = fresh_workspace;
380 my $A = open_window;
381 my $B = open_window;
382 cmd "split v";
383 my $C = open_window;
384 cmd "split h";
385 my $F = open_window;
386 cmd "split v";
387 my $D = open_window;
388 is($x->input_focus, $D->id, "D is focused");
389
390 sync_with_i3;
391 my $workspace = get_ws($tmp);
392 is($workspace->{nodes}->[1]->{nodes}->[1]->{nodes}->[1]->{nodes}->[0]->{window}, $F->id, 'F opened in its expected position');
393
394 fresh_workspace;
395 cmd "[id=" . $F->id . "] floating enable";
396 cmd "workspace $tmp";
397 sync_with_i3;
398
399 $workspace = get_ws($tmp);
400 is($workspace->{floating_nodes}->[0]->{nodes}->[0]->{window}, $F->id, 'F on first workspace, floating');
401 is($workspace->{nodes}->[1]->{nodes}->[1]->{nodes}->[1]->{nodes}->[0]->{window}, $D->id, 'D where F used to be');
402 is($x->input_focus, $D->id, 'D still focused');
403
404 fresh_workspace;
405 cmd "[id=" . $F->id . "] floating disable";
406 cmd "workspace $tmp";
407 sync_with_i3;
408
409 $workspace = get_ws($tmp);
410 is($workspace->{nodes}->[1]->{nodes}->[1]->{nodes}->[1]->{nodes}->[1]->{window}, $F->id, 'F where D used to be');
411 is($x->input_focus, $D->id, 'D still focused');
412
413 kill_and_confirm_focus($F->id, 'F focused after D is killed');
414 kill_and_confirm_focus($C->id, 'C focused after F is killed');
415 kill_and_confirm_focus($B->id, 'B focused after C is killed');
416 kill_and_confirm_focus($A->id, 'A focused after B is killed');
417
418 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: when only having a floating window on a workspace, it should
17 # not be deleted.
18
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22
23 my $tmp = fresh_workspace;
24
25 #############################################################################
26 # 1: open a floating window, get it mapped
27 #############################################################################
28
29 ok(workspace_exists($tmp), "workspace $tmp exists");
30
31 # Create a floating window which is smaller than the minimum enforced size of i3
32 my $window = open_floating_window;
33 ok($window->mapped, 'Window is mapped');
34
35 # switch to a different workspace, see if the window is still mapped?
36
37 my $otmp = fresh_workspace;
38
39 ok(workspace_exists($otmp), "new workspace $otmp exists");
40 ok(workspace_exists($tmp), "old workspace $tmp still exists");
41
42 ################################################################################
43 # 2: Similar test: Have two floating windows on a workspace, close one of them.
44 # The workspace should not be closed. Regression present until (including) commit
45 # 1f2c9306a27cced83ad960e929bb9e9a163b7843
46 ################################################################################
47
48 $tmp = fresh_workspace;
49
50 ok(workspace_exists($tmp), "workspace $tmp exists");
51
52 # Create a floating window which is smaller than the minimum enforced size of i3
53 my $first = open_floating_window;
54 my $second = open_floating_window;
55 ok($first->mapped, 'Window is mapped');
56 ok($second->mapped, 'Window is mapped');
57
58 $otmp = fresh_workspace;
59
60 ok(workspace_exists($otmp), "new workspace $otmp exists");
61 ok(workspace_exists($tmp), "old workspace $tmp still exists");
62
63 $first->unmap;
64 wait_for_unmap $first;
65
66 ok(workspace_exists($otmp), "new workspace $otmp exists");
67 ok(workspace_exists($tmp), "old workspace $tmp still exists");
68
69 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: Floating windows were not correctly unmapped when switching
17 # to a different workspace.
18
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22
23 my $tmp = fresh_workspace;
24
25 #############################################################################
26 # 1: open a floating window, get it mapped
27 #############################################################################
28
29 # Create a floating window which is smaller than the minimum enforced size of i3
30 my $window = open_floating_window;
31 ok($window->mapped, 'Window is mapped');
32
33 # switch to a different workspace, see if the window is still mapped?
34
35 my $otmp = fresh_workspace;
36
37 sync_with_i3;
38
39 ok(!$window->mapped, 'Window is not mapped after switching ws');
40
41 cmd "nop testcase done";
42
43 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: New windows were attached to the container of a floating window
17 # if only a floating window is present on the workspace.
18
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22
23 my $tmp = fresh_workspace;
24
25 #############################################################################
26 # 1: open a floating window, get it mapped
27 #############################################################################
28
29 # Create a floating window
30 my $window = open_floating_window;
31 ok($window->mapped, 'Window is mapped');
32
33 my $ws = get_ws($tmp);
34 my ($nodes, $focus) = get_ws_content($tmp);
35
36 is(@{$ws->{floating_nodes}}, 1, 'one floating node');
37 is(@{$nodes}, 0, 'no tiling nodes');
38
39 # Create a tiling window
40 my $twindow = open_window;
41
42 ($nodes, $focus) = get_ws_content($tmp);
43
44 is(@{$nodes}, 1, 'one tiling node');
45
46 #############################################################################
47 # 2: similar case: floating windows should be attached at the currently focused
48 # position in the workspace (for example a stack), not just at workspace level.
49 #############################################################################
50
51 $tmp = fresh_workspace;
52
53 my $first = open_window;
54 my $second = open_window;
55
56 cmd 'layout stacked';
57
58 $ws = get_ws($tmp);
59 is(@{$ws->{floating_nodes}}, 0, 'no floating nodes so far');
60 is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
61
62 # Create a floating window
63 $window = open_floating_window;
64 ok($window->mapped, 'Window is mapped');
65
66 $ws = get_ws($tmp);
67 is(@{$ws->{floating_nodes}}, 1, 'one floating nodes');
68 is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
69
70 my $third = open_window;
71
72
73 $ws = get_ws($tmp);
74 is(@{$ws->{floating_nodes}}, 1, 'one floating nodes');
75 is(@{$ws->{nodes}}, 1, 'one tiling node (stacked con)');
76
77 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Check if numbered workspaces and named workspaces are sorted in the right way
17 # in get_workspaces IPC output (necessary for i3bar etc.).
18 use i3test;
19
20 my $i3 = i3(get_socket_path());
21
22 sub check_order {
23 my ($msg) = @_;
24
25 my @ws = @{$i3->get_workspaces->recv};
26 my @nums = map { $_->{num} } grep { $_->{num} != -1 } @ws;
27 my @sorted = sort @nums;
28
29 is_deeply(\@nums, \@sorted, $msg);
30 }
31
32 check_order('workspace order alright before testing');
33
34 #############################################################################
35 # open a window to keep this ws open
36 #############################################################################
37
38 cmd "workspace 93";
39
40 open_window;
41
42 my @ws = @{$i3->get_workspaces->recv};
43 my @f = grep { defined($_->{num}) && $_->{num} == 93 } @ws;
44 is(@f, 1, 'ws 93 found by num');
45 check_order('workspace order alright after opening 93');
46
47 cmd "workspace 92";
48 open_window;
49 check_order('workspace order alright after opening 92');
50
51 cmd "workspace 94";
52 open_window;
53 check_order('workspace order alright after opening 94');
54
55 cmd "workspace 96";
56 open_window;
57 check_order('workspace order alright after opening 96');
58
59 cmd "workspace foo";
60 open_window;
61 check_order('workspace order alright after opening foo');
62
63 cmd "workspace 91";
64 open_window;
65 check_order('workspace order alright after opening 91');
66
67 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: Check if the focus stays the same when switching the layout
17 # bug introduced by 77d0d42ed2d7ac8cafe267c92b35a81c1b9491eb
18 use i3test;
19
20 my $i3 = i3(get_socket_path());
21
22 sub check_order {
23 my ($msg) = @_;
24
25 my @ws = @{$i3->get_workspaces->recv};
26 my @nums = map { $_->{num} } grep { defined($_->{num}) } @ws;
27 my @sorted = sort @nums;
28
29 is_deeply(\@nums, \@sorted, $msg);
30 }
31
32 my $tmp = fresh_workspace;
33
34 my $left = open_window;
35 my $mid = open_window;
36 my $right = open_window;
37
38 diag("left = " . $left->id . ", mid = " . $mid->id . ", right = " . $right->id);
39
40 is($x->input_focus, $right->id, 'Right window focused');
41
42 cmd 'focus left';
43
44 is($x->input_focus, $mid->id, 'Mid window focused');
45
46 cmd 'layout stacked';
47
48 is($x->input_focus, $mid->id, 'Mid window focused');
49
50 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests resizing tiling containers
17 use i3test;
18
19 my ($left, $right);
20 my $tmp = fresh_workspace;
21
22 cmd 'split v';
23
24 my $top = open_window;
25 my $bottom = open_window;
26
27 diag("top = " . $top->id . ", bottom = " . $bottom->id);
28
29 is($x->input_focus, $bottom->id, 'Bottom window focused');
30
31 ############################################################
32 # resize
33 ############################################################
34
35 cmd 'resize grow up 10 px or 25 ppt';
36
37 my ($nodes, $focus) = get_ws_content($tmp);
38
39 cmp_float($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
40 cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
41
42
43 ############################################################
44 # split and check if the 'percent' factor is still correct
45 ############################################################
46
47 cmd 'split h';
48
49 ($nodes, $focus) = get_ws_content($tmp);
50
51 cmp_float($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
52 cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
53
54 ############################################################
55 # checks that resizing within stacked/tabbed cons works
56 ############################################################
57
58 $tmp = fresh_workspace;
59
60 cmd 'split v';
61
62 $top = open_window;
63 $bottom = open_window;
64
65 cmd 'split h';
66 cmd 'layout stacked';
67
68 ($nodes, $focus) = get_ws_content($tmp);
69 cmp_float($nodes->[0]->{percent}, 0.5, 'top window got 50%');
70 cmp_float($nodes->[1]->{percent}, 0.5, 'bottom window got 50%');
71
72 cmd 'resize grow up 10 px or 25 ppt';
73
74 ($nodes, $focus) = get_ws_content($tmp);
75 cmp_float($nodes->[0]->{percent}, 0.25, 'top window got 25%');
76 cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
77
78 ############################################################
79 # Checks that resizing in the parent's parent's orientation works.
80 # Take for example a horizontal workspace with one window on the left side and
81 # a v-split container with two windows on the right side. Focus is on the
82 # bottom right window, use 'resize left'.
83 ############################################################
84
85 $tmp = fresh_workspace;
86
87 $left = open_window;
88 $right = open_window;
89
90 cmd 'split v';
91
92 $top = open_window;
93 $bottom = open_window;
94
95 ($nodes, $focus) = get_ws_content($tmp);
96 cmp_float($nodes->[0]->{percent}, 0.5, 'left window got 50%');
97 cmp_float($nodes->[1]->{percent}, 0.5, 'right window got 50%');
98
99 cmd 'resize grow left 10 px or 25 ppt';
100
101 ($nodes, $focus) = get_ws_content($tmp);
102 cmp_float($nodes->[0]->{percent}, 0.25, 'left window got 25%');
103 cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%');
104
105 ################################################################################
106 # Check that the resize grow/shrink width/height syntax works.
107 ################################################################################
108
109 # Use two windows
110 $tmp = fresh_workspace;
111
112 $left = open_window;
113 $right = open_window;
114
115 cmd 'resize grow width 10 px or 25 ppt';
116
117 ($nodes, $focus) = get_ws_content($tmp);
118 cmp_float($nodes->[0]->{percent}, 0.25, 'left window got 25%');
119 cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%');
120
121 # Now test it with four windows
122 $tmp = fresh_workspace;
123
124 open_window for (1..4);
125
126 cmd 'resize grow width 10 px or 25 ppt';
127
128 ($nodes, $focus) = get_ws_content($tmp);
129 cmp_float($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%');
130 cmp_float($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
131 cmp_float($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
132 cmp_float($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
133
134 # height should be a no-op in this situation
135 cmd 'resize grow height 10 px or 25 ppt';
136
137 ($nodes, $focus) = get_ws_content($tmp);
138 cmp_float($nodes->[0]->{percent}, 0.166666666666667, 'first window got 16%');
139 cmp_float($nodes->[1]->{percent}, 0.166666666666667, 'second window got 16%');
140 cmp_float($nodes->[2]->{percent}, 0.166666666666667, 'third window got 16%');
141 cmp_float($nodes->[3]->{percent}, 0.50, 'fourth window got 50%');
142
143 ################################################################################
144 # Same but using pixels instead of ppt.
145 ################################################################################
146
147 # Use two windows
148 $tmp = fresh_workspace;
149
150 $left = open_window;
151 $right = open_window;
152
153 ($nodes, $focus) = get_ws_content($tmp);
154 my @widths = ($nodes->[0]->{rect}->{width}, $nodes->[1]->{rect}->{width});
155
156 cmd 'resize grow width 10 px';
157
158 ($nodes, $focus) = get_ws_content($tmp);
159 cmp_float($nodes->[0]->{rect}->{width}, $widths[0] - 10, 'left window is 10px smaller');
160 cmp_float($nodes->[1]->{rect}->{width}, $widths[1] + 10, 'right window is 10px larger');
161
162 # Now test it with four windows
163 $tmp = fresh_workspace;
164
165 open_window for (1..4);
166
167 ($nodes, $focus) = get_ws_content($tmp);
168 my $width = $nodes->[0]->{rect}->{width};
169
170 cmd 'resize grow width 10 px';
171
172 ($nodes, $focus) = get_ws_content($tmp);
173 cmp_float($nodes->[3]->{rect}->{width}, $width + 10, 'last window is 10px larger');
174
175 ################################################################################
176 # Same but for height
177 ################################################################################
178
179 # Use two windows
180 $tmp = fresh_workspace;
181 cmd 'split v';
182
183 $left = open_window;
184 $right = open_window;
185
186 ($nodes, $focus) = get_ws_content($tmp);
187 my @heights = ($nodes->[0]->{rect}->{height}, $nodes->[1]->{rect}->{height});
188
189 cmd 'resize grow height 10 px';
190
191 ($nodes, $focus) = get_ws_content($tmp);
192 cmp_float($nodes->[0]->{rect}->{height}, $heights[0] - 10, 'left window is 10px smaller');
193 cmp_float($nodes->[1]->{rect}->{height}, $heights[1] + 10, 'right window is 10px larger');
194
195 # Now test it with four windows
196 $tmp = fresh_workspace;
197 cmd 'split v';
198
199 open_window for (1..4);
200
201 ($nodes, $focus) = get_ws_content($tmp);
202 my $height = $nodes->[0]->{rect}->{height};
203
204 cmd 'resize grow height 10 px';
205
206 ($nodes, $focus) = get_ws_content($tmp);
207 cmp_float($nodes->[3]->{rect}->{height}, $height + 10, 'last window is 10px larger');
208
209 ################################################################################
210 # Check that we can grow tiled windows by pixels
211 ################################################################################
212
213 $tmp = fresh_workspace;
214
215 $left = open_window;
216 $right = open_window;
217
218 ($nodes, $focus) = get_ws_content($tmp);
219 cmp_float($nodes->[0]->{rect}->{width}, 640, 'left window is 640px');
220 cmp_float($nodes->[1]->{rect}->{width}, 640, 'right window is 640px');
221
222 cmd 'resize grow left 10px';
223 ($nodes, $focus) = get_ws_content($tmp);
224 cmp_float($nodes->[0]->{rect}->{width}, 630, 'left window is 630px');
225 cmp_float($nodes->[1]->{rect}->{width}, 650, 'right window is 650px');
226
227 ################################################################################
228 # Check that we can shrink tiled windows by pixels
229 ################################################################################
230
231 $tmp = fresh_workspace;
232
233 $left = open_window;
234 $right = open_window;
235
236 ($nodes, $focus) = get_ws_content($tmp);
237 cmp_float($nodes->[0]->{rect}->{width}, 640, 'left window is 640px');
238 cmp_float($nodes->[1]->{rect}->{width}, 640, 'right window is 640px');
239
240 cmd 'resize shrink left 10px';
241 ($nodes, $focus) = get_ws_content($tmp);
242 cmp_float($nodes->[0]->{rect}->{width}, 650, 'left window is 650px');
243 cmp_float($nodes->[1]->{rect}->{width}, 630, 'right window is 630px');
244
245
246 ################################################################################
247 # Check that we can shrink vertical tiled windows by pixels
248 ################################################################################
249
250 $tmp = fresh_workspace;
251
252 cmd 'split v';
253
254 $top = open_window;
255 $bottom = open_window;
256
257 ($nodes, $focus) = get_ws_content($tmp);
258 my @heights = ($nodes->[0]->{rect}->{height}, $nodes->[1]->{rect}->{height});
259
260 cmd 'resize grow up 10px';
261 ($nodes, $focus) = get_ws_content($tmp);
262 cmp_float($nodes->[0]->{rect}->{height}, $heights[0] - 10, 'top window is 10px larger');
263 cmp_float($nodes->[1]->{rect}->{height}, $heights[1] + 10, 'bottom window is 10px smaller');
264
265 ################################################################################
266 # Check that we can shrink vertical tiled windows by pixels
267 ################################################################################
268
269 $tmp = fresh_workspace;
270
271 cmd 'split v';
272
273 $top = open_window;
274 $bottom = open_window;
275
276 ($nodes, $focus) = get_ws_content($tmp);
277 my @heights = ($nodes->[0]->{rect}->{height}, $nodes->[1]->{rect}->{height});
278
279 cmd 'resize shrink up 10px';
280 ($nodes, $focus) = get_ws_content($tmp);
281 cmp_float($nodes->[0]->{rect}->{height}, $heights[0] + 10, 'top window is 10px smaller');
282 cmp_float($nodes->[1]->{rect}->{height}, $heights[1] - 10, 'bottom window is 10px larger');
283
284 ################################################################################
285 # Check that the resize grow/shrink width/height syntax works if a nested split
286 # was set on the container, but no sibling has been opened yet. See #2015.
287 ################################################################################
288
289 $tmp = fresh_workspace;
290 $left = open_window;
291 $right = open_window;
292
293 cmd 'split h';
294 cmd 'resize grow width 10px or 25 ppt';
295
296 ($nodes, $focus) = get_ws_content($tmp);
297 cmp_float($nodes->[0]->{percent}, 0.25, 'left window got 25%');
298 cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%');
299
300 ############################################################
301 # checks that resizing floating windows works
302 ############################################################
303
304 $tmp = fresh_workspace;
305
306 $top = open_window;
307
308 cmd 'floating enable';
309
310 my @content = @{get_ws($tmp)->{floating_nodes}};
311 cmp_ok(@content, '==', 1, 'one floating node on this ws');
312
313 # up
314 my $oldrect = $content[0]->{rect};
315
316 cmd 'resize grow up 10 px or 25 ppt';
317
318 @content = @{get_ws($tmp)->{floating_nodes}};
319 cmp_ok($content[0]->{rect}->{y}, '<', $oldrect->{y}, 'y smaller than before');
320 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y} - 10, 'y exactly 10 px smaller');
321 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x untouched');
322 cmp_ok($content[0]->{rect}->{height}, '>', $oldrect->{height}, 'height bigger than before');
323 cmp_ok($content[0]->{rect}->{height}, '==', $oldrect->{height} + 10, 'height exactly 10 px higher');
324 cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'x untouched');
325
326 # up, but with a different amount of px
327 $oldrect = $content[0]->{rect};
328
329 cmd 'resize grow up 12 px or 25 ppt';
330
331 @content = @{get_ws($tmp)->{floating_nodes}};
332 cmp_ok($content[0]->{rect}->{y}, '<', $oldrect->{y}, 'y smaller than before');
333 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y} - 12, 'y exactly 10 px smaller');
334 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x untouched');
335 cmp_ok($content[0]->{rect}->{height}, '>', $oldrect->{height}, 'height bigger than before');
336 cmp_ok($content[0]->{rect}->{height}, '==', $oldrect->{height} + 12, 'height exactly 10 px higher');
337 cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'x untouched');
338
339 # left
340 $oldrect = $content[0]->{rect};
341
342 cmd 'resize grow left 10 px or 25 ppt';
343
344 @content = @{get_ws($tmp)->{floating_nodes}};
345 cmp_ok($content[0]->{rect}->{x}, '<', $oldrect->{x}, 'x smaller than before');
346 cmp_ok($content[0]->{rect}->{width}, '>', $oldrect->{width}, 'width bigger than before');
347
348 # right
349 $oldrect = $content[0]->{rect};
350
351 cmd 'resize grow right 10 px or 25 ppt';
352
353 @content = @{get_ws($tmp)->{floating_nodes}};
354 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x the same as before');
355 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y the same as before');
356 cmp_ok($content[0]->{rect}->{width}, '>', $oldrect->{width}, 'width bigger than before');
357 cmp_ok($content[0]->{rect}->{height}, '==', $oldrect->{height}, 'height the same as before');
358
359 # down
360 $oldrect = $content[0]->{rect};
361
362 cmd 'resize grow down 10 px or 25 ppt';
363
364 @content = @{get_ws($tmp)->{floating_nodes}};
365 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x the same as before');
366 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y the same as before');
367 cmp_ok($content[0]->{rect}->{height}, '>', $oldrect->{height}, 'height bigger than before');
368 cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'width the same as before');
369
370 # grow width
371 $oldrect = $content[0]->{rect};
372
373 cmd 'resize grow width 10px or 10ppt';
374
375 @content = @{get_ws($tmp)->{floating_nodes}};
376 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x the same as before');
377 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y the same as before');
378 cmp_ok($content[0]->{rect}->{height}, '==', $oldrect->{height}, 'height the same as before');
379 cmp_ok($content[0]->{rect}->{width}, '>', $oldrect->{width}, 'width bigger than before');
380
381 # shrink width
382 $oldrect = $content[0]->{rect};
383
384 cmd 'resize shrink width 10px or 10ppt';
385
386 @content = @{get_ws($tmp)->{floating_nodes}};
387 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x the same as before');
388 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y the same as before');
389 cmp_ok($content[0]->{rect}->{height}, '==', $oldrect->{height}, 'height the same as before');
390 cmp_ok($content[0]->{rect}->{width}, '<', $oldrect->{width}, 'width smaller than before');
391
392 # grow height
393 $oldrect = $content[0]->{rect};
394
395 cmd 'resize grow height 10px or 10ppt';
396
397 @content = @{get_ws($tmp)->{floating_nodes}};
398 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x the same as before');
399 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y the same as before');
400 cmp_ok($content[0]->{rect}->{height}, '>', $oldrect->{height}, 'height bigger than before');
401 cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'width the same as before');
402
403 # shrink height
404 $oldrect = $content[0]->{rect};
405
406 cmd 'resize shrink height 10px or 10ppt';
407
408 @content = @{get_ws($tmp)->{floating_nodes}};
409 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x the same as before');
410 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y the same as before');
411 cmp_ok($content[0]->{rect}->{height}, '<', $oldrect->{height}, 'height smaller than before');
412 cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'width the same as before');
413
414 ################################################################################
415 # Check that resizing with criteria works
416 ################################################################################
417
418 $tmp = fresh_workspace;
419
420 $left = open_floating_window;
421 $right = open_floating_window;
422
423 sub get_floating_rect {
424 my ($window_id) = @_;
425
426 my $floating_nodes = get_ws($tmp)->{floating_nodes};
427 for my $floating_node (@$floating_nodes) {
428 # Get all the windows within that floating container
429 my @window_ids = map { $_->{window} } @{$floating_node->{nodes}};
430 if ($window_id ~~ @window_ids) {
431 return $floating_node->{rect};
432 }
433 }
434
435 return undef;
436 }
437
438 # focus is on the right window, so we resize the left one using criteria
439 my $leftold = get_floating_rect($left->id);
440 my $rightold = get_floating_rect($right->id);
441 cmd '[id="' . $left->id . '"] resize grow height 10px or 10ppt';
442
443 my $leftnew = get_floating_rect($left->id);
444 my $rightnew = get_floating_rect($right->id);
445 is($rightnew->{height}, $rightold->{height}, 'height of right container unchanged');
446 is($leftnew->{height}, $leftold->{height} + 10, 'height of left container changed');
447
448 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: move a floating window to a different workspace crashes i3
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21 my $otmp = get_unused_workspace();
22
23 cmd 'open';
24 cmd 'mode toggle';
25 cmd "move workspace $otmp";
26
27 does_i3_live;
28
29 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: floating windows are tiling after restarting, closing them crashes i3
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 cmd 'open';
23 cmd 'floating toggle';
24
25 my $ws = get_ws($tmp);
26 is(scalar @{$ws->{nodes}}, 0, 'no tiling nodes');
27 is(scalar @{$ws->{floating_nodes}}, 1, 'precisely one floating node');
28
29 cmd 'restart';
30
31 diag('Checking if i3 still lives');
32
33 does_i3_live;
34
35 $ws = get_ws($tmp);
36 is(scalar @{$ws->{nodes}}, 0, 'no tiling nodes');
37 is(scalar @{$ws->{floating_nodes}}, 1, 'precisely one floating node');
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: when resizing two containers on a workspace, opening a floating
17 # client, then closing it again, i3 will re-distribute the space on the
18 # workspace as if a tiling container was closed, leading to the containers
19 # taking much more space than they possibly could.
20 #
21 use i3test;
22 use List::Util qw(sum);
23
24 my $tmp = fresh_workspace;
25
26 my $first = open_window;
27 my $second = open_window;
28
29 my ($nodes, $focus) = get_ws_content($tmp);
30 my $old_sum = sum map { $_->{rect}->{width} } @{$nodes};
31
32 cmd 'resize grow left 10 px or 25 ppt';
33 cmd 'split v';
34
35 sync_with_i3;
36
37 my $third = open_window;
38
39 cmd 'mode toggle';
40 sync_with_i3;
41
42 cmd 'kill';
43 sync_with_i3;
44
45 ($nodes, $focus) = get_ws_content($tmp);
46 my $new_sum = sum map { $_->{rect}->{width} } @{$nodes};
47
48 is($old_sum, $new_sum, 'combined container width is still equal');
49
50 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # by moving the window in the opposite orientation that its parent has, we
17 # force i3 to create a new split container with the appropriate orientation.
18 # However, when doing that two times in a row, we end up with two split
19 # containers which are then redundant (workspace is horizontal, then v-split,
20 # then h-split – we could just append the children of the latest h-split to the
21 # workspace itself).
22 #
23 # This testcase checks that the tree is properly flattened after moving.
24 #
25 use i3test;
26
27 my $tmp = fresh_workspace;
28
29 my $left = open_window;
30 my $mid = open_window;
31 my $right = open_window;
32
33 cmd 'move up';
34 cmd 'move right';
35 my $ws = get_ws($tmp);
36
37 is($ws->{layout}, 'splith', 'workspace layout is splith');
38 is(@{$ws->{nodes}}, 3, 'all three windows on workspace level');
39
40 ################################################################################
41 # Ticket #1053 provides a sequence of operations where the flattening does not
42 # work correctly:
43 ################################################################################
44
45 $tmp = fresh_workspace;
46
47 my $tab1 = open_window;
48 my $tab2 = open_window;
49 $mid = open_window;
50 $right = open_window;
51 cmd 'focus right';
52 cmd 'split v';
53 cmd 'focus right';
54 cmd 'move left';
55 cmd 'layout tabbed';
56 cmd 'focus parent';
57 cmd 'split v';
58
59 $ws = get_ws($tmp);
60 my @nodes = @{$ws->{nodes}};
61 is(@nodes, 3, 'all three windows on workspace level');
62 is($nodes[0]->{layout}, 'splitv', 'first node is splitv');
63 is(@{$nodes[0]->{nodes}}, 1, 'one node in the first node');
64 is($nodes[0]->{nodes}->[0]->{layout}, 'tabbed', 'tabbed layout');
65 is(@{$nodes[0]->{nodes}->[0]->{nodes}}, 2, 'two nodes in that node');
66
67 cmd 'focus right';
68 cmd 'move left';
69
70 $ws = get_ws($tmp);
71 @nodes = @{$ws->{nodes}};
72 is(@nodes, 2, 'all three windows on workspace level');
73
74 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 use i3test;
17
18 my $tmp = fresh_workspace;
19
20 my $left = open_window;
21 my $mid = open_window;
22
23 cmd 'split v';
24 my $bottom = open_window;
25
26 my ($nodes, $focus) = get_ws_content($tmp);
27
28 #############################################################################
29 # 1: open a floating window, get it mapped
30 #############################################################################
31
32 # Create a floating window
33 my $window = open_floating_window;
34 ok($window->mapped, 'Window is mapped');
35
36 ($nodes, $focus) = get_ws_content($tmp);
37 is(@{$nodes->[1]->{nodes}}, 2, 'two windows in split con');
38
39 #############################################################################
40 # 2: make it tiling, see where it ends up
41 #############################################################################
42
43 cmd 'floating toggle';
44
45 ($nodes, $focus) = get_ws_content($tmp);
46
47 is(@{$nodes->[1]->{nodes}}, 3, 'three windows in split con after floating toggle');
48
49 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for moving a con outside of a floating con when there are no
17 # tiling cons on a workspace
18 #
19 use i3test;
20
21 sub sync_cmd {
22 cmd @_;
23 sync_with_i3;
24 }
25
26 my $tmp = fresh_workspace;
27
28 my $left = open_window;
29 my $mid = open_window;
30 my $right = open_window;
31
32 # go to workspace level
33 sync_cmd 'focus parent';
34
35 # make it floating
36 sync_cmd 'mode toggle';
37
38 # move the con outside the floating con
39 sync_cmd 'move up';
40
41 does_i3_live;
42
43 # move another con outside
44 sync_cmd '[id="' . $mid->id . '"] focus';
45 sync_cmd 'move up';
46
47 does_i3_live;
48
49 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for correct focus behaviour when moving a floating con to
17 # another workspace.
18 #
19 use i3test;
20
21 my $tmp = fresh_workspace;
22
23 # open a tiling window on the first workspace
24 open_window;
25 my $first = get_focused($tmp);
26
27 # on a different ws, open a floating window
28 my $otmp = fresh_workspace;
29 open_window;
30 my $float = get_focused($otmp);
31 cmd 'mode toggle';
32 sync_with_i3;
33
34 # move the floating con to first workspace
35 cmd "move workspace $tmp";
36 sync_with_i3;
37
38 # switch to the first ws and check focus
39 is(get_focused($tmp), $float, 'floating client correctly focused');
40
41 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for inplace restarting with dock clients
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 #####################################################################
23 # verify that there is no dock window yet
24 #####################################################################
25
26 # Children of all dockareas
27 my @docked = get_dock_clients;
28
29 is(@docked, 0, 'no dock clients yet');
30
31 # open a dock client
32
33 my $window = open_window({
34 background_color => '#FF0000',
35 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
36 });
37
38 #####################################################################
39 # check that we can find it in the layout tree at the expected position
40 #####################################################################
41
42 @docked = get_dock_clients;
43 is(@docked, 1, 'one dock client found');
44
45 # verify the height
46 my $docknode = $docked[0];
47
48 is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
49
50 # perform an inplace-restart
51 cmd 'restart';
52
53 does_i3_live;
54
55
56 #####################################################################
57 # check that we can still find the dock client
58 #####################################################################
59
60 @docked = get_dock_clients;
61 is(@docked, 1, 'one dock client found');
62 $docknode = $docked[0];
63
64 is($docknode->{rect}->{height}, 30, 'dock node has unchanged height after restart');
65
66 $window->destroy;
67
68 wait_for_unmap $window;
69
70 @docked = get_dock_clients;
71 is(@docked, 0, 'no dock clients found');
72
73 #####################################################################
74 # create a dock client with a 1px border
75 #####################################################################
76
77 $window = open_window({
78 border => 1,
79 rect => [ 0, 0, 30, 20 ],
80 background_color => '#00FF00',
81 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
82 });
83
84 @docked = get_dock_clients;
85 is(@docked, 1, 'one dock client found');
86 $docknode = $docked[0];
87
88 is($docknode->{rect}->{height}, 20, 'dock node has unchanged height');
89
90 cmd 'restart';
91
92 @docked = get_dock_clients;
93 is(@docked, 1, 'one dock client found');
94 $docknode = $docked[0];
95
96 is($docknode->{rect}->{height}, 20, 'dock node has unchanged height');
97
98
99 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for setting a window to floating, tiling and opening a new window
17 #
18 use i3test;
19
20 fresh_workspace;
21
22 cmd 'open';
23 cmd 'mode toggle';
24 cmd 'mode toggle';
25 cmd 'open';
26
27 does_i3_live;
28
29 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for using level-up to get to the 'content'-container and
17 # toggle floating
18 #
19 use i3test;
20
21 fresh_workspace;
22
23 cmd 'open';
24 cmd 'focus parent';
25 cmd 'focus parent';
26 cmd 'mode toggle';
27
28 does_i3_live;
29
30 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test if the requested width/height is set after making the window floating.
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 # Create a floating window which is smaller than the minimum enforced size of i3
23 my $window = open_window({ rect => [ 0, 0, 400, 150 ] });
24
25 my ($absolute, $top) = $window->rect;
26
27 ok($window->mapped, 'Window is mapped');
28 cmp_ok($absolute->{width}, '>', 400, 'i3 raised the width');
29 cmp_ok($absolute->{height}, '>', 150, 'i3 raised the height');
30
31 cmd 'floating toggle';
32 sync_with_i3;
33
34 ($absolute, $top) = $window->rect;
35
36 diag('new width: ' . $absolute->{width});
37 diag('new height: ' . $absolute->{height});
38
39 # we compare with a tolerance of ± 20 pixels for borders in each direction
40 # (overkill, but hey)
41 cmp_ok($absolute->{width}, '>', 400-20, 'width now > 380');
42 cmp_ok($absolute->{width}, '<', 400+20, 'width now < 420');
43 cmp_ok($absolute->{height}, '>', 150-20, 'height now > 130');
44 cmp_ok($absolute->{height}, '<', 150+20, 'height now < 170');
45
46 #cmp_ok($absolute->{width}, '>=', 75, 'i3 raised the width to 75');
47 #cmp_ok($absolute->{height}, '>=', 50, 'i3 raised the height to 50');
48
49 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for closing one of multiple dock clients
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 #####################################################################
23 # verify that there is no dock window yet
24 #####################################################################
25
26 # Children of all dockareas
27 my @docked = get_dock_clients;
28
29 is(@docked, 0, 'no dock clients yet');
30
31 #####################################################################
32 # open a dock client
33 #####################################################################
34
35 my $first = open_window({
36 background_color => '#FF0000',
37 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
38 });
39
40 #####################################################################
41 # Open a second dock client
42 #####################################################################
43
44 my $second = open_window({
45 background_color => '#FF0000',
46 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
47 });
48
49 #####################################################################
50 # Kill the second dock client
51 #####################################################################
52 cmd "nop destroying dock client";
53 $second->destroy;
54
55 #####################################################################
56 # Now issue a focus command
57 #####################################################################
58 cmd 'focus right';
59
60 does_i3_live;
61
62 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test to see if i3 combines the geometry of all children in a split container
17 # when setting the split container to floating
18 #
19 use i3test;
20
21 my $tmp = fresh_workspace;
22
23 open_window;
24 cmd 'split v';
25
26 #####################################################################
27 # open a window with 200x80
28 #####################################################################
29
30 my $first = open_window({
31 rect => [ 0, 0, 200, 80],
32 background_color => '#FF0000',
33 });
34
35 cmd 'split h';
36
37 #####################################################################
38 # Open a second window with 300x90
39 #####################################################################
40
41 my $second = open_window({
42 rect => [ 0, 0, 300, 90],
43 background_color => '#00FF00',
44 });
45
46 #####################################################################
47 # Set the parent to floating
48 #####################################################################
49 cmd 'nop setting floating';
50 cmd 'focus parent';
51 cmd 'floating enable';
52
53 #####################################################################
54 # Get geometry of the first floating node (the split container)
55 #####################################################################
56
57 my @nodes = @{get_ws($tmp)->{floating_nodes}};
58 my $rect = $nodes[0]->{rect};
59
60 # we compare the width with ± 20 pixels for borders
61 cmp_ok($rect->{width}, '>', 500-20, 'width now > 480');
62 cmp_ok($rect->{width}, '<', 500+20, 'width now < 520');
63 # we compare the height with ± 40 pixels for decorations
64 cmp_ok($rect->{height}, '>', 90-40, 'width now > 50');
65 cmp_ok($rect->{height}, '<', 90+40, 'width now < 130');
66
67 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test if new containers get focused when there is a fullscreen container at
17 # the time of launching the new one. Also make sure that focusing containers
18 # in other workspaces work even when there is a fullscreen container.
19 #
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25 EOT
26 # Screen setup looks like this:
27 # +----+----+
28 # | S1 | S2 |
29 # +----+----+
30
31 sub verify_focus {
32 # Report original line
33 local $Test::Builder::Level = $Test::Builder::Level + 1;
34
35 my ($expected, $msg) = @_;
36 $expected = $expected->id;
37
38 # Ensure the expected focus if the test fails.
39 cmd "[id=$expected] focus" unless is($x->input_focus, $expected, $msg);
40 }
41
42 ################################################################################
43 # Verify that window opened behind fullscreen window will get focus after the
44 # fullscreen window gets moved to a different workspace.
45 ################################################################################
46
47 fresh_workspace;
48 my $left = open_window;
49 verify_focus($left, 'left window focused');
50 diag("left = " . $left->id);
51
52 my $right = open_window;
53 cmd 'fullscreen';
54 diag("right = " . $right->id);
55
56 # Open a third window. Since we're fullscreen, the window won't be mapped, so
57 # don't wait for it to be mapped. Instead, just send the map request and sync
58 # with i3 to make sure i3 recognizes it.
59 my $third = open_window({dont_map => 1});
60 $third->map;
61 sync_with_i3;
62 diag("third = " . $third->id);
63
64 # Move the window to a different workspace, and verify that the third window now
65 # gets focused in the current workspace.
66 my $tmp2 = get_unused_workspace;
67 cmd "move workspace $tmp2";
68 verify_focus($third, 'third window focused');
69
70 kill_all_windows;
71
72 ################################################################################
73 # Ensure that moving a window to a workspace which has a fullscreen window does
74 # not focus it (otherwise the user cannot get out of fullscreen mode anymore).
75 ################################################################################
76
77 my $tmp = fresh_workspace;
78
79 open_window;
80 cmd 'fullscreen';
81
82 my $nodes = get_ws_content($tmp);
83 is(scalar @$nodes, 1, 'precisely one window');
84 is($nodes->[0]->{focused}, 1, 'fullscreen window focused');
85 my $old_id = $nodes->[0]->{id};
86
87 $tmp2 = fresh_workspace;
88 my $move_window = open_window;
89 cmd "move workspace $tmp";
90
91 cmd "workspace $tmp";
92
93 $nodes = get_ws_content($tmp);
94 is(scalar @$nodes, 2, 'precisely two windows');
95 is($nodes->[0]->{id}, $old_id, 'id unchanged');
96 is($nodes->[0]->{focused}, 1, 'fullscreen window focused');
97
98 kill_all_windows;
99
100 ################################################################################
101 # Ensure it's possible to change focus if it doesn't escape the fullscreen
102 # container with fullscreen global. We can't even focus a container in a
103 # different workspace.
104 ################################################################################
105
106 $tmp = fresh_workspace(output => 1);
107 my $diff_ws = open_window;
108
109 $tmp2 = fresh_workspace(output => 0);
110 $left = open_window;
111 my $right1 = open_window;
112 cmd 'split v';
113 my $right2 = open_window;
114
115 cmd 'focus parent';
116 cmd 'fullscreen global';
117
118 cmd '[id="' . $right1->id . '"] focus';
119 verify_focus($right1, 'upper right window focused');
120
121 cmd '[id="' . $right2->id . '"] focus';
122 verify_focus($right2, 'bottom right window focused');
123
124 cmd 'focus parent';
125 isnt($x->input_focus, $right2->id, 'bottom right window no longer focused');
126
127 cmd 'focus child';
128 verify_focus($right2, 'bottom right window focused again');
129
130 cmd 'focus up';
131 verify_focus($right1, 'allowed focus up');
132
133 cmd 'focus down';
134 verify_focus($right2, 'allowed focus down');
135
136 cmd 'focus left';
137 verify_focus($right2, 'prevented focus left');
138
139 cmd 'focus right';
140 verify_focus($right2, 'prevented focus right');
141
142 cmd 'focus down';
143 verify_focus($right1, 'allowed focus wrap (down)');
144
145 cmd 'focus up';
146 verify_focus($right2, 'allowed focus wrap (up)');
147
148 ################################################################################
149 # (depends on previous layout)
150 # Same tests when we're in non-global fullscreen mode. It should now be possible
151 # to focus a container in a different workspace.
152 ################################################################################
153
154 cmd 'focus parent';
155 cmd 'fullscreen disable';
156 cmd 'fullscreen';
157
158 cmd '[id="' . $right1->id . '"] focus';
159 verify_focus($right1, 'upper right window focused');
160
161 cmd '[id="' . $right2->id . '"] focus';
162 verify_focus($right2, 'bottom right window focused');
163
164 cmd 'focus parent';
165 isnt($x->input_focus, $right2->id, 'bottom right window no longer focused');
166
167 cmd 'focus child';
168 verify_focus($right2, 'bottom right window focused again');
169
170 cmd 'focus up';
171 verify_focus($right1, 'allowed focus up');
172
173 cmd 'focus down';
174 verify_focus($right2, 'allowed focus down');
175
176 cmd 'focus down';
177 verify_focus($right1, 'allowed focus wrap (down)');
178
179 cmd 'focus up';
180 verify_focus($right2, 'allowed focus wrap (up)');
181
182 cmd 'focus left';
183 verify_focus($right2, 'focus left wrapped (no-op)');
184
185 cmd 'focus right';
186 verify_focus($diff_ws, 'allowed focus change to different ws');
187
188 cmd 'focus left';
189 verify_focus($right2, 'focused back into fullscreen container');
190
191 cmd '[id="' . $diff_ws->id . '"] focus';
192 verify_focus($diff_ws, 'allowed focus change to different ws by id');
193
194 ################################################################################
195 # (depends on previous layout)
196 # More testing of the interaction between wrapping and the fullscreen focus
197 # restrictions.
198 ################################################################################
199
200 cmd '[id="' . $right1->id . '"] focus';
201 verify_focus($right1, 'upper right window focused');
202
203 cmd 'focus parent';
204 cmd 'fullscreen disable';
205 cmd 'focus child';
206
207 cmd 'split v';
208 my $right12 = open_window;
209
210 cmd 'focus down';
211 verify_focus($right2, 'bottom right window focused');
212
213 cmd 'split v';
214 my $right22 = open_window;
215
216 cmd 'focus parent';
217 cmd 'fullscreen';
218 cmd 'focus child';
219
220 cmd 'focus down';
221 verify_focus($right2, 'focus did not leave parent container (1)');
222
223 cmd 'focus down';
224 verify_focus($right22, 'focus did not leave parent container (2)');
225
226 cmd 'focus up';
227 verify_focus($right2, 'focus did not leave parent container (3)');
228
229 cmd 'focus up';
230 verify_focus($right22, 'focus did not leave parent container (4)');
231
232 ################################################################################
233 # (depends on previous layout)
234 # Ensure that moving in a direction doesn't violate the focus restrictions.
235 ################################################################################
236
237 sub verify_move {
238 my $num = shift;
239 my $msg = shift;
240 my $nodes = get_ws_content($tmp2);
241 my $split = $nodes->[1];
242 my $fs = $split->{nodes}->[1];
243 is(scalar @{$fs->{nodes}}, $num, $msg);
244 }
245
246 cmd 'move left';
247 verify_move(2, 'prevented move left');
248 cmd 'move right';
249 verify_move(2, 'prevented move right');
250 cmd 'move down';
251 verify_move(2, 'prevented move down');
252 cmd 'move up';
253 cmd 'move up';
254 verify_move(2, 'prevented move up');
255
256 ################################################################################
257 # (depends on previous layout)
258 # Moving to a different workspace is allowed with per-output fullscreen
259 # containers.
260 ################################################################################
261
262 cmd "move to workspace $tmp";
263 verify_move(1, 'did not prevent move to workspace by name');
264
265 cmd "workspace $tmp";
266 cmd "move to workspace $tmp2";
267 cmd "workspace $tmp2";
268
269 cmd "move to workspace prev";
270 verify_move(1, 'did not prevent move to workspace by position');
271
272 ################################################################################
273 # (depends on previous layout)
274 # Ensure that is not allowed with global fullscreen containers.
275 ################################################################################
276
277 cmd "workspace $tmp";
278 cmd "move to workspace $tmp2";
279 cmd "workspace $tmp2";
280
281 cmd 'focus parent';
282 cmd 'fullscreen disable';
283 cmd 'fullscreen global';
284 cmd 'focus child';
285
286 cmd "move to workspace $tmp";
287 verify_move(2, 'prevented move to workspace by name');
288
289 cmd "move to workspace prev";
290 verify_move(2, 'prevented move to workspace by position');
291
292 kill_all_windows;
293
294 ################################################################################
295 # Ensure it's possible to focus a window using the focus command despite
296 # fullscreen window blocking it. Fullscreen window should lose its fullscreen
297 # mode.
298 ################################################################################
299
300 # first & second tiling, focus using id
301 $tmp = fresh_workspace;
302 my $first = open_window;
303 my $second = open_window;
304 cmd 'fullscreen';
305 verify_focus($second, 'fullscreen window focused');
306 is_num_fullscreen($tmp, 1, '1 fullscreen window');
307
308 cmd '[id="' . $first->id . '"] focus';
309 sync_with_i3;
310
311 verify_focus($first, 'correctly focused using id');
312 is_num_fullscreen($tmp, 0, 'no fullscreen windows');
313
314 kill_all_windows;
315
316 # first floating, second tiling, focus using 'focus floating'
317 $tmp = fresh_workspace;
318 $first = open_floating_window;
319 $second = open_window;
320 cmd 'fullscreen';
321 verify_focus($second, 'fullscreen window focused');
322 is_num_fullscreen($tmp, 1, '1 fullscreen window');
323
324 cmd 'focus floating';
325 sync_with_i3;
326
327 verify_focus($first, 'correctly focused using focus floating');
328 is_num_fullscreen($tmp, 0, 'no fullscreen windows');
329
330 kill_all_windows;
331
332 # first tiling, second floating, focus using 'focus tiling'
333 $tmp = fresh_workspace;
334 $first = open_window;
335 $second = open_floating_window;
336 cmd 'fullscreen';
337 verify_focus($second, 'fullscreen window focused');
338 is_num_fullscreen($tmp, 1, '1 fullscreen window');
339
340 cmd 'focus tiling';
341 sync_with_i3;
342
343 verify_focus($first, 'correctly focused using focus tiling');
344 is_num_fullscreen($tmp, 0, 'no fullscreen windows');
345
346 kill_all_windows;
347
348 ################################################################################
349 # When the fullscreen window is in an other workspace it should maintain its
350 # fullscreen mode since it's not blocking the window to be focused.
351 ################################################################################
352
353 $tmp = fresh_workspace;
354 $first = open_window;
355
356 $tmp2 = fresh_workspace;
357 $second = open_window;
358 cmd 'fullscreen';
359 verify_focus($second, 'fullscreen window focused');
360 is_num_fullscreen($tmp2, 1, '1 fullscreen window');
361
362 cmd '[id="' . $first->id . '"] focus';
363 sync_with_i3;
364
365 verify_focus($first, 'correctly focused using focus id');
366 is_num_fullscreen($tmp, 0, 'no fullscreen windows on first workspace');
367 is_num_fullscreen($tmp2, 1, 'still one fullscreen window on second workspace');
368
369 kill_all_windows;
370
371 ################################################################################
372 # But a global window in another workspace is blocking the window to be focused.
373 # Ensure that it loses its fullscreen mode.
374 ################################################################################
375
376 $tmp = fresh_workspace;
377 $first = open_window;
378
379 $tmp2 = fresh_workspace;
380 $second = open_window;
381 cmd 'fullscreen global';
382 verify_focus($second, 'global window focused');
383 is_num_fullscreen($tmp2, 1, '1 fullscreen window');
384
385 cmd '[id="' . $first->id . '"] focus';
386 sync_with_i3;
387
388 verify_focus($first, 'correctly focused using focus id');
389 is_num_fullscreen($tmp2, 0, 'no fullscreen windows');
390
391
392 # TODO: Tests for "move to output" and "move workspace to output".
393 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if the WM_TAKE_FOCUS protocol is correctly handled by i3
17 #
18 # For more information on the protocol and input handling, see:
19 # https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
20 #
21 use i3test;
22
23 sub recv_take_focus {
24 my ($window) = @_;
25
26 # sync_with_i3 will send a ClientMessage to i3 and i3 will send the same
27 # payload back to $window->id.
28 my $myrnd = sync_with_i3(
29 window_id => $window->id,
30 dont_wait_for_event => 1,
31 );
32
33 # We check whether the first received message has the correct payload — if
34 # not, the received message was a WM_TAKE_FOCUS message.
35 my $first_event_is_clientmessage;
36 wait_for_event 2, sub {
37 my ($event) = @_;
38 # TODO: const
39 return 0 unless $event->{response_type} == 161;
40
41 my ($win, $rnd) = unpack "LL", $event->{data};
42 if (!defined($first_event_is_clientmessage)) {
43 $first_event_is_clientmessage = ($rnd == $myrnd);
44 }
45 return ($rnd == $myrnd);
46 };
47
48 return !$first_event_is_clientmessage;
49 }
50
51 subtest 'Window without WM_TAKE_FOCUS', sub {
52 my $ws = fresh_workspace;
53
54 my $window = open_window;
55
56 ok(!recv_take_focus($window), 'did not receive ClientMessage');
57 ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
58
59 my ($nodes) = get_ws_content($ws);
60 my $con = shift @$nodes;
61 ok($con->{focused}, 'con is focused');
62
63 done_testing;
64 };
65
66 # https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
67 # > Clients using the Globally Active model can only use a SetInputFocus request
68 # > to acquire the input focus when they do not already have it on receipt of one
69 # > of the following events:
70 # > * ButtonPress
71 # > * ButtonRelease
72 # > * Passive-grabbed KeyPress
73 # > * Passive-grabbed KeyRelease
74 #
75 # Since managing a window happens on a MapNotify (which is absent from this
76 # list), the window cannot accept input focus, so we should not try to focus
77 # the window at all.
78 subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
79 my $ws = fresh_workspace;
80
81 my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');
82
83 my $window = open_window({
84 dont_map => 1,
85 protocols => [ $take_focus ],
86 });
87
88 # add an (empty) WM_HINTS property without the InputHint
89 $window->delete_hint('input');
90
91 $window->map;
92
93 ok(!recv_take_focus($window), 'did not receive ClientMessage');
94 ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
95
96 my ($nodes) = get_ws_content($ws);
97 my $con = shift @$nodes;
98 ok($con->{focused}, 'con is focused');
99
100 done_testing;
101 };
102
103 # If the InputHint is unspecified, i3 should use the simpler method of focusing
104 # the window directly rather than using the WM_TAKE_FOCUS protocol.
105 # XXX: The code paths for an unspecified and set InputHint are
106 # nearly identical presently, so this is currently used also as a proxy test
107 # for the latter case.
108 subtest 'Window with WM_TAKE_FOCUS and unspecified InputHint', sub {
109 my $ws = fresh_workspace;
110
111 my $take_focus = $x->atom(name => 'WM_TAKE_FOCUS');
112
113 my $window = open_window({ protocols => [ $take_focus ] });
114
115 ok(!recv_take_focus($window), 'did not receive ClientMessage');
116 ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
117
118 my ($nodes) = get_ws_content($ws);
119 my $con = shift @$nodes;
120 ok($con->{focused}, 'con is focused');
121
122 done_testing;
123 };
124
125 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if the various ipc_socket_path options are correctly handled
17 #
18 use i3test i3_autostart => 0;
19 use File::Temp qw(tempfile tempdir);
20 use File::Basename;
21 use POSIX qw(getuid);
22 use v5.10;
23
24 #####################################################################
25 # default case: socket will be created in /tmp/i3-<username>/ipc-socket.<pid>
26 #####################################################################
27
28 my $config = <<EOT;
29 # i3 config file (v4)
30 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
31 EOT
32
33 # ensure XDG_RUNTIME_DIR is not set
34 delete $ENV{XDG_RUNTIME_DIR};
35
36 my $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
37 my $socketpath = get_socket_path(0);
38 my $folder = "/tmp/i3-" . getpwuid(getuid());
39 like(dirname($socketpath), qr/^$folder/, 'temp directory matches expected pattern');
40 $folder = dirname($socketpath);
41
42 ok(-d $folder, "folder $folder exists");
43 $socketpath = "$folder/ipc-socket." . $pid;
44 ok(-S $socketpath, "file $socketpath exists and is a socket");
45
46 exit_gracefully($pid);
47
48 #####################################################################
49 # XDG_RUNTIME_DIR case: socket gets created in $XDG_RUNTIME_DIR/i3/ipc-socket.<pid>
50 #####################################################################
51
52 my $rtdir = tempdir(CLEANUP => 1);
53
54 ok(! -e "$rtdir/i3", "$rtdir/i3 does not exist yet");
55
56 $ENV{XDG_RUNTIME_DIR} = $rtdir;
57
58 $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
59
60 ok(-d "$rtdir/i3", "$rtdir/i3 exists and is a directory");
61 $socketpath = "$rtdir/i3/ipc-socket." . $pid;
62 ok(-S $socketpath, "file $socketpath exists and is a socket");
63
64 exit_gracefully($pid);
65
66 #####################################################################
67 # configuration file case: socket gets placed wherever we specify
68 #####################################################################
69
70 my $tmpdir = tempdir(CLEANUP => 1);
71 $socketpath = $tmpdir . "/config.sock";
72 ok(! -e $socketpath, "$socketpath does not exist yet");
73
74 $config = <<EOT;
75 # i3 config file (v4)
76 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
77 ipc-socket $socketpath
78 EOT
79
80 $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
81
82 ok(-S $socketpath, "file $socketpath exists and is a socket");
83
84 exit_gracefully($pid);
85
86 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test to check if borders are correctly restored after an inplace
17 # restart.
18 # found in eb8ad348b28e243cba1972e802ca8ee636472fc9
19 #
20 use List::Util qw(first);
21 use i3test;
22
23 my $i3 = i3(get_socket_path());
24 my $tmp = fresh_workspace;
25 my $window = open_window;
26
27 sub get_border_style {
28 my @content = @{get_ws_content($tmp)};
29 my $wininfo = first { $_->{window} == $window->id } @content;
30
31 return $wininfo->{border};
32 }
33
34 is(get_border_style(), 'normal', 'border style normal');
35
36 cmd 'border 1pixel';
37
38 is(get_border_style(), 'pixel', 'border style 1pixel after changing');
39
40 # perform an inplace-restart
41 cmd 'restart';
42
43 does_i3_live;
44
45 is(get_border_style(), 'pixel', 'border style still 1pixel after restart');
46
47 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for setting the urgent hint on dock clients.
17 # found in 4be3178d4d360c2996217d811e61161c84d25898
18 #
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22
23 my $tmp = fresh_workspace;
24
25 #####################################################################
26 # verify that there is no dock window yet
27 #####################################################################
28
29 # Children of all dockareas
30 my @docked = get_dock_clients;
31
32 is(@docked, 0, 'no dock clients yet');
33
34 # open a dock client
35
36 my $window = open_window(
37 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
38 );
39
40 #####################################################################
41 # check that we can find it in the layout tree at the expected position
42 #####################################################################
43
44 @docked = get_dock_clients;
45 is(@docked, 1, 'one dock client found');
46
47 # verify the height
48 my $docknode = $docked[0];
49
50 is($docknode->{rect}->{height}, 30, 'dock node has unchanged height');
51
52 $window->add_hint('urgency');
53
54 sync_with_i3;
55
56 does_i3_live;
57
58 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when
17 # unmapped.
18 #
19 use i3test;
20 use X11::XCB qw(ICCCM_WM_STATE_NORMAL ICCCM_WM_STATE_WITHDRAWN);
21
22 my $window = open_window;
23
24 is($window->state, ICCCM_WM_STATE_NORMAL, 'WM_STATE normal');
25
26 $window->unmap;
27
28 wait_for_unmap $window;
29
30 is($window->state, ICCCM_WM_STATE_WITHDRAWN, 'WM_STATE withdrawn');
31
32 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when
17 # unmapped.
18 #
19 use i3test;
20
21 sub two_windows {
22 my $tmp = fresh_workspace;
23
24 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
25
26 my $first = open_window;
27 my $second = open_window;
28
29 is($x->input_focus, $second->id, 'second window focused');
30 ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
31
32 return $tmp;
33 }
34
35 ##############################################################
36 # 1: open two windows (in the same client), kill one and see if
37 # the other one is still there
38 ##############################################################
39
40 my $tmp = two_windows;
41
42 cmd 'kill';
43 sync_with_i3;
44
45 ok(@{get_ws_content($tmp)} == 1, 'one container left after killing');
46
47 ##############################################################
48 # 2: same test case as test 1, but with the explicit variant
49 # 'kill window'
50 ##############################################################
51
52 $tmp = two_windows;
53
54 cmd 'kill window';
55 sync_with_i3;
56
57 ok(@{get_ws_content($tmp)} == 1, 'one container left after killing');
58
59 ##############################################################
60 # 3: open two windows (in the same client), use 'kill client'
61 # and check if both are gone
62 ##############################################################
63
64 $tmp = two_windows;
65
66 cmd 'kill client';
67 # We need to re-establish the X11 connection which we just killed :).
68 $x = i3test::X11->new;
69 sync_with_i3(no_cache => 1);
70
71 ok(@{get_ws_content($tmp)} == 0, 'no containers left after killing');
72
73 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 use i3test i3_autostart => 0;
17 use X11::XCB qw(PROP_MODE_REPLACE);
18
19 my (@nodes);
20
21 my $config = <<'EOT';
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24
25 # test 1, test 2
26 for_window [class="borderless$"] border none
27 for_window [title="special borderless title"] border none
28
29 # test 3
30 for_window [class="borderless3$" title="usethis"] border none
31 for_window [class="borderless3$"] border none
32 for_window [title="special borderless title"] border none
33 for_window [title="special mark title"] border none, mark bleh
34
35 # test 4
36 for_window [class="borderless4$" title="usethis"] border none
37
38 # test 5, test 6
39 for_window [class="foo$"] border 1pixel
40
41 # test 6
42 for_window [instance="foo6"] border none
43
44 # test 7
45 for_window [id="asdf"] border none
46
47 # test 8, test 9
48 for_window [window_role="i3test"] border none
49
50 # test 12
51 for_window [workspace="trigger"] floating enable, mark triggered
52 EOT
53
54 # test all window types
55 my %window_types = (
56 'normal' => '_NET_WM_WINDOW_TYPE_NORMAL',
57 'dialog' => '_NET_WM_WINDOW_TYPE_DIALOG',
58 'utility' => '_NET_WM_WINDOW_TYPE_UTILITY',
59 'toolbar' => '_NET_WM_WINDOW_TYPE_TOOLBAR',
60 'splash' => '_NET_WM_WINDOW_TYPE_SPLASH',
61 'menu' => '_NET_WM_WINDOW_TYPE_MENU',
62 'dropdown_menu' => '_NET_WM_WINDOW_TYPE_DROPDOWN_MENU',
63 'popup_menu' => '_NET_WM_WINDOW_TYPE_POPUP_MENU',
64 'tooltip' => '_NET_WM_WINDOW_TYPE_TOOLTIP',
65 'notification' => '_NET_WM_WINDOW_TYPE_NOTIFICATION'
66 );
67
68 for my $window_type (keys %window_types) {
69 $config .= <<EOT;
70 for_window [window_type="$window_type"] floating enable, mark branded-$window_type
71 EOT
72 }
73
74 my $pid = launch_with_config($config);
75
76 ##############################################################
77 # 1: test the following directive:
78 # for_window [class="borderless"] border none
79 # by first creating a window with a different class (should get
80 # the normal border), then creating a window with the class
81 # "borderless" (should get no border)
82 ##############################################################
83
84 my $tmp = fresh_workspace;
85
86 my $window = open_window(name => 'Border window');
87
88 my @content = @{get_ws_content($tmp)};
89 cmp_ok(@content, '==', 1, 'one node on this workspace now');
90 is($content[0]->{border}, 'normal', 'normal border');
91
92 $window->unmap;
93 wait_for_unmap $window;
94
95 @content = @{get_ws_content($tmp)};
96 cmp_ok(@content, '==', 0, 'no more nodes');
97 diag('content = '. Dumper(\@content));
98
99 $window = open_window(
100 name => 'Borderless window',
101 wm_class => 'borderless',
102 );
103
104 @content = @{get_ws_content($tmp)};
105 cmp_ok(@content, '==', 1, 'one node on this workspace now');
106 is($content[0]->{border}, 'none', 'no border');
107
108 $window->unmap;
109 wait_for_unmap $window;
110
111 @content = @{get_ws_content($tmp)};
112 cmp_ok(@content, '==', 0, 'no more nodes');
113
114 kill_all_windows;
115
116 ##############################################################
117 # 2: match on the title, check if for_window is really executed
118 # only once
119 ##############################################################
120
121 $tmp = fresh_workspace;
122
123 $window = open_window(name => 'special title');
124
125 @content = @{get_ws_content($tmp)};
126 cmp_ok(@content, '==', 1, 'one node on this workspace now');
127 is($content[0]->{border}, 'normal', 'normal border');
128
129 $window->name('special borderless title');
130 sync_with_i3;
131
132 @content = @{get_ws_content($tmp)};
133 is($content[0]->{border}, 'none', 'no border');
134
135 $window->name('special title');
136 sync_with_i3;
137
138 cmd 'border normal';
139
140 @content = @{get_ws_content($tmp)};
141 is($content[0]->{border}, 'normal', 'border reset to normal');
142
143 $window->name('special borderless title');
144 sync_with_i3;
145
146 @content = @{get_ws_content($tmp)};
147 is($content[0]->{border}, 'normal', 'still normal border');
148
149 $window->unmap;
150 wait_for_unmap $window;
151
152 @content = @{get_ws_content($tmp)};
153 cmp_ok(@content, '==', 0, 'no more nodes');
154
155 kill_all_windows;
156
157 ##############################################################
158 # 3: match on the title, set border style *and* a mark
159 ##############################################################
160
161 $tmp = fresh_workspace;
162
163 $window = open_window(name => 'special mark title');
164
165 @content = @{get_ws_content($tmp)};
166 cmp_ok(@content, '==', 1, 'one node on this workspace now');
167 is($content[0]->{border}, 'none', 'no border');
168
169 my $other = open_window;
170
171 @content = @{get_ws_content($tmp)};
172 cmp_ok(@content, '==', 2, 'two nodes');
173 is($content[0]->{border}, 'none', 'no border');
174 is($content[1]->{border}, 'normal', 'normal border');
175 ok(!$content[0]->{focused}, 'first one not focused');
176
177 cmd qq|[con_mark="bleh"] focus|;
178
179 @content = @{get_ws_content($tmp)};
180 ok($content[0]->{focused}, 'first node focused');
181
182 kill_all_windows;
183
184 ##############################################################
185 # 4: multiple criteria for the for_window command
186 ##############################################################
187
188 $tmp = fresh_workspace;
189
190 $window = open_window(
191 name => 'usethis',
192 wm_class => 'borderless4',
193 );
194
195 @content = @{get_ws_content($tmp)};
196 cmp_ok(@content, '==', 1, 'one node on this workspace now');
197 is($content[0]->{border}, 'none', 'no border');
198
199 cmd 'kill';
200 wait_for_unmap $window;
201 $window->destroy;
202
203 # give i3 a chance to delete the window from its tree
204 sync_with_i3;
205
206 @content = @{get_ws_content($tmp)};
207 cmp_ok(@content, '==', 0, 'no nodes on this workspace now');
208
209 $window->_create;
210 $window->wm_class('borderless4');
211 $window->name('notthis');
212 $window->map;
213 wait_for_map $window;
214
215 @content = @{get_ws_content($tmp)};
216 cmp_ok(@content, '==', 1, 'one node on this workspace now');
217 is($content[0]->{border}, 'normal', 'no border');
218
219 kill_all_windows;
220
221 ##############################################################
222 # 5: check that a class criterion does not match the instance
223 ##############################################################
224
225 $tmp = fresh_workspace;
226
227 $window = open_window(
228 name => 'usethis',
229 wm_class => 'bar',
230 instance => 'foo',
231 );
232
233 @content = @{get_ws_content($tmp)};
234 cmp_ok(@content, '==', 1, 'one node on this workspace now');
235 is($content[0]->{border}, 'normal', 'normal border, not matched');
236
237 kill_all_windows;
238
239 ##############################################################
240 # 6: check that the 'instance' criterion works
241 ##############################################################
242
243 $tmp = fresh_workspace;
244
245 $window = open_window(
246 name => 'usethis',
247 wm_class => 'bar',
248 instance => 'foo6',
249 );
250
251 @content = @{get_ws_content($tmp)};
252 cmp_ok(@content, '==', 1, 'one node on this workspace now');
253 is($content[0]->{border}, 'none', 'no border');
254
255 kill_all_windows;
256
257 ##############################################################
258 # 7: check that invalid criteria don’t end up matching all windows
259 ##############################################################
260
261 $tmp = fresh_workspace;
262
263 $window = open_window(
264 name => 'usethis',
265 wm_class => 'bar',
266 instance => 'foo',
267 );
268
269 @content = @{get_ws_content($tmp)};
270 cmp_ok(@content, '==', 1, 'one node on this workspace now');
271 is($content[0]->{border}, 'normal', 'normal border');
272
273 kill_all_windows;
274
275 ##############################################################
276 # 8: check that the role criterion works properly
277 ##############################################################
278
279 $tmp = fresh_workspace;
280
281 $window = open_window(
282 name => 'usethis',
283 before_map => sub {
284 my ($window) = @_;
285 my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
286 my $atomtype = $x->atom(name => 'STRING');
287 $x->change_property(
288 PROP_MODE_REPLACE,
289 $window->id,
290 $atomname->id,
291 $atomtype->id,
292 8,
293 length("i3test") + 1,
294 "i3test\x00"
295 );
296 },
297 );
298
299 @content = @{get_ws_content($tmp)};
300 cmp_ok(@content, '==', 1, 'one node on this workspace now');
301 is($content[0]->{border}, 'none', 'no border (window_role)');
302
303 kill_all_windows;
304
305 ##############################################################
306 # 9: another test for the window_role, but this time it changes
307 # *after* the window has been mapped
308 ##############################################################
309
310 $tmp = fresh_workspace;
311
312 $window = open_window(name => 'usethis');
313
314 @content = @{get_ws_content($tmp)};
315 cmp_ok(@content, '==', 1, 'one node on this workspace now');
316 is($content[0]->{border}, 'normal', 'normal border (window_role 2)');
317
318 my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
319 my $atomtype = $x->atom(name => 'STRING');
320 $x->change_property(
321 PROP_MODE_REPLACE,
322 $window->id,
323 $atomname->id,
324 $atomtype->id,
325 8,
326 length("i3test") + 1,
327 "i3test\x00"
328 );
329
330 $x->flush;
331
332 sync_with_i3;
333
334 @content = @{get_ws_content($tmp)};
335 cmp_ok(@content, '==', 1, 'one node on this workspace now');
336 is($content[0]->{border}, 'none', 'no border (window_role 2)');
337
338 kill_all_windows;
339
340 ##############################################################
341 # 10: check that the criterion 'window_type' works
342 ##############################################################
343
344 while (my ($window_type, $atom) = each %window_types) {
345 $tmp = fresh_workspace;
346
347 $window = open_window(window_type => $x->atom(name => $atom));
348
349 my @nodes = @{get_ws($tmp)->{floating_nodes}};
350 cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
351 is_deeply($nodes[0]->{nodes}[0]->{marks}, [ "branded-$window_type" ], "mark set (window_type = $atom)");
352
353 kill_all_windows;
354 }
355
356 ##############################################################
357 # 11: check that the criterion 'window_type' works if the
358 # _NET_WM_WINDOW_TYPE is changed after managing.
359 ##############################################################
360
361 while (my ($window_type, $atom) = each %window_types) {
362 $tmp = fresh_workspace;
363
364 $window = open_window();
365
366 my $atomname = $x->atom(name => '_NET_WM_WINDOW_TYPE');
367 my $atomtype = $x->atom(name => 'ATOM');
368 $x->change_property(PROP_MODE_REPLACE, $window->id, $atomname->id, $atomtype->id,
369 32, 1, pack('L1', $x->atom(name => $atom)->id));
370 $x->flush;
371 sync_with_i3;
372
373 my @nodes = @{get_ws($tmp)->{floating_nodes}};
374 cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
375 is_deeply($nodes[0]->{nodes}[0]->{marks}, [ "branded-$window_type" ], "mark set (window_type = $atom)");
376
377 kill_all_windows;
378 }
379
380 ##############################################################
381 # 12: check that the criterion 'workspace' works
382 ##############################################################
383
384 cmd 'workspace trigger';
385 $window = open_window;
386
387 @nodes = @{get_ws('trigger')->{floating_nodes}};
388 cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
389 is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'triggered' ], "mark set for workspace criterion");
390
391 kill_all_windows;
392
393 ##############################################################
394
395 exit_gracefully($pid);
396
397 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if assignments work
17 #
18 use i3test i3_autostart => 0;
19
20 sub open_special {
21 my %args = @_;
22 $args{name} //= 'special window';
23 $args{wm_class} //= 'special';
24
25 # We use dont_map because i3 will not map the window on the current
26 # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
27 my $window = open_window(
28 %args,
29 dont_map => 1,
30 );
31 $window->map;
32 return $window;
33 }
34
35 sub test_workspace_assignment {
36 my $target_ws = "@_";
37
38 # initialize the target workspace, then go to a fresh one
39 ok(!($target_ws ~~ @{get_workspace_names()}), "$target_ws does not exist yet");
40 cmd "workspace $target_ws";
41 cmp_ok(@{get_ws_content($target_ws)}, '==', 0, "no containers on $target_ws yet");
42 cmd 'open';
43 cmp_ok(@{get_ws_content($target_ws)}, '==', 1, "one container on $target_ws");
44 my $tmp = fresh_workspace;
45
46 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
47 ok($target_ws ~~ @{get_workspace_names()}, "$target_ws does not exist yet");
48
49 # We use sync_with_i3 instead of wait_for_map here because i3 will not actually
50 # map the window -- it will be assigned to a different workspace and will only
51 # be mapped once you switch to that workspace
52 my $window = open_special;
53 sync_with_i3;
54
55 ok(@{get_ws_content($tmp)} == 0, 'still no containers');
56 ok(@{get_ws_content($target_ws)} == 2, "two containers on $target_ws");
57
58 return $window
59 }
60
61 #####################################################################
62 # start a window and see that it does not get assigned with an empty config
63 #####################################################################
64
65 my $config = <<EOT;
66 # i3 config file (v4)
67 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
68 EOT
69
70 my $pid = launch_with_config($config);
71
72 my $tmp = fresh_workspace;
73
74 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
75
76 my $window = open_special;
77 wait_for_map($window);
78
79 ok(@{get_ws_content($tmp)} == 1, 'special window got managed to current (random) workspace');
80
81 exit_gracefully($pid);
82
83 $window->destroy;
84
85 #####################################################################
86 # start a window and see that it gets assigned to a formerly unused
87 # workspace
88 #####################################################################
89
90 $config = <<EOT;
91 # i3 config file (v4)
92 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
93 assign [class="special"] → targetws
94 EOT
95
96 $pid = launch_with_config($config);
97
98 $tmp = fresh_workspace;
99
100 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
101 my $workspaces = get_workspace_names;
102 ok(!("targetws" ~~ @{$workspaces}), 'targetws does not exist yet');
103
104 $window = open_special;
105 sync_with_i3;
106
107 ok(@{get_ws_content($tmp)} == 0, 'still no containers');
108 ok("targetws" ~~ @{get_workspace_names()}, 'targetws exists');
109
110 $window->destroy;
111
112 exit_gracefully($pid);
113
114 #####################################################################
115 # start a window and see that it gets assigned to a formerly unused
116 # numbered workspace
117 #####################################################################
118
119 my $config_numbered = <<EOT;
120 # i3 config file (v4)
121 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
122 assign [class="special"] → workspace number 2
123 EOT
124
125 $pid = launch_with_config($config_numbered);
126
127 $tmp = fresh_workspace;
128
129 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
130 $workspaces = get_workspace_names;
131 ok(!("2" ~~ @{$workspaces}), 'workspace number 2 does not exist yet');
132
133 $window = open_special;
134 sync_with_i3;
135
136 ok(@{get_ws_content($tmp)} == 0, 'still no containers');
137 ok("2" ~~ @{get_workspace_names()}, 'workspace number 2 exists');
138
139 $window->destroy;
140
141 exit_gracefully($pid);
142
143 #####################################################################
144 # start a window and see that it gets assigned to a numbered
145 # workspace which has content already, next to the existing node.
146 #####################################################################
147
148 $pid = launch_with_config($config_numbered);
149
150 $window = test_workspace_assignment("2");
151 $window->destroy;
152
153 exit_gracefully($pid);
154
155 #####################################################################
156 # start a window and see that it gets assigned to a numbered workspace with
157 # a name which has content already, next to the existing node.
158 #####################################################################
159
160 $pid = launch_with_config($config_numbered);
161
162 cmd 'workspace 2'; # Make sure that we are not testing for "2" again.
163 $window = test_workspace_assignment("2: targetws");
164 $window->destroy;
165
166 exit_gracefully($pid);
167
168 #####################################################################
169 # start a window and see that it gets assigned to a workspace which
170 # has content already, next to the existing node.
171 #####################################################################
172
173 $pid = launch_with_config($config);
174
175 test_workspace_assignment("targetws");
176
177 exit_gracefully($pid);
178
179 #####################################################################
180 # start a window and see that it gets assigned to a workspace which has content
181 # already, next to the existing node.
182 #####################################################################
183
184 $config = <<EOT;
185 # i3 config file (v4)
186 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
187 for_window [class="special"] floating enable
188 EOT
189
190 $pid = launch_with_config($config);
191
192 $tmp = fresh_workspace;
193
194 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
195 $workspaces = get_workspace_names;
196 ok(!("targetws" ~~ @{$workspaces}), 'targetws does not exist yet');
197
198 $window = open_special;
199 sync_with_i3;
200
201 my $content = get_ws($tmp);
202 ok(@{$content->{nodes}} == 0, 'no tiling cons');
203 ok(@{$content->{floating_nodes}} == 1, 'one floating con');
204
205 kill_all_windows;
206 exit_gracefully($pid);
207
208 #####################################################################
209 # test assignments to named outputs
210 #####################################################################
211 $config = <<EOT;
212 # i3 config file (v4)
213 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
214
215 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
216
217 workspace ws-0 output fake-0
218 workspace ws-1 output fake-1
219 workspace ws-2 output fake-2
220 workspace ws-3 output fake-3
221
222 assign [class="special-0"] → output fake-0
223 assign [class="special-1"] → output fake-1
224 assign [class="special-2"] → output fake-2
225 assign [class="special-3"] → output fake-3
226 assign [class="special-4"] → output invalid
227
228 EOT
229
230 $pid = launch_with_config($config);
231
232 sub open_in_output {
233 my ($num, $expected_count) = @_;
234 my $ws = "ws-$num";
235 my $class = "special-$num";
236 my $output = "fake-$num";
237
238 is_num_children($ws, $expected_count - 1,
239 "before: " . ($expected_count - 1) . " containers on output $output");
240 $window = open_special(wm_class => $class);
241 sync_with_i3;
242 is_num_children($ws, $expected_count,
243 "after: $expected_count containers on output $output");
244 }
245
246 cmd "workspace ws-0";
247 open_in_output(0, 1);
248 my $focused = $x->input_focus;
249
250 open_in_output(1, 1);
251 is($x->input_focus, $focused, 'focus remains on output fake-0');
252
253 open_in_output(2, 1);
254 is($x->input_focus, $focused, 'focus remains on output fake-0');
255
256 for my $i (1 .. 5){
257 open_in_output(3, $i);
258 is($x->input_focus, $focused, 'focus remains on output fake-0');
259 }
260
261 # Check invalid output
262 $tmp = fresh_workspace;
263 open_special(wm_class => "special-4");
264 sync_with_i3;
265 is_num_children($tmp, 1, 'window assigned to invalid output opened in current workspace');
266 open_special(wm_class => "special-3");
267 sync_with_i3;
268 is_num_children($tmp, 1, 'but window assigned to valid output did not');
269
270 kill_all_windows;
271 exit_gracefully($pid);
272
273 #####################################################################
274 # Test assignments to outputs with relative names
275 #####################################################################
276 $config = <<EOT;
277 # i3 config file (v4)
278 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
279
280 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
281
282 workspace left-top output fake-0
283 workspace right-top output fake-1
284 workspace right-bottom output fake-2
285 workspace left-bottom output fake-3
286
287 assign [class="current"] → output current
288 assign [class="left"] → output left
289 assign [class="right"] → output right
290 assign [class="up"] → output up
291 assign [class="down"] → output down
292 EOT
293
294 $pid = launch_with_config($config);
295
296 cmd 'workspace left-top';
297
298 is_num_children('left-top', 0, 'no childreon on left-top');
299 for my $i (1 .. 5){
300 open_special(wm_class => 'current');
301 }
302 sync_with_i3;
303 is_num_children('left-top', 5, 'windows opened in current workspace');
304
305 is_num_children('right-top', 0, 'no children on right-top');
306 open_special(wm_class => 'right');
307 sync_with_i3;
308 is_num_children('right-top', 1, 'one child on right-top');
309
310 is_num_children('left-bottom', 0, 'no children on left-bottom');
311 open_special(wm_class => 'down');
312 sync_with_i3;
313 is_num_children('left-bottom', 1, 'one child on left-bottom');
314
315 cmd 'workspace right-bottom';
316
317 open_special(wm_class => 'up');
318 sync_with_i3;
319 is_num_children('right-top', 2, 'two children on right-top');
320
321 open_special(wm_class => 'left');
322 sync_with_i3;
323 is_num_children('left-bottom', 2, 'two children on left-bottom');
324
325 kill_all_windows;
326 exit_gracefully($pid);
327
328 #####################################################################
329 # regression test: dock clients with floating assignments should not crash
330 # (instead, nothing should happen - dock clients can’t float)
331 # ticket #501
332 #####################################################################
333
334 # Walks /proc to figure out whether a child process of $i3pid with the name
335 # 'i3-nagbar' exists.
336 sub i3nagbar_running {
337 my ($i3pid) = @_;
338
339 my @procfiles = grep { m,^/proc/[0-9]+$, } </proc/*>;
340 for my $path (@procfiles) {
341 open(my $fh, '<', "$path/stat") or next;
342 my $line = <$fh>;
343 close($fh);
344 my ($comm, $ppid) = ($line =~ /^[0-9]+ \(([^)]+)\) . ([0-9]+)/);
345 return 1 if $ppid == $i3pid && $comm eq 'i3-nagbar';
346 }
347 return 0;
348 }
349
350 $config = <<EOT;
351 # i3 config file (v4)
352 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
353 for_window [title="special"] floating enable
354 EOT
355
356 $pid = launch_with_config($config);
357
358 $tmp = fresh_workspace;
359
360 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
361 my @docked = get_dock_clients;
362 is(@docked, 0, 'no dock client yet');
363
364 $window = open_special(
365 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
366 );
367 sync_with_i3;
368
369 $content = get_ws($tmp);
370 ok(@{$content->{nodes}} == 0, 'no tiling cons');
371 ok(@{$content->{floating_nodes}} == 0, 'one floating con');
372 @docked = get_dock_clients;
373 is(@docked, 1, 'one dock client now');
374
375 $window->destroy;
376
377 does_i3_live;
378
379 exit_gracefully($pid);
380
381 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the workspace_layout config option.
17 #
18
19 use i3test i3_autostart => 0;
20
21 #####################################################################
22 # 1: check that with an empty config, cons are place next to each
23 # other and no split containers are created
24 #####################################################################
25
26 my $config = <<EOT;
27 # i3 config file (v4)
28 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
29 EOT
30
31 my $pid = launch_with_config($config);
32
33 my $tmp = fresh_workspace;
34
35 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
36
37 my $first = open_window;
38 my $second = open_window;
39
40 is($x->input_focus, $second->id, 'second window focused');
41 my @content = @{get_ws_content($tmp)};
42 ok(@content == 2, 'two containers opened');
43 isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
44 isnt($content[1]->{layout}, 'stacked', 'layout not stacked');
45
46 exit_gracefully($pid);
47
48 #####################################################################
49 # 2: set workspace_layout stacked, check that when opening two cons,
50 # they end up in a stacked con
51 #####################################################################
52
53 $config = <<EOT;
54 # i3 config file (v4)
55 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
56 workspace_layout stacked
57 EOT
58
59 $pid = launch_with_config($config);
60
61 $tmp = fresh_workspace;
62
63 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
64
65 $first = open_window;
66 $second = open_window;
67
68 is($x->input_focus, $second->id, 'second window focused');
69 @content = @{get_ws_content($tmp)};
70 ok(@content == 1, 'one con at workspace level');
71 is($content[0]->{layout}, 'stacked', 'layout stacked');
72
73 #####################################################################
74 # 3: focus parent, open two new cons, check that they end up in a stacked
75 # con
76 #####################################################################
77
78 cmd 'focus parent';
79 my $right_top = open_window;
80 my $right_bot = open_window;
81
82 @content = @{get_ws_content($tmp)};
83 is(@content, 2, 'two cons at workspace level after focus parent');
84 is($content[0]->{layout}, 'stacked', 'layout stacked');
85 is($content[1]->{layout}, 'stacked', 'layout stacked');
86
87 #####################################################################
88 # 4: move one of the cons to the right, check that it will end up in
89 # a stacked con
90 #####################################################################
91
92 cmd 'move right';
93
94 @content = @{get_ws_content($tmp)};
95 is(@content, 3, 'three cons at workspace level after move');
96 is($content[0]->{layout}, 'stacked', 'layout stacked');
97 is($content[1]->{layout}, 'stacked', 'layout stacked');
98 is($content[2]->{layout}, 'stacked', 'layout stacked');
99
100 #####################################################################
101 # 5: move it to the left again, check that the stacked con is deleted
102 #####################################################################
103
104 cmd 'move left';
105
106 @content = @{get_ws_content($tmp)};
107 is(@content, 2, 'two cons at workspace level after moving back');
108 is($content[0]->{layout}, 'stacked', 'layout stacked');
109 is($content[1]->{layout}, 'stacked', 'layout stacked');
110
111 #####################################################################
112 # 6: move it to a different workspace, check that it ends up in a
113 # stacked con
114 #####################################################################
115
116 my $otmp = get_unused_workspace;
117
118 cmd "move workspace $otmp";
119
120 @content = @{get_ws_content($tmp)};
121 is(@content, 2, 'still two cons on this workspace');
122 is($content[0]->{layout}, 'stacked', 'layout stacked');
123 is($content[1]->{layout}, 'stacked', 'layout stacked');
124
125 @content = @{get_ws_content($otmp)};
126 is(@content, 1, 'one con on target workspace');
127 is($content[0]->{layout}, 'stacked', 'layout stacked');
128
129 #####################################################################
130 # 7: toggle floating mode and check that we have a stacked con when
131 # re-inserting a floating container.
132 #####################################################################
133
134 $tmp = fresh_workspace;
135
136 $first = open_window;
137 cmd 'floating toggle';
138 cmd 'floating toggle';
139
140 $second = open_window;
141
142 is($x->input_focus, $second->id, 'second window focused');
143 @content = @{get_ws_content($tmp)};
144 ok(@content == 1, 'one con at workspace level');
145 is($content[0]->{layout}, 'stacked', 'layout stacked');
146
147 #####################################################################
148 # 8: when the workspace is empty check that its layout can be changed
149 # from stacked to horizontal split using the 'layout splith' command.
150 #####################################################################
151
152 $tmp = fresh_workspace;
153
154 cmd 'layout stacked';
155 $first = open_window;
156 $second = open_window;
157
158 @content = @{get_ws_content($tmp)};
159 is($content[0]->{layout}, 'stacked', 'layout stacked');
160
161 cmd '[id="' . $first->id . '"] kill';
162 cmd '[id="' . $second->id . '"] kill';
163 sync_with_i3;
164
165 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
166
167 cmd 'layout splith';
168 $first = open_window;
169 $second = open_window;
170 @content = @{get_ws_content($tmp)};
171 ok(@content == 2, 'two containers opened');
172 isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
173 isnt($content[1]->{layout}, 'stacked', 'layout not stacked');
174
175 #####################################################################
176 # 9: when the workspace is empty check that its layout can be changed
177 # from stacked to vertical split using the 'layout splitv' command.
178 #####################################################################
179
180 $tmp = fresh_workspace;
181
182 cmd 'layout stacked';
183 $first = open_window;
184 $second = open_window;
185
186 @content = @{get_ws_content($tmp)};
187 is($content[0]->{layout}, 'stacked', 'layout stacked');
188
189 cmd '[id="' . $first->id . '"] kill';
190 cmd '[id="' . $second->id . '"] kill';
191 sync_with_i3;
192
193 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
194
195 cmd 'layout splitv';
196 $first = open_window;
197 $second = open_window;
198
199 @content = @{get_ws_content($tmp)};
200 ok(@content == 2, 'two containers opened');
201 isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
202 isnt($content[1]->{layout}, 'stacked', 'layout not stacked');
203
204 #####################################################################
205 # 10: when the workspace is empty check that its layout can be changed
206 # from tabbed to horizontal split using the 'layout splith' command.
207 #####################################################################
208
209 $tmp = fresh_workspace;
210
211 cmd 'layout tabbed';
212 $first = open_window;
213 $second = open_window;
214
215 @content = @{get_ws_content($tmp)};
216 is($content[0]->{layout}, 'tabbed', 'layout tabbed');
217
218 cmd '[id="' . $first->id . '"] kill';
219 cmd '[id="' . $second->id . '"] kill';
220 sync_with_i3;
221
222 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
223
224 cmd 'layout splith';
225 $first = open_window;
226 $second = open_window;
227
228 @content = @{get_ws_content($tmp)};
229 ok(@content == 2, 'two containers opened');
230 isnt($content[0]->{layout}, 'tabbed', 'layout not tabbed');
231 isnt($content[1]->{layout}, 'tabbed', 'layout not tabbed');
232
233 #####################################################################
234 # 11: when the workspace is empty check that its layout can be changed
235 # from tabbed to vertical split using the 'layout splitv' command.
236 #####################################################################
237
238 $tmp = fresh_workspace;
239
240 cmd 'layout tabbed';
241 $first = open_window;
242 $second = open_window;
243
244 @content = @{get_ws_content($tmp)};
245 is($content[0]->{layout}, 'tabbed', 'layout tabbed');
246
247 cmd '[id="' . $first->id . '"] kill';
248 cmd '[id="' . $second->id . '"] kill';
249 sync_with_i3;
250
251 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
252
253 cmd 'layout splitv';
254 $first = open_window;
255 $second = open_window;
256
257 @content = @{get_ws_content($tmp)};
258 ok(@content == 2, 'two containers opened');
259 isnt($content[0]->{layout}, 'tabbed', 'layout not tabbed');
260 isnt($content[1]->{layout}, 'tabbed', 'layout not tabbed');
261
262 #####################################################################
263 # 12: when the workspace is empty check that its layout can be changed
264 # from stacked to horizontal split using the 'split horizontal' command.
265 #####################################################################
266
267 $tmp = fresh_workspace;
268
269 cmd 'layout stacked';
270 $first = open_window;
271 $second = open_window;
272
273 @content = @{get_ws_content($tmp)};
274 is($content[0]->{layout}, 'stacked', 'layout stacked');
275
276 cmd '[id="' . $first->id . '"] kill';
277 cmd '[id="' . $second->id . '"] kill';
278 sync_with_i3;
279
280 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
281
282 cmd 'split horizontal';
283 $first = open_window;
284 $second = open_window;
285 @content = @{get_ws_content($tmp)};
286 ok(@content == 2, 'two containers opened');
287 isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
288 isnt($content[1]->{layout}, 'stacked', 'layout not stacked');
289
290 #####################################################################
291 # 13: when the workspace is empty check that its layout can be changed
292 # from stacked to vertical split using the 'split vertical' command.
293 #####################################################################
294
295 $tmp = fresh_workspace;
296
297 cmd 'layout stacked';
298 $first = open_window;
299 $second = open_window;
300
301 @content = @{get_ws_content($tmp)};
302 is($content[0]->{layout}, 'stacked', 'layout stacked');
303
304 cmd '[id="' . $first->id . '"] kill';
305 cmd '[id="' . $second->id . '"] kill';
306 sync_with_i3;
307
308 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
309
310 cmd 'split vertical';
311 $first = open_window;
312 $second = open_window;
313
314 @content = @{get_ws_content($tmp)};
315 ok(@content == 2, 'two containers opened');
316 isnt($content[0]->{layout}, 'stacked', 'layout not stacked');
317 isnt($content[1]->{layout}, 'stacked', 'layout not stacked');
318
319 #####################################################################
320 # 14: when the workspace is empty check that its layout can be changed
321 # from tabbed to horizontal split using the 'split horizontal' command.
322 #####################################################################
323
324 $tmp = fresh_workspace;
325
326 cmd 'layout tabbed';
327 $first = open_window;
328 $second = open_window;
329
330 @content = @{get_ws_content($tmp)};
331 is($content[0]->{layout}, 'tabbed', 'layout tabbed');
332
333 cmd '[id="' . $first->id . '"] kill';
334 cmd '[id="' . $second->id . '"] kill';
335 sync_with_i3;
336
337 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
338
339 cmd 'split horizontal';
340 $first = open_window;
341 $second = open_window;
342
343 @content = @{get_ws_content($tmp)};
344 ok(@content == 2, 'two containers opened');
345 isnt($content[0]->{layout}, 'tabbed', 'layout not tabbed');
346 isnt($content[1]->{layout}, 'tabbed', 'layout not tabbed');
347
348 #####################################################################
349 # 15: when the workspace is empty check that its layout can be changed
350 # from tabbed to vertical split using the 'split vertical' command.
351 #####################################################################
352
353 $tmp = fresh_workspace;
354
355 cmd 'layout tabbed';
356 $first = open_window;
357 $second = open_window;
358
359 @content = @{get_ws_content($tmp)};
360 is($content[0]->{layout}, 'tabbed', 'layout tabbed');
361
362 cmd '[id="' . $first->id . '"] kill';
363 cmd '[id="' . $second->id . '"] kill';
364 sync_with_i3;
365
366 ok(@{get_ws_content($tmp)} == 0, 'workspace is empty');
367
368 cmd 'split vertical';
369 $first = open_window;
370 $second = open_window;
371
372 @content = @{get_ws_content($tmp)};
373 ok(@content == 2, 'two containers opened');
374 isnt($content[0]->{layout}, 'tabbed', 'layout not tabbed');
375 isnt($content[1]->{layout}, 'tabbed', 'layout not tabbed');
376
377 exit_gracefully($pid);
378
379 #####################################################################
380 # 16: Check that the command 'layout toggle split' works regardless
381 # of what layout we're using.
382 #####################################################################
383
384 $config = <<EOT;
385 # i3 config file (v4)
386 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
387 workspace_layout default
388 EOT
389
390 $pid = launch_with_config($config);
391
392 $tmp = fresh_workspace;
393
394 my @layouts = ('splith', 'splitv', 'tabbed', 'stacked');
395 my $first_layout;
396
397 foreach $first_layout (@layouts) {
398 cmd 'layout ' . $first_layout;
399 $first = open_window;
400 $second = open_window;
401 cmd 'layout toggle split';
402 @content = @{get_ws_content($tmp)};
403 if ($first_layout eq 'splith') {
404 is($content[0]->{layout}, 'splitv', 'layout toggles to splitv');
405 } else {
406 is($content[0]->{layout}, 'splith', 'layout toggles to splith');
407 }
408
409 cmd '[id="' . $first->id . '"] kill';
410 cmd '[id="' . $second->id . '"] kill';
411 sync_with_i3;
412 }
413
414 exit_gracefully($pid);
415
416 #####################################################################
417 # 17: Check about setting a new layout.
418 #####################################################################
419
420 $config = <<EOT;
421 # i3 config file (v4)
422 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
423 workspace_layout default
424 EOT
425
426 $pid = launch_with_config($config);
427
428 $tmp = fresh_workspace;
429
430 my $second_layout;
431
432 foreach $first_layout (@layouts) {
433 foreach $second_layout (@layouts) {
434 cmd 'layout ' . $first_layout;
435 $first = open_window;
436 $second = open_window;
437 cmd 'layout ' . $second_layout;
438 @content = @{get_ws_content($tmp)};
439 is($content[0]->{layout}, $second_layout, 'layout changes to ' . $second_layout);
440
441 cmd '[id="' . $first->id . '"] kill';
442 cmd '[id="' . $second->id . '"] kill';
443 sync_with_i3;
444 }
445 }
446
447 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 survives inplace restarts with fullscreen containers
17 #
18 use i3test;
19
20 fresh_workspace;
21
22 open_window;
23 open_window;
24
25 cmd 'layout stacking';
26 sync_with_i3;
27
28 cmd 'fullscreen';
29 sync_with_i3;
30
31 cmd 'restart';
32
33 does_i3_live;
34
35 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if the 'border toggle' command works correctly
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 cmd 'open';
23
24 my @nodes = @{get_ws_content($tmp)};
25 is(@nodes, 1, 'one container on this workspace');
26 is($nodes[0]->{border}, 'normal', 'border style normal');
27
28 cmd 'border 1pixel';
29 @nodes = @{get_ws_content($tmp)};
30 is($nodes[0]->{border}, 'pixel', 'border style pixel');
31 is($nodes[0]->{current_border_width}, 1, 'border width = 1px');
32
33 cmd 'border none';
34 @nodes = @{get_ws_content($tmp)};
35 is($nodes[0]->{border}, 'none', 'border style none');
36 is($nodes[0]->{current_border_width}, 0, 'border width = 0px');
37
38 cmd 'border normal';
39 @nodes = @{get_ws_content($tmp)};
40 is($nodes[0]->{border}, 'normal', 'border style back to normal');
41 is($nodes[0]->{current_border_width}, 2, 'border width = 2px');
42
43 cmd 'border toggle';
44 @nodes = @{get_ws_content($tmp)};
45 is($nodes[0]->{border}, 'none', 'border style none');
46 is($nodes[0]->{current_border_width}, 0, 'border width = 0px');
47
48 cmd 'border toggle';
49 @nodes = @{get_ws_content($tmp)};
50 is($nodes[0]->{border}, 'pixel', 'border style pixel');
51 is($nodes[0]->{current_border_width}, 1, 'border width = 1px');
52
53 cmd 'border toggle';
54 @nodes = @{get_ws_content($tmp)};
55 is($nodes[0]->{border}, 'normal', 'border style back to normal');
56 is($nodes[0]->{current_border_width}, 2, 'border width = 2px');
57
58 cmd 'border toggle 10';
59 @nodes = @{get_ws_content($tmp)};
60 is($nodes[0]->{border}, 'none', 'border style back to none even with width argument');
61 is($nodes[0]->{current_border_width}, 0, 'border width = 0px');
62
63 cmd 'border toggle 10';
64 @nodes = @{get_ws_content($tmp)};
65 is($nodes[0]->{border}, 'pixel', 'border style pixel');
66 is($nodes[0]->{current_border_width}, 10, 'border width = 10px');
67
68 cmd 'border toggle 10';
69 @nodes = @{get_ws_content($tmp)};
70 is($nodes[0]->{border}, 'normal', 'border style back to normal');
71 is($nodes[0]->{current_border_width}, 10, 'border width = 10px');
72
73 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if the 'force_focus_wrapping' config directive works correctly.
17 #
18 use i3test i3_autostart => 0;
19
20 #####################################################################
21 # 1: test the wrapping behaviour without force_focus_wrapping
22 #####################################################################
23
24 my $config = <<EOT;
25 # i3 config file (v4)
26 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
27 EOT
28
29 my $pid = launch_with_config($config);
30
31 my $tmp = fresh_workspace;
32
33 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
34
35 my $first = open_window;
36 my $second = open_window;
37
38 cmd 'layout tabbed';
39 cmd 'focus parent';
40
41 my $third = open_window;
42 is($x->input_focus, $third->id, 'third window focused');
43
44 cmd 'focus left';
45 is($x->input_focus, $second->id, 'second window focused');
46
47 cmd 'focus left';
48 is($x->input_focus, $first->id, 'first window focused');
49
50 # now test the wrapping
51 cmd 'focus left';
52 is($x->input_focus, $second->id, 'second window focused');
53
54 # but focusing right should not wrap now, but instead focus the third window
55 cmd 'focus right';
56 is($x->input_focus, $third->id, 'third window focused');
57
58 exit_gracefully($pid);
59
60 #####################################################################
61 # 2: test the wrapping behaviour with force_focus_wrapping
62 #####################################################################
63
64 $config = <<EOT;
65 # i3 config file (v4)
66 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
67 force_focus_wrapping true
68 EOT
69
70 $pid = launch_with_config($config);
71
72 $tmp = fresh_workspace;
73
74 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
75
76 $first = open_window;
77 $second = open_window;
78
79 cmd 'layout tabbed';
80 cmd 'focus parent';
81
82 $third = open_window;
83
84 is($x->input_focus, $third->id, 'third window focused');
85
86 cmd 'focus left';
87 is($x->input_focus, $second->id, 'second window focused');
88
89 cmd 'focus left';
90 is($x->input_focus, $first->id, 'first window focused');
91
92 # now test the wrapping
93 cmd 'focus left';
94 is($x->input_focus, $second->id, 'second window focused');
95
96 # focusing right should now be forced to wrap
97 cmd 'focus right';
98 is($x->input_focus, $first->id, 'first window focused');
99
100 exit_gracefully($pid);
101
102 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if i3-migrate-config-to-v4 correctly migrates all config file
17 # directives and commands
18 #
19 use i3test i3_autostart => 0;
20 use Cwd qw(abs_path);
21 use File::Temp qw(tempfile tempdir);
22 use v5.10;
23
24 sub migrate_config {
25 my ($config) = @_;
26
27 my ($fh, $tmpfile) = tempfile('/tmp/i3-migrate-cfg.XXXXXX', UNLINK => 1);
28 print $fh $config;
29 close($fh);
30
31 my $cmd = "sh -c 'exec i3-migrate-config-to-v4 --v3 <$tmpfile'";
32 return [ split /\n/, qx($cmd) ];
33 }
34
35 sub line_exists {
36 my ($lines, $pattern) = @_;
37
38 for my $line (@$lines) {
39 return 1 if $line =~ $pattern;
40 }
41
42 return 0
43 }
44
45 #####################################################################
46 # check that some directives remain untouched
47 #####################################################################
48
49 my $input = <<EOT;
50 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
51 EOT
52
53 my $output = migrate_config($input);
54 ok(line_exists($output, qr|font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1|), 'font directive unchanged');
55
56 $input = <<EOT;
57 floating_Modifier Mod1
58 focus_follows_mouse true
59 ipc-socket /tmp/i3-ipc.sock
60 ipc_socket /tmp/i3-ipc.sock
61 exec /usr/bin/i3
62 set stuff Mod1
63 assign "XTerm" → 3
64 assign "XTerm" → ~5
65 client.focused #2F343A #900000 #FFFFFF
66 client.focused_inactive #FF0000 #FF0000 #FF0000
67 client.unfocused #00FF00 #00FF00 #00FF00
68 client.urgent #0000FF #0000FF #0000FF
69 client.background #000000
70 EOT
71
72 $output = migrate_config($input);
73 ok(line_exists($output, qr|^floating_Modifier Mod1$|), 'floating_modifier unchanged');
74 ok(line_exists($output, qr|^focus_follows_mouse true$|), 'focus_follows_mouse unchanged');
75 ok(line_exists($output, qr|^ipc-socket /tmp/i3-ipc.sock$|), 'ipc-socket unchanged');
76 ok(line_exists($output, qr|^ipc_socket /tmp/i3-ipc.sock$|), 'ipc_socket unchanged');
77 ok(line_exists($output, qr|^exec /usr/bin/i3|), 'exec unchanged');
78 ok(line_exists($output, qr|^set stuff Mod1|), 'set unchanged');
79 ok(line_exists($output, qr|^assign "XTerm" → 3|), 'assign unchanged');
80 ok(line_exists($output, qr|^assign "XTerm" → ~5|), 'assign unchanged');
81 ok(line_exists($output, qr|^client\.focused #2F343A #900000 #FFFFFF$|), 'client.focused unchanged');
82 ok(line_exists($output, qr|^client\.focused_inactive #FF0000 #FF0000 #FF0000$|), 'client.focused_inactive unchanged');
83 ok(line_exists($output, qr|^client\.unfocused #00FF00 #00FF00 #00FF00$|), 'client.unfocused unchanged');
84 ok(line_exists($output, qr|^client\.urgent #0000FF #0000FF #0000FF$|), 'client.urgent unchanged');
85 ok(line_exists($output, qr|^client\.background #000000$|), 'client.background unchanged');
86
87 #####################################################################
88 # check whether the bar colors get removed properly
89 #####################################################################
90
91 $input = <<EOT;
92 bar.focused #FFFF00 #FFFF00 #FFFF00
93 bar.unfocused #FFFF00 #FFFF00 #FFFF00
94 bar.urgent #FFFF00 #FFFF00 #FFFF00
95 EOT
96
97 $output = migrate_config($input);
98 ok(!line_exists($output, qr|^bar\.|), 'no bar. lines');
99 ok(line_exists($output, qr|^#.*REMOVED bar|), 'note bar. removed');
100
101
102 #####################################################################
103 # check whether the other directives get converted correctly
104 #####################################################################
105
106 $input = <<EOT;
107 new_container stacking
108 workspace_bar no
109 new_window bb
110 EOT
111
112 $output = migrate_config($input);
113 ok(line_exists($output, qr|^workspace_layout stacking$|), 'new_container changed');
114 ok(line_exists($output, qr|REMOVED workspace_bar|), 'workspace_bar removed');
115 ok(!line_exists($output, qr|^workspace_bar|), 'no workspace_bar in the output');
116 ok(line_exists($output, qr|^new_window none$|), 'new_window changed');
117
118 #####################################################################
119 # check whether new_window's parameters get changed correctly
120 #####################################################################
121
122 $input = <<EOT;
123 new_window bb
124 new_window bn
125 new_window bp
126 EOT
127 $output = migrate_config($input);
128 like($output->[0], qr|^new_window none$|, 'new_window bb changed');
129 like($output->[1], qr|^new_window normal$|, 'new_window bn changed');
130 like($output->[2], qr|^new_window 1pixel$|, 'new_window bp changed');
131
132 #####################################################################
133 # check that some commands remain untouched
134 #####################################################################
135
136 $input = <<EOT;
137 bindsym Mod1+s exec /usr/bin/urxvt
138 bindsym Mod1+s mark foo
139 bindsym Mod1+s restart
140 bindsym Mod1+s reload
141 bindsym Mod1+s exit
142 bind Mod1+c exec /usr/bin/urxvt
143 mode "asdf" {
144 bind 36 mode default
145 }
146 EOT
147
148 $output = migrate_config($input);
149 ok(line_exists($output, qr|^bindsym Mod1\+s exec /usr/bin/urxvt$|), 'exec unchanged');
150 ok(line_exists($output, qr|^bindsym Mod1\+s mark foo$|), 'mark unchanged');
151 ok(line_exists($output, qr|^bindsym Mod1\+s restart$|), 'restart unchanged');
152 ok(line_exists($output, qr|^bindsym Mod1\+s reload$|), 'reload unchanged');
153 ok(line_exists($output, qr|^bindsym Mod1\+s exit$|), 'exit unchanged');
154 ok(line_exists($output, qr|^bindcode Mod1\+c exec /usr/bin/urxvt$|), 'bind changed to bindcode');
155 ok(line_exists($output, qr|^mode "asdf" \{$|), 'mode asdf unchanged');
156 ok(line_exists($output, qr|^bindcode 36 mode \"default\"$|), 'mode default unchanged');
157 ok(line_exists($output, qr|^}$|), 'closing mode bracket still there');
158
159 #####################################################################
160 # check the simple command replacements
161 #####################################################################
162
163 $input = <<EOT;
164 bindsym Mod1+s s
165 bindsym Mod1+s d
166 bindsym Mod1+s T
167
168 bindsym Mod1+s f
169 bindsym Mod1+s fg
170
171 bindsym Mod1+s t
172
173 bindsym Mod1+s h
174 bindsym Mod1+s j
175 bindsym Mod1+s k
176 bindsym Mod1+s l
177
178 bindsym Mod1+s mh
179 bindsym Mod1+s mj
180 bindsym Mod1+s mk
181 bindsym Mod1+s ml
182
183 bindsym Mod1+s bn
184 bindsym Mod1+s bp
185 bindsym Mod1+s bb
186 bindsym Mod1+s bt
187
188 bindsym Mod1+j wch
189 bindsym Mod1+j wcml
190
191 bindsym Mod1+k kill
192
193 bindsym Mod1+n nw
194 bindsym Mod1+p pw
195 EOT
196
197 $output = migrate_config($input);
198 ok(line_exists($output, qr|^bindsym Mod1\+s layout stacking$|), 's replaced');
199 ok(line_exists($output, qr|^bindsym Mod1\+s layout toggle split$|), 'd replaced');
200 ok(line_exists($output, qr|^bindsym Mod1\+s layout tabbed$|), 'T replaced');
201 ok(line_exists($output, qr|^bindsym Mod1\+s fullscreen$|), 'f replaced');
202 ok(line_exists($output, qr|^bindsym Mod1\+s fullscreen global$|), 'fg replaced');
203 ok(line_exists($output, qr|^bindsym Mod1\+s floating toggle$|), 't replaced');
204 ok(line_exists($output, qr|^bindsym Mod1\+s focus left$|), 'h replaced');
205 ok(line_exists($output, qr|^bindsym Mod1\+s focus down$|), 'j replaced');
206 ok(line_exists($output, qr|^bindsym Mod1\+s focus up$|), 'k replaced');
207 ok(line_exists($output, qr|^bindsym Mod1\+s focus right$|), 'l replaced');
208 ok(line_exists($output, qr|^bindsym Mod1\+s move left$|), 'mh replaced');
209 ok(line_exists($output, qr|^bindsym Mod1\+s move down$|), 'mj replaced');
210 ok(line_exists($output, qr|^bindsym Mod1\+s move up$|), 'mk replaced');
211 ok(line_exists($output, qr|^bindsym Mod1\+s move right$|), 'ml replaced');
212 ok(line_exists($output, qr|^bindsym Mod1\+s border normal$|), 'bn replaced');
213 ok(line_exists($output, qr|^bindsym Mod1\+s border 1pixel$|), 'bp replaced');
214 ok(line_exists($output, qr|^bindsym Mod1\+s border none$|), 'bb replaced');
215 ok(line_exists($output, qr|^bindsym Mod1\+s border toggle$|), 'bt replaced');
216 ok(line_exists($output, qr|^bindsym Mod1\+j focus parent; focus left$|), 'with container replaced with focus parent; focus left');
217 ok(line_exists($output, qr|^bindsym Mod1\+j focus parent; move right$|), 'with container replaced with focus parent; move right');
218 ok(line_exists($output, qr|^bindsym Mod1\+k kill$|), 'kill unchanged');
219 ok(line_exists($output, qr|^bindsym Mod1\+n workspace next$|), 'nw replaced');
220 ok(line_exists($output, qr|^bindsym Mod1\+p workspace prev$|), 'pw replaced');
221
222 #####################################################################
223 # check more advanced replacements
224 #####################################################################
225
226 $input = <<EOT;
227 bindsym Mod1+s goto foo
228 EOT
229
230 $output = migrate_config($input);
231 ok(line_exists($output, qr|^bindsym Mod1\+s \[con_mark="foo"\] focus$|), 'goto replaced');
232
233 #####################################################################
234 # check whether focus's parameters get changed correctly
235 #####################################################################
236
237 $input = <<EOT;
238 bindsym Mod1+f focus 3
239 bindsym Mod1+f focus floating
240 bindsym Mod1+f focus tiling
241 bindsym Mod1+f focus ft
242 EOT
243
244 $output = migrate_config($input);
245 like($output->[0], qr|^#.*focus.*obsolete.*focus 3$|, 'focus [number] gone');
246 like($output->[1], qr|^bindsym Mod1\+f focus floating$|, 'focus floating unchanged');
247 like($output->[2], qr|^bindsym Mod1\+f focus tiling$|, 'focus tiling unchanged');
248 like($output->[3], qr|^bindsym Mod1\+f focus mode_toggle$|, 'focus ft changed');
249
250 #####################################################################
251 # check whether resize's parameters get changed correctly
252 #####################################################################
253
254 $input = <<EOT;
255 bindsym Mod1+f resize left +10
256 bindsym Mod1+f resize top -20
257 bindsym Mod1+f resize right -20
258 bindsym Mod1+f resize bottom +23
259 bindsym Mod1+f resize left \t +10
260 EOT
261
262 $output = migrate_config($input);
263 like($output->[0], qr|^bindsym Mod1\+f resize grow left 10 px$|, 'resize left changed');
264 like($output->[1], qr|^bindsym Mod1\+f resize shrink up 20 px$|, 'resize top changed');
265 like($output->[2], qr|^bindsym Mod1\+f resize shrink right 20 px$|, 'resize right changed');
266 like($output->[3], qr|^bindsym Mod1\+f resize grow down 23 px$|, 'resize bottom changed');
267
268 #####################################################################
269 # also resizing, but with indention this time
270 #####################################################################
271
272 like($output->[4], qr|^bindsym Mod1\+f resize grow left 10 px$|, 'resize left changed');
273
274 #####################################################################
275 # check whether jump's parameters get changed correctly
276 #####################################################################
277
278 $input = <<EOT;
279 bindsym Mod1+f jump 3
280 bindsym Mod1+f jump 3 4 5
281 bindsym Mod1+f jump "XTerm"
282 bindsym Mod1+f jump "XTerm/irssi"
283 EOT
284
285 $output = migrate_config($input);
286 like($output->[0], qr|^#.*obsolete.*jump 3$|, 'jump to workspace removed');
287 like($output->[1], qr|^#.*obsolete.*jump 3 4 5$|, 'jump to workspace + col/row removed');
288 like($output->[2], qr|^bindsym Mod1\+f \[class="XTerm"\] focus$|, 'jump changed');
289 like($output->[3], qr|^bindsym Mod1\+f \[class="XTerm" title="irssi"\] focus$|, 'jump changed');
290
291 #####################################################################
292 # check whether workspace commands are handled correctly
293 #####################################################################
294
295 $output = migrate_config('workspace 3 output VGA-1');
296 ok(line_exists($output, qr|^workspace 3 output VGA-1$|), 'workspace assignment unchanged');
297
298 $output = migrate_config('workspace 3 work');
299 ok(!line_exists($output, qr|^workspace|), 'workspace name not present');
300 ok(line_exists($output, qr|#.*workspace name.*bindings|), 'note present');
301
302 $input = <<EOT;
303 workspace 3 work
304 bindsym Mod1+3 3
305 EOT
306 $output = migrate_config($input);
307 ok(!line_exists($output, qr|^workspace|), 'workspace name not present');
308 ok(line_exists($output, qr|^bindsym Mod1\+3 workspace work|), 'named workspace in bindings');
309
310 # The same, but in reverse order
311 $input = <<EOT;
312 bindsym Mod1+3 3
313 workspace 3 work
314 EOT
315 $output = migrate_config($input);
316 ok(!line_exists($output, qr|^workspace|), 'workspace name not present');
317 ok(line_exists($output, qr|^bindsym Mod1\+3 workspace work|), 'named workspace in bindings');
318
319 $output = migrate_config('bindsym Mod1+3 3');
320 ok(line_exists($output, qr|^bindsym Mod1\+3 workspace 3|), 'workspace changed');
321
322 $output = migrate_config('bindsym Mod1+3 m3');
323 ok(line_exists($output, qr|^bindsym Mod1\+3 move container to workspace 3|), 'move workspace changed');
324
325 $input = <<EOT;
326 workspace 3 work
327 bindsym Mod1+3 m3
328 EOT
329 $output = migrate_config($input);
330 ok(!line_exists($output, qr|^workspace|), 'workspace name not present');
331 ok(line_exists($output, qr|^bindsym Mod1\+3 move container to workspace work|), 'move to named workspace in bindings');
332
333 #####################################################################
334 # check whether an i3bar call is added if the workspace bar bar was enabled
335 #####################################################################
336
337 $output = migrate_config('');
338 ok(line_exists($output, qr|bar \{|), 'i3bar added');
339
340 $output = migrate_config('workspace_bar enable');
341 ok(line_exists($output, qr|bar \{|), 'i3bar added');
342
343 $output = migrate_config('workspace_bar no');
344 ok(!line_exists($output, qr|bar \{|), 'no i3bar added');
345
346 #####################################################################
347 # check whether the mode command gets quotes
348 #####################################################################
349
350 $output = migrate_config('bindsym Mod1+m mode foobar');
351 ok(line_exists($output, qr|^bindsym Mod1\+m mode "foobar"|), 'mode got quotes');
352
353 done_testing();
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # checks if i3 starts up on workspace '1' or the first configured named workspace
17 #
18 use i3test i3_autostart => 0;
19
20 ##############################################################
21 # 1: i3 should start with workspace '1'
22 ##############################################################
23
24 my $config = <<EOT;
25 # i3 config file (v4)
26 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
27 EOT
28
29 my $pid = launch_with_config($config);
30
31 my @names = @{get_workspace_names()};
32 is_deeply(\@names, [ '1' ], 'i3 starts on workspace 1 without any configuration');
33
34 exit_gracefully($pid);
35
36 ##############################################################
37 # 2: with named workspaces, i3 should start on the first named one
38 ##############################################################
39
40 $config = <<EOT;
41 # i3 config file (v4)
42 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
43
44 bindsym Mod1+1 workspace foobar
45 EOT
46
47 $pid = launch_with_config($config);
48
49 @names = @{get_workspace_names()};
50 is_deeply(\@names, [ 'foobar' ], 'i3 starts on named workspace foobar');
51
52 exit_gracefully($pid);
53
54 ##############################################################
55 # 3: the same test as 2, but with a quoted workspace name
56 ##############################################################
57
58 $config = <<EOT;
59 # i3 config file (v4)
60 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
61
62 bindsym Mod1+1 workspace "foobar"
63 EOT
64
65 $pid = launch_with_config($config);
66
67 @names = @{get_workspace_names()};
68 is_deeply(\@names, [ 'foobar' ], 'i3 starts on named workspace foobar');
69
70 exit_gracefully($pid);
71
72 ################################################################################
73 # 4: now with whitespace in front of the workspace number
74 ################################################################################
75
76 $config = <<EOT;
77 # i3 config file (v4)
78 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
79
80 bindsym Mod1+1 workspace 3
81 EOT
82
83 $pid = launch_with_config($config);
84
85 @names = @{get_workspace_names()};
86 is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3 without whitespace');
87
88 exit_gracefully($pid);
89
90 ################################################################################
91 # 5: now with a binding that contains multiple commands
92 ################################################################################
93
94 $config = <<EOT;
95 # i3 config file (v4)
96 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
97
98 bindsym Mod1+1 workspace 3; exec foo
99 EOT
100
101 $pid = launch_with_config($config);
102
103 @names = @{get_workspace_names()};
104 is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3 without ;exec foo');
105
106 exit_gracefully($pid);
107
108 ################################################################################
109 # 6: verify internal binding reordering does not affect startup workspace
110 ################################################################################
111
112 $config = <<EOT;
113 # i3 config file (v4)
114 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
115
116 bindsym Mod1+1 workspace 3
117 bindsym Shift+Mod1+1 workspace 4
118 EOT
119
120 $pid = launch_with_config($config);
121
122 @names = @{get_workspace_names()};
123 is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3');
124
125 exit_gracefully($pid);
126
127 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # checks if the IPC message type get_marks works correctly
17 #
18 use i3test;
19
20 sub get_marks {
21 return i3(get_socket_path())->get_marks->recv;
22 }
23
24 ##############################################################
25 # 1: check that get_marks returns no marks yet
26 ##############################################################
27
28 my $tmp = fresh_workspace;
29
30 my $marks = get_marks();
31 is_deeply($marks, [], 'no marks set so far');
32
33 ##############################################################
34 # 2: check that setting a mark is reflected in the get_marks reply
35 ##############################################################
36
37 cmd 'open';
38 cmd 'mark foo';
39
40 is_deeply(get_marks(), [ 'foo' ], 'mark foo set');
41
42 ##############################################################
43 # 3: check that the mark is gone after killing the container
44 ##############################################################
45
46 cmd 'kill';
47
48 is_deeply(get_marks(), [ ], 'mark gone');
49
50 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the new_window and new_float config option.
17 #
18
19 use i3test i3_autostart => 0;
20
21 #####################################################################
22 # 1: check that new windows start with 'normal' border unless configured
23 # otherwise
24 #####################################################################
25
26 my $config = <<EOT;
27 # i3 config file (v4)
28 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
29 EOT
30
31 my $pid = launch_with_config($config);
32
33 my $tmp = fresh_workspace;
34
35 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
36
37 my $first = open_window;
38
39 my @content = @{get_ws_content($tmp)};
40 ok(@content == 1, 'one container opened');
41 is($content[0]->{border}, 'normal', 'border normal by default');
42
43 exit_gracefully($pid);
44
45 #####################################################################
46 # 2: check that new tiling windows start with '1pixel' border when
47 # configured
48 #####################################################################
49
50 $config = <<EOT;
51 # i3 config file (v4)
52 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
53
54 new_window 1pixel
55 EOT
56
57 $pid = launch_with_config($config);
58
59 $tmp = fresh_workspace;
60
61 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
62
63 $first = open_window;
64
65 @content = @{get_ws_content($tmp)};
66 ok(@content == 1, 'one container opened');
67 is($content[0]->{border}, 'pixel', 'border pixel by default');
68 is($content[0]->{current_border_width}, 1, 'border width pixels 1 (default)');
69
70 exit_gracefully($pid);
71
72 #####################################################################
73 # 3: check that new floating windows start with 'normal' border unless
74 # configured otherwise
75 #####################################################################
76
77 $config = <<EOT;
78 # i3 config file (v4)
79 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
80 EOT
81
82 $pid = launch_with_config($config);
83
84 $tmp = fresh_workspace;
85
86 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
87
88 $first = open_floating_window;
89
90 my $wscontent = get_ws($tmp);
91 my @floating = @{$wscontent->{floating_nodes}};
92 ok(@floating == 1, 'one floating container opened');
93 my $floatingcon = $floating[0];
94 is($floatingcon->{nodes}->[0]->{border}, 'normal', 'border normal by default');
95
96 exit_gracefully($pid);
97
98 #####################################################################
99 # 4: check that new floating windows start with '1pixel' border when
100 # configured
101 #####################################################################
102
103 $config = <<EOT;
104 # i3 config file (v4)
105 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
106
107 new_float 1pixel
108 EOT
109
110 $pid = launch_with_config($config);
111
112 $tmp = fresh_workspace;
113
114 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
115
116 $first = open_floating_window;
117
118 $wscontent = get_ws($tmp);
119 @floating = @{$wscontent->{floating_nodes}};
120 ok(@floating == 1, 'one floating container opened');
121 $floatingcon = $floating[0];
122 is($floatingcon->{nodes}->[0]->{border}, 'pixel', 'border pixel by default');
123
124 exit_gracefully($pid);
125
126 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test for the startup notification protocol.
17 #
18
19 use i3test;
20 use POSIX qw(mkfifo);
21 use File::Temp qw(:POSIX);
22
23 SKIP: {
24
25 skip "X11::XCB too old (need >= 0.07)", 24 if $X11::XCB::VERSION < 0.07;
26
27 use ExtUtils::PkgConfig;
28
29 # setup dependency on libstartup-notification using pkg-config
30 my %sn_config;
31 BEGIN {
32 %sn_config = ExtUtils::PkgConfig->find('libstartup-notification-1.0');
33 }
34
35 use Inline C => Config => LIBS => $sn_config{libs}, CCFLAGS => $sn_config{cflags};
36 use Inline C => <<'END_OF_C_CODE';
37
38 #include <xcb/xcb.h>
39
40 #define SN_API_NOT_YET_FROZEN 1
41 #include <libsn/sn-common.h>
42 #include <libsn/sn-launchee.h>
43
44 static SnDisplay *sndisplay;
45 static SnLauncheeContext *ctx;
46 static xcb_connection_t *conn;
47
48 void init_ctx(void *connptr) {
49 conn = (xcb_connection_t*)connptr;
50 sndisplay = sn_xcb_display_new(conn, NULL, NULL);
51 ctx = sn_launchee_context_new_from_environment(sndisplay, 0);
52 }
53
54 const char *get_startup_id() {
55 return sn_launchee_context_get_startup_id(ctx);
56 }
57
58 void mark_window(int window) {
59 sn_launchee_context_setup_window(ctx, (Window)window);
60 xcb_flush(conn);
61 }
62
63 void complete_startup() {
64 /* mark the startup process complete */
65 sn_launchee_context_complete(ctx);
66 }
67 END_OF_C_CODE
68
69 my $first_ws = fresh_workspace;
70
71 is_num_children($first_ws, 0, 'no containers on this workspace yet');
72
73 ######################################################################
74 # 1) initiate startup, switch workspace, create window
75 # (should be placed on the original workspace)
76 ######################################################################
77
78 # Start a new process via i3 (to initialize a new startup notification
79 # context), then steal its DESKTOP_STARTUP_ID variable. We handle the startup
80 # notification in the testcase from there on.
81 #
82 # This works by setting up a FIFO in which the process (started by i3) will
83 # echo its $DESKTOP_STARTUP_ID. We (blockingly) read the variable into
84 # $startup_id in the testcase.
85 my $tmp = tmpnam();
86 mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!";
87
88 cmd qq|exec echo \$DESKTOP_STARTUP_ID >$tmp|;
89
90 open(my $fh, '<', $tmp);
91 chomp(my $startup_id = <$fh>);
92 close($fh);
93
94 unlink($tmp);
95
96 isnt($startup_id, '', 'startup_id not empty');
97
98 $ENV{DESKTOP_STARTUP_ID} = $startup_id;
99
100 # Create a new libstartup-notification launchee context
101 init_ctx($x->get_xcb_conn());
102
103 # Make sure the context was set up successfully
104 is(get_startup_id(), $startup_id, 'libstartup-notification returns the same id');
105
106 my $second_ws = fresh_workspace;
107
108 is_num_children($second_ws, 0, 'no containers on the second workspace yet');
109
110 my $win = open_window({ dont_map => 1 });
111 mark_window($win->id);
112 $win->map;
113 # We don’t use wait_for_map because the window will not get mapped -- it is on
114 # a different workspace.
115 # We sync with i3 here to make sure $x->input_focus is updated.
116 sync_with_i3;
117
118 is_num_children($second_ws, 0, 'still no containers on the second workspace');
119 is_num_children($first_ws, 1, 'one container on the first workspace');
120
121 ######################################################################
122 # same thing, but with _NET_STARTUP_ID set on the leader
123 ######################################################################
124
125 my $leader = open_window({ dont_map => 1 });
126 mark_window($leader->id);
127
128 $win = open_window({ dont_map => 1, client_leader => $leader });
129 $win->map;
130 sync_with_i3;
131
132 is_num_children($second_ws, 0, 'still no containers on the second workspace');
133 is_num_children($first_ws, 2, 'two containers on the first workspace');
134
135 ######################################################################
136 # verifies that finishing startup doesn't immediately stop windows
137 # from being placed on the sequence's workspace, but that moving
138 # the leader actually deletes the startup sequence mapping
139 ######################################################################
140
141 complete_startup();
142 sync_with_i3;
143
144 # even when renaming the workspace, windows should end up on the correct one
145 cmd "rename workspace $first_ws to temp";
146
147 # Startup has completed but the 30-second deletion time hasn't elapsed,
148 # so this window should still go on the leader's initial workspace.
149 $win = open_window({ dont_map => 1, client_leader => $leader });
150 $win->map;
151 sync_with_i3;
152
153 cmd "rename workspace temp to $first_ws";
154
155 is_num_children($first_ws, 3, 'three containers on the first workspace');
156
157 # empty 'from' workspaces should not crash the renaming of startup sequences
158 cmd "workspace $first_ws";
159 cmd "rename workspace to temp";
160 cmd "rename workspace to $first_ws";
161
162 # Switch to the first workspace and move the focused window to the
163 # second workspace.
164 cmd "workspace $first_ws";
165 cmd "move workspace $second_ws";
166
167 is_num_children($second_ws, 1, 'one container on the second workspace');
168
169 # Create and switch to a new workspace, just to be safe.
170 my $third_ws = fresh_workspace;
171
172 # Moving the window between workspaces should have immediately
173 # removed the startup workspace mapping. New windows with that
174 # leader should be created on the current workspace.
175 $win = open_window({ dont_map => 1, client_leader => $leader });
176 $win->map;
177 sync_with_i3;
178
179 is_num_children($third_ws, 1, 'one container on the third workspace');
180
181 ######################################################################
182 # 2) open another window after the startup process is completed
183 # (should be placed on the current workspace)
184 ######################################################################
185
186 my $otherwin = open_window;
187 is_num_children($third_ws, 2, 'two containers on the third workspace');
188
189 ######################################################################
190 # 3) test that the --no-startup-id flag for exec leads to no DESKTOP_STARTUP_ID
191 # environment variable.
192 ######################################################################
193
194 mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!";
195
196 cmd qq|exec --no-startup-id echo \$DESKTOP_STARTUP_ID >$tmp|;
197
198 open($fh, '<', $tmp);
199 chomp($startup_id = <$fh>);
200 close($fh);
201
202 unlink($tmp);
203
204 is($startup_id, '', 'startup_id empty');
205
206 ######################################################################
207 # 4) same thing, but with double quotes in exec
208 ######################################################################
209
210 mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!";
211
212 cmd qq|exec --no-startup-id "echo \$DESKTOP_STARTUP_ID >$tmp"|;
213
214 open($fh, '<', $tmp);
215 chomp($startup_id = <$fh>);
216 close($fh);
217
218 unlink($tmp);
219
220 is($startup_id, '', 'startup_id empty');
221 }
222
223 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks if the 'workspace back_and_forth' command and the
17 # 'workspace_auto_back_and_forth' config directive work correctly.
18 #
19
20 use i3test i3_autostart => 0;
21
22 my $config = <<EOT;
23 # i3 config file (v4)
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25 EOT
26
27 my $pid = launch_with_config($config);
28
29 my $first_ws = fresh_workspace;
30 ok(get_ws($first_ws)->{focused}, 'first workspace focused');
31
32 my $second_ws = fresh_workspace;
33 ok(get_ws($second_ws)->{focused}, 'second workspace focused');
34
35 my $third_ws = fresh_workspace;
36 ok(get_ws($third_ws)->{focused}, 'third workspace focused');
37
38 cmd 'workspace back_and_forth';
39 ok(get_ws($second_ws)->{focused}, 'second workspace focused');
40
41 #####################################################################
42 # test that without workspace_auto_back_and_forth switching to the same
43 # workspace that is currently focused is a no-op
44 #####################################################################
45
46 cmd qq|workspace "$second_ws"|;
47 ok(get_ws($second_ws)->{focused}, 'second workspace still focused');
48
49 ################################################################################
50 # verify that 'move workspace back_and_forth' works as expected
51 ################################################################################
52
53 cmd qq|workspace "$first_ws"|;
54 my $first_win = open_window;
55
56 cmd qq|workspace "$second_ws"|;
57 my $second_win = open_window;
58
59 is(@{get_ws_content($first_ws)}, 1, 'one container on ws 1 before moving');
60 cmd 'move workspace back_and_forth';
61 is(@{get_ws_content($first_ws)}, 2, 'two containers on ws 1 before moving');
62
63 my $third_win = open_window;
64
65 ################################################################################
66 # verify that moving to the current ws is a no-op without
67 # workspace_auto_back_and_forth.
68 ################################################################################
69
70 cmd qq|workspace "$first_ws"|;
71
72 is(@{get_ws_content($second_ws)}, 1, 'one container on ws 2 before moving');
73 cmd qq|move workspace "$first_ws"|;
74 is(@{get_ws_content($second_ws)}, 1, 'still one container');
75
76 exit_gracefully($pid);
77
78 #####################################################################
79 # the same test, but with workspace_auto_back_and_forth
80 #####################################################################
81
82 $config = <<EOT;
83 # i3 config file (v4)
84 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
85 workspace_auto_back_and_forth yes
86 EOT
87
88 $pid = launch_with_config($config);
89
90 $first_ws = fresh_workspace;
91 ok(get_ws($first_ws)->{focused}, 'first workspace focused');
92
93 $second_ws = fresh_workspace;
94 ok(get_ws($second_ws)->{focused}, 'second workspace focused');
95
96 $third_ws = fresh_workspace;
97 ok(get_ws($third_ws)->{focused}, 'third workspace focused');
98
99 cmd qq|workspace "$third_ws"|;
100 ok(get_ws($second_ws)->{focused}, 'second workspace focused');
101 $first_win = open_window;
102
103 ################################################################################
104 # verify that moving to the current ws moves to the previous one with
105 # workspace_auto_back_and_forth.
106 ################################################################################
107
108 cmd qq|workspace "$first_ws"|;
109 $second_win = open_window;
110
111 is(@{get_ws_content($second_ws)}, 1, 'one container on ws 2 before moving');
112 cmd qq|move workspace "$first_ws"|;
113 is(@{get_ws_content($second_ws)}, 2, 'two containers on ws 2');
114
115 ################################################################################
116 # Now see if "workspace number <number>" also works as expected with
117 # workspace_auto_back_and_forth enabled.
118 ################################################################################
119
120 cmd 'workspace number 5';
121 ok(get_ws('5')->{focused}, 'workspace 5 focused');
122 # ensure it stays open
123 cmd 'open';
124
125 cmd 'workspace number 6';
126 ok(get_ws('6')->{focused}, 'workspace 6 focused');
127 # ensure it stays open
128 cmd 'open';
129
130 cmd 'workspace number 6';
131 is(focused_ws, '5', 'workspace 5 focused again');
132
133 ################################################################################
134 # Rename the workspaces and see if workspace number still works with BAF.
135 ################################################################################
136
137 cmd 'rename workspace 5 to 5: foo';
138 cmd 'rename workspace 6 to 6: baz';
139
140 is(focused_ws, '5: foo', 'workspace 5 still focused');
141
142 cmd 'workspace number 6';
143 is(focused_ws, '6: baz', 'workspace 6 now focused');
144
145 cmd 'workspace number 6';
146 is(focused_ws, '5: foo', 'workspace 5 focused again');
147
148 ################################################################################
149 # Place a window in the scratchpad, see if BAF works after showing the
150 # scratchpad window.
151 ################################################################################
152
153 my $scratchwin = open_window;
154 cmd 'move scratchpad';
155
156 # show scratchpad window
157 cmd 'scratchpad show';
158
159 # hide scratchpad window
160 cmd 'scratchpad show';
161
162 cmd 'workspace back_and_forth';
163 is(focused_ws, '6: baz', 'workspace 6 now focused');
164
165 exit_gracefully($pid);
166
167 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks that the bar config is parsed correctly.
17 #
18
19 use i3test i3_autostart => 0;
20
21 #####################################################################
22 # test a config without any bars
23 #####################################################################
24
25 my $config = <<EOT;
26 # i3 config file (v4)
27 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
28 EOT
29
30 my $pid = launch_with_config($config);
31
32 my $i3 = i3(get_socket_path(0));
33 my $bars = $i3->get_bar_config()->recv;
34 is(@$bars, 0, 'no bars configured');
35
36 exit_gracefully($pid);
37
38 #####################################################################
39 # now provide a simple bar configuration
40 #####################################################################
41
42 $config = <<EOT;
43 # i3 config file (v4)
44 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
45
46 bar {
47 # Start a default instance of i3bar which provides workspace buttons.
48 # Additionally, i3status will provide a statusline.
49 status_command i3status --foo
50 }
51 EOT
52
53 $pid = launch_with_config($config);
54
55 $i3 = i3(get_socket_path(0));
56 $bars = $i3->get_bar_config()->recv;
57 is(@$bars, 1, 'one bar configured');
58
59 my $bar_id = shift @$bars;
60
61 my $bar_config = $i3->get_bar_config($bar_id)->recv;
62 is($bar_config->{status_command}, 'i3status --foo', 'status_command correct');
63 ok(!$bar_config->{verbose}, 'verbose off by default');
64 ok($bar_config->{workspace_buttons}, 'workspace buttons enabled per default');
65 ok($bar_config->{binding_mode_indicator}, 'mode indicator enabled per default');
66 is($bar_config->{mode}, 'dock', 'dock mode by default');
67 is($bar_config->{position}, 'bottom', 'position bottom by default');
68 is($bar_config->{tray_padding}, 2, 'tray_padding ok');
69
70 #####################################################################
71 # ensure that reloading cleans up the old bar configs
72 #####################################################################
73
74 cmd 'reload';
75 $bars = $i3->get_bar_config()->recv;
76 is(@$bars, 1, 'still one bar configured');
77
78 exit_gracefully($pid);
79
80 #####################################################################
81 # validate a more complex configuration
82 #####################################################################
83
84 $config = <<EOT;
85 # i3 config file (v4)
86 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
87
88 bar {
89 # Start a default instance of i3bar which does not provide
90 # workspace buttons.
91 # Additionally, i3status will provide a statusline.
92 status_command i3status --bar
93
94 output HDMI1
95 output HDMI2
96
97 tray_output LVDS1
98 tray_output HDMI2
99 tray_padding 0
100 position top
101 mode dock
102 font Terminus
103 workspace_buttons no
104 binding_mode_indicator no
105 verbose yes
106 socket_path /tmp/foobar
107
108 colors {
109 background #ff0000
110 statusline #00ff00
111 focused_background #cc0000
112 focused_statusline #cccc00
113 focused_separator #0000cc
114
115 focused_workspace #4c7899 #285577 #ffffff
116 active_workspace #333333 #222222 #888888
117 inactive_workspace #333333 #222222 #888888
118 urgent_workspace #2f343a #900000 #ffffff
119 binding_mode #abc123 #123abc #ababab
120 }
121 }
122 EOT
123
124 $pid = launch_with_config($config);
125
126 $i3 = i3(get_socket_path(0));
127 $bars = $i3->get_bar_config()->recv;
128 is(@$bars, 1, 'one bar configured');
129
130 $bar_id = shift @$bars;
131
132 $bar_config = $i3->get_bar_config($bar_id)->recv;
133 is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
134 ok($bar_config->{verbose}, 'verbose on');
135 ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled');
136 ok(!$bar_config->{binding_mode_indicator}, 'mode indicator disabled');
137 is($bar_config->{mode}, 'dock', 'dock mode');
138 is($bar_config->{position}, 'top', 'position top');
139 is_deeply($bar_config->{outputs}, [ 'HDMI1', 'HDMI2' ], 'outputs ok');
140 is_deeply($bar_config->{tray_outputs}, [ 'LVDS1', 'HDMI2' ], 'tray_output ok');
141 is($bar_config->{tray_padding}, 0, 'tray_padding ok');
142 is($bar_config->{font}, 'Terminus', 'font ok');
143 is($bar_config->{socket_path}, '/tmp/foobar', 'socket_path ok');
144 is_deeply($bar_config->{colors},
145 {
146 background => '#ff0000',
147 statusline => '#00ff00',
148 focused_background => '#cc0000',
149 focused_statusline=> '#cccc00',
150 focused_separator => '#0000cc',
151 focused_workspace_border => '#4c7899',
152 focused_workspace_text => '#ffffff',
153 focused_workspace_bg => '#285577',
154 active_workspace_border => '#333333',
155 active_workspace_text => '#888888',
156 active_workspace_bg => '#222222',
157 inactive_workspace_border => '#333333',
158 inactive_workspace_text => '#888888',
159 inactive_workspace_bg => '#222222',
160 urgent_workspace_border => '#2f343a',
161 urgent_workspace_text => '#ffffff',
162 urgent_workspace_bg => '#900000',
163 binding_mode_border => '#abc123',
164 binding_mode_text => '#ababab',
165 binding_mode_bg => '#123abc',
166 }, 'colors ok');
167
168 exit_gracefully($pid);
169
170 #####################################################################
171 # ensure that multiple bars get different IDs
172 #####################################################################
173
174 $config = <<EOT;
175 # i3 config file (v4)
176 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
177
178 bar {
179 # Start a default instance of i3bar which provides workspace buttons.
180 # Additionally, i3status will provide a statusline.
181 status_command i3status --bar
182
183 output HDMI1
184 }
185
186 bar {
187 output VGA1
188 }
189 EOT
190
191 $pid = launch_with_config($config);
192
193 $i3 = i3(get_socket_path(0));
194 $bars = $i3->get_bar_config()->recv;
195 is(@$bars, 2, 'two bars configured');
196 isnt($bars->[0], $bars->[1], 'bar IDs are different');
197
198 my $bar1_config = $i3->get_bar_config($bars->[0])->recv;
199 my $bar2_config = $i3->get_bar_config($bars->[1])->recv;
200
201 isnt($bar1_config->{outputs}, $bar2_config->{outputs}, 'outputs different');
202
203 exit_gracefully($pid);
204
205 #####################################################################
206 # make sure comments work properly
207 #####################################################################
208
209 $config = <<EOT;
210 # i3 config file (v4)
211 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
212
213 bar {
214 # Start a default instance of i3bar which provides workspace buttons.
215 # Additionally, i3status will provide a statusline.
216 status_command i3status --bar
217 #status_command i3status --qux
218 #status_command i3status --qux
219
220 output HDMI1
221 colors {
222 background #000000
223 #background #ffffff
224 }
225 }
226 EOT
227
228 $pid = launch_with_config($config);
229
230 $i3 = i3(get_socket_path(0));
231 $bars = $i3->get_bar_config()->recv;
232 $bar_id = shift @$bars;
233
234 $bar_config = $i3->get_bar_config($bar_id)->recv;
235 is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
236 is($bar_config->{colors}->{background}, '#000000', 'background color ok');
237
238 exit_gracefully($pid);
239
240 #####################################################################
241 # verify that the old syntax still works
242 #####################################################################
243
244 $config = <<EOT;
245 # i3 config file (v4)
246 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
247
248 bar {
249 # Start a default instance of i3bar which does not provide
250 # workspace buttons.
251 # Additionally, i3status will provide a statusline.
252 status_command i3status --bar
253
254 output HDMI1
255 output HDMI2
256
257 tray_output LVDS1
258 tray_output HDMI2
259 position top
260 mode dock
261 font Terminus
262 workspace_buttons no
263 binding_mode_indicator yes
264 verbose yes
265 socket_path /tmp/foobar
266
267 colors {
268 background #ff0000
269 statusline #00ff00
270
271 focused_workspace #ffffff #285577
272 active_workspace #888888 #222222
273 inactive_workspace #888888 #222222
274 urgent_workspace #ffffff #900000
275 }
276 }
277 EOT
278
279 $pid = launch_with_config($config);
280
281 $i3 = i3(get_socket_path(0));
282 $bars = $i3->get_bar_config()->recv;
283 is(@$bars, 1, 'one bar configured');
284
285 $bar_id = shift @$bars;
286
287 cmd 'nop yeah';
288 $bar_config = $i3->get_bar_config($bar_id)->recv;
289 is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
290 ok($bar_config->{verbose}, 'verbose on');
291 ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled');
292 ok($bar_config->{binding_mode_indicator}, 'mode indicator enabled');
293 is($bar_config->{mode}, 'dock', 'dock mode');
294 is($bar_config->{position}, 'top', 'position top');
295 is_deeply($bar_config->{outputs}, [ 'HDMI1', 'HDMI2' ], 'outputs ok');
296 is_deeply($bar_config->{tray_outputs}, [ 'LVDS1', 'HDMI2' ], 'tray_output ok');
297 is($bar_config->{font}, 'Terminus', 'font ok');
298 is($bar_config->{socket_path}, '/tmp/foobar', 'socket_path ok');
299 is_deeply($bar_config->{colors},
300 {
301 background => '#ff0000',
302 statusline => '#00ff00',
303 focused_workspace_text => '#ffffff',
304 focused_workspace_bg => '#285577',
305 active_workspace_text => '#888888',
306 active_workspace_bg => '#222222',
307 inactive_workspace_text => '#888888',
308 inactive_workspace_bg => '#222222',
309 urgent_workspace_text => '#ffffff',
310 urgent_workspace_bg => '#900000',
311 }, '(old) colors ok');
312
313 exit_gracefully($pid);
314
315
316 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if empty workspaces are closed when the last child
17 # exits, as long as they're not empty.
18 #
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22
23 # Get a workspace and open a container
24 my $ws = fresh_workspace;
25 my $con = open_empty_con($i3);
26
27 # Go to a second workspace, kill the container
28 fresh_workspace;
29 cmd "[con_id=\"$con\"] kill";
30
31 # The first workspace should have been closed
32 ok(!workspace_exists($ws), 'workspace closed');
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # The command "move workspace prev; workspace prev" will lead to an error.
17 # This regression is present in 7f9b65f6a752e454c492447be4e21e2ee8faf8fd
18 use i3test;
19
20 my $i3 = i3(get_socket_path());
21
22 # Open one workspace to move the con to
23 my $old = fresh_workspace;
24 my $keep_open_con = open_empty_con($i3);
25
26 # Get a workspace and open a container
27 my $tmp = fresh_workspace;
28 my $con = open_empty_con($i3);
29
30 is_num_children($tmp, 1, 'one container');
31 is_num_children($old, 1, 'one container on old ws');
32
33 cmd 'move workspace prev; workspace prev';
34
35 is_num_children($old, 2, 'container moved away');
36
37 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 does not leak any file descriptors in 'exec'.
17 #
18 use i3test;
19 use POSIX qw(mkfifo);
20 use File::Temp qw(:POSIX tempfile);
21
22 SKIP: {
23 skip "Procfs not available on $^O", 1 if $^O eq 'openbsd';
24
25 my $i3 = i3(get_socket_path());
26
27 my $tmp = tmpnam();
28 mkfifo($tmp, 0600) or die "Could not create FIFO in $tmp";
29 my ($outfh, $outname) = tempfile('/tmp/i3-ls-output.XXXXXX', UNLINK => 1);
30
31 cmd qq|exec ls -l /proc/self/fd >$outname && echo done >$tmp|;
32
33 open(my $fh, '<', $tmp);
34 # Block on the FIFO, this will return exactly when the command is done.
35 <$fh>;
36 close($fh);
37 unlink($tmp);
38
39 # Get the ls /proc/self/fd output
40 my $output;
41 {
42 local $/;
43 $output = <$outfh>;
44 }
45 close($outfh);
46
47 # Split lines, keep only those which are symlinks.
48 my @lines = grep { /->/ } split("\n", $output);
49
50 my %fds = map { /([0-9]+) -> (.+)$/; ($1, $2) } @lines;
51
52 # Filter out 0, 1, 2 (stdin, stdout, stderr).
53 delete $fds{0};
54 delete $fds{1};
55 delete $fds{2};
56
57 # Filter out the fd which is caused by ls calling readdir().
58 for my $fd (keys %fds) {
59 delete $fds{$fd} if $fds{$fd} =~ m,^/proc/\d+/fd$,;
60 }
61
62 is(scalar keys %fds, 0, 'No file descriptors leaked');
63
64 }
65
66 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: Changing border style should not have an impact on the size
17 # (geometry) of the child window. See ticket https://bugs.i3wm.org/561
18 # Wrong behaviour manifested itself up to (including) commit
19 # d805d1bbeaf89e11f67c981f94c9f55bbb4b89d9
20 #
21 use i3test;
22
23 my $tmp = fresh_workspace;
24
25 my $win = open_floating_window(rect => [10, 10, 200, 100]);
26
27 my $geometry = $win->rect;
28 is($geometry->{width}, 200, 'width correct');
29 is($geometry->{height}, 100, 'height correct');
30
31 cmd 'border 1pixel';
32
33 $geometry = $win->rect;
34 is($geometry->{width}, 200, 'width correct');
35 is($geometry->{height}, 100, 'height correct');
36
37 ################################################################################
38 # When in fullscreen mode, the original position must not be overwritten.
39 ################################################################################
40
41 sub get_floating_con_rect {
42 my ($nodes, $focus) = get_ws($tmp);
43 my $floating_con = $nodes->{floating_nodes}->[0];
44 return $floating_con->{rect};
45 }
46 my $old_rect = get_floating_con_rect();
47
48 cmd 'fullscreen';
49
50 is_deeply(get_floating_con_rect(), $old_rect, 'Rect the same after going into fullscreen');
51
52 cmd 'border pixel 2';
53
54 is_deeply(get_floating_con_rect(), $old_rect, 'Rect the same after changing border style');
55
56 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: Focusing a dock window should just do nothing, not crash i3.
17 # See ticket https://bugs.i3wm.org/575
18 # Wrong behaviour manifested itself up to (including) commit
19 # 340592a532b5259c3a3f575de5a9639fad4d1459
20 #
21 use i3test;
22
23 fresh_workspace;
24
25 my $window = open_window(
26 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
27 );
28
29 cmd '[title="' . $window->name . '"] focus';
30
31 does_i3_live;
32
33 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks that variables are parsed correctly by using for_window rules with
17 # variables in it.
18 #
19
20 use i3test i3_autostart => 0;
21
22 # starts i3 with the given config, opens a window, returns its border style
23 sub launch_get_border {
24 my ($config) = @_;
25
26 my $pid = launch_with_config($config);
27
28 my $i3 = i3(get_socket_path(0));
29 my $tmp = fresh_workspace;
30
31 my $window = open_window(name => 'special title');
32
33 my @content = @{get_ws_content($tmp)};
34 cmp_ok(@content, '==', 1, 'one node on this workspace now');
35 my $border = $content[0]->{border};
36
37 exit_gracefully($pid);
38
39 return $border;
40 }
41
42 #####################################################################
43 # test thet windows get the default border
44 #####################################################################
45
46 my $config = <<EOT;
47 # i3 config file (v4)
48 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
49 EOT
50
51 is(launch_get_border($config), 'normal', 'normal border');
52
53 #####################################################################
54 # now use a variable and for_window
55 #####################################################################
56
57 $config = <<'EOT';
58 # i3 config file (v4)
59 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
60
61 set $vartest special title
62 for_window [title="$vartest"] border none
63 EOT
64
65 is(launch_get_border($config), 'none', 'no border');
66
67 #####################################################################
68 # check that whitespaces and tabs are ignored
69 #####################################################################
70
71 $config = <<'EOT';
72 # i3 config file (v4)
73 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
74
75 set $vartest special title
76 for_window [title="$vartest"] border none
77 EOT
78
79 is(launch_get_border($config), 'none', 'no border');
80
81 #####################################################################
82 # test that longest matching variable name is substituted
83 #####################################################################
84
85 $config = <<'EOT';
86 # i3 config file (v4)
87 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
88
89 set $var normal title
90 set $vartest special title
91 set $vart mundane title
92 for_window [title="$vartest"] border none
93 EOT
94
95 is(launch_get_border($config), 'none', 'no border');
96
97 #####################################################################
98 # test that variables with longer name than value don't crash i3 with
99 # v3 to v4 conversion.
100 # See: #3076
101 #####################################################################
102
103 $config = <<'EOT';
104 set $var a
105 EOT
106
107 my $pid = launch_with_config($config);
108 does_i3_live;
109 exit_gracefully($pid);
110
111 done_testing;
112
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: resizing a floating split container leads to a crash.
17 # (Ticket #588, present until 4412ccbe5a4fad8a4cd594e6f10f937515a4d37c)
18 #
19 use i3test;
20
21 my $tmp = fresh_workspace;
22
23 my $first = open_window;
24 cmd 'split v';
25 my $second = open_window;
26
27 cmd 'focus parent';
28 cmd 'floating toggle';
29 cmd 'layout stacking';
30
31 cmd 'resize grow up 10 px or 10 ppt';
32
33 does_i3_live;
34
35 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for the scratchpad functionality.
17 #
18 use i3test;
19 use List::Util qw(first);
20
21 my $i3 = i3(get_socket_path());
22 my $tmp = fresh_workspace;
23
24 ################################################################################
25 # 1: Verify that the __i3 output contains the __i3_scratch workspace and that
26 # it’s empty initially. Also, __i3 should not show up in GET_OUTPUTS so that
27 # tools like i3bar will not handle it. Similarly, __i3_scratch should not show
28 # up in GET_WORKSPACES. After all, you should not be able to switch to it.
29 ################################################################################
30
31 my $tree = $i3->get_tree->recv;
32 is($tree->{name}, 'root', 'root node is the first thing we get');
33
34 my @__i3 = grep { $_->{name} eq '__i3' } @{$tree->{nodes}};
35 is(scalar @__i3, 1, 'output __i3 found');
36
37 my $content = first { $_->{type} eq 'con' } @{$__i3[0]->{nodes}};
38 my @workspaces = @{$content->{nodes}};
39 my @workspace_names = map { $_->{name} } @workspaces;
40 ok('__i3_scratch' ~~ @workspace_names, '__i3_scratch workspace found');
41
42 my $get_outputs = $i3->get_outputs->recv;
43 my $get_ws = $i3->get_workspaces->recv;
44 my @output_names = map { $_->{name} } @$get_outputs;
45 my @ws_names = map { $_->{name} } @$get_ws;
46
47 ok(!('__i3' ~~ @output_names), '__i3 not in GET_OUTPUTS');
48 ok(!('__i3_scratch' ~~ @ws_names), '__i3_scratch ws not in GET_WORKSPACES');
49
50 ################################################################################
51 # 2: Verify that you cannot switch to the __i3_scratch workspace and moving
52 # windows to __i3_scratch does not work (users should be aware of the different
53 # behavior and acknowledge that by using the scratchpad commands).
54 ################################################################################
55
56 # Try focusing the workspace.
57 my $__i3_scratch = get_ws('__i3_scratch');
58 is($__i3_scratch->{focused}, 0, '__i3_scratch ws not focused');
59
60 cmd 'workspace __i3_scratch';
61
62 $__i3_scratch = get_ws('__i3_scratch');
63 is($__i3_scratch->{focused}, 0, '__i3_scratch ws still not focused');
64
65
66 # Try moving a window to it.
67 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
68
69 my $window = open_window;
70 cmd 'move workspace __i3_scratch';
71
72 $__i3_scratch = get_ws('__i3_scratch');
73 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
74
75
76 # Try moving the window with the 'output <direction>' command.
77 # We hardcode output left since the pseudo-output will be initialized before
78 # every other output, so it will always be the first one.
79 cmd 'move output left';
80
81 $__i3_scratch = get_ws('__i3_scratch');
82 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
83
84
85 # Try moving the window with the 'output <name>' command.
86 cmd 'move output __i3';
87
88 $__i3_scratch = get_ws('__i3_scratch');
89 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, '__i3_scratch ws empty');
90
91
92 ################################################################################
93 # 3: Verify that 'scratchpad toggle' sends a window to the __i3_scratch
94 # workspace and sets the scratchpad flag to SCRATCHPAD_FRESH. The window’s size
95 # and position will be changed on the next 'scratchpad show'.
96 ################################################################################
97
98 my ($nodes, $focus) = get_ws_content($tmp);
99 is(scalar @$nodes, 1, 'precisely one window on current ws');
100 is($nodes->[0]->{scratchpad_state}, 'none', 'scratchpad_state none');
101
102 cmd 'move scratchpad';
103
104 $__i3_scratch = get_ws('__i3_scratch');
105 my @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
106 is(scalar @scratch_nodes, 1, '__i3_scratch contains our window');
107 ($nodes, $focus) = get_ws_content($tmp);
108 is(scalar @$nodes, 0, 'no window on current ws anymore');
109
110 is($scratch_nodes[0]->{scratchpad_state}, 'fresh', 'scratchpad_state fresh');
111
112 $tree = $i3->get_tree->recv;
113 my $__i3 = first { $_->{name} eq '__i3' } @{$tree->{nodes}};
114 isnt($tree->{focus}->[0], $__i3->{id}, '__i3 output not focused');
115
116 $get_outputs = $i3->get_outputs->recv;
117 $get_ws = $i3->get_workspaces->recv;
118 @output_names = map { $_->{name} } @$get_outputs;
119 @ws_names = map { $_->{name} } @$get_ws;
120
121 ok(!('__i3' ~~ @output_names), '__i3 not in GET_OUTPUTS');
122 ok(!('__i3_scratch' ~~ @ws_names), '__i3_scratch ws not in GET_WORKSPACES');
123
124 ################################################################################
125 # 4: Verify that 'scratchpad show' makes the window visible.
126 ################################################################################
127
128 # Open another window so that we can check if focus is on the scratchpad window
129 # after showing it.
130 my $second_window = open_window;
131 my $old_focus = get_focused($tmp);
132
133 cmd 'scratchpad show';
134
135 isnt(get_focused($tmp), $old_focus, 'focus changed');
136
137 $__i3_scratch = get_ws('__i3_scratch');
138 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
139 is(scalar @scratch_nodes, 0, '__i3_scratch is now empty');
140
141 my $ws = get_ws($tmp);
142 my $output = $tree->{nodes}->[1];
143 my $scratchrect = $ws->{floating_nodes}->[0]->{rect};
144 my $outputrect = $output->{rect};
145
146 is($scratchrect->{width}, $outputrect->{width} * 0.5, 'scratch width is 50%');
147 is($scratchrect->{height}, $outputrect->{height} * 0.75, 'scratch height is 75%');
148 is($scratchrect->{x},
149 ($outputrect->{width} / 2) - ($scratchrect->{width} / 2),
150 'scratch window centered horizontally');
151 is($scratchrect->{y},
152 ($outputrect->{height} / 2 ) - ($scratchrect->{height} / 2),
153 'scratch window centered vertically');
154
155 ################################################################################
156 # 5: Another 'scratchpad show' should make that window go to the scratchpad
157 # again.
158 ################################################################################
159
160 cmd 'scratchpad show';
161
162 $__i3_scratch = get_ws('__i3_scratch');
163 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
164 is(scalar @scratch_nodes, 1, '__i3_scratch contains our window');
165
166 ################################################################################
167 # 6: Resizing the window should disable auto centering on scratchpad show
168 ################################################################################
169
170 cmd 'scratchpad show';
171
172 $ws = get_ws($tmp);
173 is($ws->{floating_nodes}->[0]->{scratchpad_state}, 'fresh',
174 'scratchpad_state fresh');
175
176 cmd 'resize grow width 10 px';
177 cmd 'scratchpad show';
178 cmd 'scratchpad show';
179
180 $ws = get_ws($tmp);
181 $scratchrect = $ws->{floating_nodes}->[0]->{rect};
182 $outputrect = $output->{rect};
183
184 is($ws->{floating_nodes}->[0]->{scratchpad_state}, 'changed',
185 'scratchpad_state changed');
186 is($scratchrect->{width}, $outputrect->{width} * 0.5 + 10, 'scratch width is 50% + 10px');
187
188 cmd 'resize shrink width 10 px';
189 cmd 'scratchpad show';
190
191 ################################################################################
192 # 7: Verify that repeated 'scratchpad show' cycle through the stack, that is,
193 # toggling a visible window should insert it at the bottom of the stack of the
194 # __i3_scratch workspace.
195 ################################################################################
196
197 my $third_window = open_window(name => 'scratch-match');
198 cmd 'move scratchpad';
199
200 $__i3_scratch = get_ws('__i3_scratch');
201 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
202 is(scalar @scratch_nodes, 2, '__i3_scratch contains both windows');
203
204 is($scratch_nodes[0]->{scratchpad_state}, 'changed', 'changed window first');
205 is($scratch_nodes[1]->{scratchpad_state}, 'fresh', 'fresh window is second');
206
207 my $changed_id = $scratch_nodes[0]->{nodes}->[0]->{id};
208 my $fresh_id = $scratch_nodes[1]->{nodes}->[0]->{id};
209 is($scratch_nodes[0]->{id}, $__i3_scratch->{focus}->[0], 'changed window first');
210 is($scratch_nodes[1]->{id}, $__i3_scratch->{focus}->[1], 'fresh window second');
211
212 # Repeatedly use 'scratchpad show' and check that the windows are different.
213 cmd 'scratchpad show';
214
215 is(get_focused($tmp), $changed_id, 'focus changed');
216
217 $ws = get_ws($tmp);
218 $scratchrect = $ws->{floating_nodes}->[0]->{rect};
219 is($scratchrect->{width}, $outputrect->{width} * 0.5, 'scratch width is 50%');
220 is($scratchrect->{height}, $outputrect->{height} * 0.75, 'scratch height is 75%');
221 is($scratchrect->{x},
222 ($outputrect->{width} / 2) - ($scratchrect->{width} / 2),
223 'scratch window centered horizontally');
224 is($scratchrect->{y},
225 ($outputrect->{height} / 2 ) - ($scratchrect->{height} / 2),
226 'scratch window centered vertically');
227
228 cmd 'scratchpad show';
229
230 isnt(get_focused($tmp), $changed_id, 'focus changed');
231
232 cmd 'scratchpad show';
233
234 is(get_focused($tmp), $fresh_id, 'focus changed');
235
236 cmd 'scratchpad show';
237
238 isnt(get_focused($tmp), $fresh_id, 'focus changed');
239
240 ################################################################################
241 # 8: Show it, move it around, hide it. Verify that the position is retained
242 # when showing it again.
243 ################################################################################
244
245 cmd '[title="scratch-match"] scratchpad show';
246
247 isnt(get_focused($tmp), $old_focus, 'scratchpad window shown');
248
249 my $oldrect = get_ws($tmp)->{floating_nodes}->[0]->{rect};
250
251 cmd 'move left';
252
253 $scratchrect = get_ws($tmp)->{floating_nodes}->[0]->{rect};
254 isnt($scratchrect->{x}, $oldrect->{x}, 'x position changed');
255 $oldrect = $scratchrect;
256
257 # hide it, then show it again
258 cmd '[title="scratch-match"] scratchpad show';
259 cmd '[title="scratch-match"] scratchpad show';
260
261 # verify the position is still the same
262 $scratchrect = get_ws($tmp)->{floating_nodes}->[0]->{rect};
263
264 is_deeply($scratchrect, $oldrect, 'position/size the same');
265
266 # hide it again for the next test
267 cmd '[title="scratch-match"] scratchpad show';
268
269 is(get_focused($tmp), $old_focus, 'scratchpad window hidden');
270
271 is(scalar @{get_ws($tmp)->{nodes}}, 1, 'precisely one window on current ws');
272
273 ################################################################################
274 # 9: restart i3 and verify that the scratchpad show still works
275 ################################################################################
276
277 $__i3_scratch = get_ws('__i3_scratch');
278 my $old_nodes = scalar @{$__i3_scratch->{nodes}};
279 my $old_floating_nodes = scalar @{$__i3_scratch->{floating_nodes}};
280
281 cmd 'restart';
282
283 does_i3_live;
284
285 $__i3_scratch = get_ws('__i3_scratch');
286 is(scalar @{$__i3_scratch->{nodes}}, $old_nodes, "number of nodes matches ($old_nodes)");
287 is(scalar @{$__i3_scratch->{floating_nodes}}, $old_floating_nodes, "number of floating nodes matches ($old_floating_nodes)");
288
289 is(scalar @{get_ws($tmp)->{nodes}}, 1, 'still precisely one window on current ws');
290 is(scalar @{get_ws($tmp)->{floating_nodes}}, 0, 'still no floating windows on current ws');
291
292 # verify that we can display the scratchpad window
293 cmd '[title="scratch-match"] scratchpad show';
294
295 $ws = get_ws($tmp);
296 is(scalar @{$ws->{nodes}}, 1, 'still precisely one window on current ws');
297 is(scalar @{$ws->{floating_nodes}}, 1, 'precisely one floating windows on current ws');
298 is($ws->{floating_nodes}->[0]->{scratchpad_state}, 'changed', 'scratchpad_state is "changed"');
299
300 ################################################################################
301 # 10: on an empty workspace, ensure the 'move scratchpad' command does nothing
302 ################################################################################
303
304 $tmp = fresh_workspace;
305
306 cmd 'move scratchpad';
307
308 does_i3_live;
309
310 ################################################################################
311 # 11: focus a workspace and move all of its children to the scratchpad area
312 ################################################################################
313
314 sub verify_scratchpad_move_multiple_win {
315 my $floating = shift;
316
317 my $first = open_window;
318 my $second = open_window;
319
320 if ($floating) {
321 cmd 'floating toggle';
322 cmd 'focus tiling';
323 }
324
325 cmd 'focus parent';
326 cmd 'move scratchpad';
327
328 does_i3_live;
329
330 $ws = get_ws($tmp);
331 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
332 is(scalar @{$ws->{floating_nodes}}, 0, 'no floating windows on ws');
333
334 # show the first window.
335 cmd 'scratchpad show';
336
337 $ws = get_ws($tmp);
338 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
339 is(scalar @{$ws->{floating_nodes}}, 1, 'one floating windows on ws');
340
341 $old_focus = get_focused($tmp);
342
343 cmd 'scratchpad show';
344
345 # show the second window.
346 cmd 'scratchpad show';
347
348 $ws = get_ws($tmp);
349 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
350 is(scalar @{$ws->{floating_nodes}}, 1, 'one floating windows on ws');
351
352 isnt(get_focused($tmp), $old_focus, 'focus changed');
353 }
354
355 $tmp = fresh_workspace;
356 verify_scratchpad_move_multiple_win(0);
357 $tmp = fresh_workspace;
358 verify_scratchpad_move_multiple_win(1);
359
360 ################################################################################
361 # 12: open a scratchpad window on a workspace, switch to another workspace and
362 # call 'scratchpad show' again
363 ################################################################################
364
365 sub verify_scratchpad_move_with_visible_scratch_con {
366 my ($first, $second, $cross_output) = @_;
367
368 cmd "workspace $first";
369
370 my $window1 = open_window;
371 cmd 'move scratchpad';
372
373 my $window2 = open_window;
374 cmd 'move scratchpad';
375
376 # this should bring up window 1
377 cmd 'scratchpad show';
378
379 $ws = get_ws($first);
380 is(scalar @{$ws->{floating_nodes}}, 1, 'one floating node on ws1');
381 is($x->input_focus, $window1->id, "showed the correct scratchpad window1");
382
383 # this should bring up window 1
384 cmd "workspace $second";
385 cmd 'scratchpad show';
386 is($x->input_focus, $window1->id, "showed the correct scratchpad window1");
387
388 my $ws2 = get_ws($second);
389 is(scalar @{$ws2->{floating_nodes}}, 1, 'one floating node on ws2');
390 unless ($cross_output) {
391 ok(!workspace_exists($first), 'ws1 was empty and therefore closed');
392 } else {
393 $ws = get_ws($first);
394 is(scalar @{$ws->{floating_nodes}}, 0, 'ws1 has no floating nodes');
395 }
396
397 # hide window 1 again
398 cmd 'move scratchpad';
399
400 # this should bring up window 2
401 cmd "workspace $first";
402 cmd 'scratchpad show';
403 is($x->input_focus, $window2->id, "showed the correct scratchpad window");
404 }
405
406 # let's clear the scratchpad first
407 sub clear_scratchpad {
408 while (scalar @{get_ws('__i3_scratch')->{floating_nodes}}) {
409 cmd 'scratchpad show';
410 cmd 'kill';
411 }
412 }
413
414 clear_scratchpad;
415 is (scalar @{get_ws('__i3_scratch')->{floating_nodes}}, 0, "scratchpad is empty");
416
417 my ($first, $second);
418 $first = fresh_workspace;
419 $second = fresh_workspace;
420
421 verify_scratchpad_move_with_visible_scratch_con($first, $second, 0);
422 does_i3_live;
423
424
425 ################################################################################
426 # 13: Test whether scratchpad show moves focus to the scratchpad window
427 # when another window on the same workspace has focus
428 ################################################################################
429
430 clear_scratchpad;
431 $ws = fresh_workspace;
432
433 open_window;
434 my $scratch = get_focused($ws);
435 cmd 'move scratchpad';
436 cmd 'scratchpad show';
437
438 open_window;
439 my $not_scratch = get_focused($ws);
440 is(get_focused($ws), $not_scratch, 'not scratch window has focus');
441
442 cmd 'scratchpad show';
443
444 is(get_focused($ws), $scratch, 'scratchpad is focused');
445
446 # TODO: make i3bar display *something* when a window on the scratchpad has the urgency hint
447
448 ################################################################################
449 # 14: Verify that 'move scratchpad' sends floating containers to scratchpad but
450 # does not resize/resposition the container on the next 'scratchpad show', i.e.,
451 # i3 sets the scratchpad flag to SCRATCHPAD_CHANGED
452 ################################################################################
453
454 clear_scratchpad;
455 $tmp = fresh_workspace;
456 open_window;
457
458 ($nodes, $focus) = get_ws_content($tmp);
459 is(scalar @$nodes, 1, 'precisely one window on current ws');
460 is($nodes->[0]->{scratchpad_state}, 'none', 'scratchpad_state none');
461
462 cmd 'floating toggle';
463 cmd 'move scratchpad';
464
465 $__i3_scratch = get_ws('__i3_scratch');
466 @scratch_nodes = @{$__i3_scratch->{floating_nodes}};
467 is(scalar @scratch_nodes, 1, '__i3_scratch contains our window');
468 ($nodes, $focus) = get_ws_content($tmp);
469 is(scalar @$nodes, 0, 'no window on current ws anymore');
470
471 is($scratch_nodes[0]->{scratchpad_state}, 'changed', 'scratchpad_state changed');
472
473 ################################################################################
474 # 15: Verify that 'scratchpad show' returns correct info.
475 ################################################################################
476
477 kill_all_windows;
478
479 my $result = cmd 'scratchpad show';
480 is($result->[0]->{success}, 0, 'no scratchpad window and call to scratchpad failed');
481
482 open_window;
483 cmd 'move scratchpad';
484 $result = cmd 'scratchpad show';
485 is($result->[0]->{success}, 1, 'call to scratchpad succeeded');
486 $result = cmd 'scratchpad show';
487 is($result->[0]->{success}, 1, 'call to scratchpad succeeded');
488
489 kill_all_windows;
490 $result = cmd 'scratchpad show';
491 is($result->[0]->{success}, 0, 'call to scratchpad failed');
492
493 ################################################################################
494 # 16: Verify that 'scratchpad show' with the criteria returns correct info.
495 ################################################################################
496
497 open_window(name => "scratch-match");
498 cmd 'move scratchpad';
499
500 $result = cmd '[title="scratch-match"] scratchpad show';
501 is($result->[0]->{success}, 1, 'call to scratchpad with the criteria succeeded');
502
503 $result = cmd '[title="nomatch"] scratchpad show';
504 is($result->[0]->{success}, 0, 'call to scratchpad with non-matching criteria failed');
505
506 ################################################################################
507 # 17: Open a scratchpad window on a workspace, switch to another workspace and
508 # call 'scratchpad show' again. Verify that it returns correct info.
509 ################################################################################
510
511 fresh_workspace;
512 open_window;
513 cmd 'move scratchpad';
514
515 fresh_workspace;
516 $result = cmd 'scratchpad show';
517 is($result->[0]->{success}, 1, 'call to scratchpad in another workspace succeeded');
518
519 ################################################################################
520 # 18: Disabling floating for a scratchpad window should not work.
521 ################################################################################
522
523 kill_all_windows;
524
525 $ws = fresh_workspace;
526 $window = open_window;
527 cmd 'move scratchpad';
528 cmd '[id=' . $window->id . '] floating disable';
529
530 is(scalar @{get_ws_content($ws)}, 0, 'no window in workspace');
531 cmd 'scratchpad show';
532 is($x->input_focus, $window->id, 'scratchpad window shown');
533
534
535 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: New windows were not opened in the correct place if they
17 # matched an assignment.
18 # Wrong behaviour manifested itself up to (including) commit
19 # f78caf8c5815ae7a66de9e4b734546fd740cc19d
20 #
21 use i3test i3_config => <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24
25 assign [title="testcase"] targetws
26 EOT
27
28 my $i3 = i3(get_socket_path(0));
29
30 cmd 'workspace targetws';
31
32 open_window(name => "testcase");
33 is_num_children('targetws', 1, 'precisely one window');
34
35 open_window(name => "testcase");
36 is_num_children('targetws', 2, 'precisely two windows');
37
38 cmd 'split v';
39
40 open_window(name => "testcase");
41 is_num_children('targetws', 2, 'still two windows');
42
43 # focus parent. the new window should now be opened right next to the last one.
44 cmd 'focus parent';
45
46 open_window(name => "testcase");
47 is_num_children('targetws', 3, 'new window opened next to last one');
48
49 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the standalone parser binary to see if it calls the right code when
17 # confronted with various commands, if it prints proper error messages for
18 # wrong commands and if it terminates in every case.
19 #
20 use i3test i3_autostart => 0;
21
22 sub parser_calls {
23 my ($command) = @_;
24
25 # TODO: use a timeout, so that we can error out if it doesn’t terminate
26 # TODO: better way of passing arguments
27 my $stdout = qx(test.commands_parser '$command' 2>&1 >&-);
28
29 # Filter out all debugging output.
30 my @lines = split("\n", $stdout);
31 @lines = grep { not /^# / } @lines;
32
33 # The criteria management calls are irrelevant and not what we want to test
34 # in the first place.
35 @lines = grep { !(/cmd_criteria_init()/ || /cmd_criteria_match_windows/) } @lines;
36 return join("\n", @lines);
37 }
38
39 ################################################################################
40 # 1: First that the parser properly recognizes commands which are ok.
41 ################################################################################
42
43 # The first call has only a single command, the following ones are consolidated
44 # for performance.
45 is(parser_calls('move workspace 3'),
46 'cmd_move_con_to_workspace_name(3, (null))',
47 'single number (move workspace 3) ok');
48
49 is(parser_calls(
50 'move to workspace 3; ' .
51 'move window to workspace 3; ' .
52 'move container to workspace 3; ' .
53 'move workspace foobar; ' .
54 'move workspace torrent; ' .
55 'move workspace to output LVDS1; ' .
56 'move workspace 3: foobar; ' .
57 'move workspace "3: foobar"; ' .
58 'move workspace "3: foobar, baz"; '),
59 "cmd_move_con_to_workspace_name(3, (null))\n" .
60 "cmd_move_con_to_workspace_name(3, (null))\n" .
61 "cmd_move_con_to_workspace_name(3, (null))\n" .
62 "cmd_move_con_to_workspace_name(foobar, (null))\n" .
63 "cmd_move_con_to_workspace_name(torrent, (null))\n" .
64 "cmd_move_workspace_to_output(LVDS1)\n" .
65 "cmd_move_con_to_workspace_name(3: foobar, (null))\n" .
66 "cmd_move_con_to_workspace_name(3: foobar, (null))\n" .
67 "cmd_move_con_to_workspace_name(3: foobar, baz, (null))",
68 'move ok');
69
70 is(parser_calls('move workspace 3: foobar, nop foo'),
71 "cmd_move_con_to_workspace_name(3: foobar, (null))\n" .
72 "cmd_nop(foo)",
73 'multiple ops (move workspace 3: foobar, nop foo) ok');
74
75 is(parser_calls(
76 'exec i3-sensible-terminal; ' .
77 'exec --no-startup-id i3-sensible-terminal'),
78 "cmd_exec((null), i3-sensible-terminal)\n" .
79 "cmd_exec(--no-startup-id, i3-sensible-terminal)",
80 'exec ok');
81
82 is(parser_calls(
83 'resize shrink left; ' .
84 'resize shrink left 25 px; ' .
85 'resize shrink left 25 px or 33 ppt; ' .
86 'resize shrink left 25'),
87 "cmd_resize(shrink, left, 10, 10)\n" .
88 "cmd_resize(shrink, left, 25, 0)\n" .
89 "cmd_resize(shrink, left, 25, 33)\n" .
90 "cmd_resize(shrink, left, 25, 0)",
91 'simple resize ok');
92
93 is(parser_calls('resize shrink left 25 px or 33 ppt,'),
94 'cmd_resize(shrink, left, 25, 33)',
95 'trailing comma resize ok');
96
97 is(parser_calls('resize shrink left 25 px or 33 ppt;'),
98 'cmd_resize(shrink, left, 25, 33)',
99 'trailing semicolon resize ok');
100
101 is(parser_calls('[con_mark=yay] focus'),
102 "cmd_criteria_add(con_mark, yay)\n" .
103 "cmd_focus()",
104 'criteria focus ok');
105
106 is(parser_calls("[con_mark=yay con_mark=bar] focus"),
107 "cmd_criteria_add(con_mark, yay)\n" .
108 "cmd_criteria_add(con_mark, bar)\n" .
109 "cmd_focus()",
110 'criteria focus ok');
111
112 is(parser_calls("[con_mark=yay\tcon_mark=bar] focus"),
113 "cmd_criteria_add(con_mark, yay)\n" .
114 "cmd_criteria_add(con_mark, bar)\n" .
115 "cmd_focus()",
116 'criteria focus ok');
117
118 is(parser_calls("[con_mark=yay\tcon_mark=bar]\tfocus"),
119 "cmd_criteria_add(con_mark, yay)\n" .
120 "cmd_criteria_add(con_mark, bar)\n" .
121 "cmd_focus()",
122 'criteria focus ok');
123
124 is(parser_calls('[con_mark="yay"] focus'),
125 "cmd_criteria_add(con_mark, yay)\n" .
126 "cmd_focus()",
127 'quoted criteria focus ok');
128
129 # Make sure trailing whitespace is stripped off: While this is not an issue for
130 # commands being parsed due to the configuration, people might send IPC
131 # commands with leading or trailing newlines.
132 is(parser_calls("workspace test\n"),
133 'cmd_workspace_name(test, (null))',
134 'trailing whitespace stripped off ok');
135
136 is(parser_calls("\nworkspace test"),
137 'cmd_workspace_name(test, (null))',
138 'trailing whitespace stripped off ok');
139
140 ################################################################################
141 # 2: Verify that the parser spits out the right error message on commands which
142 # are not ok.
143 ################################################################################
144
145 is(parser_calls('unknown_literal'),
146 "ERROR: Expected one of these tokens: <end>, '[', '" . join("', '", qw(
147 move
148 exec
149 exit
150 restart
151 reload
152 shmlog
153 debuglog
154 border
155 layout
156 append_layout
157 workspace
158 focus
159 kill
160 open
161 fullscreen
162 sticky
163 split
164 floating
165 mark
166 unmark
167 resize
168 rename
169 nop
170 scratchpad
171 swap
172 title_format
173 mode
174 bar
175 )) . "'\n" .
176 "ERROR: Your command: unknown_literal\n" .
177 "ERROR: ^^^^^^^^^^^^^^^",
178 'error for unknown literal ok');
179
180 is(parser_calls('move something to somewhere'),
181 "ERROR: Expected one of these tokens: 'window', 'container', 'to', '--no-auto-back-and-forth', 'workspace', 'output', 'mark', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" .
182 "ERROR: Your command: move something to somewhere\n" .
183 "ERROR: ^^^^^^^^^^^^^^^^^^^^^^",
184 'error for unknown literal ok');
185
186 ################################################################################
187 # 3: Verify that escaping works correctly
188 ################################################################################
189
190 is(parser_calls('workspace "foo"'),
191 'cmd_workspace_name(foo, (null))',
192 'Command with simple double quotes ok');
193
194 is(parser_calls('workspace "foo'),
195 'cmd_workspace_name(foo, (null))',
196 'Command without ending double quotes ok');
197
198 is(parser_calls('workspace "foo \"bar"'),
199 'cmd_workspace_name(foo "bar, (null))',
200 'Command with escaped double quotes ok');
201
202 is(parser_calls('workspace "foo \\'),
203 'cmd_workspace_name(foo \\, (null))',
204 'Command with single backslash in the end ok');
205
206 is(parser_calls('workspace "foo\\\\bar"'),
207 'cmd_workspace_name(foo\\bar, (null))',
208 'Command with escaped backslashes ok');
209
210 is(parser_calls('workspace "foo\\\\\\"bar"'),
211 'cmd_workspace_name(foo\\"bar, (null))',
212 'Command with escaped double quotes after escaped backslashes ok');
213
214 ################################################################################
215 # 4: Verify that resize commands with a "px or ppt"-construction are parsed
216 # correctly
217 ################################################################################
218
219 is(parser_calls("resize shrink width 10 px or"),
220 "ERROR: Expected one of these tokens: <number>\n" .
221 "ERROR: Your command: resize shrink width 10 px or\n" .
222 "ERROR: ",
223 "error for resize command with incomplete 'or'-construction ok");
224
225 is(parser_calls("resize grow left 10 px or 20 ppt"),
226 "cmd_resize(grow, left, 10, 20)",
227 "resize command with 'or'-construction ok");
228
229 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 survives inplace restarts with fullscreen containers
17 #
18 use i3test;
19
20 my $tmp = fresh_workspace;
21
22 open_window(name => 'first');
23 open_window(name => 'second');
24
25 cmd 'focus left';
26
27 my ($nodes, $focus) = get_ws_content($tmp);
28 is(scalar @$nodes, 2, 'two tiling nodes on workspace');
29 is($nodes->[0]->{name}, 'first', 'first node name ok');
30 is($nodes->[1]->{name}, 'second', 'second node name ok');
31 is($focus->[0], $nodes->[0]->{id}, 'first node focused');
32 is($focus->[1], $nodes->[1]->{id}, 'second node second in focus stack');
33
34 cmd 'restart';
35
36 does_i3_live;
37
38 ($nodes, $focus) = get_ws_content($tmp);
39 is(scalar @$nodes, 2, 'still two tiling nodes on workspace');
40 is($nodes->[0]->{name}, 'first', 'first node name ok');
41 is($nodes->[1]->{name}, 'second', 'second node name ok');
42 is($focus->[0], $nodes->[0]->{id}, 'first node focused');
43 is($focus->[1], $nodes->[1]->{id}, 'second node second in focus stack');
44
45 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the floating_{minimum,maximum}_size config options.
17 #
18 # Note that the minimum floating window size is already verified in
19 # t/005-floating.t.
20 #
21
22 use i3test i3_autostart => 0;
23 use X11::XCB qw/PROP_MODE_REPLACE/;
24
25 ################################################################################
26 # 1: check floating_minimum_size (with non-default limits)
27 ################################################################################
28
29 my $config = <<EOT;
30 # i3 config file (v4)
31 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
32
33 # Test with different dimensions than the i3 default.
34 floating_minimum_size 60 x 40
35 EOT
36
37 my $pid = launch_with_config($config);
38
39 my $window = open_floating_window(rect => [ 0, 0, 20, 20 ]);
40 my $rect = $window->rect;
41
42 is($rect->{width}, 60, 'width = 60');
43 is($rect->{height}, 40, 'height = 40');
44
45 exit_gracefully($pid);
46
47 ################################################################################
48 # 2: check floating_minimum_size with -1 (unlimited)
49 ################################################################################
50
51 $config = <<EOT;
52 # i3 config file (v4)
53 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
54
55 floating_minimum_size -1 x -1
56 EOT
57
58 $pid = launch_with_config($config);
59
60 cmd 'nop MEH';
61 $window = open_floating_window(rect => [ 0, 0, 50, 40 ]);
62 $rect = $window->rect;
63
64 is($rect->{width}, 50, 'width = 50');
65 is($rect->{height}, 40, 'height = 40');
66
67 exit_gracefully($pid);
68
69 ################################################################################
70 # 3: check floating_maximum_size
71 ################################################################################
72
73 $config = <<EOT;
74 # i3 config file (v4)
75 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
76
77 # Test with different dimensions than the i3 default.
78 floating_maximum_size 100 x 100
79 EOT
80
81 $pid = launch_with_config($config);
82
83 $window = open_floating_window(rect => [ 0, 0, 150, 150 ]);
84 $rect = $window->rect;
85
86 is($rect->{width}, 100, 'width = 100');
87 is($rect->{height}, 100, 'height = 100');
88
89 exit_gracefully($pid);
90
91 # Test that the feature works at all (without explicit configuration) by
92 # opening a window which is bigger than the testsuite screen (1280x1024).
93
94 $config = <<EOT;
95 # i3 config file (v4)
96 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
97 EOT
98
99 $pid = launch_with_config($config);
100
101 $window = open_floating_window(rect => [ 0, 0, 2048, 2048 ]);
102 $rect = $window->rect;
103
104 cmp_ok($rect->{width}, '<', 2048, 'width < 2048');
105 cmp_ok($rect->{height}, '<', 2048, 'height < 2048');
106
107 exit_gracefully($pid);
108
109 ################################################################################
110 # 4: check floating_maximum_size
111 ################################################################################
112
113 $config = <<EOT;
114 # i3 config file (v4)
115 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
116
117 # Test with different dimensions than the i3 default.
118 floating_maximum_size -1 x -1
119 EOT
120
121 $pid = launch_with_config($config);
122
123 $window = open_floating_window(rect => [ 0, 0, 2048, 2048 ]);
124 $rect = $window->rect;
125
126 is($rect->{width}, 2048, 'width = 2048');
127 is($rect->{height}, 2048, 'height = 2048');
128
129 exit_gracefully($pid);
130
131 ################################################################################
132 # 5: check floating_minimum_size with cmd_resize
133 ################################################################################
134
135 $config = <<EOT;
136 # i3 config file (v4)
137 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
138
139 # Test with different dimensions than the i3 default.
140 floating_minimum_size 60 x 50
141 EOT
142
143 $pid = launch_with_config($config);
144
145 $window = open_floating_window(rect => [ 0, 0, 100, 100 ]);
146 cmd 'border none';
147 cmd 'resize shrink height 80px or 80ppt';
148 cmd 'resize shrink width 80px or 80ppt';
149 sync_with_i3;
150 $rect = $window->rect;
151 is($rect->{width}, 60, 'width = 60');
152 is($rect->{height}, 50, 'height = 50');
153
154 exit_gracefully($pid);
155
156 ################################################################################
157 # 6: check floating_maximum_size with cmd_resize
158 ################################################################################
159
160 $config = <<EOT;
161 # i3 config file (v4)
162 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
163
164 # Test with different dimensions than the i3 default.
165 floating_maximum_size 100 x 100
166 EOT
167
168 $pid = launch_with_config($config);
169
170 $window = open_floating_window(rect => [ 200, 200, 50, 50 ]);
171 cmd 'border none';
172 cmd 'resize grow height 100px or 100ppt';
173 cmd 'resize grow width 100px or 100ppt';
174 sync_with_i3;
175 $rect = $window->rect;
176 is($rect->{width}, 100, 'width = 100');
177 is($rect->{height}, 100, 'height = 100');
178
179 my $old_x = $rect->{x};
180 my $old_y = $rect->{y};
181 cmd 'resize grow up 10px or 10ppt';
182 sync_with_i3;
183 $rect = $window->rect;
184 is($rect->{x}, $old_x, 'window did not move when trying to resize');
185 is($rect->{y}, $old_y, 'window did not move when trying to resize');
186
187 exit_gracefully($pid);
188
189 ################################################################################
190 # 7: check floating_maximum_size with cmd_size
191 ################################################################################
192
193 $config = <<EOT;
194 # i3 config file (v4)
195 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
196
197 # Test with different dimensions than the i3 default.
198 floating_minimum_size 80 x 70
199 floating_maximum_size 100 x 90
200 EOT
201
202 $pid = launch_with_config($config);
203
204 $window = open_floating_window(rect => [ 0, 0, 90, 80 ]);
205 cmd 'border none';
206
207 cmd 'resize set 101 91';
208 sync_with_i3;
209 $rect = $window->rect;
210 is($rect->{width}, 100, 'width did not exceed maximum width');
211 is($rect->{height}, 90, 'height did not exceed maximum height');
212
213 cmd 'resize set 79 69';
214 sync_with_i3;
215 $rect = $window->rect;
216 is($rect->{width}, 80, 'width did not exceed minimum width');
217 is($rect->{height}, 70, 'height did not exceed minimum height');
218
219 exit_gracefully($pid);
220
221 ################################################################################
222 # 8: check minimum_size and maximum_size set by WM_NORMAL_HINTS
223 ################################################################################
224
225 $config = <<EOT;
226 # i3 config file (v4)
227 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
228 EOT
229
230 my $min_width = 150;
231 my $min_height = 100;
232 my $max_width = 250;
233 my $max_height = 200;
234
235 my sub open_with_max_size {
236 # The type of the WM_NORMAL_HINTS property is WM_SIZE_HINTS
237 # https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.2.3
238 my $XCB_ICCCM_SIZE_HINT_P_MIN_SIZE = 0x32;
239 my $XCB_ICCCM_SIZE_HINT_P_MAX_SIZE = 0x16;
240
241 my $flags = $XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | $XCB_ICCCM_SIZE_HINT_P_MAX_SIZE;
242
243 my $pad = 0x00;
244
245 my $window = open_window(
246 before_map => sub {
247 my ($window) = @_;
248
249 my $atomname = $x->atom(name => 'WM_NORMAL_HINTS');
250 my $atomtype = $x->atom(name => 'WM_SIZE_HINTS');
251 $x->change_property(
252 PROP_MODE_REPLACE,
253 $window->id,
254 $atomname->id,
255 $atomtype->id,
256 32,
257 13,
258 pack('C5N8', $flags, $pad, $pad, $pad, $pad, 0, 0, 0, $min_width, $min_height, $max_width, $max_height),
259 );
260 },
261 );
262
263 return $window;
264 }
265
266 my sub check_minsize {
267 sync_with_i3;
268 is($window->rect->{width}, $min_width, 'width = min_width');
269 is($window->rect->{height}, $min_height, 'height = min_height');
270 }
271
272 my sub check_maxsize {
273 sync_with_i3;
274 is($window->rect->{width}, $max_width, 'width = max_width');
275 is($window->rect->{height}, $max_height, 'height = max_height');
276 }
277
278 $pid = launch_with_config($config);
279
280 $window = open_with_max_size;
281 cmd 'floating enable';
282 cmd 'border none';
283
284 cmd "resize set $min_width px $min_height px";
285 check_minsize;
286
287 # Try to resize below minimum width
288 cmd 'resize set ' . ($min_width - 10) . ' px ' . ($min_height - 50) . ' px';
289 check_minsize;
290
291 cmd "resize set $max_width px $max_height px";
292 check_maxsize;
293
294 # Try to resize above maximum width
295 cmd 'resize set ' . ($max_width + 150) . ' px ' . ($max_height + 500) . ' px';
296 check_maxsize;
297
298 exit_gracefully($pid);
299
300 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test for ticket #676: 'scratchpad show' causes a segfault if the scratchpad
17 # window is shown on another workspace.
18 #
19 use i3test;
20
21 my $i3 = i3(get_socket_path());
22 my $tmp = fresh_workspace;
23
24 my $win = open_window;
25
26 my $scratch = open_window(wm_class => 'special');
27 cmd '[class="special"] move scratchpad';
28
29 is_num_children($tmp, 1, 'one window on current ws');
30
31 my $otmp = fresh_workspace;
32 cmd 'scratchpad show';
33
34 cmd "workspace $tmp";
35 cmd '[class="special"] scratchpad show';
36
37 does_i3_live;
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that you can resize across different levels of containers even when
17 # they are all of the same orientation.
18 # (Ticket #754)
19 use i3test;
20
21 my $tmp = fresh_workspace;
22
23 open_window;
24 open_window;
25 cmd 'split v';
26 my $middle = open_window;
27 open_window;
28 cmd 'focus parent';
29 cmd 'split h';
30 open_window;
31
32 cmd '[id="' . $middle->id . '"] focus';
33 is($x->input_focus, $middle->id, 'middle window focused');
34
35 cmd 'resize grow left 10px or 25ppt';
36
37 my ($nodes, $focus) = get_ws_content($tmp);
38
39 cmp_float($nodes->[0]->{percent}, 0.25, 'left container got only 25%');
40 cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
41
42 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that switching between the different layouts works as expected.
17 use i3test;
18
19 my $tmp = fresh_workspace;
20
21 open_window;
22 open_window;
23 cmd 'split v';
24 open_window;
25
26 my ($nodes, $focus) = get_ws_content($tmp);
27 is($nodes->[1]->{layout}, 'splitv', 'layout is splitv currently');
28
29 cmd 'layout stacked';
30 ($nodes, $focus) = get_ws_content($tmp);
31 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
32
33 cmd 'layout tabbed';
34 ($nodes, $focus) = get_ws_content($tmp);
35 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
36
37 cmd 'layout toggle split';
38 ($nodes, $focus) = get_ws_content($tmp);
39 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv again');
40
41 cmd 'layout toggle split';
42 ($nodes, $focus) = get_ws_content($tmp);
43 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
44
45 cmd 'layout toggle split';
46 ($nodes, $focus) = get_ws_content($tmp);
47 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
48
49 cmd 'layout toggle split';
50 ($nodes, $focus) = get_ws_content($tmp);
51 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
52
53 cmd 'layout toggle';
54 ($nodes, $focus) = get_ws_content($tmp);
55 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
56
57 cmd 'layout toggle';
58 ($nodes, $focus) = get_ws_content($tmp);
59 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
60
61 cmd 'layout toggle';
62 ($nodes, $focus) = get_ws_content($tmp);
63 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
64
65 cmd 'layout toggle';
66 ($nodes, $focus) = get_ws_content($tmp);
67 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
68
69 cmd 'layout toggle all';
70 ($nodes, $focus) = get_ws_content($tmp);
71 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
72
73 cmd 'layout toggle all';
74 ($nodes, $focus) = get_ws_content($tmp);
75 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
76
77 cmd 'layout toggle all';
78 ($nodes, $focus) = get_ws_content($tmp);
79 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
80
81 cmd 'layout toggle all';
82 ($nodes, $focus) = get_ws_content($tmp);
83 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
84
85 cmd 'layout toggle all';
86 ($nodes, $focus) = get_ws_content($tmp);
87 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
88
89 cmd 'layout toggle all';
90 ($nodes, $focus) = get_ws_content($tmp);
91 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
92
93 cmd 'layout toggle all';
94 ($nodes, $focus) = get_ws_content($tmp);
95 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
96
97 cmd 'layout toggle splith splitv';
98 ($nodes, $focus) = get_ws_content($tmp);
99 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
100
101 cmd 'layout toggle splith splitv';
102 ($nodes, $focus) = get_ws_content($tmp);
103 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
104
105 cmd 'layout toggle stacked splitv tabbed';
106 ($nodes, $focus) = get_ws_content($tmp);
107 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
108
109 cmd 'layout toggle stacking splitv tabbed';
110 ($nodes, $focus) = get_ws_content($tmp);
111 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
112
113 cmd 'layout toggle stacking splitv tabbed';
114 ($nodes, $focus) = get_ws_content($tmp);
115 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
116
117 cmd 'layout toggle splitv i stacking tabbed';
118 ($nodes, $focus) = get_ws_content($tmp);
119 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
120
121 cmd 'layout toggle stacked';
122 ($nodes, $focus) = get_ws_content($tmp);
123 # this is correct if it does nothing
124 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
125
126 cmd 'layout toggle tabbed stacked';
127 ($nodes, $focus) = get_ws_content($tmp);
128 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
129
130 # obsoletes 'split' ;)
131 cmd 'layout toggle splith splitv';
132 ($nodes, $focus) = get_ws_content($tmp);
133 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
134
135 # nonsense but works expectedly
136 cmd 'layout toggle split split';
137 ($nodes, $focus) = get_ws_content($tmp);
138 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
139
140 cmd 'layout toggle split split';
141 ($nodes, $focus) = get_ws_content($tmp);
142 is($nodes->[1]->{layout}, 'splith', 'layout now splith');
143
144 # testing with arbitrary length and garbage
145 cmd 'layout toggle stacking splith tabbed splitv stacking';
146 ($nodes, $focus) = get_ws_content($tmp);
147 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
148
149 cmd 'layout toggle stacking splith garbage tabbed splitv stacking';
150 ($nodes, $focus) = get_ws_content($tmp);
151 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
152
153 cmd 'layout toggle stacking splith garbage tabbed splitv stacking';
154 ($nodes, $focus) = get_ws_content($tmp);
155 is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
156
157 cmd 'layout toggle splitv splith garbage splitv tabbed stacking splitv';
158 ($nodes, $focus) = get_ws_content($tmp);
159 is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
160
161 cmd 'layout toggle splitv garbage tabbed';
162 ($nodes, $focus) = get_ws_content($tmp);
163 is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
164
165 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that we can get the version number of i3 via IPC.
17 use i3test;
18
19 my $i3 = i3(get_socket_path());
20 $i3->connect->recv;
21 # We explicitly send the version message because AnyEvent::I3’s 'version' sugar
22 # method has a fallback which tries to parse the version number from i3
23 # --version for older versions, and we want to avoid using that.
24 my $version = $i3->message(7, "")->recv;
25
26 # We need to change this when the major version changes (but we need to touch a
27 # lot of changes then anyways).
28 is($version->{major}, 4, 'major version is 4');
29
30 cmp_ok($version->{minor}, '>', 0, 'minor version > 0');
31
32 is(int($version->{minor}), $version->{minor}, 'minor version is an integer');
33 is(int($version->{patch}), $version->{patch}, 'patch version is an integer');
34 like($version->{human_readable}, qr/branch/, 'human readable version contains branch name');
35
36 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that the size requested by floating windows is set by i3, no matter
17 # to which value the new_window option is set.
18 # ticket #770, bug still present in commit ae88accf6fe3817ff42d0d51be1965071194766e
19 use i3test i3_autostart => 0;
20
21 sub test_with_config {
22 my ($value) = @_;
23
24 my $config = <<EOT;
25 # i3 config file (v4)
26 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
27 EOT
28
29 if (defined($value)) {
30 $config .= "$value\n";
31 diag("testing with $value");
32 } else {
33 diag("testing without new_window");
34 }
35
36 my $pid = launch_with_config($config);
37
38 my $tmp = fresh_workspace;
39
40 my $window = open_floating_window({ rect => [ 0, 0, 400, 150 ] });
41
42 my ($absolute, $top) = $window->rect;
43
44 ok($window->mapped, 'Window is mapped');
45 cmp_ok($absolute->{width}, '==', 400, 'requested width kept');
46 cmp_ok($absolute->{height}, '==', 150, 'requested height kept');
47
48 exit_gracefully($pid);
49 }
50
51 test_with_config(undef);
52 test_with_config('new_window 1pixel');
53 test_with_config('new_window normal');
54 test_with_config('new_window none');
55 test_with_config('hide_edge_borders both');
56
57 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that the _NET_ACTIVE_WINDOW message only changes focus when the
17 # window is on a visible workspace and that focus changes properly update this
18 # property on the root window.
19 # ticket #774, bug still present in commit 1e49f1b08a3035c1f238fcd6615e332216ab582e
20 # ticket #1136, bug still present in commit fd07f989fdf441ef335245dd3436a70ff60e8896
21 use i3test;
22
23 sub send_net_active_window {
24 my ($id, $source) = @_;
25
26 $source = (((defined $source) && ($source eq 'pager')) ? 2 : 0);
27
28 my $msg = pack "CCSLLLLLLL",
29 X11::XCB::CLIENT_MESSAGE, # response_type
30 32, # format
31 0, # sequence
32 $id, # destination window
33 $x->atom(name => '_NET_ACTIVE_WINDOW')->id,
34 $source,
35 0,
36 0,
37 0,
38 0;
39
40 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
41 sync_with_i3;
42 }
43
44 sub get_net_active_window {
45 my $cookie = $x->get_property(
46 0,
47 $x->get_root_window(),
48 $x->atom(name => '_NET_ACTIVE_WINDOW')->id,
49 $x->atom(name => 'WINDOW')->id,
50 0,
51 4096,
52 );
53 my $reply = $x->get_property_reply($cookie->{sequence});
54 my $len = $reply->{length};
55
56 return -1 if $len == 0;
57 return unpack("L", $reply->{value});
58
59 }
60
61 my $ws1 = fresh_workspace;
62 my $win1 = open_window;
63 my $win2 = open_window;
64
65 ################################################################################
66 # Ensure that the _NET_ACTIVE_WINDOW ClientMessage works when windows are visible
67 ################################################################################
68
69 is($x->input_focus, $win2->id, 'window 2 has focus');
70
71 send_net_active_window($win1->id);
72
73 is($x->input_focus, $win1->id, 'window 1 has focus');
74
75 ################################################################################
76 # Switch to a different workspace and ensure sending the _NET_ACTIVE_WINDOW
77 # ClientMessage switches to that workspaces only if source indicates it is a
78 # pager and otherwise sets the urgent hint.
79 ################################################################################
80
81 my $ws2 = fresh_workspace;
82 my $win3 = open_window;
83
84 is($x->input_focus, $win3->id, 'window 3 has focus');
85
86 send_net_active_window($win1->id, 'pager');
87
88 is($x->input_focus, $win1->id, 'focus switched to window 1 when message source was a pager');
89
90 cmd '[id="' . $win3->id . '"] focus';
91
92 send_net_active_window($win1->id);
93
94 is($x->input_focus, $win3->id,
95 'focus did not switch to window 1 on a hidden workspace when message source was an application');
96
97 ok(get_ws($ws1)->{urgent}, 'urgent hint set on ws 1');
98
99
100 ################################################################################
101 # Make sure the ClientMessage only works with managed windows, and specifying a
102 # window that is not managed does not crash i3 (#774)
103 ################################################################################
104
105 my $dock = open_window(window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'));
106
107 send_net_active_window($dock->id);
108
109 does_i3_live;
110 is($x->input_focus, $win3->id, 'dock did not get input focus');
111
112 send_net_active_window($x->get_root_window());
113
114 does_i3_live;
115 is($x->input_focus, $win3->id, 'root window did not get input focus');
116
117 ################################################################################
118 # Move a window to the scratchpad, send a _NET_ACTIVE_WINDOW for it and verify
119 # that focus is still unchanged.
120 ################################################################################
121
122 my $scratch = open_window;
123
124 is($x->input_focus, $scratch->id, 'to-scratchpad window has focus');
125
126 cmd 'move scratchpad';
127
128 is($x->input_focus, $win3->id, 'focus reverted to window 3');
129
130 send_net_active_window($scratch->id);
131
132 is($x->input_focus, $win3->id, 'window 3 still focused');
133
134 ################################################################################
135 # A scratchpad window should be shown if _NET_ACTIVE_WINDOW from a pager
136 # is received.
137 ################################################################################
138
139 $scratch = open_window;
140
141 is($x->input_focus, $scratch->id, 'to-scratchpad window has focus');
142
143 cmd 'move scratchpad';
144
145 is($x->input_focus, $win3->id, 'focus reverted to window 3');
146
147 send_net_active_window($scratch->id, 'pager');
148
149 is($x->input_focus, $scratch->id, 'scratchpad window is shown');
150
151 ################################################################################
152 # Verify that the _NET_ACTIVE_WINDOW property is updated on the root window
153 # correctly.
154 ################################################################################
155
156 fresh_workspace;
157
158 sync_with_i3;
159
160 is(get_net_active_window(), 0, 'workspace content focus is indicated by the root property as "None" window');
161
162 my $win4 = open_window;
163
164 cmd '[id="' . $win4->id . '"] focus';
165
166 sync_with_i3;
167
168 is(get_net_active_window(), $win4->id, 'window 4 is indicated as focused by the root property');
169
170 # make a branch
171 open_window;
172 open_window;
173 cmd 'split h';
174 open_window;
175 cmd 'focus parent';
176
177 sync_with_i3;
178
179 is(get_net_active_window(), 0, 'branch focus is indicated by the root property as "None" window');
180
181 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verify that i3 allows strange RandR output names such as DVI-I_1/digital.
17 # Ticket: #785
18 # Bug still in: 4.2-256-ga007283
19 use i3test i3_autostart => 0;
20 use File::Temp qw(tempfile);
21
22 my ($fh, $filename) = tempfile(UNLINK => 1);
23 print $fh <<EOT;
24 # i3 config file (v4)
25 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
26
27 workspace 2 output DVI-I_1/digital
28 EOT
29
30 my $output = qx(i3 -C -c $filename);
31 unlike($output, qr/ERROR/, 'no errors in i3 -C');
32
33 close($fh);
34
35 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: moving a window to the right out of a splitv container would
17 # make it vanish.
18 # Ticket: #790
19 # Bug still in: 4.2-277-ga598544
20 use i3test;
21
22 my $ws = fresh_workspace;
23
24 my $top = open_window;
25 cmd 'split v';
26 my $bottom = open_window;
27
28 is_num_children($ws, 2, 'two windows on workspace level');
29
30 cmd 'move right';
31
32 is_num_children($ws, 2, 'still two windows on workspace level');
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # When using a command which moves a window to scratchpad from an invisible
17 # (e.g. unfocused) workspace and immediately shows that window again, i3
18 # crashed.
19 # Bug still in: 4.2-305-g22922a9
20 use i3test;
21
22 my $ws1 = fresh_workspace;
23 my $invisible_window = open_window;
24 my $other_focusable_window = open_window;
25
26 my $ws2 = fresh_workspace;
27 my $id = $invisible_window->id;
28 cmd qq|[id="$id"] move scratchpad, scratchpad show|;
29
30 does_i3_live;
31
32 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that the IPC 'mode' event is sent when modes are changed.
17 use i3test i3_config => <<EOT;
18 # i3 config file (v4)
19 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
20
21 mode "m1" {
22 bindsym Mod1+x nop foo
23 }
24
25 mode "with spaces" {
26 bindsym Mod1+y nop bar
27 }
28 EOT
29
30 my @events = events_for(
31 sub { cmd 'mode "m1"' },
32 'mode');
33
34 my @changes = map { $_->{change} } @events;
35 is_deeply(\@changes, [ 'm1' ], 'Mode event received');
36
37 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 #
17 # Tests whether the urgency timer works as expected and does not break
18 # urgency handling.
19 #
20
21 use List::Util qw(first);
22 use i3test i3_config => <<EOT;
23 # i3 config file (v4)
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25
26 force_display_urgency_hint 500ms
27 EOT
28 use Time::HiRes qw(sleep);
29
30 #####################################################################
31 # Initial setup: one window on ws1, empty ws2
32 #####################################################################
33
34 my $tmp1 = fresh_workspace;
35 my $w = open_window;
36
37 my $tmp2 = fresh_workspace;
38 cmd "workspace $tmp2";
39
40 $w->add_hint('urgency');
41 sync_with_i3;
42
43 #######################################################################
44 # Create a window on ws1, then switch to ws2, set urgency, switch back
45 #######################################################################
46
47 isnt($x->input_focus, $w->id, 'window not focused');
48
49 my @content = @{get_ws_content($tmp1)};
50 my @urgent = grep { $_->{urgent} } @content;
51 is(@urgent, 1, "window marked as urgent");
52
53 # switch to ws1
54 cmd "workspace $tmp1";
55
56 # this will start the timer
57 sleep(0.1);
58 @content = @{get_ws_content($tmp1)};
59 @urgent = grep { $_->{urgent} } @content;
60 is(@urgent, 1, 'window still marked as urgent');
61
62 # now check if the timer was triggered
63 cmd "workspace $tmp2";
64
65 sleep(0.5);
66 @content = @{get_ws_content($tmp1)};
67 @urgent = grep { $_->{urgent} } @content;
68 is(@urgent, 0, 'window not marked as urgent anymore');
69
70 #######################################################################
71 # Create another window on ws1, focus it, switch to ws2, make the other
72 # window urgent, and switch back. This should not trigger the timer.
73 #######################################################################
74
75 cmd "workspace $tmp1";
76 my $w2 = open_window;
77 is($x->input_focus, $w2->id, 'window 2 focused');
78
79 cmd "workspace $tmp2";
80 $w->delete_hint('urgency');
81 $w->add_hint('urgency');
82 sync_with_i3;
83
84 @content = @{get_ws_content($tmp1)};
85 @urgent = grep { $_->{urgent} } @content;
86 is(@urgent, 1, 'window 1 marked as urgent');
87
88 # Switch back to ws1. This should focus w2.
89 cmd "workspace $tmp1";
90 is($x->input_focus, $w2->id, 'window 2 focused');
91
92 @content = @{get_ws_content($tmp1)};
93 @urgent = grep { $_->{urgent} } @content;
94 is(@urgent, 1, 'window 1 still marked as urgent');
95
96 # explicitly focusing the window should result in immediate urgency reset
97 cmd '[id="' . $w->id . '"] focus';
98 @content = @{get_ws_content($tmp1)};
99 @urgent = grep { $_->{urgent} } @content;
100 is(@urgent, 0, 'window 1 not marked as urgent anymore');
101
102 ################################################################################
103 # open a stack, mark one window as urgent, switch to that workspace and verify
104 # it’s cleared correctly.
105 ################################################################################
106
107 sub count_total_urgent {
108 my ($con) = @_;
109
110 my $urgent = ($con->{urgent} ? 1 : 0);
111 $urgent += count_total_urgent($_) for (@{$con->{nodes}}, @{$con->{floating_nodes}});
112 return $urgent;
113 }
114
115 my $tmp3 = fresh_workspace;
116 open_window;
117 open_window;
118 cmd 'split v';
119 my $split_left = open_window;
120 cmd 'layout stacked';
121
122 cmd "workspace $tmp2";
123
124 is(count_total_urgent(get_ws($tmp3)), 0, "no urgent windows on workspace $tmp3");
125
126 $split_left->add_hint('urgency');
127 sync_with_i3;
128
129 cmp_ok(count_total_urgent(get_ws($tmp3)), '>=', 0, "more than one urgent window on workspace $tmp3");
130
131 cmd "workspace $tmp3";
132
133 # Remove the urgency hint.
134 $split_left->delete_hint('urgency');
135 sync_with_i3;
136
137 sleep(0.6);
138 is(count_total_urgent(get_ws($tmp3)), 0, "no more urgent windows on workspace $tmp3");
139
140 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the standalone parser binary to see if it calls the right code when
17 # confronted with various commands, if it prints proper error messages for
18 # wrong commands and if it terminates in every case.
19 #
20 use i3test i3_autostart => 0;
21 use IPC::Run qw(run);
22
23 sub parser_calls {
24 my ($command) = @_;
25
26 my $stdout;
27 run [ 'test.config_parser', $command ],
28 '>/dev/null',
29 '2>', \$stdout;
30 # TODO: use a timeout, so that we can error out if it doesn’t terminate
31
32 # Filter out all debugging output.
33 my @lines = split("\n", $stdout);
34 @lines = grep { not /^# / } @lines;
35
36 # The criteria management calls are irrelevant and not what we want to test
37 # in the first place.
38 @lines = grep { !(/cfg_criteria_init/ || /cfg_criteria_pop_state/) } @lines;
39 return join("\n", @lines) . "\n";
40 }
41
42 my $config = <<'EOT';
43 mode "meh" {
44 bindsym Mod1 + Shift + x resize grow
45 bindcode Mod1+44 resize shrink
46 bindsym --release Mod1+x exec foo
47 bindsym --whole-window button3 nop
48 bindsym --release --whole-window button3 nop
49 bindsym --border button3 nop
50 bindsym --release --border button3 nop
51 bindsym --exclude-titlebar button3 nop
52 bindsym --whole-window --border --exclude-titlebar button3 nop
53 }
54 EOT
55
56 my $expected = <<'EOT';
57 cfg_enter_mode((null), meh)
58 cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), (null), (null), resize grow)
59 cfg_mode_binding(bindcode, Mod1, 44, (null), (null), (null), (null), resize shrink)
60 cfg_mode_binding(bindsym, Mod1, x, --release, (null), (null), (null), exec foo)
61 cfg_mode_binding(bindsym, (null), button3, (null), (null), --whole-window, (null), nop)
62 cfg_mode_binding(bindsym, (null), button3, --release, (null), --whole-window, (null), nop)
63 cfg_mode_binding(bindsym, (null), button3, (null), --border, (null), (null), nop)
64 cfg_mode_binding(bindsym, (null), button3, --release, --border, (null), (null), nop)
65 cfg_mode_binding(bindsym, (null), button3, (null), (null), (null), --exclude-titlebar, nop)
66 cfg_mode_binding(bindsym, (null), button3, (null), --border, --whole-window, --exclude-titlebar, nop)
67 EOT
68
69 is(parser_calls($config),
70 $expected,
71 'mode bindings ok');
72
73 ################################################################################
74 # exec and exec_always
75 ################################################################################
76
77 $config = <<'EOT';
78 exec geeqie
79 exec --no-startup-id /tmp/foo.sh
80 exec_always firefox
81 exec_always --no-startup-id /tmp/bar.sh
82 EOT
83
84 $expected = <<'EOT';
85 cfg_exec(exec, (null), geeqie)
86 cfg_exec(exec, --no-startup-id, /tmp/foo.sh)
87 cfg_exec(exec_always, (null), firefox)
88 cfg_exec(exec_always, --no-startup-id, /tmp/bar.sh)
89 EOT
90
91 is(parser_calls($config),
92 $expected,
93 'exec okay');
94
95 ################################################################################
96 # for_window
97 ################################################################################
98
99 $config = <<'EOT';
100 for_window [class="^Chrome"] floating enable
101 EOT
102
103 $expected = <<'EOT';
104 cfg_criteria_add(class, ^Chrome)
105 cfg_for_window(floating enable)
106 EOT
107
108 is(parser_calls($config),
109 $expected,
110 'for_window okay');
111
112 ################################################################################
113 # assign
114 ################################################################################
115
116 $config = <<'EOT';
117 assign [class="^Chrome"] 4
118 assign [class="^Chrome"] workspace number 3
119 assign [class="^Chrome"] named workspace
120 assign [class="^Chrome"] "quoted named workspace"
121 assign [class="^Chrome"] → "quoted named workspace"
122 EOT
123
124 $expected = <<'EOT';
125 cfg_criteria_add(class, ^Chrome)
126 cfg_assign(4, 0)
127 cfg_criteria_add(class, ^Chrome)
128 cfg_assign(3, 1)
129 cfg_criteria_add(class, ^Chrome)
130 cfg_assign(named workspace, 0)
131 cfg_criteria_add(class, ^Chrome)
132 cfg_assign(quoted named workspace, 0)
133 cfg_criteria_add(class, ^Chrome)
134 cfg_assign(quoted named workspace, 0)
135 EOT
136
137 is(parser_calls($config),
138 $expected,
139 'for_window okay');
140
141 ################################################################################
142 # floating_minimum_size / floating_maximum_size
143 ################################################################################
144
145 $config = <<'EOT';
146 floating_minimum_size 80x55
147 floating_minimum_size 80 x 55
148 floating_maximum_size 73 x 10
149 EOT
150
151 $expected = <<'EOT';
152 cfg_floating_minimum_size(80, 55)
153 cfg_floating_minimum_size(80, 55)
154 cfg_floating_maximum_size(73, 10)
155 EOT
156
157 is(parser_calls($config),
158 $expected,
159 'floating_minimum_size ok');
160
161 ################################################################################
162 # popup_during_fullscreen
163 ################################################################################
164
165 $config = <<'EOT';
166 popup_during_fullscreen ignore
167 popup_during_fullscreen leave_fullscreen
168 popup_during_fullscreen SMArt
169 EOT
170
171 $expected = <<'EOT';
172 cfg_popup_during_fullscreen(ignore)
173 cfg_popup_during_fullscreen(leave_fullscreen)
174 cfg_popup_during_fullscreen(smart)
175 EOT
176
177 is(parser_calls($config),
178 $expected,
179 'popup_during_fullscreen ok');
180
181
182 ################################################################################
183 # floating_modifier
184 ################################################################################
185
186 $config = <<'EOT';
187 floating_modifier Mod1
188 floating_modifier mOd1
189 EOT
190
191 $expected = <<'EOT';
192 cfg_floating_modifier(Mod1)
193 cfg_floating_modifier(Mod1)
194 EOT
195
196 is(parser_calls($config),
197 $expected,
198 'floating_modifier ok');
199
200 ################################################################################
201 # default_orientation
202 ################################################################################
203
204 $config = <<'EOT';
205 default_orientation horizontal
206 default_orientation vertical
207 default_orientation auto
208 EOT
209
210 $expected = <<'EOT';
211 cfg_default_orientation(horizontal)
212 cfg_default_orientation(vertical)
213 cfg_default_orientation(auto)
214 EOT
215
216 is(parser_calls($config),
217 $expected,
218 'default_orientation ok');
219
220 ################################################################################
221 # workspace_layout
222 ################################################################################
223
224 $config = <<'EOT';
225 workspace_layout default
226 workspace_layout stacked
227 workspace_layout stacking
228 workspace_layout tabbed
229 EOT
230
231 $expected = <<'EOT';
232 cfg_workspace_layout(default)
233 cfg_workspace_layout(stacked)
234 cfg_workspace_layout(stacking)
235 cfg_workspace_layout(tabbed)
236 EOT
237
238 is(parser_calls($config),
239 $expected,
240 'workspace_layout ok');
241
242 ################################################################################
243 # workspace assignments, with trailing whitespace (ticket #921)
244 ################################################################################
245
246 $config = <<'EOT';
247 workspace "3" output DP-1
248 workspace "3" output VGA-1
249 EOT
250
251 $expected = <<'EOT';
252 cfg_workspace(3, DP-1)
253 cfg_workspace(3, VGA-1)
254 EOT
255
256 is(parser_calls($config),
257 $expected,
258 'workspace assignment ok');
259
260 ################################################################################
261 # new_window
262 ################################################################################
263
264 $config = <<'EOT';
265 new_window 1pixel
266 new_window normal
267 new_window none
268 default_border 1pixel
269 default_border normal
270 default_border none
271 new_float 1pixel
272 new_float normal
273 new_float none
274 default_floating_border 1pixel
275 default_floating_border normal
276 default_floating_border none
277 EOT
278
279 $expected = <<'EOT';
280 cfg_default_border(new_window, 1pixel, -1)
281 cfg_default_border(new_window, normal, 2)
282 cfg_default_border(new_window, none, -1)
283 cfg_default_border(default_border, 1pixel, -1)
284 cfg_default_border(default_border, normal, 2)
285 cfg_default_border(default_border, none, -1)
286 cfg_default_border(new_float, 1pixel, -1)
287 cfg_default_border(new_float, normal, 2)
288 cfg_default_border(new_float, none, -1)
289 cfg_default_border(default_floating_border, 1pixel, -1)
290 cfg_default_border(default_floating_border, normal, 2)
291 cfg_default_border(default_floating_border, none, -1)
292 EOT
293
294 # TODO: are there no tests for "border pixel 1" etc?
295
296 is(parser_calls($config),
297 $expected,
298 'new_window ok');
299
300 ################################################################################
301 # hide_edge_borders
302 ################################################################################
303
304 $config = <<'EOT';
305 hide_edge_borders none
306 hide_edge_borders vertical
307 hide_edge_borders horizontal
308 hide_edge_borders both
309 hide_edge_borders smart
310 EOT
311
312 $expected = <<'EOT';
313 cfg_hide_edge_borders(none)
314 cfg_hide_edge_borders(vertical)
315 cfg_hide_edge_borders(horizontal)
316 cfg_hide_edge_borders(both)
317 cfg_hide_edge_borders(smart)
318 EOT
319
320 is(parser_calls($config),
321 $expected,
322 'hide_edge_borders ok');
323
324 ################################################################################
325 # focus_follows_mouse
326 ################################################################################
327
328 $config = <<'EOT';
329 focus_follows_mouse yes
330 focus_follows_mouse no
331 EOT
332
333 $expected = <<'EOT';
334 cfg_focus_follows_mouse(yes)
335 cfg_focus_follows_mouse(no)
336 EOT
337
338 is(parser_calls($config),
339 $expected,
340 'focus_follows_mouse ok');
341
342 ################################################################################
343 # mouse_warping
344 ################################################################################
345
346 $config = <<'EOT';
347 mouse_warping output
348 mouse_warping none
349 EOT
350
351 $expected = <<'EOT';
352 cfg_mouse_warping(output)
353 cfg_mouse_warping(none)
354 EOT
355
356 is(parser_calls($config),
357 $expected,
358 'mouse_warping ok');
359
360 ################################################################################
361 # force_display_urgency_hint
362 ################################################################################
363
364 is(parser_calls('force_display_urgency_hint 300'),
365 "cfg_force_display_urgency_hint(300)\n",
366 'force_display_urgency_hint ok');
367
368 is(parser_calls('force_display_urgency_hint 500 ms'),
369 "cfg_force_display_urgency_hint(500)\n",
370 'force_display_urgency_hint ok');
371
372 is(parser_calls('force_display_urgency_hint 700ms'),
373 "cfg_force_display_urgency_hint(700)\n",
374 'force_display_urgency_hint ok');
375
376 $config = <<'EOT';
377 force_display_urgency_hint 300
378 force_display_urgency_hint 500 ms
379 force_display_urgency_hint 700ms
380 force_display_urgency_hint 700
381 EOT
382
383 $expected = <<'EOT';
384 cfg_force_display_urgency_hint(300)
385 cfg_force_display_urgency_hint(500)
386 cfg_force_display_urgency_hint(700)
387 cfg_force_display_urgency_hint(700)
388 EOT
389
390 is(parser_calls($config),
391 $expected,
392 'force_display_urgency_hint ok');
393
394 ################################################################################
395 # workspace
396 ################################################################################
397
398 $config = <<'EOT';
399 workspace 3 output VGA-1
400 workspace "4: output" output VGA-2
401 workspace bleh output LVDS1/I_1
402 EOT
403
404 $expected = <<'EOT';
405 cfg_workspace(3, VGA-1)
406 cfg_workspace(4: output, VGA-2)
407 cfg_workspace(bleh, LVDS1/I_1)
408 EOT
409
410 is(parser_calls($config),
411 $expected,
412 'workspace ok');
413
414 ################################################################################
415 # ipc-socket
416 ################################################################################
417
418 $config = <<'EOT';
419 ipc-socket /tmp/i3.sock
420 ipc_socket ~/.i3/i3.sock
421 EOT
422
423 $expected = <<'EOT';
424 cfg_ipc_socket(/tmp/i3.sock)
425 cfg_ipc_socket(~/.i3/i3.sock)
426 EOT
427
428 is(parser_calls($config),
429 $expected,
430 'ipc-socket ok');
431
432 ################################################################################
433 # colors
434 ################################################################################
435
436 $config = <<'EOT';
437 client.focused #4c7899 #285577 #ffffff #2e9ef4 #b34d4c
438 client.focused_inactive #333333 #5f676a #ffffff #484e50
439 client.unfocused #333333 #222222 #888888 #292d2e
440 client.urgent #2f343a #900000 #ffffff #900000 #c00000
441 client.placeholder #000000 #0c0c0c #ffffff #000000
442 EOT
443
444 $expected = <<'EOT';
445 cfg_color(client.focused, #4c7899, #285577, #ffffff, #2e9ef4, #b34d4c)
446 cfg_color(client.focused_inactive, #333333, #5f676a, #ffffff, #484e50, NULL)
447 cfg_color(client.unfocused, #333333, #222222, #888888, #292d2e, NULL)
448 cfg_color(client.urgent, #2f343a, #900000, #ffffff, #900000, #c00000)
449 cfg_color(client.placeholder, #000000, #0c0c0c, #ffffff, #000000, NULL)
450 EOT
451
452 is(parser_calls($config),
453 $expected,
454 'colors ok');
455
456 ################################################################################
457 # Verify that errors don’t harm subsequent valid statements
458 ################################################################################
459
460 $config = <<'EOT';
461 hide_edge_border both
462 client.focused #4c7899 #285577 #ffffff #2e9ef4
463 EOT
464
465 my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: <end>, '#', '" . join("', '", 'set ', 'set ', qw(
466 set_from_resource
467 bindsym
468 bindcode
469 bind
470 bar
471 font
472 mode
473 floating_minimum_size
474 floating_maximum_size
475 floating_modifier
476 default_orientation
477 workspace_layout
478 default_border
479 new_window
480 default_floating_border
481 new_float
482 hide_edge_borders
483 for_window
484 assign
485 no_focus
486 focus_follows_mouse
487 mouse_warping
488 focus_wrapping
489 force_focus_wrapping
490 force_xinerama
491 force-xinerama
492 disable_randr15
493 disable-randr15
494 workspace_auto_back_and_forth
495 fake_outputs
496 fake-outputs
497 force_display_urgency_hint
498 focus_on_window_activation
499 title_align
500 show_marks
501 workspace
502 ipc_socket
503 ipc-socket
504 ipc_kill_timeout
505 restart_state
506 popup_during_fullscreen
507 exec_always
508 exec
509 client.background
510 client.focused_inactive
511 client.focused
512 client.unfocused
513 client.urgent
514 client.placeholder
515 )) . "'\n";
516
517 my $expected_end = <<'EOT';
518 ERROR: CONFIG: (in file <stdin>)
519 ERROR: CONFIG: Line 1: hide_edge_border both
520 ERROR: CONFIG: ^^^^^^^^^^^^^^^^^^^^^
521 ERROR: CONFIG: Line 2: client.focused #4c7899 #285577 #ffffff #2e9ef4
522 cfg_color(client.focused, #4c7899, #285577, #ffffff, #2e9ef4, NULL)
523 EOT
524
525 $expected = $expected_all_tokens . $expected_end;
526
527 is(parser_calls($config),
528 $expected,
529 'errors dont harm subsequent statements');
530
531 $config = <<'EOT';
532 hide_edge_borders FOOBAR
533 client.focused #4c7899 #285577 #ffffff #2e9ef4
534 EOT
535
536 $expected = <<'EOT';
537 ERROR: CONFIG: Expected one of these tokens: 'none', 'vertical', 'horizontal', 'both', 'smart', '1', 'yes', 'true', 'on', 'enable', 'active'
538 ERROR: CONFIG: (in file <stdin>)
539 ERROR: CONFIG: Line 1: hide_edge_borders FOOBAR
540 ERROR: CONFIG: ^^^^^^
541 ERROR: CONFIG: Line 2: client.focused #4c7899 #285577 #ffffff #2e9ef4
542 cfg_color(client.focused, #4c7899, #285577, #ffffff, #2e9ef4, NULL)
543 EOT
544
545 is(parser_calls($config),
546 $expected,
547 'errors dont harm subsequent statements');
548
549 ################################################################################
550 # Regression: semicolons end comments, but shouldn’t
551 ################################################################################
552
553 $config = <<'EOT';
554 # "foo" client.focused #4c7899 #285577 #ffffff #2e9ef4
555 EOT
556
557 $expected = <<'EOT';
558
559 EOT
560
561 is(parser_calls($config),
562 $expected,
563 'semicolon does not end a comment line');
564
565 ################################################################################
566 # Error message with 2+2 lines of context
567 ################################################################################
568
569 $config = <<'EOT';
570 # i3 config file (v4)
571
572 font foobar
573
574 unknown qux
575
576 # yay
577 # this should not show up
578 EOT
579
580 my $expected_head = <<'EOT';
581 cfg_font(foobar)
582 EOT
583
584 my $expected_tail = <<'EOT';
585 ERROR: CONFIG: (in file <stdin>)
586 ERROR: CONFIG: Line 3: font foobar
587 ERROR: CONFIG: Line 4:
588 ERROR: CONFIG: Line 5: unknown qux
589 ERROR: CONFIG: ^^^^^^^^^^^
590 ERROR: CONFIG: Line 6:
591 ERROR: CONFIG: Line 7: # yay
592 EOT
593
594 $expected = $expected_head . $expected_all_tokens . $expected_tail;
595
596 is(parser_calls($config),
597 $expected,
598 'error message (2+2 context) ok');
599
600 ################################################################################
601 # Error message with 0+0 lines of context
602 ################################################################################
603
604 $config = <<'EOT';
605 unknown qux
606 EOT
607
608 $expected_tail = <<'EOT';
609 ERROR: CONFIG: (in file <stdin>)
610 ERROR: CONFIG: Line 1: unknown qux
611 ERROR: CONFIG: ^^^^^^^^^^^
612 EOT
613
614 $expected = $expected_all_tokens . $expected_tail;
615
616 is(parser_calls($config),
617 $expected,
618 'error message (0+0 context) ok');
619
620 ################################################################################
621 # Error message with 1+0 lines of context
622 ################################################################################
623
624 $config = <<'EOT';
625 # context before
626 unknown qux
627 EOT
628
629 $expected_tail = <<'EOT';
630 ERROR: CONFIG: (in file <stdin>)
631 ERROR: CONFIG: Line 1: # context before
632 ERROR: CONFIG: Line 2: unknown qux
633 ERROR: CONFIG: ^^^^^^^^^^^
634 EOT
635
636 $expected = $expected_all_tokens . $expected_tail;
637
638 is(parser_calls($config),
639 $expected,
640 'error message (1+0 context) ok');
641
642 ################################################################################
643 # Error message with 0+1 lines of context
644 ################################################################################
645
646 $config = <<'EOT';
647 unknown qux
648 # context after
649 EOT
650
651 $expected_tail = <<'EOT';
652 ERROR: CONFIG: (in file <stdin>)
653 ERROR: CONFIG: Line 1: unknown qux
654 ERROR: CONFIG: ^^^^^^^^^^^
655 ERROR: CONFIG: Line 2: # context after
656 EOT
657
658 $expected = $expected_all_tokens . $expected_tail;
659
660 is(parser_calls($config),
661 $expected,
662 'error message (0+1 context) ok');
663
664 ################################################################################
665 # Error message with 0+2 lines of context
666 ################################################################################
667
668 $config = <<'EOT';
669 unknown qux
670 # context after
671 # context 2 after
672 EOT
673
674 $expected_tail = <<'EOT';
675 ERROR: CONFIG: (in file <stdin>)
676 ERROR: CONFIG: Line 1: unknown qux
677 ERROR: CONFIG: ^^^^^^^^^^^
678 ERROR: CONFIG: Line 2: # context after
679 ERROR: CONFIG: Line 3: # context 2 after
680 EOT
681
682 $expected = $expected_all_tokens . $expected_tail;
683
684 is(parser_calls($config),
685 $expected,
686 'error message (0+2 context) ok');
687
688 ################################################################################
689 # Error message within mode blocks
690 ################################################################################
691
692 $config = <<'EOT';
693 mode "yo" {
694 bindsym x resize shrink left
695 unknown qux
696 }
697 EOT
698
699 $expected = <<'EOT';
700 cfg_enter_mode((null), yo)
701 cfg_mode_binding(bindsym, (null), x, (null), (null), (null), (null), resize shrink left)
702 ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', '}'
703 ERROR: CONFIG: (in file <stdin>)
704 ERROR: CONFIG: Line 1: mode "yo" {
705 ERROR: CONFIG: Line 2: bindsym x resize shrink left
706 ERROR: CONFIG: Line 3: unknown qux
707 ERROR: CONFIG: ^^^^^^^^^^^
708 ERROR: CONFIG: Line 4: }
709 EOT
710
711 is(parser_calls($config),
712 $expected,
713 'error message (mode block) ok');
714
715 ################################################################################
716 # Error message within bar blocks
717 ################################################################################
718
719 $config = <<'EOT';
720 bar {
721 output LVDS-1
722 unknown qux
723 }
724 EOT
725
726 $expected = <<'EOT';
727 cfg_bar_start()
728 cfg_bar_output(LVDS-1)
729 ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'bindsym', 'position', 'output', 'tray_output', 'tray_padding', 'font', 'separator_symbol', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'strip_workspace_name', 'verbose', 'colors', '}'
730 ERROR: CONFIG: (in file <stdin>)
731 ERROR: CONFIG: Line 1: bar {
732 ERROR: CONFIG: Line 2: output LVDS-1
733 ERROR: CONFIG: Line 3: unknown qux
734 ERROR: CONFIG: ^^^^^^^^^^^
735 ERROR: CONFIG: Line 4: }
736 cfg_bar_finish()
737 EOT
738
739 is(parser_calls($config),
740 $expected,
741 'error message (bar block) ok');
742
743 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that using criteria to address scratchpad windows works.
17 use i3test;
18
19 my $i3 = i3(get_socket_path());
20
21 #####################################################################
22 # Verify that using scratchpad show with criteria works as expected:
23 # - When matching a scratchpad window which is visible,
24 # it should hide it.
25 # - When matching a scratchpad window which is on __i3_scratch,
26 # it should show it.
27 # - When matching a non-scratchpad window, it should be a no-op.
28 # - When matching a scratchpad window,
29 # non-matching windows shouldn't appear
30 ######################################################################
31
32 my $tmp = fresh_workspace;
33
34 my $third_window = open_window(name => 'scratch-match');
35 cmd 'move scratchpad';
36
37 #####################################################################
38 # Verify that using 'scratchpad show' without any matching windows is
39 # a no-op.
40 #####################################################################
41 my $old_focus = get_focused($tmp);
42
43 cmd '[title="nomatch"] scratchpad show';
44
45 is(get_focused($tmp), $old_focus, 'non-matching criteria have no effect');
46
47 #####################################################################
48 # Verify that we can use criteria to show a scratchpad window.
49 #####################################################################
50 cmd '[title="scratch-match"] scratchpad show';
51
52 my $scratch_focus = get_focused($tmp);
53 isnt($scratch_focus, $old_focus, 'matching criteria works');
54
55 # Check that the window was centered and resized too.
56 my $tree = $i3->get_tree->recv;
57 my $ws = get_ws($tmp);
58 my $scratchrect = $ws->{floating_nodes}->[0]->{rect};
59 my $output = $tree->{nodes}->[1];
60 my $outputrect = $output->{rect};
61
62 is($scratchrect->{width}, $outputrect->{width} * 0.5, 'scratch width is 50%');
63 is($scratchrect->{height}, $outputrect->{height} * 0.75, 'scratch height is 75%');
64 is($scratchrect->{x},
65 ($outputrect->{width} / 2) - ($scratchrect->{width} / 2),
66 'scratch window centered horizontally');
67 is($scratchrect->{y},
68 ($outputrect->{height} / 2 ) - ($scratchrect->{height} / 2),
69 'scratch window centered vertically');
70
71 cmd '[title="scratch-match"] scratchpad show';
72
73 isnt(get_focused($tmp), $scratch_focus, 'matching criteria works');
74 is(get_focused($tmp), $old_focus, 'focus restored');
75
76
77 #####################################################################
78 # Verify that we cannot use criteria to show a non-scratchpad window.
79 #####################################################################
80 my $tmp2 = fresh_workspace;
81 my $non_scratch_window = open_window(name => 'non-scratch');
82 cmd "workspace $tmp";
83 is(get_focused($tmp), $old_focus, 'focus still ok');
84 cmd '[title="non-scratch"] scratchpad show';
85 is(get_focused($tmp), $old_focus, 'focus unchanged');
86
87 #####################################################################
88 # Verify that non-matching windows doesn't appear
89 #####################################################################
90 # Subroutine to clear scratchpad
91 sub clear_scratchpad {
92 while (scalar @{get_ws('__i3_scratch')->{floating_nodes}}) {
93 cmd 'scratchpad show';
94 cmd 'kill';
95 }
96 }
97
98 #Start from an empty fresh workspace
99 my $empty_ws = fresh_workspace;
100 cmd "workspace $empty_ws";
101
102 my $no_focused = get_focused($empty_ws);
103 cmd '[title="nothingmatchthistitle"] scratchpad show';
104 #Check nothing match
105 is(get_focused($empty_ws), $no_focused, "no window to focus on");
106
107 clear_scratchpad;
108
109 open_window(name => "my-scratch-window");
110 my $w1_focus = get_focused($empty_ws);
111 cmd 'move scratchpad';
112 cmd '[title="my-scratch-window"] scratchpad show';
113 #Check we created and shown a scratchpad window
114 is(get_focused($empty_ws), $w1_focus, "focus on scratchpad window");
115
116 #Switching workspace
117 my $empty_ws2 = fresh_workspace;
118 cmd "workspace $empty_ws2";
119 open_window(name => "my-second-scratch-window");
120
121 my $w2_focus = get_focused($empty_ws2);
122 cmd 'move scratchpad';
123 cmd '[title="my-second-scratch-window"] scratchpad show';
124
125 #Check we got the correct window
126 is(get_focused($empty_ws2), $w2_focus, "focus is on second window");
127
128 #####################################################################
129 # Verify that 'scratchpad show' correctly hide multiple scratchpad
130 # windows
131 #####################################################################
132 clear_scratchpad;
133
134 sub check_floating {
135 my($rws, $n) = @_;
136 my $ws = get_ws($rws);
137 is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
138 is(scalar @{$ws->{floating_nodes}}, $n, "$n floating windows on ws");
139 }
140
141 my $empty_ws3 = fresh_workspace;
142 cmd "workspace $empty_ws3";
143
144 check_floating($empty_ws3, 0);
145
146 #Creating two scratchpad windows
147 open_window(name => "toggle-1");
148 cmd 'move scratchpad';
149 open_window(name => "toggle-2");
150 cmd 'move scratchpad';
151 check_floating($empty_ws3, 0);
152 #Showing both
153 cmd '[title="toggle-"] scratchpad show';
154
155 check_floating($empty_ws3, 2);
156
157 #Hiding both
158 cmd '[title="toggle-"] scratchpad show';
159 check_floating($empty_ws3, 0);
160
161 #Showing both again
162 cmd '[title="toggle-"] scratchpad show';
163 check_floating($empty_ws3, 2);
164
165
166 #Hiding one
167 cmd 'scratchpad show';
168 check_floating($empty_ws3, 1);
169
170 #Hiding the last
171 cmd 'scratchpad show';
172 check_floating($empty_ws3, 0);
173
174 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that you can assign a window _and_ use for_window with a move
17 # command.
18 # Ticket: #909
19 # Bug still in: 4.4-69-g6856b23
20 use i3test i3_autostart => 0;
21
22 my $config = <<EOT;
23 # i3 config file (v4)
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25
26 assign [instance=__i3-test-window] 2
27 for_window [instance=__i3-test-window] move workspace 1
28 EOT
29
30 my $pid = launch_with_config($config);
31
32 # We use dont_map because i3 will not map the window on the current
33 # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
34 my $window = open_window(
35 wm_class => '__i3-test-window',
36 dont_map => 1,
37 );
38 $window->map;
39
40 does_i3_live;
41
42 exit_gracefully($pid);
43
44 ################################################################################
45 # Related bug: multiple for_window assignments caused a crash
46 ################################################################################
47
48 $config = <<EOT;
49 # i3 config file (v4)
50 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
51
52 for_window [instance=__i3-test-window1] move workspace 3
53 for_window [instance=__i3-test-window2] move workspace 2
54 EOT
55
56 $pid = launch_with_config($config);
57
58 my $window1 = open_window(
59 wm_class => '__i3-test-window1',
60 dont_map => 1,
61 );
62 $window1->map;
63
64 my $window2 = open_window(
65 wm_class => '__i3-test-window2',
66 dont_map => 1,
67 );
68 $window2->map;
69
70 does_i3_live;
71
72 exit_gracefully($pid);
73
74 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Moves the last window of a workspace to the scratchpad. The workspace will be
17 # cleaned up and previously, the subsequent focusing of a destroyed container
18 # would crash i3.
19 # Ticket: #913
20 # Bug still in: 4.4-97-gf767ac3
21 use i3test;
22
23 my $tmp = fresh_workspace;
24
25 # Open a new window which we can identify later on based on its WM_CLASS.
26 my $scratch = open_window(wm_class => 'special');
27
28 my $tmp2 = fresh_workspace;
29
30 cmd '[class="special"] move scratchpad';
31
32 does_i3_live;
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $new = AnyEvent->condvar;
19 my $focus = AnyEvent->condvar;
20
21 my @events = events_for(
22 sub { open_window },
23 'window');
24
25 is(scalar @events, 2, 'Received 2 events');
26 is($events[0]->{container}->{focused}, 0, 'Window "new" event received');
27 is($events[1]->{container}->{focused}, 1, 'Window "focus" event received');
28
29 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Assure that no window is in fullscreen mode after showing a scratchpad window
17 # Bug still in: 4.5.1-54-g0f6b5fe
18
19 use i3test;
20
21 my $tmp = fresh_workspace;
22
23 ##########################################################################################
24 # map two windows in one container, fullscreen one of them and then move it to scratchpad
25 ##########################################################################################
26
27 my $first_win = open_window;
28 my $second_win = open_window;
29
30 # fullscreen the focused window
31 cmd 'fullscreen';
32
33 # see if the window really is in fullscreen mode
34 is_num_fullscreen($tmp, 1, 'amount of fullscreen windows after enabling fullscreen');
35
36 # move window to scratchpad
37 cmd 'move scratchpad';
38
39 ###############################################################################
40 # show the scratchpad window again; it should not be in fullscreen mode anymore
41 ###############################################################################
42
43 # show window from scratchpad
44 cmd 'scratchpad show';
45
46 # switch window back to tiling mode
47 cmd 'floating toggle';
48
49 # see if no window is in fullscreen mode
50 is_num_fullscreen($tmp, 0, 'amount of fullscreen windows after showing previously fullscreened scratchpad window');
51
52 ########################################################################################
53 # move a window to scratchpad, focus parent container, make it fullscreen, focus a child
54 ########################################################################################
55
56 # make layout tabbed
57 cmd 'layout tabbed';
58
59 # move one window to scratchpad
60 cmd 'move scratchpad';
61
62 # focus parent
63 cmd 'focus parent';
64
65 # fullscreen the container
66 cmd 'fullscreen';
67
68 # focus child
69 cmd 'focus child';
70
71 # see if the window really is in fullscreen mode
72 is_num_fullscreen($tmp, 1, 'amount of fullscreen windows after enabling fullscreen on parent');
73
74 ##########################################################################
75 # show a scratchpad window; no window should be in fullscreen mode anymore
76 ##########################################################################
77
78 # show the scratchpad window
79 cmd 'scratchpad show';
80
81 # see if no window is in fullscreen mode
82 is_num_fullscreen($tmp, 0, 'amount of fullscreen windows after showing a scratchpad window while a parent container was in fullscreen mode');
83
84 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 use i3test;
17 use IPC::Run qw(run);
18 use File::Temp;
19
20 ################################################################################
21 # 1: test that shared memory logging does not work yet
22 ################################################################################
23
24 # NB: launch_with_config (called in i3test) sets --shmlog-size=0 because the
25 # logfile gets redirected via stdout redirection anyways.
26
27 my $stdout;
28 my $stderr;
29 run [ 'i3-dump-log' ],
30 '>', \$stdout,
31 '2>', \$stderr;
32
33 like($stderr, qr#^i3-dump-log: ERROR: i3 is running, but SHM logging is not enabled\.#,
34 'shm logging not enabled');
35
36 ################################################################################
37 # 2: enable shared memory logging and verify new content shows up
38 ################################################################################
39
40 cmd 'shmlog on';
41
42 my $random_nop = mktemp('nop.XXXXXX');
43 cmd "nop $random_nop";
44
45 run [ 'i3-dump-log' ],
46 '>', \$stdout,
47 '2>', \$stderr;
48
49 like($stdout, qr#$random_nop#, 'random nop found in shm log');
50 like($stderr, qr#^$#, 'stderr empty');
51
52 ################################################################################
53 # 3: change size of the shared memory log buffer and verify old content is gone
54 ################################################################################
55
56 cmd 'shmlog ' . (1 * 1024 * 1024);
57
58 run [ 'i3-dump-log' ],
59 '>', \$stdout,
60 '2>', \$stderr;
61
62 unlike($stdout, qr#$random_nop#, 'random nop not found in shm log');
63 like($stderr, qr#^$#, 'stderr empty');
64
65 ################################################################################
66 # 4: disable logging and verify it no longer works
67 ################################################################################
68
69 cmd 'shmlog off';
70
71 run [ 'i3-dump-log' ],
72 '>', \$stdout,
73 '2>', \$stderr;
74
75 like($stderr, qr#^i3-dump-log: ERROR: i3 is running, but SHM logging is not enabled\.#,
76 'shm logging not enabled');
77
78 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test for focus handling when using floating enable/disable with
17 # criteria for windows on non-visible workspaces.
18 # Ticket: #1027
19 # Bug still in: 4.5.1-90-g6582da9
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 assign [class="^special$"] → mail
25 for_window [class="^special$"] floating enable, floating disable
26 EOT
27
28 my $window = open_window(
29 name => 'Borderless window',
30 wm_class => 'special',
31 dont_map => 1,
32 );
33 $window->map;
34
35 sync_with_i3;
36
37 cmd '[class="^special$"] focus';
38
39 does_i3_live;
40
41 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies the _NET_WORKAREA hint is deleted in case it is already set on the
17 # root window.
18 # Ticket: #1038
19 # Bug still in: 4.5.1-103-g1f8a860
20 use i3test i3_autostart => 0;
21 use X11::XCB qw(PROP_MODE_REPLACE);
22
23 my $atom_cookie = $x->intern_atom(
24 0, # create!
25 length('_NET_WORKAREA'),
26 '_NET_WORKAREA',
27 );
28
29 my $_net_workarea_id = $x->intern_atom_reply($atom_cookie->{sequence})->{atom};
30
31 $x->change_property(
32 PROP_MODE_REPLACE,
33 $x->get_root_window(),
34 $_net_workarea_id,
35 $x->atom(name => 'CARDINAL')->id,
36 32,
37 4,
38 pack('L4', 0, 0, 1024, 768));
39 $x->flush;
40
41 sub is_net_workarea_set {
42 my $cookie = $x->get_property(
43 0,
44 $x->get_root_window(),
45 $x->atom(name => '_NET_WORKAREA')->id,
46 $x->atom(name => 'CARDINAL')->id,
47 0,
48 4096,
49 );
50 my $reply = $x->get_property_reply($cookie->{sequence});
51 return 0 if $reply->{value_len} == 0;
52 return 0 if $reply->{format} == 0;
53 return 1
54 }
55
56 ok(is_net_workarea_set(), '_NET_WORKAREA is set before starting i3');
57
58 my $config = <<EOT;
59 # i3 config file (v4)
60 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
61 fake-outputs 1024x768+0+0
62 EOT
63
64 my $pid = launch_with_config($config);
65
66 ok(!is_net_workarea_set(), '_NET_WORKAREA not set after starting i3');
67
68 exit_gracefully($pid);
69
70 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # checks if mark and unmark work correctly
17 use i3test;
18 use List::Util qw(first);
19
20 my ($con, $first, $second);
21
22 sub get_marks {
23 return i3(get_socket_path())->get_marks->recv;
24 }
25
26 sub get_mark_for_window_on_workspace {
27 my ($ws, $con) = @_;
28
29 my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
30 return $current->{marks};
31 }
32
33 ##############################################################
34 # 1: check that there are no marks set yet
35 ##############################################################
36
37 my $tmp = fresh_workspace;
38
39 cmd 'split h';
40
41 is_deeply(get_marks(), [], 'no marks set yet');
42
43 ##############################################################
44 # 2: mark a con, check that it's marked, unmark it, check that
45 ##############################################################
46
47 my $one = open_window;
48 cmd 'mark foo';
49
50 is_deeply(get_marks(), ["foo"], 'mark foo set');
51
52 cmd 'unmark foo';
53
54 is_deeply(get_marks(), [], 'mark foo removed');
55
56 ##############################################################
57 # 3: mark three cons, check that they are marked
58 # unmark one con, check that it's unmarked
59 # unmark all cons, check that they are unmarked
60 ##############################################################
61
62 my $left = open_window;
63 my $middle = open_window;
64 my $right = open_window;
65
66 cmd 'mark right';
67 cmd 'focus left';
68 cmd 'mark middle';
69 cmd 'focus left';
70 cmd 'mark left';
71
72 #
73 # get_marks replys an array of marks, whose order is undefined,
74 # so we use sort to be able to compare the output
75 #
76
77 is_deeply(sort(get_marks()), ["left","middle","right"], 'all three marks set');
78
79 cmd 'unmark right';
80
81 is_deeply(sort(get_marks()), ["left","middle"], 'mark right removed');
82
83 cmd 'unmark';
84
85 is_deeply(get_marks(), [], 'all marks removed');
86
87 ##############################################################
88 # 4: mark a con, use same mark to mark another con,
89 # check that only the latter is marked
90 ##############################################################
91
92 $first = open_window;
93 $second = open_window;
94
95 cmd 'mark important';
96 cmd 'focus left';
97 cmd 'mark important';
98
99 is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'first container now has the mark');
100 ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the mark');
101
102 ##############################################################
103 # 5: mark a con, toggle the mark, check that the mark is gone
104 ##############################################################
105
106 $con = open_window;
107 cmd 'mark important';
108 cmd 'mark --toggle important';
109 ok(!get_mark_for_window_on_workspace($tmp, $con), 'container no longer has the mark');
110
111 ##############################################################
112 # 6: toggle a mark on an unmarked con, check it is marked
113 ##############################################################
114
115 $con = open_window;
116 cmd 'mark --toggle important';
117 is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container now has the mark');
118
119 ##############################################################
120 # 7: mark a con, toggle a different mark, check it is marked
121 # with the new mark
122 ##############################################################
123
124 $con = open_window;
125 cmd 'mark boring';
126 cmd 'mark --replace --toggle important';
127 is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container has the most recent mark');
128
129 ##############################################################
130 # 8: mark a con, toggle the mark on another con,
131 # check only the latter has the mark
132 ##############################################################
133
134 $first = open_window;
135 $second = open_window;
136
137 cmd 'mark important';
138 cmd 'focus left';
139 cmd 'mark --toggle important';
140
141 is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'left container has the mark now');
142 ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark');
143
144 ##############################################################
145 # 9: try to mark two cons with the same mark and check that
146 # it fails
147 ##############################################################
148
149 $first = open_window(wm_class => 'iamnotunique');
150 $second = open_window(wm_class => 'iamnotunique');
151
152 my $result = cmd "[instance=iamnotunique] mark important";
153
154 is($result->[0]->{success}, 0, 'command was unsuccessful');
155 is($result->[0]->{error}, 'A mark must not be put onto more than one window', 'correct error is returned');
156 ok(!get_mark_for_window_on_workspace($tmp, $first), 'first container is not marked');
157 ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr is not marked');
158
159 ##############################################################
160
161 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that windows are properly recognized as urgent when they start up
17 # with the urgency hint already set (and are assigned to a non-visible
18 # workspace).
19 #
20 # Ticket: #1086
21 # Bug still in: 4.6-62-g7098ef6
22 use i3test i3_config => <<EOT;
23 # i3 config file (v4)
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25
26 assign [class="special"] nonvisible
27 EOT
28
29 sub open_special {
30 my %args = @_;
31 $args{name} //= 'special window';
32
33 # We use dont_map because i3 will not map the window on the current
34 # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
35 my $window = open_window(
36 %args,
37 wm_class => 'special',
38 dont_map => 1,
39 );
40 $window->add_hint('urgency');
41 $window->map;
42 return $window;
43 }
44
45 my $tmp = fresh_workspace;
46
47 ok((scalar grep { $_ eq 'nonvisible' } @{get_workspace_names()}) == 0,
48 'assignment destination workspace does not exist yet');
49
50 my $window = open_special;
51 sync_with_i3;
52
53 ok((scalar grep { $_ eq 'nonvisible' } @{get_workspace_names()}) > 0,
54 'assignment destination workspace exists');
55
56 my @urgent = grep { $_->{urgent} } @{get_ws_content('nonvisible')};
57 isnt(@urgent, 0, 'urgent window(s) found on destination workspace');
58
59 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if the urgency hint will be set appropriately when opening a window
17 # assigned to a workspace.
18 #
19 use i3test i3_autostart => 0;
20
21 # Based on the eponymous function in t/166-assign.t
22 sub open_special {
23 my %args = @_;
24 $args{name} //= 'special window';
25
26 # We use dont_map because i3 will not map the window on the current
27 # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
28 my $window = open_window(
29 %args,
30 wm_class => 'special',
31 dont_map => 1,
32 );
33 $window->map;
34 return $window;
35 }
36
37 #####################################################################
38 # start a window assigned to a non-visible workspace and see that the urgency
39 # hint is set.
40 #####################################################################
41
42 my $config = <<EOT;
43 # i3 config file (v4)
44 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
45 assign [class="special"] → targetws
46 EOT
47
48 my $pid = launch_with_config($config);
49
50 cmd 'workspace ordinaryws';
51 my $window = open_special;
52 sync_with_i3;
53
54 ok(get_ws('targetws')->{urgent}, 'target workspace is urgent');
55
56 $window->destroy;
57
58 exit_gracefully($pid);
59
60
61 #####################################################################
62 # start a window assigned to a visible workspace and see that the urgency hint
63 # is not set.
64 #####################################################################
65
66 $config = <<EOT;
67 # i3 config file (v4)
68 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
69 assign [class="special"] → targetws
70 EOT
71
72 $pid = launch_with_config($config);
73
74 cmd 'workspace targetws';
75 $window = open_special;
76 sync_with_i3;
77
78 ok(!get_ws('targetws')->{urgent}, 'visible workspace is not urgent');
79
80 $window->destroy;
81
82 exit_gracefully($pid);
83
84 #####################################################################
85 # start a window assigned to a visible workspace on a different output and see
86 # that the urgency hint is not set.
87 #####################################################################
88
89 $config = <<EOT;
90 # i3 config file (v4)
91 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
92
93 fake-outputs 1024x768+0+0,1024x768+1024+0
94 workspace targetws output fake-0
95 workspace ordinaryws output fake-1
96
97 assign [class="special"] → targetws
98 EOT
99
100 $pid = launch_with_config($config);
101
102 cmd 'workspace ordinaryws';
103 $window = open_special;
104 sync_with_i3;
105
106 ok(!get_ws('targetws')->{urgent}, 'target workspace is not urgent');
107
108 $window->destroy;
109
110 exit_gracefully($pid);
111
112 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Restores a simple layout from a JSON file.
17 use i3test;
18 use File::Temp qw(tempfile);
19 use IO::Handle;
20
21 ################################################################################
22 # empty layout file.
23 ################################################################################
24
25 my ($fh, $filename) = tempfile(UNLINK => 1);
26 cmd "append_layout $filename";
27
28 does_i3_live;
29
30 close($fh);
31
32 ################################################################################
33 # simple vsplit layout
34 ################################################################################
35
36 my $ws = fresh_workspace;
37
38 my @content = @{get_ws_content($ws)};
39 is(@content, 0, 'no nodes on the new workspace yet');
40
41 ($fh, $filename) = tempfile(UNLINK => 1);
42 print $fh <<EOT;
43 {
44 "layout": "splitv",
45 "nodes": [
46 {
47 },
48 {
49 }
50 ]
51 }
52 EOT
53 $fh->flush;
54 cmd "append_layout $filename";
55
56 does_i3_live;
57
58 @content = @{get_ws_content($ws)};
59 is(@content, 1, 'one node on the workspace now');
60 is($content[0]->{layout}, 'splitv', 'node has splitv layout');
61 is(@{$content[0]->{nodes}}, 2, 'node has two children');
62
63 close($fh);
64
65 ################################################################################
66 # two simple vsplit containers in the same file
67 ################################################################################
68
69 $ws = fresh_workspace;
70
71 @content = @{get_ws_content($ws)};
72 is(@content, 0, 'no nodes on the new workspace yet');
73
74 ($fh, $filename) = tempfile(UNLINK => 1);
75 print $fh <<EOT;
76 {
77 "layout": "splitv",
78 "nodes": [
79 {
80 },
81 {
82 }
83 ]
84 }
85
86 {
87 "layout": "splitv"
88 }
89 EOT
90 $fh->flush;
91 cmd "append_layout $filename";
92
93 does_i3_live;
94
95 @content = @{get_ws_content($ws)};
96 is(@content, 2, 'one node on the workspace now');
97 is($content[0]->{layout}, 'splitv', 'first node has splitv layout');
98 is(@{$content[0]->{nodes}}, 2, 'first node has two children');
99 is($content[1]->{layout}, 'splitv', 'second node has splitv layout');
100 is(@{$content[1]->{nodes}}, 0, 'first node has no children');
101
102 close($fh);
103
104 ################################################################################
105 # simple vsplit layout with swallow specifications
106 ################################################################################
107
108 $ws = fresh_workspace;
109
110 @content = @{get_ws_content($ws)};
111 is(@content, 0, 'no nodes on the new workspace yet');
112
113 ($fh, $filename) = tempfile(UNLINK => 1);
114 print $fh <<EOT;
115 {
116 "layout": "splitv",
117 "nodes": [
118 {
119 "swallows": [
120 {
121 "class": "top"
122 }
123 ]
124 },
125 {
126 "swallows": [
127 {
128 "class": "bottom"
129 }
130 ]
131 }
132 ]
133 }
134 EOT
135 $fh->flush;
136 cmd "append_layout $filename";
137
138 does_i3_live;
139
140 @content = @{get_ws_content($ws)};
141 is(@content, 1, 'one node on the workspace now');
142
143 my $top = open_window(
144 name => 'top window',
145 wm_class => 'top',
146 instance => 'top',
147 );
148
149 my $bottom = open_window(
150 name => 'bottom window',
151 wm_class => 'bottom',
152 instance => 'bottom',
153 );
154
155 @content = @{get_ws_content($ws)};
156 is(@content, 1, 'still one node on the workspace now');
157 my @nodes = @{$content[0]->{nodes}};
158 is($nodes[0]->{name}, 'top window', 'top window on top');
159
160 close($fh);
161
162 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests all supported criteria for the "swallows" key.
17 use i3test;
18 use File::Temp qw(tempfile);
19 use IO::Handle;
20 use X11::XCB qw(PROP_MODE_REPLACE);
21
22 sub verify_swallow_criterion {
23 my ($cfgline, $open_window_cb) = @_;
24
25 my $ws = fresh_workspace;
26
27 my @content = @{get_ws_content($ws)};
28 is(@content, 0, "no nodes on the new workspace yet ($cfgline)");
29
30 my ($fh, $filename) = tempfile(UNLINK => 1);
31 print $fh <<EOT;
32 {
33 "layout": "splitv",
34 "nodes": [
35 {
36 "swallows": [
37 {
38 $cfgline
39 }
40 ]
41 }
42 ]
43 }
44 EOT
45 $fh->flush;
46 cmd "append_layout $filename";
47
48 does_i3_live;
49
50 @content = @{get_ws_content($ws)};
51 is(@content, 1, "one node on the workspace now ($cfgline)");
52
53 my $top = $open_window_cb->();
54
55 @content = @{get_ws_content($ws)};
56 is(@content, 1, "still one node on the workspace now ($cfgline)");
57 my @nodes = @{$content[0]->{nodes}};
58 is($nodes[0]->{window}, $top->id, "top window on top ($cfgline)");
59
60 close($fh);
61 }
62
63 verify_swallow_criterion(
64 '"class": "^special_class$"',
65 sub { open_window(wm_class => 'special_class') }
66 );
67
68 # Run the same test again to verify that the window is not being swallowed by
69 # the first container. Each swallow condition should only swallow precisely one
70 # window.
71 verify_swallow_criterion(
72 '"class": "^special_class$"',
73 sub { open_window(wm_class => 'special_class') }
74 );
75
76 verify_swallow_criterion(
77 '"instance": "^special_instance$"',
78 sub { open_window(wm_class => '', instance => 'special_instance') }
79 );
80
81 verify_swallow_criterion(
82 '"title": "^special_title$"',
83 sub { open_window(name => 'special_title') }
84 );
85
86 verify_swallow_criterion(
87 '"window_role": "^special_role$"',
88 sub {
89 open_window(
90 name => 'roletest',
91 before_map => sub {
92 my ($window) = @_;
93 my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
94 my $atomtype = $x->atom(name => 'STRING');
95 $x->change_property(
96 PROP_MODE_REPLACE,
97 $window->id,
98 $atomname->id,
99 $atomtype->id,
100 8,
101 length("special_role") + 1,
102 "special_role\x00"
103 );
104 },
105 );
106 }
107 );
108
109 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 does not crash when a layout is partially loadable.
17 # ticket #1145, bug still present in commit b109b1b20dd51401dc929407453d3acdd8ff5566
18 use i3test;
19 use File::Temp qw(tempfile);
20 use IO::Handle;
21
22 ################################################################################
23 # empty layout file.
24 ################################################################################
25
26 my ($fh, $filename) = tempfile(UNLINK => 1);
27 cmd "append_layout $filename";
28
29 does_i3_live;
30
31 close($fh);
32
33 ################################################################################
34 # file with a superfluous trailing comma
35 ################################################################################
36
37 my $ws = fresh_workspace;
38
39 my @content = @{get_ws_content($ws)};
40 is(@content, 0, 'no nodes on the new workspace yet');
41
42 ($fh, $filename) = tempfile(UNLINK => 1);
43 print $fh <<'EOT';
44 // vim:ts=4:sw=4:et
45 {
46 "border": "pixel",
47 "floating": "auto_off",
48 "geometry": {
49 "height": 777,
50 "width": 199,
51 "x": 0,
52 "y": 0
53 },
54 "name": "Buddy List",
55 "percent": 0.116145833333333,
56 "swallows": [
57 {
58 "class": "^Pidgin$",
59 "window_role": "^buddy_list$"
60 }
61 ],
62 "type": "con"
63 }
64
65 {
66 // splitv split container with 1 children
67 "border": "pixel",
68 "floating": "auto_off",
69 "layout": "splitv",
70 "percent": 0.883854166666667,
71 "swallows": [
72 {}
73 ],
74 "type": "con",
75 "nodes": [
76 {
77 // splitv split container with 2 children
78 "border": "pixel",
79 "floating": "auto_off",
80 "layout": "splitv",
81 "percent": 1,
82 "swallows": [
83 {}
84 ],
85 "type": "con",
86 "nodes": [
87 {
88 "border": "pixel",
89 "floating": "auto_off",
90 "geometry": {
91 "height": 318,
92 "width": 566,
93 "x": 0,
94 "y": 0
95 },
96 "name": "zsh",
97 "percent": 0.5,
98 "swallows": [
99 {
100 "class": "^URxvt$",
101 "instance": "^IRC$",
102 }
103 ],
104 "type": "con"
105 },
106 {
107 "border": "pixel",
108 "floating": "auto_off",
109 "geometry": {
110 "height": 1057,
111 "width": 636,
112 "x": 0,
113 "y": 0
114 },
115 "name": "Michael Stapelberg",
116 "percent": 0.5,
117 "swallows": [
118 {
119 "class": "^Pidgin$",
120 "window_role": "^conversation$"
121 }
122 ],
123 "type": "con"
124 }
125 ]
126 }
127 ]
128 }
129
130 EOT
131 $fh->flush;
132 my $reply = cmd "append_layout $filename";
133 ok(!$reply->[0]->{success}, 'IPC reply did not indicate success');
134
135 does_i3_live;
136
137
138 close($fh);
139
140 ################################################################################
141 # another file with a superfluous trailing comma (issue #2755)
142 ################################################################################
143
144 subtest 'issue 2755' => sub {
145 plan tests => 4;
146 $ws = fresh_workspace;
147
148 @content = @{get_ws_content($ws)};
149 is(@content, 0, 'no nodes on the new workspace yet');
150
151 ($fh, $filename) = tempfile(UNLINK => 1);
152 print $fh <<'EOT';
153 // vim:ts=4:sw=4:et
154 {
155 // splith split container with 2 children
156 "border": "normal",
157 "floating": "auto_off",
158 "layout": "splith",
159 "percent": null,
160 "type": "con",
161 "nodes": [
162 {
163 "border": "normal",
164 "current_border_width": 2,
165 "floating": "auto_off",
166 "geometry": {
167 "height": 860,
168 "width": 1396,
169 "x": 1922,
170 "y": 38
171 },
172 "name": "Chromium1",
173 "percent": 0.5,
174 "swallows": [
175 {
176 "class": "^Chromium$",
177 // "instance": "^chromium$",
178 // "title": "^Git\\ Tutorial\\ \\-\\ corp\\ \\-\\ Chromium$",
179 // "transient_for": "^$",
180 // "window_role": "^browser$"
181 }
182 ],
183 "type": "con"
184 },
185 {
186 "border": "normal",
187 "current_border_width": 2,
188 "floating": "auto_off",
189 "geometry": {
190 "height": 1040,
191 "width": 956,
192 "x": 2,
193 "y": 38
194 },
195 "name": "Chromium2",
196 "percent": 0.5,
197 "swallows": [
198 {
199 "class": "^Chromium$",
200 // "instance": "^chromium$",
201 // "title": "^Nutanix\\ \\-\\ Prod\\ \\-\\ Sign\\ In\\ \\-\\ Chromium$",
202 // "transient_for": "^$",
203 // "window_role": "^browser$"
204 }
205 ],
206 "type": "con"
207 }
208 ]
209 }
210
211 EOT
212 $fh->flush;
213 $reply = cmd "append_layout $filename";
214 ok(!$reply->[0]->{success}, 'IPC reply indicated success');
215
216 does_i3_live;
217
218 # Move to a different workspace rendered the half-attached con’s con->parent
219 # invalid.
220 fresh_workspace;
221
222 cmd '[urgent=latest] focus';
223 $reply = cmd 'scratchpad show';
224
225 does_i3_live;
226
227 close($fh);
228 };
229
230 ################################################################################
231 # wrong percent key in a child node
232 ################################################################################
233
234 $ws = fresh_workspace;
235
236 @content = @{get_ws_content($ws)};
237 is(@content, 0, 'no nodes on the new workspace yet');
238
239 ($fh, $filename) = tempfile(UNLINK => 1);
240 print $fh <<'EOT';
241 // vim:ts=4:sw=4:et
242 {
243 "border": "pixel",
244 "floating": "auto_off",
245 "layout": "splitv",
246 "type": "con",
247 "nodes": [
248 {
249 "border": "pixel",
250 "floating": "auto_off",
251 "geometry": {
252 "height": 318,
253 "width": 566,
254 "x": 0,
255 "y": 0
256 },
257 "name": "zsh",
258 "percent": 0.833333,
259 "swallows": [
260 {
261 "class": "^URxvt$",
262 "instance": "^IRC$"
263 }
264 ],
265 "type": "con"
266 }
267 ]
268 }
269
270 EOT
271 $fh->flush;
272 cmd "append_layout $filename";
273
274 does_i3_live;
275
276 close($fh);
277
278
279 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 removes swallows specifications for split containers.
17 # ticket #1149, bug still present in commit 2fea5ef82bd3528ed62681f9ac64f45830f4acdf
18 use i3test;
19 use File::Temp qw(tempfile);
20 use IO::Handle;
21
22 my $ws = fresh_workspace;
23
24 my @content = @{get_ws_content($ws)};
25 is(@content, 0, 'no nodes on the new workspace yet');
26
27 my ($fh, $filename) = tempfile(UNLINK => 1);
28 print $fh <<'EOT';
29 // vim:ts=4:sw=4:et
30 {
31 "border": "pixel",
32 "floating": "auto_off",
33 "geometry": {
34 "height": 777,
35 "width": 199,
36 "x": 0,
37 "y": 0
38 },
39 "name": "Buddy List",
40 "percent": 0.116145833333333,
41 "swallows": [
42 {
43 "class": "^Pidgin$",
44 "window_role": "^buddy_list$"
45 }
46 ],
47 "type": "con"
48 }
49
50 {
51 // splitv split container with 1 children
52 "border": "pixel",
53 "floating": "auto_off",
54 "layout": "splitv",
55 "percent": 0.883854166666667,
56 "type": "con",
57 "nodes": [
58 {
59 // splitv split container with 2 children
60 "border": "pixel",
61 "floating": "auto_off",
62 "layout": "splitv",
63 "percent": 1,
64 "type": "con",
65 "nodes": [
66 {
67 "border": "pixel",
68 "floating": "auto_off",
69 "geometry": {
70 "height": 318,
71 "width": 566,
72 "x": 0,
73 "y": 0
74 },
75 "name": "zsh",
76 "percent": 0.5,
77 "swallows": [
78 {
79 "class": "^URxvt$",
80 "instance": "^IRC$"
81 }
82 ],
83 "type": "con"
84 },
85 {
86 "border": "pixel",
87 "floating": "auto_off",
88 "geometry": {
89 "height": 1057,
90 "width": 636,
91 "x": 0,
92 "y": 0
93 },
94 "name": "Michael Stapelberg",
95 "percent": 0.5,
96 "swallows": [
97 {
98 "class": "^Pidgin$",
99 "window_role": "^conversation$"
100 }
101 ],
102 "type": "con"
103 }
104 ]
105 }
106 ]
107 }
108
109 EOT
110 $fh->flush;
111 my $reply = cmd "append_layout $filename";
112
113 does_i3_live;
114
115 ok($reply->[0]->{success}, 'IPC reply indicates success');
116
117 my @nodes = @{get_ws_content($ws)};
118
119 is_deeply($nodes[0]->{swallows},
120 [
121 {
122 class => '^Pidgin$',
123 window_role => '^buddy_list$',
124 },
125 ],
126 'swallows specification not parsed correctly');
127
128 is_deeply($nodes[1]->{swallows},
129 [],
130 'swallows specification empty on split container');
131
132 my @children = @{$nodes[1]->{nodes}->[0]->{nodes}};
133
134 is_deeply($children[0]->{swallows},
135 [
136 {
137 class => '^URxvt$',
138 instance => '^IRC$',
139 },
140 ],
141 'swallows specification not parsed correctly');
142
143 is_deeply($children[1]->{swallows},
144 [
145 {
146 class => '^Pidgin$',
147 window_role => '^conversation$',
148 },
149 ],
150 'swallows specification not parsed correctly');
151
152 close($fh);
153 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies the _NET_CURRENT_DESKTOP property correctly tracks the currently
17 # active workspace. Specifically checks that the property is 0 on startup with
18 # an empty config, tracks changes when switching workspaces and when
19 # workspaces are created and deleted.
20 #
21 # The property not being set on startup was last present in commit
22 # 6578976b6159437c16187cf8d1ea38aa9fec9fc8.
23
24 use i3test i3_autostart => 0;
25 use X11::XCB qw(PROP_MODE_REPLACE);
26
27 my $config = <<EOT;
28 # i3 config file (v4)
29 font font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
30 EOT
31
32 my $root = $x->get_root_window;
33 # Manually intern _NET_CURRENT_DESKTOP as $x->atom will not create atoms if
34 # they are not yet interned.
35 my $atom_cookie = $x->intern_atom(0, length("_NET_CURRENT_DESKTOP"), "_NET_CURRENT_DESKTOP");
36 my $_NET_CURRENT_DESKTOP = $x->intern_atom_reply($atom_cookie->{sequence})->{atom};
37 my $CARDINAL = $x->atom(name => 'CARDINAL')->id;
38
39 $x->delete_property($root, $_NET_CURRENT_DESKTOP);
40
41 $x->flush();
42
43 # Returns the _NET_CURRENT_DESKTOP property from the root window. This is
44 # the 0 based index of the current desktop.
45 sub current_desktop_index {
46 sync_with_i3;
47
48 my $cookie = $x->get_property(0, $root, $_NET_CURRENT_DESKTOP,
49 $CARDINAL, 0, 1);
50 my $reply = $x->get_property_reply($cookie->{sequence});
51
52 return undef if $reply->{value_len} != 1;
53 return undef if $reply->{format} != 32;
54 return undef if $reply->{type} != $CARDINAL;
55
56 return unpack 'L', $reply->{value};
57 }
58
59 my $pid = launch_with_config($config);
60
61 is(current_desktop_index, 0, "Starting on desktop 0");
62 cmd 'workspace 1';
63 is(current_desktop_index, 0, "Change from empty to empty");
64 open_window;
65 cmd 'workspace 0';
66 is(current_desktop_index, 0, "Open on 1 and view 0");
67 open_window;
68 cmd 'workspace 1';
69 is(current_desktop_index, 1, "Open on 0 and view 1");
70 cmd 'workspace 2';
71 is(current_desktop_index, 2, "Open and view empty");
72
73 #########################################################
74 # Test the _NET_CURRENT_DESKTOP client request
75 # This request is sent by pagers and bars to switch the current desktop (which
76 # is like an ersatz workspace) to the given index
77 #########################################################
78
79 sub send_current_desktop_request {
80 my ($idx) = @_;
81
82 my $msg = pack "CCSLLLLLL",
83 X11::XCB::CLIENT_MESSAGE, # response_type
84 32, # format
85 0, # sequence
86 0,
87 $_NET_CURRENT_DESKTOP,
88 $idx, # data32[0] (the desktop index)
89 0, # data32[1] (can be a timestamp)
90 0, # data32[2]
91 0, # data32[3]
92 0; # data32[4]
93
94 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
95 }
96
97 send_current_desktop_request(1);
98 is(current_desktop_index, 1, 'current desktop request switched to desktop 1');
99 # note that _NET_CURRENT_DESKTOP is an index and that in this case, workspace 1
100 # is at index 1 as a convenience for the test
101 is(focused_ws, '1', 'current desktop request switched to workspace 1');
102
103 send_current_desktop_request(0);
104 is(current_desktop_index, 0, 'current desktop request switched to desktop 0');
105 is(focused_ws, '0', 'current desktop request switched to workspace 0');
106
107 exit_gracefully($pid);
108
109 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Make sure floating containers really can't be split.
17 # Ticket: #1177
18 # Bug still in: 4.7.2-81-g905440d
19 use i3test;
20
21 my $ws = fresh_workspace;
22 my $window = open_floating_window;
23 cmd "layout stacked";
24 cmd "splitv";
25
26 my $floating_con = get_ws($ws)->{floating_nodes}[0]->{nodes}[0];
27
28 is(@{$floating_con->{nodes}}, 0, 'floating con is still a leaf');
29
30 cmd 'floating disable';
31
32 does_i3_live;
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 ################################
19 # Window focus event
20 ################################
21
22 cmd 'split h';
23
24 my $win0 = open_window;
25 my $win1 = open_window;
26 my $win2 = open_window;
27
28 # ensure the rightmost window contains input focus
29 cmd '[id="' . $win2->id . '"] focus';
30 is($x->input_focus, $win2->id, "Window 2 focused");
31
32 sub focus_subtest {
33 my ($cmd, $name) = @_;
34
35 my $focus = AnyEvent->condvar;
36
37 my @events = events_for(
38 sub { cmd $cmd },
39 'window');
40
41 is(scalar @events, 1, 'Received 1 event');
42 is($events[0]->{change}, 'focus', 'Focus event received');
43 is($events[0]->{container}->{name}, $name, "$name focused");
44 }
45
46 subtest 'focus left (1)', \&focus_subtest, 'focus left', $win1->name;
47 subtest 'focus left (2)', \&focus_subtest, 'focus left', $win0->name;
48 subtest 'focus right (1)', \&focus_subtest, 'focus right', $win1->name;
49 subtest 'focus right (2)', \&focus_subtest, 'focus right', $win2->name;
50 subtest 'focus right (3)', \&focus_subtest, 'focus right', $win0->name;
51 subtest 'focus left', \&focus_subtest, 'focus left', $win2->name;
52
53 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15
16 use i3test;
17
18 my $window = open_window(name => 'Window 0');
19
20 my @events = events_for(
21 sub {
22 $window->name('New Window Title');
23 sync_with_i3;
24 },
25 'window');
26
27 is(scalar @events, 1, 'Received 1 event');
28 is($events[0]->{change}, 'title', 'Window title change event received');
29 is($events[0]->{container}->{name}, 'New Window Title', 'Window title changed');
30
31 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that windows with properties that indicate they should be floating
17 # are indeed opened floating.
18 # Ticket: #1182
19 # Bug still in: 4.7.2-97-g84fc808
20 use i3test;
21 use X11::XCB qw(PROP_MODE_REPLACE);
22
23 sub open_with_type {
24 my $window_type = shift;
25
26 my $window = open_window(
27 window_type => $x->atom(name => $window_type),
28 );
29 return $window;
30 }
31
32 sub open_with_state {
33 my $window_state = shift;
34
35 my $window = open_window(
36 before_map => sub {
37 my ($window) = @_;
38
39 my $atomname = $x->atom(name => '_NET_WM_STATE');
40 my $atomtype = $x->atom(name => 'ATOM');
41 $x->change_property(
42 PROP_MODE_REPLACE,
43 $window->id,
44 $atomname->id,
45 $atomtype->id,
46 32,
47 1,
48 pack('L1', $x->atom(name => $window_state)->id),
49 );
50 },
51 );
52
53 return $window;
54 }
55
56 sub open_with_fixed_size {
57 # The type of the WM_NORMAL_HINTS property is WM_SIZE_HINTS
58 # https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.2.3
59 my $XCB_ICCCM_SIZE_HINT_P_MIN_SIZE = 0x32;
60 my $XCB_ICCCM_SIZE_HINT_P_MAX_SIZE = 0x16;
61
62 my $flags = $XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | $XCB_ICCCM_SIZE_HINT_P_MAX_SIZE;
63
64 my $min_width = 150;
65 my $max_width = 150;
66 my $min_height = 100;
67 my $max_height = 100;
68
69 my $pad = 0x00;
70
71 my $window = open_window(
72 before_map => sub {
73 my ($window) = @_;
74
75 my $atomname = $x->atom(name => 'WM_NORMAL_HINTS');
76 my $atomtype = $x->atom(name => 'WM_SIZE_HINTS');
77 $x->change_property(
78 PROP_MODE_REPLACE,
79 $window->id,
80 $atomname->id,
81 $atomtype->id,
82 32,
83 13,
84 pack('C5N8', $flags, $pad, $pad, $pad, $pad, 0, 0, 0, $min_width, $min_height, $max_width, $max_height),
85 );
86 },
87 );
88
89 return $window;
90 }
91
92 my $ws = fresh_workspace;
93
94 my $window = open_with_type '_NET_WM_WINDOW_TYPE_DIALOG';
95 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window}, $window->id, 'Dialog window opened floating');
96 $window->unmap;
97
98 $window = open_with_type '_NET_WM_WINDOW_TYPE_UTILITY';
99 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window}, $window->id, 'Utility window opened floating');
100 $window->unmap;
101
102 $window = open_with_type '_NET_WM_WINDOW_TYPE_TOOLBAR';
103 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window}, $window->id, 'Toolbar window opened floating');
104 $window->unmap;
105
106 $window = open_with_type '_NET_WM_WINDOW_TYPE_SPLASH';
107 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window}, $window->id, 'Splash window opened floating');
108 $window->unmap;
109
110 $window = open_with_state '_NET_WM_STATE_MODAL';
111 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window}, $window->id, 'Modal window opened floating');
112 $window->unmap;
113
114 $window = open_with_fixed_size;
115 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window}, $window->id, 'Fixed size window opened floating');
116 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window_rect}->{width}, 150, 'Fixed size window opened with minimum width');
117 is(get_ws($ws)->{floating_nodes}[0]->{nodes}[0]->{window_rect}->{height}, 100, 'Fixed size window opened with minimum height');
118 $window->unmap;
119
120 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that i3 does not crash when a command is issued that would resize a dock
17 # client.
18 # Ticket: #1201
19 # Bug still in: 4.7.2-107-g9b03be6
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23 EOT
24
25 my $window = open_window(
26 wm_class => 'special',
27 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
28 );
29
30 cmd('[class="special"] resize grow height 160 px or 16 ppt');
31
32 does_i3_live;
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that _NET_CLIENT_LIST is properly updated on the root window as windows
17 # are mapped and unmapped.
18 #
19 # Information on this property can be found here:
20 # https://standards.freedesktop.org/wm-spec/latest/ar01s03.html
21 #
22 # > These arrays contain all X Windows managed by the Window Manager.
23 # > _NET_CLIENT_LIST has initial mapping order, starting with the oldest window.
24 #
25 # Ticket: #1099
26 # Bug still in: 4.7.2-8-ge6cce92
27 use i3test;
28
29 sub get_client_list {
30 my $cookie = $x->get_property(
31 0,
32 $x->get_root_window(),
33 $x->atom(name => '_NET_CLIENT_LIST')->id,
34 $x->atom(name => 'WINDOW')->id,
35 0,
36 4096,
37 );
38 my $reply = $x->get_property_reply($cookie->{sequence});
39 my $len = $reply->{length};
40
41 return () if $len == 0;
42 return unpack("L$len", $reply->{value});
43 }
44
45 # Mapping a window should give us one client in _NET_CLIENT_LIST
46 my $win1 = open_window;
47
48 my @clients = get_client_list;
49
50 is(@clients, 1, 'One client in _NET_CLIENT_LIST');
51 is($clients[0], $win1->{id}, 'Correct client in position one');
52
53 # Mapping another window should give us two clients in the list with the last
54 # client mapped in the last position
55 my $win2 = open_window;
56
57 @clients = get_client_list;
58 is(@clients, 2, 'Added mapped client to list (2)');
59 is($clients[0], $win1->{id}, 'Correct client in position one');
60 is($clients[1], $win2->{id}, 'Correct client in position two');
61
62 # Mapping another window should give us three clients in the list in the order
63 # they were mapped
64 my $win3 = open_window;
65
66 @clients = get_client_list;
67 is(@clients, 3, 'Added mapped client to list (3)');
68 is($clients[0], $win1->{id}, 'Correct client in position one');
69 is($clients[1], $win2->{id}, 'Correct client in position two');
70 is($clients[2], $win3->{id}, 'Correct client in position three');
71
72 # Unmapping the second window should give us the two remaining clients in the
73 # order they were mapped
74 $win2->unmap;
75 wait_for_unmap($win2);
76
77 @clients = get_client_list;
78 is(@clients, 2, 'Removed unmapped client from list (2)');
79 is($clients[0], $win1->{id}, 'Correct client in position one');
80 is($clients[1], $win3->{id}, 'Correct client in position two');
81
82 # Unmapping the first window should give us only the remaining mapped window in
83 # the list
84 $win1->unmap;
85 wait_for_unmap($win1);
86
87 @clients = get_client_list;
88 is(@clients, 1, 'Removed unmapped client from list (1)');
89 is($clients[0], $win3->{id}, 'Correct client in position one');
90
91 # Unmapping the last window should give us an empty list
92 $win3->unmap;
93 wait_for_unmap($win3);
94
95 @clients = get_client_list;
96 is(@clients, 0, 'Removed unmapped client from list (0)');
97
98 # Dock clients should not be included in this list
99
100 my $dock_win = open_window({
101 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
102 });
103
104 @clients = get_client_list;
105 is(@clients, 0, 'Dock clients are not included in the list');
106
107 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that i3 does not crash when resizing a split container
17 # Ticket: #1220
18 # Bug still in: 4.7.2-128-g702906d
19 use i3test;
20
21 open_window;
22 open_window;
23
24 cmd 'split h';
25
26 open_window;
27
28 cmd 'focus parent, resize grow left';
29
30 does_i3_live;
31
32 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the ipc window::fullscreen_mode event works properly
17 #
18 # Bug still in: 4.7.2-135-g7deb23c
19 use i3test;
20
21 open_window;
22
23 sub fullscreen_subtest {
24 my ($want) = @_;
25 my @events = events_for(
26 sub { cmd 'fullscreen' },
27 'window');
28
29 is(scalar @events, 1, 'Received 1 event');
30 is($events[0]->{container}->{fullscreen_mode}, $want, "fullscreen_mode now $want");
31 }
32
33 subtest 'fullscreen on', \&fullscreen_subtest, 1;
34 subtest 'fullscreen off', \&fullscreen_subtest, 0;
35
36 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that internal workspaces (those whose name starts with __) cannot be
17 # used in all commands that deal with workspaces.
18 # Ticket: #1209
19 # Bug still in: 4.7.2-154-g144e3fb
20 use i3test i3_autostart => 0;
21
22 sub internal_workspaces {
23 scalar grep { /^__/ } @{get_workspace_names()}
24 }
25
26 my $config = <<EOT;
27 # i3 config file (v4)
28 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
29 EOT
30
31 my $pid = launch_with_config($config);
32
33 is(internal_workspaces(), 0, 'No internal workspaces');
34
35 cmd 'workspace __foo';
36 is(internal_workspaces(), 0, 'No internal workspaces');
37
38 cmd 'move to workspace __foo';
39 is(internal_workspaces(), 0, 'No internal workspaces');
40
41 cmd 'rename workspace to __foo';
42 is(internal_workspaces(), 0, 'No internal workspaces');
43
44 exit_gracefully($pid);
45
46 ################################################################################
47 # Verify that new workspace names starting with __ are ignored.
48 ################################################################################
49
50 $config = <<EOT;
51 # i3 config file (v4)
52 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
53
54 bindsym Mod1+1 workspace __foo
55 bindsym Mod1+2 workspace bar
56 EOT
57
58 $pid = launch_with_config($config);
59
60 is_deeply(get_workspace_names(), [ 'bar' ], 'New workspace called bar, not __foo');
61
62 exit_gracefully($pid);
63
64 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks the workspace "empty" event semantics.
17 #
18 use i3test;
19
20 ################################################################################
21 # check that the workspace empty event is sent upon workspace switch when the
22 # old workspace is empty
23 ################################################################################
24 subtest 'Workspace empty event upon switch', sub {
25 my $ws2 = fresh_workspace;
26 my $w2 = open_window();
27 my $ws1 = fresh_workspace;
28 my $w1 = open_window();
29
30 cmd '[id="' . $w1->id . '"] kill';
31
32 my $cond = AnyEvent->condvar;
33 my @events = events_for(
34 sub { cmd "workspace $ws2" },
35 'workspace');
36
37 is(scalar @events, 2, 'Received 2 event');
38 is($events[1]->{change}, 'empty', '"Empty" event received upon workspace switch');
39 is($events[1]->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
40 };
41
42 ################################################################################
43 # check that no workspace empty event is sent upon workspace switch if the
44 # workspace is not empty
45 ################################################################################
46 subtest 'No workspace empty event', sub {
47 my $ws2 = fresh_workspace;
48 my $w2 = open_window();
49 my $ws1 = fresh_workspace;
50 my $w1 = open_window();
51
52 my @events = events_for(
53 sub { cmd "workspace $ws2" },
54 'workspace');
55
56 is(scalar @events, 1, 'Received 1 event');
57 is($events[0]->{change}, 'focus', 'Event change is "focus"');
58 };
59
60 ################################################################################
61 # check that workspace empty event is sent when the last window has been closed
62 # on invisible workspace
63 ################################################################################
64 subtest 'Workspace empty event upon window close', sub {
65 my $ws1 = fresh_workspace;
66 my $w1 = open_window();
67 my $ws2 = fresh_workspace;
68 my $w2 = open_window();
69
70 my @events = events_for(
71 sub {
72 $w1->unmap;
73 sync_with_i3;
74 },
75 'workspace');
76
77 is(scalar @events, 1, 'Received 1 event');
78 is($events[0]->{change}, 'empty', '"Empty" event received upon window close');
79 is($events[0]->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
80 };
81
82 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the border widths can be set separately for floating and
17 # tiled windows
18 # Ticket: #1244
19 # Bug still in: 4.7.2-166-gb69b3fc
20
21 use i3test i3_autostart => 0;
22
23 #####################################################################
24 # 1: check that the border widths can be different for floating and
25 # tiled windows
26 #####################################################################
27
28 my $config = <<EOT;
29 # i3 config file (v4)
30 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
31
32 new_window pixel 5
33 new_float pixel 10
34 EOT
35
36 my $pid = launch_with_config($config);
37
38 my $tmp = fresh_workspace;
39
40 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
41
42 my $tilewindow = open_window;
43 my $floatwindow = open_floating_window;
44
45 my $wscontent = get_ws($tmp);
46
47 my @tiled = @{$wscontent->{nodes}};
48 ok(@tiled == 1, 'one tiled container opened');
49 is($tiled[0]->{current_border_width}, 5, 'tiled current border width set to 5');
50 is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*5, 'tiled border width 5');
51
52 my @floating = @{$wscontent->{floating_nodes}};
53 ok(@floating == 1, 'one floating container opened');
54 is($floating[0]->{nodes}[0]->{current_border_width}, 10, 'floating current border width set to 10');
55 is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*10, 'floating border width 10');
56
57 exit_gracefully($pid);
58
59 #####################################################################
60 # 2: make sure the order can also be reverse
61 #####################################################################
62
63 $config = <<EOT;
64 # i3 config file (v4)
65 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
66
67 new_float pixel 7
68 new_window pixel 3
69 EOT
70
71 $pid = launch_with_config($config);
72
73 $tmp = fresh_workspace;
74
75 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
76
77 $tilewindow = open_window;
78 $floatwindow = open_floating_window;
79
80 $wscontent = get_ws($tmp);
81
82 @tiled = @{$wscontent->{nodes}};
83 ok(@tiled == 1, 'one tiled container opened');
84 is($tiled[0]->{current_border_width}, 3, 'tiled current border width set to 3');
85 is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*3, 'tiled border width 3');
86
87 @floating = @{$wscontent->{floating_nodes}};
88 ok(@floating == 1, 'one floating container opened');
89 is($floating[0]->{nodes}[0]->{current_border_width}, 7, 'floating current border width set to 7');
90 is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*7, 'floating border width 7');
91
92 exit_gracefully($pid);
93
94 #####################################################################
95 # 3: make sure normal border widths work as well
96 #####################################################################
97
98 $config = <<EOT;
99 # i3 config file (v4)
100 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
101
102 new_float normal 6
103 new_window normal 4
104 EOT
105
106 $pid = launch_with_config($config);
107
108 $tmp = fresh_workspace;
109
110 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
111
112 $tilewindow = open_window;
113 $floatwindow = open_floating_window;
114
115 $wscontent = get_ws($tmp);
116
117 @tiled = @{$wscontent->{nodes}};
118 ok(@tiled == 1, 'one tiled container opened');
119 is($tiled[0]->{current_border_width}, 4, 'tiled current border width set to 4');
120 is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*4, 'tiled border width 4');
121
122 @floating = @{$wscontent->{floating_nodes}};
123 ok(@floating == 1, 'one floating container opened');
124 is($floating[0]->{nodes}[0]->{current_border_width}, 6, 'floating current border width set to 6');
125 is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*6, 'floating border width 6');
126
127 exit_gracefully($pid);
128
129 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Makes sure i3 deletes its temporary directory when exiting.
17 # Ticket: #1253
18 # Bug still in: 4.7.2-186-g617afc6
19 use i3test i3_autostart => 0;
20 use File::Basename;
21 use File::Temp qw(tempfile tempdir);
22 use X11::XCB qw(:all);
23
24 my $config = <<EOT;
25 # i3 config file (v4)
26 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
27 EOT
28
29 # ensure XDG_RUNTIME_DIR is not set
30 delete $ENV{XDG_RUNTIME_DIR};
31
32 my $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
33 my $socketpath = get_socket_path(0);
34 my $tmpdir = dirname($socketpath);
35
36 ok(-d $tmpdir, "tmpdir $tmpdir exists");
37
38 # Clear the error logfile. The testsuite runs in an environment where RandR is
39 # not supported, so there always is a message about xinerama in the error
40 # logfile.
41 my @errorlogfiles = <$tmpdir/errorlog.*>;
42 for my $fn (@errorlogfiles) {
43 open(my $fh, '>', $fn);
44 close($fh);
45 }
46
47 exit_gracefully($pid);
48
49 ok(! -d $tmpdir, "tmpdir $tmpdir was cleaned up");
50 if (-d $tmpdir) {
51 diag('contents = ' . Dumper(<$tmpdir/*>));
52 }
53
54 $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
55 $socketpath = get_socket_path(0);
56 $tmpdir = dirname($socketpath);
57
58 ok(-d $tmpdir, "tmpdir $tmpdir exists");
59
60 # Clear the error logfile. The testsuite runs in an environment where RandR is
61 # not supported, so there always is a message about xinerama in the error
62 # logfile.
63 @errorlogfiles = <$tmpdir/errorlog.*>;
64 for my $fn (@errorlogfiles) {
65 open(my $fh, '>', $fn);
66 close($fh);
67 }
68
69 diag('socket path before restarting is ' . $socketpath);
70
71 cmd 'restart';
72
73 # The socket path will be different, and we use that for checking whether i3 has restarted yet.
74 while (get_socket_path(0) eq $socketpath) {
75 sleep 0.1;
76 }
77
78 my $new_tmpdir = dirname(get_socket_path());
79
80 does_i3_live;
81
82 # Clear the error logfile. The testsuite runs in an environment where RandR is
83 # not supported, so there always is a message about xinerama in the error
84 # logfile.
85 @errorlogfiles = <$new_tmpdir/errorlog.*>;
86 for my $fn (@errorlogfiles) {
87 open(my $fh, '>', $fn);
88 close($fh);
89 }
90
91 exit_gracefully($pid);
92
93 ok(! -d $tmpdir, "old tmpdir $tmpdir was cleaned up");
94 if (-d $tmpdir) {
95 diag('contents = ' . Dumper(<$tmpdir/*>));
96 }
97
98 ok(! -d $new_tmpdir, "new tmpdir $new_tmpdir was cleaned up");
99 if (-d $new_tmpdir) {
100 diag('contents = ' . Dumper(<$new_tmpdir/*>));
101 }
102
103 ################################################################################
104 # Regression: with a socket path outside of tmpdir, i3 would delete the tmpdir
105 # prematurely and could not use it for storing the restart layout.
106 ################################################################################
107
108 # Set XDG_RUNTIME_DIR to a temp directory so that i3 will create its directory
109 # in there and we’ll be able to find it. Necessary because we cannot deduce the
110 # temp directory from the socket path (which we explicitly set).
111 $ENV{XDG_RUNTIME_DIR} = tempdir(CLEANUP => 1);
112
113 my ($outfh, $outname) = tempfile('/tmp/i3-socket.XXXXXX', UNLINK => 1);
114 $config = <<EOT;
115 # i3 config file (v4)
116 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
117
118 ipc-socket $outname
119 EOT
120
121 $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
122 $socketpath = get_socket_path(0);
123
124 sub get_config_path {
125 my $atom = $x->atom(name => 'I3_CONFIG_PATH');
126 my $cookie = $x->get_property(0, $x->get_root_window(), $atom->id, GET_PROPERTY_TYPE_ANY, 0, 256);
127 my $reply = $x->get_property_reply($cookie->{sequence});
128 return $reply->{value};
129 }
130
131 my ($outfh2, $outname2) = tempfile('/tmp/i3-socket.XXXXXX', UNLINK => 1);
132 my $config_path = get_config_path();
133 open(my $configfh, '>', $config_path);
134 say $configfh <<EOT;
135 # i3 config file (v4)
136 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
137 ipc-socket $outname2
138 EOT
139 close($configfh);
140 $tmpdir = $ENV{XDG_RUNTIME_DIR} . '/i3';
141
142 ok(-d $tmpdir, "tmpdir $tmpdir exists");
143
144 # Clear the error logfile. The testsuite runs in an environment where RandR is
145 # not supported, so there always is a message about xinerama in the error
146 # logfile.
147 @errorlogfiles = <$tmpdir/errorlog.*>;
148 for my $fn (@errorlogfiles) {
149 open(my $fh, '>', $fn);
150 close($fh);
151 }
152
153 my $tmp = fresh_workspace;
154 my $win = open_window;
155 cmd 'border none';
156 my ($nodes, $focus) = get_ws_content($tmp);
157 is($nodes->[0]->{border}, 'none', 'border is none');
158
159 cmd 'restart';
160
161 # The socket path will be different, and we use that for checking whether i3 has restarted yet.
162 while (get_socket_path(0) eq $socketpath) {
163 sleep 0.1;
164 }
165
166 $new_tmpdir = $ENV{XDG_RUNTIME_DIR} . '/i3';
167
168 does_i3_live;
169
170 ($nodes, $focus) = get_ws_content($tmp);
171 is($nodes->[0]->{border}, 'none', 'border still none after restart');
172
173 exit_gracefully($pid);
174
175 close($outfh);
176 close($outfh2);
177
178 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensures floating windows don’t drop out of fullscreen mode when restarting
17 # and that they keep their geometry.
18 # Ticket: #1263
19 # Bug still in: 4.7.2-200-g570b572
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 for_window [instance=__i3-test-window] floating enable, border pixel 1
25 EOT
26
27 my $tmp = fresh_workspace;
28
29 my $window = open_window(wm_class => '__i3-test-window');
30
31 cmd 'fullscreen';
32
33 my ($nodes, $focus) = get_ws($tmp);
34 my $floating_win = $nodes->{floating_nodes}->[0]->{nodes}->[0];
35 is($floating_win->{fullscreen_mode}, 1, 'floating window in fullscreen mode');
36 my $old_geometry = $floating_win->{geometry};
37
38 cmd 'restart';
39
40 ($nodes, $focus) = get_ws($tmp);
41 $floating_win = $nodes->{floating_nodes}->[0]->{nodes}->[0];
42 is($floating_win->{fullscreen_mode}, 1, 'floating window still in fullscreen mode');
43 is_deeply($floating_win->{geometry}, $old_geometry, 'floating window geometry still the same');
44
45 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the window::floating event works correctly. This event should be
17 # emitted when a window transitions to or from the floating state.
18 # Bug still in: 4.8-7-gf4a8253
19 use i3test;
20
21 sub floating_subtest {
22 my ($win, $cmd, $want) = @_;
23
24 my @events = events_for(
25 sub { cmd $cmd },
26 'window');
27
28 my @floating = grep { $_->{change} eq 'floating' } @events;
29 is(scalar @floating, 1, 'Received 1 floating event');
30 is($floating[0]->{container}->{window}, $win->{id}, "window id matches");
31 is($floating[0]->{container}->{floating}, $want, "floating is $want");
32 }
33
34 my $win = open_window();
35
36 subtest 'floating enable', \&floating_subtest, $win, '[id="' . $win->{id} . '"] floating enable', 'user_on';
37 subtest 'floating disable', \&floating_subtest, $win, '[id="' . $win->{id} . '"] floating disable', 'user_off';
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the `move [direction]` command works with criteria
17 # Bug still in: 4.8-16-g6888a1f
18 use i3test;
19
20 my ($ws, $win1, $win2, $win3, $ws_con);
21
22 ###############################################################################
23 # Tets moving with 'id' criterion.
24 ###############################################################################
25
26 $ws = fresh_workspace;
27
28 $win1 = open_window;
29 $win2 = open_window;
30 $win3 = open_window;
31
32 # move win1 from the left to the right
33 cmd '[id="' . $win1->{id} . '"] move right';
34
35 # now they should be switched, with win2 still being focused
36 $ws_con = get_ws($ws);
37
38 # win2 should be on the left
39 is($ws_con->{nodes}[0]->{window}, $win2->{id}, 'the `move [direction]` command should work with criteria');
40 is($x->input_focus, $win3->{id}, 'it should not disturb focus');
41
42 ###############################################################################
43 # Tets moving with 'window_type' criterion.
44 ###############################################################################
45
46 # test all window types
47 my %window_types = (
48 'normal' => '_NET_WM_WINDOW_TYPE_NORMAL',
49 'dialog' => '_NET_WM_WINDOW_TYPE_DIALOG',
50 'utility' => '_NET_WM_WINDOW_TYPE_UTILITY',
51 'toolbar' => '_NET_WM_WINDOW_TYPE_TOOLBAR',
52 'splash' => '_NET_WM_WINDOW_TYPE_SPLASH',
53 'menu' => '_NET_WM_WINDOW_TYPE_MENU',
54 'dropdown_menu' => '_NET_WM_WINDOW_TYPE_DROPDOWN_MENU',
55 'popup_menu' => '_NET_WM_WINDOW_TYPE_POPUP_MENU',
56 'tooltip' => '_NET_WM_WINDOW_TYPE_TOOLTIP',
57 'notification' => '_NET_WM_WINDOW_TYPE_NOTIFICATION'
58 );
59
60 while (my ($window_type, $atom) = each %window_types) {
61
62 $ws = fresh_workspace;
63
64 $win1 = open_window(window_type => $x->atom(name => $atom));
65 $win2 = open_window;
66 $win3 = open_window;
67
68 cmd '[window_type="' . $window_type . '"] move right';
69
70 $ws_con = get_ws($ws);
71 is($ws_con->{nodes}[0]->{window}, $win2->{id}, 'the `move [direction]` command should work with window_type = ' . $window_type);
72 is($x->input_focus, $win3->{id}, 'it should not disturb focus');
73
74 }
75
76 ###############################################################################
77
78 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that an assignment that unmaps a window does not disturb input focus.
17 # This can cause i3 focus to be an unmapped window and different than X focus
18 # which can lead to complications
19 # Ticket: #1283
20 # Bug still in: 4.8-24-g60070de
21 use i3test i3_config => <<'EOT';
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24
25 for_window [class="^special_kill$"] kill
26 for_window [class="^special_scratchpad$"] move scratchpad
27 EOT
28
29 my $win = open_window;
30
31 my $scratch_window = open_window(
32 wm_class => 'special_scratchpad',
33 dont_map => 1
34 );
35 $scratch_window->map;
36 sync_with_i3;
37
38 is($x->input_focus, $win->{id},
39 'an assignment that sends a window to the scratchpad should not disturb focus');
40
41 my $kill_window = open_window(
42 wm_class => 'special_kill',
43 dont_map => 1
44 );
45 $kill_window->map;
46 sync_with_i3;
47
48 is($x->input_focus, $win->{id},
49 'an assignment that kills a window should not disturb focus');
50
51 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the EWMH specified property _NET_DESKTOP_NAMES is updated properly
17 # on the root window. We interpret this as a list of the open workspace names.
18 # Ticket: #1241
19 use i3test;
20
21 sub get_desktop_names {
22 # Make sure that i3 pushed its changes to X11 before querying.
23 sync_with_i3;
24
25 my $cookie = $x->get_property(
26 0,
27 $x->get_root_window(),
28 $x->atom(name => '_NET_DESKTOP_NAMES')->id,
29 $x->atom(name => 'UTF8_STRING')->id,
30 0,
31 4096,
32 );
33
34 my $reply = $x->get_property_reply($cookie->{sequence});
35
36 return 0 if $reply->{value_len} == 0;
37
38 # the property is a null-delimited list of utf8 strings ;;
39 return split /\0/, $reply->{value};
40 }
41
42 cmd 'workspace foo';
43
44 my @expected_names = ('foo');
45 my @desktop_names = get_desktop_names;
46
47 is_deeply(\@desktop_names, \@expected_names, '_NET_DESKTOP_NAMES should be an array of the workspace names');
48
49 # open a new workspace and see that the property is updated correctly
50 open_window;
51 cmd 'workspace bar';
52
53 @desktop_names = get_desktop_names;
54 @expected_names = ('foo', 'bar');
55
56 is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a new workspace appears');
57
58 # rename the workspace and see that the property is updated correctly
59 cmd 'rename workspace bar to baz';
60
61 @desktop_names = get_desktop_names;
62 @expected_names = ('foo', 'baz');
63
64 is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a workspace is renamed');
65
66 # empty a workspace and see that the property is updated correctly
67 cmd 'workspace foo';
68
69 @desktop_names = get_desktop_names;
70 @expected_names = ('foo');
71
72 is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a workspace is emptied');
73
74 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Check whether the -C option works without a display and doesn't
17 # accidentally start the nagbar.
18 #
19 use i3test i3_autostart => 0;
20 use File::Temp qw(tempfile);
21
22 my ($cfg, $ret, $out);
23
24 sub check_config {
25 my ($config) = @_;
26 my ($fh, $tmpfile) = tempfile(UNLINK => 1);
27 print $fh $config;
28 my $output = qx(DISPLAY= i3 -C -c $tmpfile 2>&1);
29 my $retval = $?;
30 $fh->flush;
31 close($fh);
32 return ($retval >> 8, $output);
33 }
34
35 ################################################################################
36 # 1: test with a bogus configuration file
37 ################################################################################
38
39 $cfg = <<EOT;
40 # i3 config file (v4)
41 i_am_an_unknown_config option
42 EOT
43
44 ($ret, $out) = check_config($cfg);
45 is($ret, 1, "exit code == 1");
46 like($out, qr/ERROR: *CONFIG: *[Ee]xpected.*tokens/, 'bogus config file');
47
48 ################################################################################
49 # 2: test with a valid configuration file
50 ################################################################################
51
52 $cfg = <<EOT;
53 # i3 config file (v4)
54 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
55 EOT
56
57 ($ret, $out) = check_config($cfg);
58 is($ret, 0, "exit code == 0");
59 is($out, "", 'valid config file');
60
61 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that focusing floating windows with the command `focus [direction]`
17 # promotes the focused window to the top of the rendering stack.
18 # Ticket: #1322
19 # Bug still in: 4.8-88-gcc09348
20 use i3test;
21
22 my $ws = fresh_workspace;
23
24 my $win1 = open_floating_window;
25 my $win2 = open_floating_window;
26 my $win3 = open_floating_window;
27
28 # it's a good idea to do this a few times because of the implementation
29 for my $i (1 .. 3) {
30 cmd 'focus left';
31 my $ws_con = get_ws($ws);
32 is($ws_con->{floating_nodes}[-1]->{nodes}[0]->{id}, get_focused($ws),
33 "focus left put the focused window on top of the floating windows (try $i)");
34 }
35
36 for my $i (1 .. 3) {
37 cmd 'focus right';
38 my $ws_con = get_ws($ws);
39 is($ws_con->{floating_nodes}[-1]->{nodes}[0]->{id}, get_focused($ws),
40 "focus right put the focused window on top of the floating windows (try $i)");
41 }
42
43 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that using layout tabbed followed by focus (on a window that is
17 # assigned to an invisible workspace) will not crash i3.
18 # Ticket: #1338
19 # Bug still in: 4.8-91-g294d52e
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 assign [title=".*"] 1
25 for_window [title=".*"] layout tabbed, focus
26 EOT
27
28 # Switch away from workspace 1
29 my $tmp = fresh_workspace;
30
31 my $win = open_window;
32
33 does_i3_live;
34
35 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the binding event works properly
17 # Ticket: #1210
18 use i3test i3_autostart => 0;
19
20 my $keysym = 't';
21 my $command = 'nop';
22 my @mods = ('Shift', 'Ctrl');
23 my $binding_symbol = join("+", @mods) . "+$keysym";
24
25 my $config = <<EOT;
26 # i3 config file (v4)
27 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
28
29 bindsym $binding_symbol $command
30 EOT
31
32 SKIP: {
33 qx(which xdotool 2> /dev/null);
34
35 skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
36
37 my $pid = launch_with_config($config);
38
39 my $cv = AnyEvent->condvar;
40
41 my @events = events_for(
42 sub {
43 # TODO: this is still flaky: we need to synchronize every X11
44 # connection with i3. Move to XTEST and synchronize that connection.
45 qx(xdotool key $binding_symbol);
46 },
47 'binding');
48
49 is(scalar @events, 1, 'Received 1 event');
50
51 is($events[0]->{change}, 'run',
52 'the `change` field should indicate this binding has run');
53
54 ok($events[0]->{binding},
55 'the `binding` field should be a hash that contains information about the binding');
56
57 is($events[0]->{binding}->{input_type}, 'keyboard',
58 'the input_type field should be the input type of the binding (keyboard or mouse)');
59
60 note 'the `mods` field should contain the symbols for the modifiers of the binding';
61 foreach (@mods) {
62 ok(grep(/$_/i, @{$events[0]->{binding}->{mods}}), "`mods` contains the modifier $_");
63 }
64
65 is($events[0]->{binding}->{command}, $command,
66 'the `command` field should contain the command the binding ran');
67
68 is($events[0]->{binding}->{input_code}, 0,
69 'the input_code should be the specified code if the key was bound with bindcode, and otherwise zero');
70
71 exit_gracefully($pid);
72
73 }
74 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test _NET_CLOSE_WINDOW requests to close a window.
17 # See https://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896
18 # Ticket: #1396
19 # Bug still in: 4.8-116-gbb1f857
20 use i3test;
21
22 sub send_close_window_request {
23 my ($win) = @_;
24
25 my $msg = pack "CCSLLLLLL",
26 X11::XCB::CLIENT_MESSAGE, # response_type
27 32, # format
28 0, # sequence
29 $win->id, # window
30 $x->atom(name => '_NET_CLOSE_WINDOW')->id, # message type
31 0, # data32[0]
32 0, # data32[1]
33 0, # data32[2]
34 0, # data32[3]
35 0; # data32[4]
36
37 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
38 }
39
40 my $ws = fresh_workspace;
41 my $win = open_window;
42
43 send_close_window_request($win);
44 sync_with_i3;
45
46 is(@{get_ws($ws)->{nodes}}, 0, 'When a pager sends a _NET_CLOSE_WINDOW request for a window, the container should be closed');
47
48 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for the focus_on_window_activation directive
17 # Ticket: #1426
18 use i3test i3_autostart => 0;
19 use List::Util qw(first);
20
21 my ($config, $pid, $first, $second, $ws1, $ws2);
22
23 sub send_net_active_window {
24 my ($id) = @_;
25
26 my $msg = pack "CCSLLLLLLL",
27 X11::XCB::CLIENT_MESSAGE, # response_type
28 32, # format
29 0, # sequence
30 $id, # destination window
31 $x->atom(name => '_NET_ACTIVE_WINDOW')->id,
32 0, # source
33 0, 0, 0, 0;
34
35 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
36 }
37
38 sub get_urgency_for_window_on_workspace {
39 my ($ws, $con) = @_;
40
41 my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
42 return $current->{urgent};
43 }
44
45 #####################################################################
46 # 1: If mode is set to 'urgent' and the target workspace is visible,
47 # check that the urgent flag is set and focus is not lost.
48 #####################################################################
49
50 $config = <<EOT;
51 # i3 config file (v4)
52 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
53
54 focus_on_window_activation urgent
55 EOT
56
57 $pid = launch_with_config($config);
58
59 my $ws = fresh_workspace;
60 $first = open_window;
61 $second = open_window;
62
63 send_net_active_window($first->id);
64 sync_with_i3;
65
66 is($x->input_focus, $second->id, 'second window is still focused');
67 is(get_urgency_for_window_on_workspace($ws, $first), 1, 'first window is marked urgent');
68
69 exit_gracefully($pid);
70
71 #####################################################################
72 # 2: If mode is set to 'urgent' and the target workspace is not
73 # visible, check that the urgent flag is set and focus is not lost.
74 #####################################################################
75
76 $config = <<EOT;
77 # i3 config file (v4)
78 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
79
80 focus_on_window_activation urgent
81 EOT
82
83 $pid = launch_with_config($config);
84
85 $ws1 = fresh_workspace;
86 $first = open_window;
87 $ws2 = fresh_workspace;
88 $second = open_window;
89
90 send_net_active_window($first->id);
91 sync_with_i3;
92
93 is(focused_ws(), $ws2, 'second workspace is still focused');
94 is($x->input_focus, $second->id, 'second window is still focused');
95 is(get_urgency_for_window_on_workspace($ws1, $first), 1, 'first window is marked urgent');
96
97 exit_gracefully($pid);
98
99 #####################################################################
100 # 3: If mode is set to 'focus' and the target workspace is visible,
101 # check that the focus is switched.
102 #####################################################################
103
104 $config = <<EOT;
105 # i3 config file (v4)
106 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
107
108 focus_on_window_activation focus
109 EOT
110
111 $pid = launch_with_config($config);
112
113 $ws = fresh_workspace;
114 $first = open_window;
115 $second = open_window;
116
117 send_net_active_window($first->id);
118 sync_with_i3;
119
120 is($x->input_focus, $first->id, 'first window is now focused');
121 ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
122
123 exit_gracefully($pid);
124
125 #####################################################################
126 # 4: If mode is set to 'focus' and the target workspace is not
127 # visible, check that the focus switched.
128 #####################################################################
129
130 $config = <<EOT;
131 # i3 config file (v4)
132 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
133
134 focus_on_window_activation focus
135 EOT
136
137 $pid = launch_with_config($config);
138
139 $ws1 = fresh_workspace;
140 $first = open_window;
141 $ws2 = fresh_workspace;
142 $second = open_window;
143
144 send_net_active_window($first->id);
145 sync_with_i3;
146
147 is(focused_ws(), $ws1, 'first workspace is now focused');
148 is($x->input_focus, $first->id, 'first window is now focused');
149 ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
150
151 exit_gracefully($pid);
152
153 #####################################################################
154 # 5: If mode is set to 'none' and the target workspace is visible,
155 # check that nothing happens.
156 #####################################################################
157
158 $config = <<EOT;
159 # i3 config file (v4)
160 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
161
162 focus_on_window_activation none
163 EOT
164
165 $pid = launch_with_config($config);
166
167 $ws = fresh_workspace;
168 $first = open_window;
169 $second = open_window;
170
171 send_net_active_window($first->id);
172 sync_with_i3;
173
174 is($x->input_focus, $second->id, 'second window is still focused');
175 ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
176
177 exit_gracefully($pid);
178
179 #####################################################################
180 # 6: If mode is set to 'none' and the target workspace is not
181 # visible, check that nothing happens.
182 #####################################################################
183
184 $config = <<EOT;
185 # i3 config file (v4)
186 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
187
188 focus_on_window_activation none
189 EOT
190
191 $pid = launch_with_config($config);
192
193 $ws1 = fresh_workspace;
194 $first = open_window;
195 $ws2 = fresh_workspace;
196 $second = open_window;
197
198 send_net_active_window($first->id);
199 sync_with_i3;
200
201 is(focused_ws(), $ws2, 'second workspace is still focused');
202 is($x->input_focus, $second->id, 'second window is still focused');
203 ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
204
205 exit_gracefully($pid);
206
207 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that most of i3's centering methods produce consistent results.
17 # Decorations are disabled to avoid floating_enable's logic which shifts
18 # windows upwards dependent on their decoration height.
19 #
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 new_window none
25 new_float none
26 EOT
27
28 #####################################################################
29 # Open a floating window, verifying that its initial position is
30 # centered, and also verify that both centering methods leave it in
31 # its original spot.
32 #####################################################################
33
34 my $first = open_floating_window;
35
36 my $initial = $first->rect;
37 is(int($initial->{x} + $initial->{width} / 2), int($x->root->rect->width / 2),
38 'x coordinates match');
39 is(int($initial->{y} + $initial->{height} / 2), int($x->root->rect->height / 2),
40 'y coordinates match');
41
42 cmd 'move position center';
43
44 my $new = $first->rect;
45 is($initial->{x}, $new->{x}, 'x coordinates match');
46 is($initial->{y}, $new->{y}, 'y coordinates match');
47
48 cmd 'move absolute position center';
49
50 $new = $first->rect;
51 is($initial->{x}, $new->{x}, 'x coordinates match');
52 is($initial->{y}, $new->{y}, 'y coordinates match');
53
54 #####################################################################
55 # Create a second window and move it into and out of the scratchpad.
56 # Because it hasn't been moved or resized, it should be floated in
57 # the center of the screen when pulled out of the scratchpad.
58 #####################################################################
59
60 my $second = open_window;
61
62 cmd 'move scratchpad, scratchpad show';
63
64 $new = $second->rect;
65 my $mid_init = $initial->{x} + int($initial->{width} / 2);
66 my $mid_new = $new->{x} + int($new->{width} / 2);
67 is($mid_init, $mid_new, 'x midpoint is ws center');
68
69 $mid_init = $initial->{y} + int($initial->{height} / 2);
70 $mid_new = $new->{y} + int($new->{height} / 2);
71 is($mid_init, $mid_new, 'y midpoint is ws center');
72
73 #####################################################################
74 # Verify that manually floating a tiled window results in proper
75 # centering.
76 #####################################################################
77
78 my $third = open_window;
79
80 cmd 'floating enable';
81
82 $new = $third->rect;
83 is($initial->{x}, $new->{x}, 'x coordinates match');
84 is($initial->{y}, $new->{y}, 'y coordinates match');
85
86 #####################################################################
87 # Create a child window of the previous window, which should result
88 # in the new window being centered over the last one.
89 #####################################################################
90
91 my $fourth = open_window( dont_map => 1, client_leader => $third );
92 $fourth->map;
93 sync_with_i3;
94
95 my $child = $fourth->rect;
96 is($new->{x}, $child->{x}, 'x coordinates match');
97 is($new->{y}, $child->{y}, 'y coordinates match');
98
99 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test the 'no_focus' directive.
17 # Ticket: #1416
18 use i3test i3_autostart => 0;
19
20 my ($config, $pid, $ws, $first, $second, $focused);
21
22 #####################################################################
23 # 1: open a window and check that it takes focus
24 #####################################################################
25
26 $config = <<EOT;
27 # i3 config file (v4)
28 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
29 EOT
30
31 $pid = launch_with_config($config);
32
33 $ws = fresh_workspace;
34 $first = open_window;
35 $focused = get_focused($ws);
36 $second = open_window;
37
38 sync_with_i3;
39 isnt(get_focused($ws), $focused, 'focus has changed');
40 is($x->input_focus, $second->id, 'input focus has changed');
41
42 exit_gracefully($pid);
43
44 #####################################################################
45 # 2: open a window matched by a no_focus directive and check that
46 # it doesn't take focus
47 #####################################################################
48
49 $config = <<EOT;
50 # i3 config file (v4)
51 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
52
53 no_focus [instance=notme]
54 EOT
55
56 $pid = launch_with_config($config);
57
58 $ws = fresh_workspace;
59 $first = open_window;
60 $focused = get_focused($ws);
61 $second = open_window(wm_class => 'notme');
62
63 sync_with_i3;
64 is(get_focused($ws), $focused, 'focus has not changed');
65 is($x->input_focus, $first->id, 'input focus has not changed');
66
67 exit_gracefully($pid);
68
69 #####################################################################
70 # 3: no_focus doesn't affect the first window opened on a workspace
71 #####################################################################
72
73 $config = <<EOT;
74 # i3 config file (v4)
75 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
76
77 no_focus [instance=focusme]
78 EOT
79
80 $pid = launch_with_config($config);
81
82 $ws = fresh_workspace;
83 $focused = get_focused($ws);
84 $first = open_window(wm_class => 'focusme');
85
86 sync_with_i3;
87 is($x->input_focus, $first->id, 'input focus has changed');
88
89 # Also check that it counts floating windows
90 # See issue #3423.
91 open_floating_window(wm_class => 'focusme');
92
93 sync_with_i3;
94 is($x->input_focus, $first->id, 'input focus didn\'t change to floating window');
95
96 exit_gracefully($pid);
97
98 #####################################################################
99
100 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for the 'move [window|container] to mark' command
17 # Ticket: #1643
18 use i3test;
19
20 # In the following tests descriptions, we will always use the following names:
21 # * 'S' for the source container which is going to be moved,
22 # * 'M' for the marked target container to which 'S' will be moved.
23
24 my ($A, $B, $S, $M, $F, $source_ws, $target_ws, $ws);
25 my ($nodes, $focus);
26 my $cmd_result;
27
28 my $_NET_WM_STATE_REMOVE = 0;
29 my $_NET_WM_STATE_ADD = 1;
30 my $_NET_WM_STATE_TOGGLE = 2;
31
32 sub set_urgency {
33 my ($win, $urgent_flag) = @_;
34 my $msg = pack "CCSLLLLLL",
35 X11::XCB::CLIENT_MESSAGE, # response_type
36 32, # format
37 0, # sequence
38 $win->id, # window
39 $x->atom(name => '_NET_WM_STATE')->id, # message type
40 ($urgent_flag ? $_NET_WM_STATE_ADD : $_NET_WM_STATE_REMOVE), # data32[0]
41 $x->atom(name => '_NET_WM_STATE_DEMANDS_ATTENTION')->id, # data32[1]
42 0, # data32[2]
43 0, # data32[3]
44 0; # data32[4]
45
46 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
47 }
48
49 ###############################################################################
50 # Given 'M' and 'S' in a horizontal split, when 'S' is moved to 'M', then
51 # verify that nothing changed.
52 ###############################################################################
53
54 $ws = fresh_workspace;
55 $M = open_window;
56 cmd 'mark target';
57 $S = open_window;
58
59 cmd 'move container to mark target';
60 sync_with_i3;
61
62 ($nodes, $focus) = get_ws_content($ws);
63 is(@{$nodes}, 2, 'there are two containers');
64 is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
65 is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
66
67 ###############################################################################
68 # Given 'S' and 'M' in a horizontal split, when 'S' is moved to 'M', then
69 # both containers switch places.
70 ###############################################################################
71
72 $ws = fresh_workspace;
73 $S = open_window;
74 $M = open_window;
75 cmd 'mark target';
76 cmd 'focus left';
77
78 cmd 'move container to mark target';
79 sync_with_i3;
80
81 ($nodes, $focus) = get_ws_content($ws);
82 is(@{$nodes}, 2, 'there are two containers');
83 is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
84 is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
85
86 ###############################################################################
87 # Given 'S' and no container 'M' exists, when 'S' is moved to 'M', then
88 # the command is unsuccessful.
89 ###############################################################################
90
91 $ws = fresh_workspace;
92 $S = open_window;
93
94 $cmd_result = cmd 'move container to mark absent';
95
96 is($cmd_result->[0]->{success}, 0, 'command was unsuccessful');
97
98 ###############################################################################
99 # Given 'S' and 'M' on different workspaces, when 'S' is moved to 'M', then
100 # 'S' ends up on the same workspace as 'M'.
101 ###############################################################################
102
103 $source_ws = fresh_workspace;
104 $S = open_window;
105 $target_ws = fresh_workspace;
106 $M = open_window;
107 cmd 'mark target';
108
109 cmd '[id="' . $S->{id} . '"] move container to mark target';
110 sync_with_i3;
111
112 ($nodes, $focus) = get_ws_content($source_ws);
113 is(@{$nodes}, 0, 'source workspace is empty');
114
115 ($nodes, $focus) = get_ws_content($target_ws);
116 is(@{$nodes}, 2, 'both containers are on the target workspace');
117 is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
118 is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
119
120 ###############################################################################
121 # Given 'S' and 'M' on different workspaces and 'S' is urgent, when 'S' is
122 # moved to 'M', then the urgency flag is transferred to the target workspace.
123 ###############################################################################
124
125 $source_ws = fresh_workspace;
126 $S = open_window;
127 $F = open_window;
128 $target_ws = fresh_workspace;
129 $M = open_window;
130 cmd 'mark target';
131 cmd 'workspace ' . $source_ws;
132 set_urgency($S, 1);
133
134 cmd '[id="' . $S->{id} . '"] move container to mark target';
135 sync_with_i3;
136
137 $source_ws = get_ws($source_ws);
138 $target_ws = get_ws($target_ws);
139 ok(!$source_ws->{urgent}, 'source workspace is no longer urgent');
140 ok($target_ws->{urgent}, 'target workspace is urgent');
141
142 ###############################################################################
143 # Given 'S' and 'M' where 'M' is inside a tabbed container, when 'S' is moved
144 # to 'M', then 'S' ends up as a new tab.
145 ###############################################################################
146
147 $source_ws = fresh_workspace;
148 $S = open_window;
149
150 # open tabbed container ['A' 'M' 'B']
151 $target_ws = fresh_workspace;
152 $A = open_window;
153 cmd 'layout tabbed';
154 $M = open_window;
155 cmd 'mark target';
156 $B = open_window;
157
158 cmd '[id="' . $S->{id} . '"] move container to mark target';
159 sync_with_i3;
160
161 ($nodes, $focus) = get_ws_content($target_ws);
162 is(@{$nodes}, 1, 'there is a tabbed container');
163
164 $nodes = $nodes->[0]->{nodes};
165 is(@{$nodes}, 4, 'all four containers are on the target workspace');
166 is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
167 is($nodes->[1]->{window}, $M->{id}, 'M is the second tab');
168 is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
169 is($nodes->[3]->{window}, $B->{id}, 'B is the fourth tab');
170
171 ###############################################################################
172 # Given 'S' and 'M' where 'M' is a tabbed container where the currently focused
173 # tab is a nested layout, when 'S' is moved to 'M', then 'S' is a new tab
174 # within 'M'.
175 ###############################################################################
176
177 $source_ws = fresh_workspace;
178 $S = open_window;
179
180 $target_ws = fresh_workspace;
181 $A = open_window;
182 cmd 'layout tabbed';
183 cmd 'focus parent';
184 cmd 'mark target';
185 cmd 'focus child';
186 $B = open_window;
187 cmd 'split h';
188 $F = open_window;
189
190 cmd '[id="' . $S->{id} . '"] move container to mark target';
191 sync_with_i3;
192
193 ($nodes, $focus) = get_ws_content($target_ws);
194 is(@{$nodes}, 1, 'there is a tabbed container');
195
196 $nodes = $nodes->[0]->{nodes};
197 is(@{$nodes}, 3, 'there are three tabs');
198
199 is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
200 is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
201
202 ###############################################################################
203 # Given 'S' and 'M' where 'M' is inside a split container inside a tabbed
204 # container, when 'S' is moved to 'M', then 'S' ends up as a container
205 # within the same tab as 'M'.
206 ###############################################################################
207
208 $source_ws = fresh_workspace;
209 $S = open_window;
210
211 # open tabbed container ['A'['B' 'M']]
212 $target_ws = fresh_workspace;
213 $A = open_window;
214 cmd 'layout tabbed';
215 $B = open_window;
216 cmd 'split h';
217 $M = open_window;
218 cmd 'mark target';
219
220 cmd '[id="' . $S->{id} . '"] move container to mark target';
221 sync_with_i3;
222
223 ($nodes, $focus) = get_ws_content($target_ws);
224 is(@{$nodes}, 1, 'there is a tabbed container');
225
226 $nodes = $nodes->[0]->{nodes};
227 is(@{$nodes}, 2, 'there are two tabs');
228
229 $nodes = $nodes->[1]->{nodes};
230 is(@{$nodes}, 3, 'the tab with the marked children has three children');
231 is($nodes->[0]->{window}, $B->{id}, 'B is the first tab');
232 is($nodes->[1]->{window}, $M->{id}, 'M is the second tab');
233 is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
234
235 ###############################################################################
236 # Given 'S', 'A' and 'B' where 'A' and 'B' are inside the tabbed container 'M',
237 # when 'S' is moved to 'M', then 'S' ends up as a new tab in 'M'.
238 ###############################################################################
239
240 $source_ws = fresh_workspace;
241 $S = open_window;
242 $target_ws = fresh_workspace;
243 $A = open_window;
244 cmd 'layout tabbed';
245 $B = open_window;
246 cmd 'focus parent';
247 cmd 'mark target';
248 cmd 'focus child';
249
250 cmd '[id="' . $S->{id} . '"] move container to mark target';
251 sync_with_i3;
252
253 ($nodes, $focus) = get_ws_content($target_ws);
254 is(@{$nodes}, 1, 'there is a tabbed container');
255
256 $nodes = $nodes->[0]->{nodes};
257 is(@{$nodes}, 3, 'there are three tabs');
258
259 is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
260 is($nodes->[1]->{window}, $B->{id}, 'B is the second tab');
261 is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
262
263 ###############################################################################
264 # Given 'S', 'A', 'F' and 'M', where 'M' is a workspace containing a tabbed
265 # container, when 'S' is moved to 'M', then 'S' does not end up as a tab, but
266 # rather as a new window next to the tabbed container.
267 ###############################################################################
268
269 $source_ws = fresh_workspace;
270 $S = open_window;
271 $target_ws = fresh_workspace;
272 $A = open_window;
273 cmd 'layout tabbed';
274 $F = open_window;
275 $M = $target_ws;
276 cmd 'focus parent';
277 cmd 'focus parent';
278 cmd 'mark target';
279 cmd 'focus ' . $source_ws;
280
281 cmd '[id="' . $S->{id} . '"] move container to mark target';
282 sync_with_i3;
283
284 ($nodes, $focus) = get_ws_content($target_ws);
285 is(@{$nodes}, 2, 'there is a tabbed container and a window');
286 is($nodes->[1]->{window}, $S->{id}, 'S is the second window');
287
288 ###############################################################################
289 # Given 'S' and 'M' where 'S' is floating and 'M' on a different workspace,
290 # when 'S' is moved to 'M', then 'S' is a floating container on the same
291 # workspaces as 'M'.
292 ###############################################################################
293
294 $source_ws = fresh_workspace;
295 $S = open_floating_window;
296 $target_ws = fresh_workspace;
297 $M = open_window;
298 cmd 'mark target';
299
300 cmd '[id="' . $S->{id} . '"] move container to mark target';
301 sync_with_i3;
302
303 is(@{get_ws($target_ws)->{floating_nodes}}, 1, 'target workspace has the container now');
304
305 ###############################################################################
306 # Given 'S' and 'M' where 'M' is floating and on a different workspace,
307 # when 'S' is moved to 'M', then 'S' ends up as a tiling container on the
308 # same workspace as 'M'.
309 ###############################################################################
310
311 $source_ws = fresh_workspace;
312 $S = open_window;
313 $target_ws = fresh_workspace;
314 $M = open_floating_window;
315 cmd 'mark target';
316
317 cmd '[id="' . $S->{id} . '"] move container to mark target';
318 sync_with_i3;
319
320 ($nodes, $focus) = get_ws_content($target_ws);
321 is(@{$nodes}, 1, 'tiling container moved to the target workspace');
322
323 ###############################################################################
324 # Given 'S' and 'M' where 'M' is inside a floating container but not its direct
325 # child, when 'S' is moved to 'M', i3 should not crash.
326 # See issue: #3402
327 ###############################################################################
328
329 $target_ws = fresh_workspace;
330 $S = open_window;
331 open_window;
332 cmd 'splitv';
333 $M = open_window;
334 cmd 'mark target';
335 cmd 'focus parent, floating enable, focus child';
336
337 cmd '[id="' . $S->{id} . '"] move container to mark target';
338 does_i3_live;
339
340 # Note: this is not actively supported behavior.
341 $nodes = get_ws($target_ws)->{floating_nodes}->[0]->{nodes}->[0]->{nodes};
342 is(1, (grep { $_->{window} == $S->{id} } @{$nodes}), 'tiling container moved inside floating container');
343
344 ###############################################################################
345 # Given 'S' and 'M' are the same container, when 'S' is moved to 'M', then
346 # the command is ignored.
347 ###############################################################################
348
349 $ws = fresh_workspace;
350 $S = open_window;
351 $M = $S;
352 cmd 'mark target';
353
354 cmd '[id="' . $S->{id} . '"] move container to mark target';
355 sync_with_i3;
356
357 does_i3_live;
358
359 ###############################################################################
360 # Given 'S' and 'M' where 'M' is a workspace and 'S' is on a different
361 # workspace, then 'S' ends up as a tiling container on 'M'.
362 ###############################################################################
363
364 fresh_workspace;
365 $S = open_window;
366 $target_ws = fresh_workspace;
367 $M = $target_ws;
368 cmd 'mark target';
369
370 cmd '[id="' . $S->{id} . '"] move container to mark target';
371 sync_with_i3;
372
373 does_i3_live;
374
375 ($nodes, $focus) = get_ws_content($target_ws);
376 is(@{$nodes}, 1, 'tiling container moved to the target workspace');
377
378 ###############################################################################
379 # Given 'S' and 'M' where 'S' is a workspace and 'M' is a container on a
380 # different workspace, then all the contents of workspace 'S' end up in 'M's
381 # workspace.
382 ###############################################################################
383
384 $S = fresh_workspace;
385 cmd 'mark S';
386 open_window;
387 open_window;
388 cmd 'splitv';
389 open_window;
390 open_floating_window;
391 $target_ws = fresh_workspace;
392 $M = open_window;
393 cmd 'mark target';
394
395 cmd '[con_mark=S] move container to mark target';
396 sync_with_i3;
397
398 ($nodes, $focus) = get_ws_content($target_ws);
399 is(@{$nodes}, 2, 'there is a window and a container with the contents of the original workspace');
400 is($nodes->[0]->{window}, $M->{id}, 'M remains the first window');
401 is(@{get_ws($target_ws)->{floating_nodes}}, 1, 'target workspace has the floating container');
402
403 ###############################################################################
404
405 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensures that 'move workspace $new, floating enable' on a marked window
17 # leaves the window centered on the new workspace.
18 # Bug still in: 4.10.2-137-ga4f0ed6
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 new_window none
24 new_float none
25 EOT
26
27 #####################################################################
28 # Open a tiled window, and then simultaneously move it to another
29 # workspace and float it, ensuring that it ends up centered.
30 #####################################################################
31
32 my $window = open_window;
33 my $unused = get_unused_workspace();
34
35 cmd "mark foo; [con_mark=\"foo\"] move workspace $unused, floating enable";
36
37 sync_with_i3;
38
39 my $pos = $window->rect;
40
41 is(int($pos->{x} + $pos->{width} / 2), int($x->root->rect->width / 2),
42 'x coordinates match');
43 is(int($pos->{y} + $pos->{height} / 2), int($x->root->rect->height / 2),
44 'y coordinates match');
45
46 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the 'move [window|container] [to] position mouse|cursor|pointer command.
17 # Ticket: #1696
18 use i3test i3_autostart => 0;
19 use POSIX qw(floor);
20
21 sub warp_pointer {
22 my ($pos_x, $pos_y) = @_;
23 $x->root->warp_pointer($pos_x, $pos_y);
24 sync_with_i3;
25 }
26
27 my ($pid, $config, $ws, $rect, @cons);
28 my $root_rect = $x->root->rect;
29
30 ##########################################################################
31
32 ##########################################################################
33 # Given a floating container and the cursor far from any edges, when
34 # moving the container to the mouse, then the container is moved such
35 # that the cursor is centered inside it.
36 ##########################################################################
37
38 $config = <<EOT;
39 # i3 config file (v4)
40 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
41 EOT
42 $pid = launch_with_config($config);
43
44 $ws = fresh_workspace;
45 open_floating_window;
46 warp_pointer(100, 100);
47
48 cmd 'move position mouse';
49
50 @cons = @{get_ws($ws)->{floating_nodes}};
51 $rect = $cons[0]->{rect};
52
53 is(floor($rect->{x} + $rect->{width} / 2), 100, "con is centered around cursor horizontally");
54 is(floor($rect->{y} + $rect->{height} / 2), 100, "con is centered around cursor vertically");
55
56 exit_gracefully($pid);
57
58 ##########################################################################
59 # Given a floating container and the cursor is in the left upper edge
60 # of the output, when moving the container to the mouse, then the
61 # container is moved but adjusted to stay in-bounds.
62 ##########################################################################
63
64 $config = <<EOT;
65 # i3 config file (v4)
66 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
67 EOT
68 $pid = launch_with_config($config);
69
70 $ws = fresh_workspace;
71 open_floating_window;
72 warp_pointer(5, 5);
73
74 cmd 'move position mouse';
75
76 @cons = @{get_ws($ws)->{floating_nodes}};
77 $rect = $cons[0]->{rect};
78
79 is($rect->{x}, 0, "con is on the left edge");
80 is($rect->{y}, 0, "con is on the upper edge");
81
82 exit_gracefully($pid);
83
84 ##########################################################################
85 # Given a floating container and the cursor is in the left right lower
86 # edge of the output, when moving the container to the mouse, then the
87 # container is moved but adjusted to stay in-bounds.
88 ##########################################################################
89
90 $config = <<EOT;
91 # i3 config file (v4)
92 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
93 EOT
94 $pid = launch_with_config($config);
95
96 $ws = fresh_workspace;
97 open_floating_window;
98 warp_pointer($root_rect->width - 5, $root_rect->height - 5);
99
100 cmd 'move position mouse';
101
102 @cons = @{get_ws($ws)->{floating_nodes}};
103 $rect = $cons[0]->{rect};
104
105 is($rect->{x} + $rect->{width}, $root_rect->width, "con is on the right edge");
106 is($rect->{y} + $rect->{height}, $root_rect->height, "con is on the lower edge");
107
108 exit_gracefully($pid);
109
110 ##########################################################################
111 # Given a floating container and the cursor being close to the corner of
112 # an output, when the container is moved to the mouse, then the container
113 # is moved but adjusted to the output boundaries.
114 # This test verifies that boundaries of individual outputs are respected
115 # and not just boundaries of the entire X root screen.
116 ##########################################################################
117
118 $config = <<EOT;
119 # i3 config file (v4)
120 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
121 fake-outputs 500x500+0+0,500x500+500+0,500x500+0+500,500x500+500+500
122 EOT
123 $pid = launch_with_config($config);
124
125 $ws = fresh_workspace;
126 open_floating_window;
127 warp_pointer(495, 495);
128
129 cmd 'move position mouse';
130
131 @cons = @{get_ws($ws)->{floating_nodes}};
132 $rect = $cons[0]->{rect};
133
134 is($rect->{x} + $rect->{width}, 500, "con is on the right edge");
135 is($rect->{y} + $rect->{height}, 500, "con is on the lower edge");
136
137 exit_gracefully($pid);
138
139 ##########################################################################
140
141 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensure that hovering over the window decoration of a window causes it to focus
17 # correctly.
18 # Ticket: #1056
19 # Bug still in: 4.10.2-174-g8029ff0
20 use i3test;
21
22 my ($ws, $A, $B, $C, $target, $y);
23 my @cons;
24
25 # ==================================================================================
26 # Given the following layout (= denotes the window decoration),
27 # when moving the mouse from 1 to 2,
28 # then the C should be focused.
29 #
30 # This should especially be the case if B is the focus head of its vertically split parent container.
31 #
32 # +===A===+===B===+
33 # | | |
34 # | 1 +=2=C===+
35 # | | |
36 # +-------+-------+
37 #
38 # ==================================================================================
39
40 $ws = fresh_workspace;
41
42 open_window;
43 $B = open_window;
44 cmd 'split v';
45 open_window;
46 $target = get_focused($ws);
47
48 @cons = @{get_ws($ws)->{nodes}};
49 $A = $cons[0];
50 $C = $cons[1]->{nodes}[1];
51
52 $y = $C->{rect}->{y} - 0.5 * $C->{deco_rect}->{height};
53
54 # make sure that B is the focus head of its parent
55 cmd '[id="' . $B->{id} . '"] focus';
56
57 # move pointer to position 1
58 $x->root->warp_pointer($C->{rect}->{x} - 0.5 * $A->{rect}->{width}, $y);
59 sync_with_i3;
60
61 # move pointer to position 2
62 $x->root->warp_pointer($C->{rect}->{x} + 0.5 * $C->{rect}->{width}, $y);
63 sync_with_i3;
64
65 is(get_focused($ws), $target, 'focus switched to container C');
66
67 # ==================================================================================
68 # Given a tabbed container when the mouse is moved onto the window decoration
69 # then the focus head of the tabbed container is focused regardless of which particular
70 # tab's decoration the mouse is on.
71 #
72 # +=========+=========+
73 # | | |
74 # | 1 +=2==|****| <- tab to the right is focus head of tabbed container
75 # | | |
76 # +---------+---------+
77 #
78 # ==================================================================================
79
80 $ws = fresh_workspace;
81
82 open_window;
83 open_window;
84 cmd 'split v';
85 open_window;
86 cmd 'split h';
87 open_window;
88 $target = get_focused($ws);
89 cmd 'layout tabbed';
90
91 @cons = @{get_ws($ws)->{nodes}};
92 $A = $cons[0];
93 $B = $cons[1]->{nodes}[1]->{nodes}[1];
94
95 $y = $B->{rect}->{y} - 0.5 * $B->{deco_rect}->{height};
96
97 $x->root->warp_pointer($B->{rect}->{x} - 0.5 * $A->{rect}->{width}, $y);
98 sync_with_i3;
99
100 $x->root->warp_pointer($B->{rect}->{x} + 0.2 * $B->{rect}->{width}, $y);
101 sync_with_i3;
102
103 is(get_focused($ws), $target, 'focus switched to the focus head of the tabbed container');
104
105 # ==================================================================================
106 # Given a stacked container when the mouse is moved onto the window decoration
107 # then the focus head of the stacked container is focused regardless of which particular
108 # tab's decoration the mouse is on.
109 #
110 # +=========+=========+
111 # | | |
112 # | 1 +=2=======+
113 # | +*********+ <- the lower tab is the focus head of the stacked container
114 # | | |
115 # +---------+---------+
116 #
117 # ==================================================================================
118
119 $ws = fresh_workspace;
120
121 open_window;
122 open_window;
123 cmd 'split v';
124 open_window;
125 cmd 'split h';
126 open_window;
127 $target = get_focused($ws);
128 cmd 'layout stacked';
129
130 @cons = @{get_ws($ws)->{nodes}};
131 $A = $cons[0];
132 $B = $cons[1]->{nodes}[1]->{nodes}[0];
133 $C = $cons[1]->{nodes}[1]->{nodes}[1];
134
135 $y = $B->{rect}->{y} - 1.5 * $B->{deco_rect}->{height};
136
137 $x->root->warp_pointer($B->{rect}->{x} - 0.5 * $A->{rect}->{width}, $y);
138 sync_with_i3;
139
140 $x->root->warp_pointer($B->{rect}->{x} + 0.5 * $B->{rect}->{width}, $y);
141 sync_with_i3;
142
143 is(get_focused($ws), $target, 'focus switched to the focus head of the stacked container');
144
145 # ==================================================================================
146
147 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Checks that the line continuation are parsed correctly
17 #
18
19 use i3test i3_autostart => 0;
20
21 # starts i3 with the given config, opens a window, returns its border style
22 sub launch_get_border {
23 my ($config) = @_;
24
25 my $pid = launch_with_config($config);
26
27 my $i3 = i3(get_socket_path(0));
28 my $tmp = fresh_workspace;
29
30 my $window = open_window(name => '"special title"');
31
32 my @content = @{get_ws_content($tmp)};
33 cmp_ok(@content, '==', 1, 'one node on this workspace now');
34 my $border = $content[0]->{border};
35
36 exit_gracefully($pid);
37
38 return $border;
39 }
40
41 #####################################################################
42 # test string escaping
43 #####################################################################
44
45 my $config = <<'EOT';
46 # i3 config file (v4)
47 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
48
49 set $vartest \"special title\"
50 for_window [title="$vartest"] border none
51 EOT
52
53 is(launch_get_border($config), 'none', 'no border');
54
55 #####################################################################
56 # test the line continuation
57 #####################################################################
58
59 $config = <<'EOT';
60 # i3 config file (v4)
61 font \
62 -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
63
64 # Use line contiuation with too many lines (>4096 characters).
65 # This config is invalid. Use it to ensure no buffer overflow.
66 bindsym Mod1+b \
67 0001-This is a very very very very very very very very very very very very very very very very very long cmd \
68 0002-This is a very very very very very very very very very very very very very very very very very long cmd \
69 0003-This is a very very very very very very very very very very very very very very very very very long cmd \
70 0004-This is a very very very very very very very very very very very very very very very very very long cmd \
71 0005-This is a very very very very very very very very very very very very very very very very very long cmd \
72 0006-This is a very very very very very very very very very very very very very very very very very long cmd \
73 0007-This is a very very very very very very very very very very very very very very very very very long cmd \
74 0008-This is a very very very very very very very very very very very very very very very very very long cmd \
75 0009-This is a very very very very very very very very very very very very very very very very very long cmd \
76 0010-This is a very very very very very very very very very very very very very very very very very long cmd \
77 0011-This is a very very very very very very very very very very very very very very very very very long cmd \
78 0012-This is a very very very very very very very very very very very very very very very very very long cmd \
79 0013-This is a very very very very very very very very very very very very very very very very very long cmd \
80 0014-This is a very very very very very very very very very very very very very very very very very long cmd \
81 0015-This is a very very very very very very very very very very very very very very very very very long cmd \
82 0016-This is a very very very very very very very very very very very very very very very very very long cmd \
83 0017-This is a very very very very very very very very very very very very very very very very very long cmd \
84 0018-This is a very very very very very very very very very very very very very very very very very long cmd \
85 0019-This is a very very very very very very very very very very very very very very very very very long cmd \
86 0020-This is a very very very very very very very very very very very very very very very very very long cmd \
87 0021-This is a very very very very very very very very very very very very very very very very very long cmd \
88 0022-This is a very very very very very very very very very very very very very very very very very long cmd \
89 0023-This is a very very very very very very very very very very very very very very very very very long cmd \
90 0024-This is a very very very very very very very very very very very very very very very very very long cmd \
91 0025-This is a very very very very very very very very very very very very very very very very very long cmd \
92 0026-This is a very very very very very very very very very very very very very very very very very long cmd \
93 0027-This is a very very very very very very very very very very very very very very very very very long cmd \
94 0028-This is a very very very very very very very very very very very very very very very very very long cmd \
95 0029-This is a very very very very very very very very very very very very very very very very very long cmd \
96 0030-This is a very very very very very very very very very very very very very very very very very long cmd \
97 0031-This is a very very very very very very very very very very very very very very very very very long cmd \
98 0032-This is a very very very very very very very very very very very very very very very very very long cmd \
99 0033-This is a very very very very very very very very very very very very very very very very very long cmd \
100 0034-This is a very very very very very very very very very very very very very very very very very long cmd \
101 0035-This is a very very very very very very very very very very very very very very very very very long cmd \
102 0036-This is a very very very very very very very very very very very very very very very very very long cmd \
103 0037-This is a very very very very very very very very very very very very very very very very very long cmd \
104 0038-This is a very very very very very very very very very very very very very very very very very long cmd \
105 0039-This is a very very very very very very very very very very very very very very very very very long cmd \
106 0040-This is a very very very very very very very very very very very very very very very very very long cmd \
107 0041-This is a very very very very very very very very very very very very very very very very very long cmd \
108 0042-This is a very very very very very very very very very very very very very very very very very long cmd \
109 0043-This is a very very very very very very very very very very very very very very very very very long cmd \
110 0044-This is a very very very very very very very very very very very very very very very very very long cmd \
111 0045-This is a very very very very very very very very very very very very very very very very very long cmd \
112 0046-This is a very very very very very very very very very very very very very very very very very long cmd \
113 0047-This is a very very very very very very very very very very very very very very very very very long cmd \
114 0048-This is a very very very very very very very very very very very very very very very very very long cmd \
115 0049-This is a very very very very very very very very very very very very very very very very very long cmd \
116 0050-This is a very very very very very very very very very very very very very very very very very long cmd \
117 0051-This is a very very very very very very very very very very very very very very very very very long cmd \
118 0052-This is a very very very very very very very very very very very very very very very very very long cmd \
119 0053-This is a very very very very very very very very very very very very very very very very very long cmd \
120 0054-This is a very very very very very very very very very very very very very very very very very long cmd \
121 0055-This is a very very very very very very very very very very very very very very very very very long cmd \
122 0056-This is a very very very very very very very very very very very very very very very very very long cmd \
123 0057-This is a very very very very very very very very very very very very very very very very very long cmd \
124 0058-This is a very very very very very very very very very very very very very very very very very long cmd \
125 0059-This is a very very very very very very very very very very very very very very very very very long cmd \
126 0060-This is a very very very very very very very very very very very very very very very very very long cmd \
127 0061-This is a very very very very very very very very very very very very very very very very very long cmd \
128 0062-This is a very very very very very very very very very very very very very very very very very long cmd \
129 0063-This is a very very very very very very very very very very very very very very very very very long cmd \
130 0064-This is a very very very very very very very very very very very very very very very very very long cmd \
131 0065-This is a very very very very very very very very very very very very very very very very very long cmd \
132 0066-This is a very very very very very very very very very very very very very very very very very long cmd \
133 0067-This is a very very very very very very very very very very very very very very very very very long cmd \
134 0068-This is a very very very very very very very very very very very very very very very very very long cmd \
135 0069-This is a very very very very very very very very very very very very very very very very very long cmd \
136 0070-This is a very very very very very very very very very very very very very very very very very long cmd \
137 0071-This is a very very very very very very very very very very very very very very very very very long cmd \
138 0072-This is a very very very very very very very very very very very very very very very very very long cmd \
139 0073-This is a very very very very very very very very very very very very very very very very very long cmd \
140 0074-This is a very very very very very very very very very very very very very very very very very long cmd \
141 0075-This is a very very very very very very very very very very very very very very very very very long cmd \
142 0076-This is a very very very very very very very very very very very very very very very very very long cmd \
143 0077-This is a very very very very very very very very very very very very very very very very very long cmd \
144 0078-This is a very very very very very very very very very very very very very very very very very long cmd \
145 0079-This is a very very very very very very very very very very very very very very very very very long cmd \
146 0080-This is a very very very very very very very very very very very very very very very very very long cmd \
147 0081-This is a very very very very very very very very very very very very very very very very very long cmd \
148 0082-This is a very very very very very very very very very very very very very very very very very long cmd \
149 0083-This is a very very very very very very very very very very very very very very very very very long cmd \
150 0084-This is a very very very very very very very very very very very very very very very very very long cmd \
151 0085-This is a very very very very very very very very very very very very very very very very very long cmd \
152 0086-This is a very very very very very very very very very very very very very very very very very long cmd \
153 0087-This is a very very very very very very very very very very very very very very very very very long cmd \
154 0088-This is a very very very very very very very very very very very very very very very very very long cmd \
155 0089-This is a very very very very very very very very very very very very very very very very very long cmd \
156 0090-This is a very very very very very very very very very very very very very very very very very long cmd \
157 0091-This is a very very very very very very very very very very very very very very very very very long cmd \
158 0092-This is a very very very very very very very very very very very very very very very very very long cmd \
159 0093-This is a very very very very very very very very very very very very very very very very very long cmd \
160 0094-This is a very very very very very very very very very very very very very very very very very long cmd \
161 0095-This is a very very very very very very very very very very very very very very very very very long cmd \
162 0096-This is a very very very very very very very very very very very very very very very very very long cmd \
163 0097-This is a very very very very very very very very very very very very very very very very very long cmd \
164 0098-This is a very very very very very very very very very very very very very very very very very long cmd \
165 0099-This is a very very very very very very very very very very very very very very very very very long cmd
166
167 # Use line continuation for variables
168 set \
169 $vartest \
170 \"special title\"
171
172 # Use line continuation for commands
173 for_window \
174 [title="$vartest"] \
175 border \
176 none
177
178 EOT
179
180 is(launch_get_border($config), 'none', 'no border');
181
182 #####################################################################
183 # test the line continuation within a string
184 #####################################################################
185
186 $config = <<'EOT';
187 # i3 config file (v4)
188 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
189
190 set \
191 $vartest \
192 \"special \
193 title\"
194 for_window [title="$vartest"] border none
195 EOT
196
197 is(launch_get_border($config), 'none', 'no border');
198
199
200 #####################################################################
201 # test ignoring of line continuation within a comment
202 #####################################################################
203
204 $config = <<'EOT';
205 # i3 config file (v4)
206 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
207
208 set $vartest \"special title\"
209 for_window [title="$vartest"] border pixel 1
210 # this line is not continued, so the following is not contained in this comment\
211 for_window [title="$vartest"] border none
212 EOT
213
214 is(launch_get_border($config), 'none', 'no border');
215
216 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensures the urgency hint is cleared properly in the case where i3 set it (due
17 # to focus_on_window_activation=urgent), hence the application not clearing it.
18 # Ticket: #1825
19 # Bug still in: 4.10.3-253-g03799dd
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 focus_on_window_activation urgent
25 EOT
26
27 sub send_net_active_window {
28 my ($id) = @_;
29
30 my $msg = pack "CCSLLLLLLL",
31 X11::XCB::CLIENT_MESSAGE, # response_type
32 32, # format
33 0, # sequence
34 $id, # destination window
35 $x->atom(name => '_NET_ACTIVE_WINDOW')->id,
36 0, # source
37 0, 0, 0, 0;
38
39 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
40 }
41
42 my $ws = fresh_workspace;
43 my $first = open_window;
44 my $second = open_window;
45
46 send_net_active_window($first->id);
47 sync_with_i3;
48 is($x->input_focus, $second->id, 'second window still focused');
49
50 cmd '[urgent=latest] focus';
51 sync_with_i3;
52 is($x->input_focus, $first->id, 'first window focused');
53
54 cmd 'focus right';
55 sync_with_i3;
56 is($x->input_focus, $second->id, 'second window focused again');
57
58 cmd '[urgent=latest] focus';
59 sync_with_i3;
60 is($x->input_focus, $second->id, 'second window still focused');
61
62 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies floating containers are restored in the correct place in the
17 # hierarchy and get a non-zero size.
18 # Ticket: #1739
19 # Bug still in: 4.10.3-264-g419b73b
20 use i3test;
21 use File::Temp qw(tempfile);
22 use IO::Handle;
23
24 my $ws = fresh_workspace;
25
26 my @content = @{get_ws_content($ws)};
27 is(@content, 0, 'no nodes on the new workspace yet');
28
29 my ($fh, $filename) = tempfile(UNLINK => 1);
30 print $fh <<'EOT';
31 // vim:ts=4:sw=4:et
32 {
33 // floating_con with 1 children
34 "border": "none",
35 "floating": "auto_off",
36 "layout": "splith",
37 "percent": null,
38 "type": "floating_con",
39 // NOTE that "rect" is missing here.
40 "nodes": [
41 {
42 "border": "none",
43 "current_border_width": 0,
44 "floating": "user_on",
45 "geometry": {
46 "height": 384,
47 "width": 128,
48 "x": 448,
49 "y": 441
50 },
51 "name": "Splash",
52 "percent": 1,
53 "swallows": [
54 {
55 "class": "^Steam$",
56 "instance": "^Steam$",
57 "title": "^Steam$"
58 // "transient_for": "^$"
59 }
60 ],
61 "type": "con"
62 }
63 ]
64 }
65 EOT
66 $fh->flush;
67 my $reply = cmd "append_layout $filename";
68
69 does_i3_live;
70
71 ok($reply->[0]->{success}, 'Layout successfully loaded');
72
73 cmd 'kill';
74
75 ok(workspace_exists($ws), 'Workspace still exists');
76
77 does_i3_live;
78
79 close($fh);
80
81 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # TODO: Description of this file.
17 # Ticket: #1817
18 # Bug still in: 4.10.3-270-g0fb784f
19 use i3test;
20 use File::Temp qw(tempfile);
21 use IO::Handle;
22
23 my $ws = fresh_workspace;
24
25 my @content = @{get_ws_content($ws)};
26 is(@content, 0, 'no nodes on the new workspace yet');
27
28 my ($fh, $filename) = tempfile(UNLINK => 1);
29 print $fh <<'EOT';
30 {
31 // stacked split container with 1 children
32 "border": "normal",
33 "floating": "auto_off",
34 "layout": "stacked",
35 "percent": 0.40,
36 "type": "con",
37 "nodes": [
38 {
39 "border": "normal",
40 "current_border_width": 2,
41 "floating": "auto_off",
42 "geometry": {
43 "height": 460,
44 "width": 804,
45 "x": 0,
46 "y": 0
47 },
48 // "name": "",
49 "percent": 0.5,
50 "swallows": [
51 { "class": "^URxvt$" },
52 { "class": "^Gitk$" },
53 { "class": "^Git-gui$" }
54 ],
55 "type": "con"
56 }
57 ]
58 }
59 EOT
60 $fh->flush;
61 my $reply = cmd "append_layout $filename";
62 close($fh);
63
64 does_i3_live;
65
66 @content = @{get_ws_content($ws)};
67 is(@content, 1, "one node on the workspace now");
68
69 my $should_swallow = open_window(wm_class => 'URxvt');
70
71 @content = @{get_ws_content($ws)};
72 is(@content, 1, "still one node on the workspace now");
73 my @nodes = @{$content[0]->{nodes}};
74 is($nodes[0]->{window}, $should_swallow->id, "swallowed window on top");
75
76 cmd 'focus parent';
77
78 my $should_ignore = open_window(wm_class => 'Gitk');
79
80 @content = @{get_ws_content($ws)};
81 is(@content, 2, "two nodes on the workspace");
82
83 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for the special value "__focused__" in command criteria.
17 # Ticket: #1770
18 use i3test;
19 use X11::XCB qw(PROP_MODE_REPLACE);
20
21 my ($ws);
22
23 sub open_window_with_role {
24 my ($role) = @_;
25 open_window(
26 before_map => sub {
27 my ($window) = @_;
28 my $atomname = $x->atom(name => 'WM_WINDOW_ROLE');
29 my $atomtype = $x->atom(name => 'STRING');
30 $x->change_property(
31 PROP_MODE_REPLACE,
32 $window->id,
33 $atomname->id,
34 $atomtype->id,
35 8,
36 length($role) + 1,
37 "$role\x00"
38 );
39 }
40 );
41 }
42
43 ###############################################################################
44 # 1: Test __focused__ for window class.
45 ###############################################################################
46
47 $ws = fresh_workspace;
48 open_window(wm_class => 'notme');
49 open_window(wm_class => 'magic');
50 open_window(wm_class => 'magic');
51 is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
52
53 cmd '[class=__focused__] move to workspace trash';
54 is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for window class');
55
56 ###############################################################################
57 # 2: Test __focused__ for window instance.
58 ###############################################################################
59
60 $ws = fresh_workspace;
61 open_window(instance => 'notme', wm_class => 'test');
62 open_window(instance => 'magic', wm_class => 'test');
63 open_window(instance => 'magic', wm_class => 'test');
64 is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
65
66 cmd '[instance=__focused__] move to workspace trash';
67 is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for window instance');
68
69 ###############################################################################
70 # 3: Test __focused__ for window title.
71 ###############################################################################
72
73 $ws = fresh_workspace;
74 open_window(name => 'notme');
75 open_window(name => 'magic');
76 open_window(name => 'magic');
77 is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
78
79 cmd '[title=__focused__] move to workspace trash';
80 is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for title');
81
82 ###############################################################################
83 # 4: Test __focused__ for window role.
84 ###############################################################################
85
86 $ws = fresh_workspace;
87 open_window_with_role("notme");
88 open_window_with_role("magic");
89 open_window_with_role("magic");
90 is(@{get_ws($ws)->{nodes}}, 3, 'sanity check: workspace contains three windows');
91
92 cmd '[window_role=__focused__] move to workspace trash';
93 is(@{get_ws($ws)->{nodes}}, 1, '__focused__ works for window_role');
94
95 ###############################################################################
96 # 5: Test __focused__ for workspace.
97 ###############################################################################
98
99 $ws = fresh_workspace;
100 open_window;
101 open_window;
102 is(@{get_ws($ws)->{nodes}}, 2, 'sanity check: workspace contains two windows');
103
104 cmd '[workspace=__focused__] move to workspace trash';
105 is(@{get_ws($ws)->{nodes}}, 0, '__focused__ works for workspace');
106
107 ###############################################################################
108 # 6: Test that __focused__ in command criteria when no window is focused does
109 # not crash i3.
110 # See issue: #3406
111 ###############################################################################
112
113 fresh_workspace;
114 cmd '[class=__focused__] focus';
115 does_i3_live;
116
117 ###############################################################################
118
119 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test behavior of "resize <width> <height>" command.
17 # Ticket: #1727
18 # Bug still in: 4.10.2-1-gc0dbc5d
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1333x999+0+0
24 workspace ws output fake-0
25 EOT
26
27 ################################################################################
28 # Init variables used for all tests.
29 ################################################################################
30
31 my $tmp = fresh_workspace;
32 open_floating_window;
33 my @content = @{get_ws($tmp)->{floating_nodes}};
34 is(@content, 1, 'one floating node on this ws');
35 my $oldrect = $content[0]->{rect};
36
37 sub do_test {
38 my ($width, $height) = @_;
39
40 cmp_ok($content[0]->{rect}->{x}, '==', $oldrect->{x}, 'x unchanged');
41 cmp_ok($content[0]->{rect}->{y}, '==', $oldrect->{y}, 'y unchanged');
42
43 @content = @{get_ws($tmp)->{floating_nodes}};
44 if ($width) {
45 cmp_ok($content[0]->{rect}->{width}, '==', $width, "width changed to $width px");
46 } else {
47 cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'width unchanged');
48 }
49 if ($height) {
50 cmp_ok($content[0]->{rect}->{height}, '==', $height, "height changed to $height px");
51 } else {
52 cmp_ok($content[0]->{rect}->{height}, '==', $oldrect->{height}, 'height unchanged');
53 }
54 $oldrect = $content[0]->{rect};
55 }
56
57 ################################################################################
58 # Check that setting floating windows size works
59 ################################################################################
60
61 cmd 'resize set 100 px 250 px';
62 do_test(100, 250);
63
64 ################################################################################
65 # Same but with ppt instead of px
66 ################################################################################
67
68 cmd 'resize set 33 ppt 20 ppt';
69 do_test(int(0.33 * 1333), int(0.2 * 999));
70
71 ################################################################################
72 # Mix ppt and px in a single resize set command
73 ################################################################################
74
75 cmd 'resize set 44 ppt 111 px';
76 do_test(int(0.44 * 1333), 111);
77
78 cmd 'resize set 222 px 100 ppt';
79 do_test(222, 999);
80
81 ################################################################################
82 # Zero is interpreted as no change.
83 # See issue: #3276.
84 ################################################################################
85
86 cmd 'resize set 0 px 333 px';
87 do_test(0, 333);
88
89 cmd 'resize set 333 px 0 ppt';
90 do_test(333, 0);
91
92 cmd 'resize set 0 px 0 ppt';
93 do_test(0, 0);
94
95 cmd 'resize set 100 ppt 0 px';
96 do_test(1333, 0);
97
98 ################################################################################
99 # Use 'width' and 'height' keywords.
100 # See issue: #3275.
101 ################################################################################
102
103 cmd 'resize set width 200 px';
104 do_test(200, 0);
105
106 cmd 'resize set height 200 px';
107 do_test(0, 200);
108
109 cmd 'resize set width 300 px height 300 px';
110 do_test(300, 300);
111
112 # ppt + keyword used only for height
113 cmd 'resize set 100 ppt height 100 px';
114 do_test(1333, 100);
115
116 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ticket: #1873
17 use i3test;
18 use X11::XCB qw(:all);
19
20 sub get_wm_state {
21 sync_with_i3;
22
23 my ($con) = @_;
24 my $cookie = $x->get_property(
25 0,
26 $con->{id},
27 $x->atom(name => '_NET_WM_STATE')->id,
28 GET_PROPERTY_TYPE_ANY,
29 0,
30 4096
31 );
32
33 my $reply = $x->get_property_reply($cookie->{sequence});
34 my $len = $reply->{length};
35 return undef if $len == 0;
36
37 my @atoms = unpack("L$len", $reply->{value});
38 return @atoms;
39 }
40
41 my $wm_state_sticky = $x->atom(name => '_NET_WM_STATE_STICKY')->id;
42 my $wm_state_fullscreen = $x->atom(name => '_NET_WM_STATE_FULLSCREEN')->id;
43
44 ##########################################################################
45 # Given a sticky container, when it is fullscreened, then both wm state
46 # atoms are set. When the container is unfullscreened, then only the
47 # sticky atom is still set.
48 ##########################################################################
49
50 fresh_workspace;
51 my $window = open_window;
52 cmd 'sticky enable';
53 my @state = get_wm_state($window);
54 ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, 'sanity check: _NET_WM_STATE_STICKY is set');
55
56 cmd 'fullscreen enable';
57 @state = get_wm_state($window);
58 ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, '_NET_WM_STATE_STICKY is set');
59 ok((scalar grep { $_ == $wm_state_fullscreen } @state) > 0, '_NET_WM_STATE_FULLSCREEN is set');
60
61 cmd 'sticky disable';
62 @state = get_wm_state($window);
63 ok((scalar grep { $_ == $wm_state_sticky } @state) == 0, '_NET_WM_STATE_STICKY is not set');
64 ok((scalar grep { $_ == $wm_state_fullscreen } @state) > 0, '_NET_WM_STATE_FULLSCREEN is set');
65
66 cmd 'sticky enable';
67 cmd 'fullscreen disable';
68 @state = get_wm_state($window);
69 ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, '_NET_WM_STATE_STICKY is set');
70 ok((scalar grep { $_ == $wm_state_fullscreen } @state) == 0, '_NET_WM_STATE_FULLSCREEN is not set');
71
72 ###############################################################################
73 # _NET_WM_STATE is removed when the window is withdrawn.
74 ###############################################################################
75
76 fresh_workspace;
77 $window = open_window;
78 cmd 'sticky enable';
79 @state = get_wm_state($window);
80 ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, '_NET_WM_STATE_STICKY is set');
81
82 $window->unmap;
83 wait_for_unmap($window);
84
85 is(get_wm_state($window), undef, '_NET_WM_STATE is removed');
86
87 ##########################################################################
88
89 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that "move container to output" works correctly when
17 # used with command criteria.
18 # Bug still in: 4.10.4-349-gee5db87
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 800x600+0+0,800x600+800+0,800x600+0+600,800x600+800+600
24 EOT
25
26 my $ws_top_left = fresh_workspace(output => 0);
27 my $ws_top_right = fresh_workspace(output => 1);
28 my $ws_bottom_left = fresh_workspace(output => 2);
29 my $ws_bottom_right = fresh_workspace(output => 3);
30
31 cmd "workspace " . $ws_top_left;
32 open_window(wm_class => 'moveme');
33 cmd "workspace " . $ws_bottom_left;
34 open_window(wm_class => 'moveme');
35
36 cmd '[class="moveme"] move window to output right';
37
38 is_num_children($ws_top_left, 0, 'no containers on the upper left workspace');
39 is_num_children($ws_top_right, 1, 'one container on the upper right workspace');
40 is_num_children($ws_bottom_left, 0, 'no containers on the lower left workspace');
41 is_num_children($ws_bottom_right, 1, 'one container on the lower right workspace');
42
43 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for mark/unmark with multiple marks on a single window.
17 # Ticket: #2014
18 use i3test;
19 use List::Util qw(first);
20
21 my ($ws, $con, $first, $second);
22
23 sub get_marks {
24 return i3(get_socket_path())->get_marks->recv;
25 }
26
27 sub get_mark_for_window_on_workspace {
28 my ($ws, $con) = @_;
29
30 my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
31 return $current->{marks};
32 }
33
34 ###############################################################################
35 # Verify that multiple marks can be set on a window.
36 ###############################################################################
37
38 $ws = fresh_workspace;
39 $con = open_window;
40 cmd 'mark --add A';
41 cmd 'mark --add B';
42
43 is_deeply(sort(get_marks()), [ 'A', 'B' ], 'both marks exist');
44 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window');
45
46 cmd 'unmark';
47
48 ###############################################################################
49 # Verify that toggling a mark can affect only the specified mark.
50 ###############################################################################
51
52 $ws = fresh_workspace;
53 $con = open_window;
54 cmd 'mark A';
55
56 cmd 'mark --add --toggle B';
57 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window');
58 cmd 'mark --add --toggle B';
59 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A' ], 'only mark B has been removed');
60
61 cmd 'unmark';
62
63 ###############################################################################
64 # Verify that unmarking a mark leaves other marks on the same window intact.
65 ###############################################################################
66
67 $ws = fresh_workspace;
68 $con = open_window;
69 cmd 'mark --add A';
70 cmd 'mark --add B';
71 cmd 'mark --add C';
72
73 cmd 'unmark B';
74 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'C' ], 'only mark B has been removed');
75
76 cmd 'unmark';
77
78 ###############################################################################
79 # Verify that matching via mark works on windows with multiple marks.
80 ###############################################################################
81
82 $ws = fresh_workspace;
83 $con = open_window;
84 cmd 'mark --add A';
85 cmd 'mark --add B';
86 open_window;
87
88 cmd '[con_mark=B] mark --add C';
89 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B', 'C' ], 'matching on a mark works with multiple marks');
90
91 cmd 'unmark';
92
93 ###############################################################################
94 # Verify that "unmark" can be matched on the focused window.
95 ###############################################################################
96
97 $ws = fresh_workspace;
98 $con = open_window;
99 cmd 'mark --add A';
100 cmd 'mark --add B';
101 open_window;
102 cmd 'mark --add C';
103 cmd 'mark --add D';
104
105 is_deeply(sort(get_marks()), [ 'A', 'B', 'C', 'D' ], 'all marks exist');
106
107 cmd '[con_id=__focused__] unmark';
108
109 is_deeply(sort(get_marks()), [ 'A', 'B' ], 'marks on the unfocused window still exist');
110 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'matching on con_id=__focused__ works for unmark');
111
112 cmd 'unmark';
113
114 ###############################################################################
115
116 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test for the --no-auto-back-and-forth flag.
17 # Ticket: #2028
18 use i3test;
19
20 my ($first, $second, $third, $con);
21 $first = "1:first";
22 $second = "2:second";
23 $third = "3:third";
24
25 ###############################################################################
26 # Switching to another workspace when passing --no-auto-back-and-forth works
27 # as if the flag wasn't set.
28 ###############################################################################
29
30 cmd qq|workspace "$first"|;
31 ok(get_ws($first)->{focused}, 'first workspace is focused');
32
33 cmd qq|workspace --no-auto-back-and-forth "$second"|;
34 ok(get_ws($second)->{focused}, 'second workspace is focused');
35
36 cmd qq|workspace --no-auto-back-and-forth number "$third"|;
37 ok(get_ws($third)->{focused}, 'third workspace is focused');
38
39 ###############################################################################
40 # Switching to the focused workspace when passing --no-auto-back-and-forth
41 # is a no-op.
42 ###############################################################################
43
44 cmd qq|workspace "$second"|;
45 cmd qq|workspace "$first"|;
46 ok(get_ws($first)->{focused}, 'first workspace is focused');
47
48 cmd qq|workspace --no-auto-back-and-forth "$first"|;
49 ok(get_ws($first)->{focused}, 'first workspace is still focused');
50
51 cmd qq|workspace --no-auto-back-and-forth number "$first"|;
52 ok(get_ws($first)->{focused}, 'first workspace is still focused');
53
54 ###############################################################################
55 # Moving a window to another workspace when passing --no-auto-back-and-forth
56 # works as if the flag wasn't set.
57 ###############################################################################
58
59 cmd qq|workspace "$third"|;
60 cmd qq|workspace "$second"|;
61 cmd qq|workspace "$first"|;
62 $con = open_window;
63 cmd 'mark mywindow';
64
65 cmd qq|move --no-auto-back-and-forth window to workspace "$second"|;
66 is(@{get_ws($second)->{nodes}}, 1, 'window was moved to second workspace');
67 cmd qq|[con_mark=mywindow] move window to workspace "$first"|;
68
69 cmd qq|move --no-auto-back-and-forth window to workspace number "$third"|;
70 is(@{get_ws($third)->{nodes}}, 1, 'window was moved to third workspace');
71 cmd qq|[con_mark=mywindow] move window to workspace "$first"|;
72
73 cmd '[con_mark=mywindow] kill';
74
75 ###############################################################################
76 # Moving a window to the same workspace when passing --no-auto-back-and-forth
77 # is a no-op.
78 ###############################################################################
79
80 cmd qq|workspace "$second"|;
81 cmd qq|workspace "$first"|;
82 $con = open_window;
83 cmd 'mark mywindow';
84
85 cmd qq|move --no-auto-back-and-forth window to workspace "$first"|;
86 is(@{get_ws($first)->{nodes}}, 1, 'window is still on first workspace');
87 cmd qq|[con_mark=mywindow] move window to workspace "$first"|;
88
89 cmd qq|move --no-auto-back-and-forth window to workspace number "$first"|;
90 is(@{get_ws($first)->{nodes}}, 1, 'window is still on first workspace');
91 cmd qq|[con_mark=mywindow] move window to workspace "$first"|;
92
93 cmd '[con_mark=mywindow] kill';
94
95 ###############################################################################
96
97 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that when using multiple keyboard layouts at the same time, bindings
17 # without a specified XKB group will work in all XKB groups.
18 # Ticket: #2062
19 # Bug still in: 4.11-103-gc8d51b4
20 # Bug introduced with commit 0e5180cae9e9295678e3f053042b559e82cb8c98
21 use i3test
22 i3_config => <<EOT;
23 # i3 config file (v4)
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25
26 bindsym Print nop Print
27 bindsym Mod4+Return nop Mod4+Return
28 EOT
29 use i3test::XTEST;
30 use ExtUtils::PkgConfig;
31
32 SKIP: {
33 skip "libxcb-xkb too old (need >= 1.11)", 1 unless
34 ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
35 skip "setxkbmap not found", 1 if
36 system(q|setxkbmap -print >/dev/null|) != 0;
37
38 system(q|setxkbmap us,ru -option grp:alt_shift_toggle|);
39
40 is(listen_for_binding(
41 sub {
42 xtest_key_press(107);
43 xtest_key_release(107);
44 xtest_sync_with_i3;
45 },
46 ),
47 'Print',
48 'triggered the "Print" keybinding');
49
50 is(listen_for_binding(
51 sub {
52 xtest_key_press(133); # Super_L
53 xtest_key_press(36); # Return
54 xtest_key_release(36); # Return
55 xtest_key_release(133); # Super_L
56 xtest_sync_with_i3;
57 },
58 ),
59 'Mod4+Return',
60 'triggered the "Mod4+Return" keybinding');
61
62 # Switch keyboard group to russian.
63 set_xkb_group(1);
64
65 is(listen_for_binding(
66 sub {
67 xtest_key_press(107);
68 xtest_key_release(107);
69 xtest_sync_with_i3;
70 },
71 ),
72 'Print',
73 'triggered the "Print" keybinding');
74
75 is(listen_for_binding(
76 sub {
77 xtest_key_press(133); # Super_L
78 xtest_key_press(36); # Return
79 xtest_key_release(36); # Return
80 xtest_key_release(133); # Super_L
81 xtest_sync_with_i3;
82 },
83 ),
84 'Mod4+Return',
85 'triggered the "Mod4+Return" keybinding');
86
87 # Disable the grp:alt_shift_toggle option, as we use Alt+Shift in other testcases.
88 system(q|setxkbmap us -option|);
89
90 }
91
92 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that --release key bindings are not shadowed by non-release key
17 # bindings for the same key.
18 # Ticket: #2002
19 # Bug still in: 4.11-103-gc8d51b4
20 # Bug introduced with commit bf3cd41b5ddf1e757515ab5fbf811be56e5f69cc
21 use i3test i3_config => <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24
25 bindsym Print nop Print
26 bindsym --release Control+Print nop Control+Print
27
28 # see issue #2442
29 bindsym Mod1+b nop Mod1+b
30 bindsym --release Mod1+Shift+b nop Mod1+Shift+b release
31
32 bindsym --release Shift+x nop Shift+x
33
34 # see issue #2733
35 # 133 == Mod4
36 bindcode 133 nop 133
37 bindcode --release 133 nop 133 release
38
39 mode "a_mode" {
40 # 27 == r
41 bindcode 27 --release mode "default"
42 }
43 bindsym Mod1+r mode "a_mode"
44 bindcode 27 nop do not receive
45 EOT
46 use i3test::XTEST;
47 use ExtUtils::PkgConfig;
48
49 SKIP: {
50 skip "libxcb-xkb too old (need >= 1.11)", 1 unless
51 ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
52
53 is(listen_for_binding(
54 sub {
55 xtest_key_press(107); # Print
56 xtest_key_release(107); # Print
57 xtest_sync_with_i3;
58 },
59 ),
60 'Print',
61 'triggered the "Print" keybinding');
62
63 is(listen_for_binding(
64 sub {
65 xtest_key_press(37); # Control_L
66 xtest_key_press(107); # Print
67 xtest_key_release(107); # Print
68 xtest_key_release(37); # Control_L
69 xtest_sync_with_i3;
70 },
71 ),
72 'Control+Print',
73 'triggered the "Control+Print" keybinding');
74
75 is(listen_for_binding(
76 sub {
77 xtest_key_press(64); # Alt_L
78 xtest_key_press(56); # b
79 xtest_key_release(56); # b
80 xtest_key_release(64); # Alt_L
81 xtest_sync_with_i3;
82 },
83 ),
84 'Mod1+b',
85 'triggered the "Mod1+b" keybinding');
86
87 is(listen_for_binding(
88 sub {
89 xtest_key_press(64); # Alt_L
90 xtest_key_press(50); # Shift_L
91 xtest_key_press(56); # b
92 xtest_key_release(56); # b
93 xtest_key_release(50); # Shift_L
94 xtest_key_release(64); # Alt_L
95 xtest_sync_with_i3;
96 },
97 ),
98 'Mod1+Shift+b release',
99 'triggered the "Mod1+Shift+b" release keybinding');
100
101 is(listen_for_binding(
102 sub {
103 xtest_key_press(50); # Shift
104 xtest_key_press(53); # x
105 xtest_key_release(53); # x
106 xtest_key_release(50); # Shift
107 xtest_sync_with_i3;
108 },
109 ),
110 'Shift+x',
111 'triggered the "Shift+x" keybinding by releasing x first');
112
113 is(listen_for_binding(
114 sub {
115 xtest_key_press(50); # Shift
116 xtest_key_press(53); # x
117 xtest_key_release(50); # Shift
118 xtest_key_release(53); # x
119 xtest_sync_with_i3;
120 },
121 ),
122 'Shift+x',
123 'triggered the "Shift+x" keybinding by releasing Shift first');
124
125 is(listen_for_binding(
126 sub {
127 xtest_key_press(133);
128 xtest_sync_with_i3;
129 },
130 ),
131 '133',
132 'triggered the 133 keycode press binding');
133
134 is(listen_for_binding(
135 sub {
136 xtest_key_release(133);
137 xtest_sync_with_i3;
138 },
139 ),
140 '133 release',
141 'triggered the 133 keycode release binding');
142
143 for my $i (1 .. 2) {
144 is(listen_for_binding(
145 sub {
146 xtest_key_press(64); # Alt_l
147 xtest_key_press(27); # r
148 xtest_key_release(27); # r
149 xtest_key_release(64); # Alt_l
150 xtest_sync_with_i3;
151 },
152 ),
153 'mode "a_mode"',
154 "switched to mode \"a_mode\" $i/2");
155
156 is(listen_for_binding(
157 sub {
158 xtest_key_press(27); # r
159 xtest_key_release(27); # r
160 xtest_sync_with_i3;
161 },
162 ),
163 'mode "default"',
164 "switched back to default $i/2");
165 }
166
167 }
168
169 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test for _NET_WM_USER_TIME.
17 # Ticket: #2064
18 use i3test;
19 use X11::XCB 'PROP_MODE_REPLACE';
20
21 my ($ws, $other, $con);
22
23 sub open_window_with_user_time {
24 my $wm_user_time = shift;
25
26 my $window = open_window(
27 before_map => sub {
28 my ($window) = @_;
29
30 my $atomname = $x->atom(name => '_NET_WM_USER_TIME');
31 my $atomtype = $x->atom(name => 'CARDINAL');
32 $x->change_property(
33 PROP_MODE_REPLACE,
34 $window->id,
35 $atomname->id,
36 $atomtype->id,
37 32,
38 1,
39 pack('L1', $wm_user_time),
40 );
41 },
42 );
43
44 return $window;
45 }
46
47 #####################################################################
48 # 1: if _NET_WM_USER_TIME is set to 0, the window is not focused
49 # initially.
50 #####################################################################
51
52 $ws = fresh_workspace;
53
54 open_window;
55 $other = get_focused($ws);
56 open_window_with_user_time(0);
57
58 is(get_focused($ws), $other, 'new window is not focused');
59
60 #####################################################################
61 # 2: if _NET_WM_USER_TIME is set to something other than 0, the
62 # window is focused anyway.
63 #####################################################################
64
65 $ws = fresh_workspace;
66
67 open_window;
68 $other = get_focused($ws);
69 open_window_with_user_time(42);
70
71 isnt(get_focused($ws), $other, 'new window is focused');
72
73 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ticket: #2091
17 use i3test;
18
19 my $ws = fresh_workspace;
20 open_window;
21
22 my $result = cmd '[con_id=foobar] kill';
23 is($result->[0]->{success}, 0, 'command was unsuccessful');
24 is($result->[0]->{error}, 'Invalid match: invalid con_id', 'correct error is returned');
25
26 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ticket: #2111
17 use i3test;
18
19 my ($ws);
20
21 ###############################################################################
22 # Verify that con_id can be combined with other criteria
23 ###############################################################################
24
25 $ws = fresh_workspace;
26 open_window(wm_class => 'matchme');
27
28 cmd '[con_id=__focused__ class=doesnotmatch] kill';
29 sync_with_i3;
30 is(@{get_ws($ws)->{nodes}}, 1, 'window was not killed');
31
32 cmd '[con_id=__focused__ class=matchme] kill';
33 sync_with_i3;
34 is(@{get_ws($ws)->{nodes}}, 0, 'window was killed');
35
36 ###############################################################################
37 # Verify that con_mark can be combined with other criteria
38 ###############################################################################
39
40 $ws = fresh_workspace;
41 open_window(wm_class => 'matchme');
42 cmd 'mark marked';
43
44 cmd '[con_mark=marked class=doesnotmatch] kill';
45 sync_with_i3;
46 is(@{get_ws($ws)->{nodes}}, 1, 'window was not killed');
47
48 cmd '[con_mark=marked class=matchme] kill';
49 sync_with_i3;
50 is(@{get_ws($ws)->{nodes}}, 0, 'window was killed');
51
52 ###############################################################################
53
54 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensures that calling i3 with the -C switch works (smoke test).
17 # Ticket: #2144
18 use i3test i3_autostart => 0;
19 use POSIX ":sys_wait_h";
20 use Time::HiRes qw(sleep);
21
22 my $config = <<EOT;
23 # i3 config file (v4)
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25
26 invalid
27 EOT
28
29 my $exit_code = launch_with_config($config, validate_config => 1);
30 isnt($exit_code, 0, 'i3 exited with an error code');
31
32 my $log = get_i3_log;
33
34 # We don't care so much about the output (there are tests for this), but rather
35 # that we got correct output at all instead of, e.g., a segfault.
36 ok($log =~ /Expected one of these tokens/, 'an invalid config token was found');
37
38 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that reloading the config reverts to the default
17 # binding mode.
18 # Ticket: #2228
19 # Bug still in: 4.11-262-geb631ce
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 mode "othermode" {
25 }
26 EOT
27
28 cmd 'mode othermode';
29
30 my @events = events_for(
31 sub { cmd 'reload' },
32 'mode');
33
34 is(scalar @events, 1, 'Received 1 event');
35 is($events[0]->{change}, 'default', 'change is "default"');
36
37 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that command or config criteria does not match dock clients
17 # Bug still in: 4.12-38-ge690e3d
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 for_window [class="dock"] move workspace current
21
22 bar {
23 # Disable i3bar, which is also a dock client.
24 i3bar_command :
25 }
26 EOT
27
28 my $ws = fresh_workspace();
29
30
31 ## command criteria should not match dock windows
32 open_window({
33 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
34 wm_class => "x"
35 });
36
37 is(get_dock_clients, 1, "created one docked client");
38 is_num_children($ws, 0, 'no container on the current workspace');
39
40 cmd '[class="^x$"] move workspace current';
41
42 does_i3_live
43 is(get_dock_clients, 1, "one docked client after move");
44 is_num_children($ws, 0, 'no container on the current workspace');
45
46 cmd '[class="^x$"] fullscreen';
47
48 does_i3_live
49 is(get_dock_clients, 1, "one docked client after fullscreen");
50 is_num_children($ws, 0, 'no container on the current workspace');
51
52 cmd '[class="^x$"] kill';
53
54 does_i3_live
55 is(get_dock_clients, 1, "one docked client after kill");
56 is_num_children($ws, 0, 'no container on the current workspace');
57
58
59 ## config criteria should not match dock windows
60 open_window({
61 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK'),
62 wm_class => "dock"
63 });
64
65 does_i3_live
66 is(get_dock_clients, 2, "created second docked client");
67 is_num_children($ws, 0, 'no container on the current workspace');
68
69
70 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for the window::mark IPC event.
17 # Ticket: #2501
18 use i3test;
19
20 sub mark_subtest {
21 my ($cmd) = @_;
22
23 my @events = events_for(
24 sub { cmd $cmd },
25 'window');
26
27 my @mark = grep { $_->{change} eq 'mark' } @events;
28 is(scalar @mark, 1, 'Received 1 window::mark event');
29 }
30
31 ###############################################################################
32 # Marking a container triggers a 'mark' event.
33 ###############################################################################
34 fresh_workspace;
35 open_window;
36
37 subtest 'mark', \&mark_subtest, 'mark x';
38
39 ###############################################################################
40 # Unmarking a container triggers a 'mark' event.
41 ###############################################################################
42 fresh_workspace;
43 open_window;
44 cmd 'mark x';
45
46 subtest 'unmark', \&mark_subtest, 'unmark x';
47
48 ###############################################################################
49
50 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for _NET_MOVERESIZE_WINDOW.
17 # Ticket: #2603
18 use i3test i3_autostart => 0;
19
20 sub moveresize_window {
21 my ($win, $pos_x, $pos_y, $width, $height) = @_;
22
23 my $flags = 0;
24 $flags |= (1 << 8) if $pos_x >= 0;
25 $flags |= (1 << 9) if $pos_y >= 0;
26 $flags |= (1 << 10) if $width >= 0;
27 $flags |= (1 << 11) if $height >= 0;
28
29 my $msg = pack "CCSLLLLLLL",
30 X11::XCB::CLIENT_MESSAGE, # response_type
31 32, # format
32 0, # sequence
33 $win->id, # window
34 $x->atom(name => '_NET_MOVERESIZE_WINDOW')->id, # message type
35 $flags, # data32[0] (flags)
36 $pos_x, # data32[1] (x)
37 $pos_y, # data32[2] (y)
38 $width, # data32[3] (width)
39 $height; # data32[4] (height)
40
41 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
42 sync_with_i3;
43 }
44
45 my $config = <<EOT;
46 # i3 config file (v4)
47 font font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
48
49 new_window none
50 new_float none
51 EOT
52
53 my ($pid, $ws, $window, $content);
54
55 ###############################################################################
56
57 ###############################################################################
58 # A _NET_MOVERESIZE_WINDOW client message can change the position and size
59 # of a floating window.
60 ###############################################################################
61
62 $pid = launch_with_config($config);
63 $ws = fresh_workspace;
64
65 $window = open_floating_window(rect => [50, 50, 100, 100]);
66 moveresize_window($window, 0, 0, 555, 666);
67
68 $content = get_ws($ws);
69 is($content->{floating_nodes}->[0]->{rect}->{x}, 0, 'the x coordinate is correct');
70 is($content->{floating_nodes}->[0]->{rect}->{y}, 0, 'the y coordinate is correct');
71 is($content->{floating_nodes}->[0]->{rect}->{width}, 555, 'the width is correct');
72 is($content->{floating_nodes}->[0]->{rect}->{height}, 666, 'the height is correct');
73
74 exit_gracefully($pid);
75
76 ###############################################################################
77 # A _NET_MOVERESIZE_WINDOW client message can change only the position of a
78 # window.
79 ###############################################################################
80
81 $pid = launch_with_config($config);
82 $ws = fresh_workspace;
83
84 $window = open_floating_window(rect => [50, 50, 100, 100]);
85 moveresize_window($window, 100, 100, -1, -1);
86
87 $content = get_ws($ws);
88 is($content->{floating_nodes}->[0]->{rect}->{x}, 100, 'the x coordinate is correct');
89 is($content->{floating_nodes}->[0]->{rect}->{y}, 100, 'the y coordinate is correct');
90 is($content->{floating_nodes}->[0]->{rect}->{width}, 100, 'the width is unchanged');
91 is($content->{floating_nodes}->[0]->{rect}->{height}, 100, 'the height is unchanged');
92
93 exit_gracefully($pid);
94
95 ###############################################################################
96 # A _NET_MOVERESIZE_WINDOW client message can change only the size of a
97 # window.
98 ###############################################################################
99
100 $pid = launch_with_config($config);
101 $ws = fresh_workspace;
102
103 $window = open_floating_window(rect => [50, 50, 100, 100]);
104 moveresize_window($window, -1, -1, 200, 200);
105
106 $content = get_ws($ws);
107 is($content->{floating_nodes}->[0]->{rect}->{x}, 50, 'the x coordinate is unchanged');
108 is($content->{floating_nodes}->[0]->{rect}->{y}, 50, 'the y coordinate is unchanged');
109 is($content->{floating_nodes}->[0]->{rect}->{width}, 200, 'the width is correct');
110 is($content->{floating_nodes}->[0]->{rect}->{height}, 200, 'the height is correct');
111
112 exit_gracefully($pid);
113
114 ###############################################################################
115
116 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test to check if mark and restart commands crash i3
17 #
18 use i3test;
19
20 cmd 'open';
21 cmd 'mark foo';
22 cmd 'open';
23 cmd 'mark bar';
24
25 cmd 'restart';
26
27 diag('Checking if i3 still lives');
28
29 does_i3_live;
30
31 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that the config file is returned raw via the IPC interface.
17 # Ticket: #2856
18 # Bug still in: 4.13-133-ge4da07e7
19 use i3test i3_autostart => 0;
20 use File::Temp qw(tempdir);
21
22 my $tmpdir = tempdir(CLEANUP => 1);
23 my $socketpath = $tmpdir . "/config.sock";
24 ok(! -e $socketpath, "$socketpath does not exist yet");
25
26 my $config = <<'EOT';
27 # i3 config file (v4)
28 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
29
30 nop foo \
31 continued
32
33 set $var normal title
34 for_window [title="$vartest"] border none
35 EOT
36
37 $config .= "ipc-socket $socketpath";
38
39 my $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
40 get_socket_path(0);
41 my $i3 = i3(get_socket_path());
42 $i3->connect->recv;
43
44 my $cv = AnyEvent->condvar;
45 my $timer = AnyEvent->timer(after => 0.5, interval => 0, cb => sub { $cv->send(0); });
46
47 my $last_config = $i3->get_config()->recv;
48 chomp($last_config->{config});
49 is($last_config->{config}, $config,
50 'received config is not equal to written config');
51
52 exit_gracefully($pid);
53
54 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies a ConfigureWindow request with stack-mode=Above is translated into
17 # focusing the target window by i3.
18 # Ticket: #2708
19 # Bug still in: 4.13-207-gafdf6792
20 use i3test;
21 use X11::XCB qw(CONFIG_WINDOW_STACK_MODE STACK_MODE_ABOVE);
22
23 my $ws = fresh_workspace;
24 my $left_window = open_window;
25 my $right_window = open_window;
26
27 is($x->input_focus, $right_window->id, 'right window has focus');
28 my $old_focus = get_focused($ws);
29
30 $x->configure_window($left_window->id, CONFIG_WINDOW_STACK_MODE, (STACK_MODE_ABOVE));
31 $x->flush;
32
33 sync_with_i3;
34
35 is($x->input_focus, $left_window->id, 'left window has focus');
36 isnt(get_focused($ws), $old_focus, 'right window is no longer focused');
37
38 ################################################################################
39 # Verify the ConfigureWindow request is only applied when on the active
40 # workspace.
41 ################################################################################
42
43 $ws = fresh_workspace;
44 my $new_window = open_window;
45
46 is($x->input_focus, $new_window->id, 'new window has focus');
47 $x->configure_window($left_window->id, CONFIG_WINDOW_STACK_MODE, (STACK_MODE_ABOVE));
48 $x->flush;
49
50 sync_with_i3;
51
52 is($x->input_focus, $new_window->id, 'new window still has focus');
53
54 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Make sure that configs that end without a newline don't crash i3.
17 # Ticket: #2934
18 use i3test i3_autostart => 0;
19
20 my $first_lines = <<'EOT';
21 set $workspace1 workspace number 1
22 set $workspace0 workspace eggs
23
24 bindsym Mod4+1 $workspace1
25 EOT
26
27 # Intentionally don't add a trailing newline for the last line since this is
28 # what triggered the bug.
29 my $last_line = 'bindsym Mod4+0 $workspace0';
30 my $config = "${first_lines}${last_line}";
31
32 my $pid = launch_with_config($config);
33 does_i3_live;
34
35 my $i3 = i3(get_socket_path());
36 my $ws = $i3->get_workspaces->recv;
37 is($ws->[0]->{name}, 'eggs', 'last line processed correctly');
38
39 exit_gracefully($pid);
40 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 use i3test i3_config => <<EOT;
17 # i3 config file (v4)
18 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
19 for_window [tiling] mark tiled
20 for_window [floating] mark floated
21 EOT
22 use X11::XCB qw(PROP_MODE_REPLACE);
23
24 ##############################################################
25 # 13: check that the tiling / floating criteria work.
26 ##############################################################
27
28 my $tmp = fresh_workspace;
29
30 open_window;
31 open_floating_window;
32
33 my @nodes = @{get_ws($tmp)->{nodes}};
34 cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace');
35 is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion");
36
37 @nodes = @{get_ws($tmp)->{floating_nodes}};
38 cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
39 is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion");
40
41 ##############################################################
42
43 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: Checks if focus is stolen when a window is managed which is
17 # assigned to an invisible workspace
18 #
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22 assign [class="special"] targetws
23 EOT
24
25 sub open_special {
26 my %args = @_;
27 $args{name} //= 'special window';
28
29 # We use dont_map because i3 will not map the window on the current
30 # workspace. Thus, open_window would time out in wait_for_map (2 seconds).
31 my $window = open_window(
32 %args,
33 wm_class => 'special',
34 dont_map => 1,
35 );
36 $window->map;
37 return $window;
38 }
39
40 #####################################################################
41 # start a window and see that it does not get assigned with an empty config
42 #####################################################################
43
44 my $tmp = fresh_workspace;
45
46 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
47 ok(get_ws($tmp)->{focused}, 'current workspace focused');
48
49 my $window = open_special;
50 sync_with_i3;
51
52 ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
53 ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
54 ok(get_ws($tmp)->{focused}, 'current workspace still focused');
55
56 #####################################################################
57 # the same test, but with a floating window
58 #####################################################################
59
60 $window = open_special(
61 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_UTILITY'),
62 );
63
64 ok(@{get_ws_content($tmp)} == 0, 'special window not on current workspace');
65 ok(@{get_ws_content('targetws')} == 1, 'special window on targetws');
66 ok(get_ws($tmp)->{focused}, 'current workspace still focused');
67
68 $window->destroy;
69
70 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: Checks if i3 still lives after using 'focus mode_toggle' on an
17 # empty workspace. This regression was fixed in
18 # 0848844f2d41055f6ffc69af1149d7a873460976.
19 #
20 use i3test;
21 use v5.10;
22
23 my $tmp = fresh_workspace;
24
25 cmd 'focus mode_toggle';
26
27 does_i3_live;
28
29 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that movement of a con into a branch will place the moving con at the
17 # correct position within the branch.
18 #
19 # If the direction of movement is the same as the orientation of the branch
20 # container, append or prepend the container to the branch in the obvious way.
21 # If the movement is to the right or downward, insert the moving container in
22 # the first position (i.e., the leftmost or top position resp.) If the movement
23 # is to the left or upward, insert the moving container in the last position
24 # (i.e., the rightmost or bottom position resp.)
25 #
26 # If the direction of movement is different from the orientation of the branch
27 # container, insert the container into the branch after the focused-inactive
28 # container.
29 #
30 # For testing purposes, we will demonstrate the behavior for tabbed containers
31 # to represent the case of split-horizontal branches and stacked containers to
32 # represent the case of split-vertical branches.
33 #
34 # Ticket: #1060
35 # Bug still in: 4.6-109-g18cfc36
36
37 use i3test;
38
39 # Opens tabs on the presently focused branch and adds several additional
40 # windows. Shifts focus to somewhere in the middle of the tabs so the most
41 # general case can be assumed.
42 sub open_tabs {
43 cmd 'layout tabbed';
44 open_window;
45 open_window;
46 open_window;
47 open_window;
48 cmd 'focus left; focus left'
49 }
50
51 # Likewise for a stack
52 sub open_stack {
53 cmd 'layout stacking';
54 open_window;
55 open_window;
56 open_window;
57 open_window;
58 cmd 'focus up; focus up'
59 }
60
61 # Gets the position of the given leaf within the given branch. The first
62 # position is one (1). Returns negative one (-1) if the leaf cannot be found
63 # within the branch.
64 sub get_leaf_position {
65 my ($branch, $leaf) = @_;
66 my $position = -1;
67 for my $i (0 .. @{$branch->{nodes}}) {
68 if ($branch->{nodes}[$i]->{id} == $leaf) {
69 $position = $i + 1;
70 last;
71 };
72 }
73 return $position;
74 }
75
76 # convenience function to focus a con by id to avoid having to type an ugly
77 # command each time
78 sub focus_con {
79 my $con_id = shift @_;
80 cmd "[con_id=\"$con_id\"] focus";
81 }
82
83 # Places a leaf into a branch and focuses the leaf. The newly created branch
84 # will have orientation specified by the second parameter.
85 sub branchify {
86 my ($con_id, $orientation) = @_;
87 focus_con($con_id);
88 $orientation eq 'horizontal' ? cmd 'splith' : cmd 'splitv';
89 open_window;
90 focus_con($con_id);
91 }
92
93 ##############################################################################
94 # When moving a con right into tabs, the moving con should be placed as the
95 # first tab in the branch
96 ##############################################################################
97 my $ws = fresh_workspace;
98
99 # create the target leaf
100 open_window;
101 my $target_leaf = get_focused($ws);
102
103 # create the tabbed branch container
104 open_window;
105 cmd 'splith';
106 open_tabs;
107
108 # move the target leaf into the tabbed branch
109 focus_con($target_leaf);
110 cmd 'move right';
111
112 # the target leaf should be the first in the branch
113 my $branch = shift @{get_ws_content($ws)};
114 is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs placed it as the first tab in the branch');
115
116 # repeat the test when the target is in a branch
117 cmd 'move up; move left';
118 branchify($target_leaf, 'vertical');
119 cmd 'move right';
120
121 $branch = pop @{get_ws_content($ws)};
122 is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con right into tabs from a branch placed it as the first tab in the branch');
123
124 ##############################################################################
125 # When moving a con right into a stack, the moving con should be placed
126 # below the focused-inactive leaf
127 ##############################################################################
128 $ws = fresh_workspace;
129
130 # create the target leaf
131 open_window;
132 $target_leaf = get_focused($ws);
133
134 # create the stacked branch container and find the focused leaf
135 open_window;
136 cmd 'splith';
137 open_stack;
138 my $secondary_leaf = get_focused($ws);
139
140 # move the target leaf into the stacked branch
141 focus_con($target_leaf);
142 cmd 'move right';
143
144 # the secondary focus leaf should be below the target
145 $branch = shift @{get_ws_content($ws)};
146 my $target_leaf_position = get_leaf_position($branch, $target_leaf);
147 my $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
148
149 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack placed it below the focused-inactive leaf');
150
151 # repeat the test when the target is in a branch
152 cmd 'move up; move left';
153 branchify($target_leaf, 'vertical');
154 cmd 'move right';
155
156 $branch = pop @{get_ws_content($ws)};
157 $target_leaf_position = get_leaf_position($branch, $target_leaf);
158 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
159
160 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con right into a stack from a branch placed it below the focused-inactive leaf');
161
162 ##############################################################################
163 # When moving a con down into a stack, the moving con should be placed at the
164 # top of the stack
165 ##############################################################################
166 $ws = fresh_workspace;
167 cmd 'layout splitv';
168
169 # create the target leaf
170 open_window;
171 $target_leaf = get_focused($ws);
172
173 # create the stacked branch container
174 open_window;
175 cmd 'splitv';
176 open_stack;
177
178 # move the target leaf into the stacked branch
179 focus_con($target_leaf);
180 cmd 'move down';
181
182 # the target leaf should be on the top of the stack
183 $branch = shift @{get_ws_content($ws)};
184 is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack placed it on the top of the stack');
185
186 # repeat the test when the target is in a branch
187 cmd 'move right; move up';
188 branchify($target_leaf, 'horizontal');
189 cmd 'move down';
190
191 $branch = pop @{get_ws_content($ws)};
192 is($branch->{nodes}[0]->{id}, $target_leaf, 'moving con down into a stack from a branch placed it on the top of the stack');
193
194 ##############################################################################
195 # When moving a con down into tabs, the moving con should be placed after the
196 # focused-inactive tab
197 ##############################################################################
198 $ws = fresh_workspace;
199 cmd 'layout splitv';
200
201 # create the target leaf
202 open_window;
203 $target_leaf = get_focused($ws);
204
205 # create the tabbed branch container and find the focused tab
206 open_window;
207 cmd 'splitv';
208 open_tabs;
209 $secondary_leaf = get_focused($ws);
210
211 # move the target leaf into the tabbed branch
212 focus_con($target_leaf);
213 cmd 'move down';
214
215 # the secondary focus tab should be to the right
216 $branch = shift @{get_ws_content($ws)};
217 $target_leaf_position = get_leaf_position($branch, $target_leaf);
218 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
219
220 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs placed it after the focused-inactive tab');
221
222 # repeat the test when the target is in a branch
223 cmd 'move right; move up';
224 branchify($target_leaf, 'horizontal');
225 cmd 'move down';
226
227 $branch = pop @{get_ws_content($ws)};
228 $target_leaf_position = get_leaf_position($branch, $target_leaf);
229 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
230
231 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con down into tabs from a branch placed it after the focused-inactive tab');
232
233 ##############################################################################
234 # When moving a con left into tabs, the moving con should be placed as the last
235 # tab in the branch
236 ##############################################################################
237 $ws = fresh_workspace;
238
239 # create the tabbed branch container
240 open_window;
241 cmd 'splith';
242 open_tabs;
243
244 # create the target leaf
245 cmd 'focus parent';
246 open_window;
247 $target_leaf = get_focused($ws);
248
249 # move the target leaf into the tabbed branch
250 cmd 'move left';
251
252 # the target leaf should be last in the branch
253 $branch = shift @{get_ws_content($ws)};
254
255 is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs placed it as the last tab in the branch');
256
257 # repeat the test when the target leaf is in a branch
258 cmd 'move up; move right';
259 branchify($target_leaf, 'vertical');
260 cmd 'move left';
261
262 $branch = shift @{get_ws_content($ws)};
263 is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con left into tabs from a branch placed it as the last tab in the branch');
264
265 ##############################################################################
266 # When moving a con left into a stack, the moving con should be placed below
267 # the focused-inactive leaf
268 ##############################################################################
269 $ws = fresh_workspace;
270
271 # create the stacked branch container and find the focused leaf
272 open_window;
273 open_stack;
274 $secondary_leaf = get_focused($ws);
275
276 # create the target leaf to the right
277 cmd 'focus parent';
278 open_window;
279 $target_leaf = get_focused($ws);
280
281 # move the target leaf into the stacked branch
282 cmd 'move left';
283
284 # the secondary focus leaf should be below
285 $branch = shift @{get_ws_content($ws)};
286 $target_leaf_position = get_leaf_position($branch, $target_leaf);
287 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
288
289 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack placed it below the focused-inactive leaf');
290
291 # repeat the test when the target leaf is in a branch
292 cmd 'move up; move right';
293 branchify($target_leaf, 'vertical');
294 cmd 'move left';
295
296 $branch = shift @{get_ws_content($ws)};
297 $target_leaf_position = get_leaf_position($branch, $target_leaf);
298 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
299
300 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con left into a stack from a branch placed it below the focused-inactive leaf');
301
302 ##############################################################################
303 # When moving a con up into a stack, the moving con should be placed last in
304 # the stack
305 ##############################################################################
306 $ws = fresh_workspace;
307 cmd 'layout splitv';
308
309 # create the stacked branch container
310 open_window;
311 cmd 'splitv';
312 open_stack;
313
314 # create the target leaf
315 cmd 'focus parent';
316 open_window;
317 $target_leaf = get_focused($ws);
318
319 # move the target leaf into the stacked branch
320 cmd 'move up';
321
322 # the target leaf should be on the bottom of the stack
323 $branch = shift @{get_ws_content($ws)};
324
325 is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack placed it on the bottom of the stack');
326
327 # repeat the test when the target leaf is in a branch
328 cmd 'move right; move down';
329 branchify($target_leaf, 'horizontal');
330 cmd 'move up';
331
332 $branch = shift @{get_ws_content($ws)};
333
334 is($branch->{nodes}->[-1]->{id}, $target_leaf, 'moving con up into stack from a branch placed it on the bottom of the stack');
335
336 ##############################################################################
337 # When moving a con up into tabs, the moving con should be placed after the
338 # focused-inactive tab
339 ##############################################################################
340 $ws = fresh_workspace;
341 cmd 'layout splitv';
342
343 # create the tabbed branch container and find the focused leaf
344 open_window;
345 cmd 'splitv';
346 open_tabs;
347 $secondary_leaf = get_focused($ws);
348
349 # create the target leaf below
350 cmd 'focus parent';
351 open_window;
352 $target_leaf = get_focused($ws);
353
354 # move the target leaf into the tabbed branch
355 cmd 'move up';
356
357 # the secondary focus tab should be to the right
358 $branch = shift @{get_ws_content($ws)};
359 $target_leaf_position = get_leaf_position($branch, $target_leaf);
360 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
361
362 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs placed it after the focused-inactive tab');
363
364 # repeat the test when the target leaf is in a branch
365 cmd 'move right; move down';
366 branchify($target_leaf, 'horizontal');
367 cmd 'move up';
368
369 $branch = shift @{get_ws_content($ws)};
370 $target_leaf_position = get_leaf_position($branch, $target_leaf);
371 $secondary_leaf_position = get_leaf_position($branch, $secondary_leaf);
372
373 is($target_leaf_position, $secondary_leaf_position + 1, 'moving con up into tabs from a branch placed it after the focused-inactive tab');
374
375 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the ipc close event works properly
17 #
18 # Bug still in: 4.8-7-gf4a8253
19 use i3test;
20
21 my $window = open_window;
22
23 my @events = events_for(
24 sub {
25 $window->unmap;
26 sync_with_i3;
27 },
28 'window');
29
30 my @close = grep { $_->{change} eq 'close' } @events;
31 is(scalar @close, 1, 'Received 1 window::close event');
32 is($close[0]->{container}->{window}, $window->{id}, 'the event should contain information about the window');
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the ipc window::move event works properly
17 #
18 # Bug still in: 4.8-7-gf4a8253
19 use i3test;
20
21 my $dummy_window = open_window;
22 my $window = open_window;
23
24 sub move_subtest {
25 my ($cmd) = @_;
26 my $cv = AnyEvent->condvar;
27 my @events = events_for(
28 sub { cmd $cmd },
29 'window');
30
31 my @move = grep { $_->{change} eq 'move' } @events;
32 is(scalar @move, 1, 'Received 1 window::move event');
33 is($move[0]->{container}->{window}, $window->{id}, 'window id matches');
34 }
35
36 subtest 'move left', \&move_subtest, 'move left';
37 subtest 'move to workspace', \&move_subtest, 'move to workspace ws_new';
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the window::urgent event works correctly. The window::urgent event
17 # should be emitted when a window becomes urgent or loses its urgent status.
18 #
19 use i3test;
20
21 fresh_workspace;
22 my $win = open_window;
23 my $dummy_win = open_window;
24
25 sub urgency_subtest {
26 my ($subscribecb, $win, $want) = @_;
27
28 my @events = events_for(
29 $subscribecb,
30 'window');
31
32 my @urgent = grep { $_->{change} eq 'urgent' } @events;
33 is(scalar @urgent, 1, 'Received 1 window::urgent event');
34 is($urgent[0]->{container}->{window}, $win->{id}, "window id matches");
35 is($urgent[0]->{container}->{urgent}, $want, "urgent is $want");
36 }
37
38 subtest "urgency set", \&urgency_subtest,
39 sub {
40 $win->add_hint('urgency');
41 sync_with_i3;
42 },
43 $win,
44 1;
45
46 subtest "urgency unset", \&urgency_subtest,
47 sub {
48 $win->delete_hint('urgency');
49 sync_with_i3;
50 },
51 $win,
52 0;
53
54 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that entire outputs can be saved and restored properly by i3.
17 # Ticket: #1306
18 # Bug still in: 4.8-26-gf96ec19
19 use i3test;
20 use File::Temp qw(tempfile);
21 use IO::Handle;
22
23 my $ws = fresh_workspace;
24
25 ################################################################################
26 # Append a new workspace with a name.
27 ################################################################################
28
29 ok(!workspace_exists('ws_new'), 'workspace "ws_new" does not exist yet');
30
31 my ($fh, $filename) = tempfile(UNLINK => 1);
32 print $fh <<'EOT';
33 // vim:ts=4:sw=4:et
34 {
35 // workspace with 1 children
36 "border": "pixel",
37 "floating": "auto_off",
38 "layout": "splith",
39 "percent": null,
40 "type": "workspace",
41 "name": "ws_new",
42 "nodes": [
43 {
44 "border": "pixel",
45 "floating": "auto_off",
46 "geometry": {
47 "height": 268,
48 "width": 484,
49 "x": 0,
50 "y": 0
51 },
52 "name": "vals@w00t: ~",
53 "percent": 1,
54 "swallows": [
55 {
56 "class": "^URxvt$"
57 // "instance": "^urxvt$",
58 // "title": "^vals\\@w00t\\:\\ \\~$"
59 }
60 ],
61 "type": "con"
62 }
63 ]
64 }
65 EOT
66 $fh->flush;
67 cmd "append_layout $filename";
68
69 ok(workspace_exists('ws_new'), 'workspace "ws_new" exists now');
70
71 does_i3_live;
72
73 close($fh);
74
75 ################################################################################
76 # Append a new workspace with a name that clashes with an existing workspace.
77 ################################################################################
78
79 my @old_workspaces = @{get_workspace_names()};
80
81 cmd "append_layout $filename";
82
83 my @new_workspaces = @{get_workspace_names()};
84 cmp_ok(scalar @new_workspaces, '>', scalar @old_workspaces, 'more workspaces than before');
85
86 my %created_workspaces = map { ($_, 1) } @new_workspaces;
87 delete $created_workspaces{$_} for @old_workspaces;
88 diag('created workspaces = ' . Dumper(keys %created_workspaces));
89 cmp_ok(scalar keys %created_workspaces, '>', 0, 'new workspaces appeared');
90
91 ################################################################################
92 # Append a new workspace without a name.
93 ################################################################################
94
95 ok(!workspace_exists('unnamed'), 'workspace "unnamed" does not exist yet');
96
97 ($fh, $filename) = tempfile(UNLINK => 1);
98 print $fh <<'EOT';
99 // vim:ts=4:sw=4:et
100 {
101 // workspace with 1 children
102 "border": "pixel",
103 "floating": "auto_off",
104 "layout": "splith",
105 "percent": null,
106 "type": "workspace",
107 "nodes": [
108 {
109 "border": "pixel",
110 "floating": "auto_off",
111 "geometry": {
112 "height": 268,
113 "width": 484,
114 "x": 0,
115 "y": 0
116 },
117 "name": "vals@w00t: ~",
118 "percent": 1,
119 "swallows": [
120 {
121 "class": "^URxvt$"
122 // "instance": "^urxvt$",
123 // "title": "^vals\\@w00t\\:\\ \\~$"
124 }
125 ],
126 "type": "con"
127 }
128 ]
129 }
130 EOT
131 $fh->flush;
132 cmd "append_layout $filename";
133
134 ok(workspace_exists('unnamed'), 'workspace "unnamed" exists now');
135
136 ################################################################################
137 # Append a workspace with a numeric name, ensure it has ->num set.
138 ################################################################################
139
140 ok(!workspace_exists('4'), 'workspace "4" does not exist yet');
141
142 ($fh, $filename) = tempfile(UNLINK => 1);
143 print $fh <<'EOT';
144 // vim:ts=4:sw=4:et
145 {
146 // workspace with 1 children
147 "border": "pixel",
148 "floating": "auto_off",
149 "layout": "splith",
150 "percent": null,
151 "type": "workspace",
152 "name": "4",
153 "nodes": [
154 {
155 "border": "pixel",
156 "floating": "auto_off",
157 "geometry": {
158 "height": 268,
159 "width": 484,
160 "x": 0,
161 "y": 0
162 },
163 "name": "vals@w00t: ~",
164 "percent": 1,
165 "swallows": [
166 {
167 "class": "^URxvt$"
168 // "instance": "^urxvt$",
169 // "title": "^vals\\@w00t\\:\\ \\~$"
170 }
171 ],
172 "type": "con"
173 }
174 ]
175 }
176 EOT
177 $fh->flush;
178 cmd "append_layout $filename";
179
180 ok(workspace_exists('4'), 'workspace "4" exists now');
181 $ws = get_ws("4");
182 is($ws->{num}, 4, 'workspace number is 4');
183
184 ################################################################################
185 # Append a workspace with a numeric name, with the “type” property at the end
186 # of the JSON blurb (which is valid and sometimes happens).
187 ################################################################################
188
189 ok(!workspace_exists('5'), 'workspace "5" does not exist yet');
190
191 ($fh, $filename) = tempfile(UNLINK => 1);
192 print $fh <<'EOT';
193 // vim:ts=4:sw=4:et
194 {
195 // workspace with 1 children
196 "border": "pixel",
197 "floating": "auto_off",
198 "layout": "splith",
199 "percent": null,
200 "name": "5",
201 "nodes": [
202 {
203 "border": "pixel",
204 "floating": "auto_off",
205 "geometry": {
206 "height": 268,
207 "width": 484,
208 "x": 0,
209 "y": 0
210 },
211 "name": "vals@w00t: ~",
212 "percent": 1,
213 "swallows": [
214 {
215 "class": "^URxvt$"
216 // "instance": "^urxvt$",
217 // "title": "^vals\\@w00t\\:\\ \\~$"
218 }
219 ],
220 "type": "con"
221 }
222 ],
223 "type": "workspace"
224 }
225 EOT
226 $fh->flush;
227 cmd "append_layout $filename";
228
229 ok(workspace_exists('5'), 'workspace "5" exists now');
230 $ws = get_ws("5");
231 is($ws->{num}, 5, 'workspace number is 5');
232
233 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # This is a regression test for a bug where a normal floating default border is
17 # not applied when the default tiling border is set to a pixel value.
18 # Ticket: #1305
19 # Bug still in: 4.8-62-g7381b50
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 new_window pixel 5
25 new_float normal
26 EOT
27
28 my $ws = fresh_workspace;
29
30 my $float_window = open_floating_window;
31
32 my @floating = @{get_ws($ws)->{floating_nodes}};
33
34 is($floating[0]->{nodes}[0]->{border}, 'normal', 'default floating border is `normal`');
35
36 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that changes to WM_CLASS are internally processed by i3 by updating the
17 # cached property and running assignments. This allows the property to be used
18 # in criteria selection
19 # Ticket: #1052
20 # Bug still in: 4.8-73-g6bf7f8e
21 use i3test i3_config => <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24 for_window [class="Special"] mark special_class_mark
25 EOT
26 use X11::XCB qw(PROP_MODE_REPLACE);
27
28 sub change_window_class {
29 my ($window, $class, $length) = @_;
30 my $atomname = $x->atom(name => 'WM_CLASS');
31 my $atomtype = $x->atom(name => 'STRING');
32 $length ||= length($class) + 1;
33 $x->change_property(
34 PROP_MODE_REPLACE,
35 $window->id,
36 $atomname->id,
37 $atomtype->id,
38 8,
39 $length,
40 $class
41 );
42 sync_with_i3;
43 }
44
45 my $ws = fresh_workspace;
46
47 my $win = open_window;
48
49 change_window_class($win, "special\0Special");
50
51 my $con = @{get_ws_content($ws)}[0];
52
53 is($con->{window_properties}->{class}, 'Special',
54 'The container class should be updated when a window changes class');
55
56 is($con->{window_properties}->{instance}, 'special',
57 'The container instance should be updated when a window changes instance');
58
59 # The mark `special_class_mark` is added in a `for_window` assignment in the
60 # config for testing purposes
61 is_deeply($con->{marks}, [ 'special_class_mark' ],
62 'A `for_window` assignment should run for a match when the window changes class');
63
64 change_window_class($win, "abcdefghijklmnopqrstuv\0abcd", 24);
65
66 $con = @{get_ws_content($ws)}[0];
67
68 is($con->{window_properties}->{class}, 'a',
69 'Non-null-terminated strings should be handled correctly');
70
71 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the binding event works properly
17 # Ticket: #1210
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 bindsym r reload
23 EOT
24
25 SKIP: {
26 qx(which xdotool 2> /dev/null);
27
28 skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
29
30 qx(xdotool key r);
31
32 does_i3_live;
33
34 }
35 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 does not crash when floating and then unfloating an
17 # unfocused window within a tabbed container.
18 # Ticket: #1484
19 # Bug still in: 4.9.1-124-g856e1f9
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23 workspace_layout tabbed
24 EOT
25
26 open_window;
27 open_window;
28
29 # Mark the second window, then focus the workspace.
30 cmd 'mark foo, focus parent, focus parent';
31
32 # Float and unfloat the marked window (without it being focused).
33 cmd '[con_mark=foo] floating enable, floating disable';
34
35 does_i3_live;
36
37 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for setting and removing the _NET_WM_STATE_HIDDEN atom properly.
17 # Ticket: #1648
18 use i3test;
19 use X11::XCB qw(:all);
20
21 sub is_hidden {
22 sync_with_i3;
23 my $atom = $x->atom(name => '_NET_WM_STATE_HIDDEN');
24
25 my ($con) = @_;
26 my $cookie = $x->get_property(
27 0,
28 $con->{id},
29 $x->atom(name => '_NET_WM_STATE')->id,
30 GET_PROPERTY_TYPE_ANY,
31 0,
32 4096
33 );
34
35 my $reply = $x->get_property_reply($cookie->{sequence});
36 my $len = $reply->{length};
37 return 0 if $len == 0;
38
39 my @atoms = unpack("L$len", $reply->{value});
40 for (my $i = 0; $i < $len; $i++) {
41 return 1 if $atoms[$i] == $atom->id;
42 }
43
44 return 0;
45 }
46
47 my ($tabA, $tabB, $tabC, $subtabA, $subtabB, $windowA, $windowB);
48
49 ###############################################################################
50 # Given two containers next to each other, when focusing one, then the other
51 # one does not have _NET_WM_STATE_HIDDEN set.
52 ###############################################################################
53
54 fresh_workspace;
55 $windowA = open_window;
56 $windowB = open_window;
57
58 ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
59 ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
60
61 ###############################################################################
62 # Given two containers on different workspaces, when one is focused, then
63 # the other one does not have _NET_WM_STATE_HIDDEN set.
64 ###############################################################################
65
66 fresh_workspace;
67 $windowA = open_window;
68 fresh_workspace;
69 $windowB = open_window;
70
71 ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
72 ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
73
74 ###############################################################################
75 # Given two containers in the same tabbed container, when one is focused, then
76 # (only) the other one has _NET_WM_STATE_HIDDEN set.
77 # Given the other tab is focused, then the atom is transferred.
78 ###############################################################################
79
80 fresh_workspace;
81 $tabA = open_window;
82 cmd 'layout tabbed';
83 $tabB = open_window;
84
85 ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
86 ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
87
88 cmd 'focus left';
89
90 ok(!is_hidden($tabA), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
91 ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
92
93 ###############################################################################
94 # Given three containers in the same stacked container, when the focused tab
95 # is moved to another workspace, then the now focused tab does not have
96 # _NET_WM_STATE_HIDDEN set anymore.
97 ###############################################################################
98
99 fresh_workspace;
100 $tabA = open_window;
101 cmd 'layout stacked';
102 $tabB = open_window;
103 $tabC = open_window;
104 cmd 'move window to workspace unused';
105
106 ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
107 ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
108 ok(!is_hidden($tabC), 'moved window does not have _NET_WM_STATE_HIDDEN set');
109
110 ###############################################################################
111 # Given three containers in the same stacked container, when a not focused
112 # tab is moved to another workspace, then it does not have _NET_WM_STATE_HIDDEN
113 # set anymore.
114 ###############################################################################
115
116 fresh_workspace;
117 $tabA = open_window;
118 cmd 'layout stacked';
119 $tabB = open_window;
120 cmd 'mark moveme';
121 $tabC = open_window;
122 cmd '[con_mark="moveme"] move window to workspace unused';
123
124 ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
125 ok(!is_hidden($tabB), 'moved window does not have _NET_WM_STATE_HIDDEN set');
126 ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
127
128 ###############################################################################
129 # Given a tabbed container and some other container, when the latter is moved
130 # into the tabbed container, then all other tabs have _NET_WM_STATE_HIDDEN
131 # set.
132 ###############################################################################
133
134 fresh_workspace;
135 $tabA = open_window;
136 cmd 'layout tabbed';
137 $tabB = open_window;
138 cmd 'focus parent';
139 cmd 'split h';
140 $tabC = open_window;
141 cmd 'move left';
142
143 ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
144 ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
145 ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
146
147 ###############################################################################
148 # Given a stacked container nested inside another tabbed container with the
149 # inner one being in the currently focused tab, then the focused tab of the
150 # inner container does not have _NET_WM_STATE_HIDDEN set.
151 ###############################################################################
152
153 fresh_workspace;
154 $tabA = open_window;
155 cmd 'layout tabbed';
156 $tabB = open_window;
157 cmd 'split h';
158 open_window;
159 cmd 'split v';
160 cmd 'layout stacked';
161 $subtabA = open_window;
162 $subtabB = open_window;
163
164 ok(is_hidden($tabA), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
165 ok(!is_hidden($tabB), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
166 ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
167 ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
168
169 cmd 'focus left';
170
171 ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
172
173 ###############################################################################
174 # Given a stacked container nested inside another tabbed container with the
175 # inner one being in a currently not focused tab, then all tabs of the inner
176 # container have _NET_WM_STATE_HIDDEN set.
177 ###############################################################################
178
179 fresh_workspace;
180 $tabA = open_window;
181 cmd 'layout tabbed';
182 $tabB = open_window;
183 cmd 'split h';
184 open_window;
185 cmd 'split v';
186 cmd 'layout stacked';
187 $subtabA = open_window;
188 $subtabB = open_window;
189 cmd 'focus parent';
190 cmd 'focus parent';
191 cmd 'focus left';
192
193 ok(!is_hidden($tabA), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
194 ok(is_hidden($tabB), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
195 ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
196 ok(is_hidden($subtabB), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
197
198 ###############################################################################
199
200 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that _NET_WM_VISIBLE_NAME is set correctly.
17 # Ticket: #1872
18 use i3test;
19 use X11::XCB qw(:all);
20
21 my ($con);
22
23 sub get_visible_name {
24 sync_with_i3;
25 my ($con) = @_;
26
27 my $cookie = $x->get_property(
28 0,
29 $con->{id},
30 $x->atom(name => '_NET_WM_VISIBLE_NAME')->id,
31 $x->atom(name => 'UTF8_STRING')->id,
32 0,
33 4096
34 );
35
36 my $reply = $x->get_property_reply($cookie->{sequence});
37 return undef if $reply->{value_len} == 0;
38 return $reply->{value};
39 }
40
41 ###############################################################################
42 # 1: _NET_WM_VISIBLE_NAME is set when the title format of a window is changed.
43 ###############################################################################
44
45 fresh_workspace;
46 $con = open_window(name => 'boring title');
47 is(get_visible_name($con), undef, 'sanity check: initially no visible name is set');
48
49 cmd 'title_format custom';
50 is(get_visible_name($con), 'custom', 'the visible name is updated');
51
52 cmd 'title_format "<s>%title</s>"';
53 is(get_visible_name($con), '<s>boring title</s>', 'markup is returned as is');
54
55 ###############################################################################
56 # 2: _NET_WM_VISIBLE_NAME is removed if not needed.
57 ###############################################################################
58
59 fresh_workspace;
60 $con = open_window(name => 'boring title');
61 cmd 'title_format custom';
62 is(get_visible_name($con), 'custom', 'sanity check: a visible name is set');
63
64 cmd 'title_format %title';
65 is(get_visible_name($con), undef, 'the visible name is removed again');
66
67 ###############################################################################
68
69 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests sticky windows.
17 # Ticket: #1455
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 workspace ws-on-0 output fake-0
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25 EOT
26
27 my ($ws, $tmp, $focused);
28
29 ###############################################################################
30 # 1: Given a sticky tiling container, when the workspace is switched, then
31 # nothing happens.
32 ###############################################################################
33 fresh_workspace;
34 open_window;
35 cmd 'sticky enable';
36 $ws = fresh_workspace;
37
38 is(@{get_ws($ws)->{nodes}}, 0, 'tiling sticky container did not move');
39 is(@{get_ws($ws)->{floating_nodes}}, 0, 'tiling sticky container did not move');
40 kill_all_windows;
41
42 ###############################################################################
43 # 2: Given a sticky floating container, when the workspace is switched, then
44 # the container moves to the new workspace.
45 ###############################################################################
46 $ws = fresh_workspace;
47 open_floating_window;
48 $focused = get_focused($ws);
49 cmd 'sticky enable';
50 $ws = fresh_workspace;
51
52 is(@{get_ws($ws)->{floating_nodes}}, 1, 'floating sticky container moved to new workspace');
53 is(get_focused($ws), $focused, 'sticky container has focus');
54 kill_all_windows;
55
56 ###############################################################################
57 # 3: Given two sticky floating containers, when the workspace is switched,
58 # then both containers move to the new workspace.
59 ###############################################################################
60 fresh_workspace;
61 open_floating_window;
62 cmd 'sticky enable';
63 open_floating_window;
64 cmd 'sticky enable';
65 $ws = fresh_workspace;
66
67 is(@{get_ws($ws)->{floating_nodes}}, 2, 'multiple sticky windows can be used at the same time');
68 kill_all_windows;
69
70 ###############################################################################
71 # 4: Given an unfocused sticky floating container and a tiling container on the
72 # target workspace, when the workspace is switched, then the tiling container
73 # is focused.
74 ###############################################################################
75 $ws = fresh_workspace;
76 open_window;
77 $focused = get_focused($ws);
78 fresh_workspace;
79 open_floating_window;
80 cmd 'sticky enable';
81 open_window;
82 cmd 'workspace ' . $ws;
83
84 is(get_focused($ws), $focused, 'the tiling container has focus');
85 kill_all_windows;
86
87 ###############################################################################
88 # 5: Given a focused sticky floating container and a tiling container on the
89 # target workspace, when the workspace is switched, then the tiling container
90 # is focused.
91 ###############################################################################
92 $ws = fresh_workspace;
93 open_window;
94 $tmp = fresh_workspace;
95 open_floating_window;
96 $focused = get_focused($tmp);
97 cmd 'sticky enable';
98 cmd 'workspace ' . $ws;
99
100 is(get_focused($ws), $focused, 'the sticky container has focus');
101 kill_all_windows;
102
103 ###############################################################################
104 # 6: Given a floating container on a non-visible workspace, when the window
105 # is made sticky, then the window immediately jumps to the currently
106 # visible workspace.
107 ###############################################################################
108 fresh_workspace;
109 open_floating_window;
110 cmd 'mark sticky';
111 $ws = fresh_workspace;
112 cmd '[con_mark=sticky] sticky enable';
113
114 is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window jumps to the front');
115 kill_all_windows;
116
117 ###############################################################################
118 # 7: Given a sticky floating container and a workspace on another output, when
119 # a new workspace assigned to the first output is focused, then the sticky
120 # container should jump to the new workspace and have input focus correctly.
121 ###############################################################################
122 $ws = fresh_workspace(output => 0);
123 open_floating_window;
124 cmd 'sticky enabled';
125 $focused = get_focused($ws);
126 $ws = fresh_workspace(output => 1);
127
128 is(@{get_ws($ws)->{floating_nodes}}, 0, 'the sticky window didn\'t jump to a workspace on a different output');
129 $ws = 'ws-on-0';
130 cmd "workspace $ws";
131 is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window moved to new workspace on first output');
132 is(get_focused($ws), $focused, 'the sticky window has focus');
133 kill_all_windows;
134
135 ###############################################################################
136
137 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that mouse bindings work on the root window if
17 # --whole-window is set.
18 # Ticket: #2115
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 workspace_auto_back_and_forth no
24 bindsym --whole-window button4 workspace special
25 EOT
26 use i3test::XTEST;
27
28 fresh_workspace;
29
30 xtest_button_press(4, 50, 50);
31 xtest_button_release(4, 50, 50);
32 xtest_sync_with_i3;
33
34 is(focused_ws(), 'special', 'the binding was triggered');
35
36 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the hide_edge_borders smart option works
17 # Ticket: #2188
18
19 use i3test i3_autostart => 0;
20
21 ####################################################################
22 # 1: check that the borders are present on a floating windows
23 #####################################################################
24
25 my $config = <<EOT;
26 # i3 config file (v4)
27 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
28
29 new_window pixel 2
30 new_float pixel 2
31 hide_edge_borders smart
32 EOT
33
34 my $pid = launch_with_config($config);
35
36 my $tmp = fresh_workspace;
37
38 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
39
40 my $floatwindow = open_floating_window;
41
42 my $wscontent = get_ws($tmp);
43
44 my @floating = @{$wscontent->{floating_nodes}};
45 ok(@floating == 1, 'one floating container opened');
46 is($floating[0]->{nodes}[0]->{current_border_width}, 2, 'floating current border width set to 2');
47 is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*2, 'floating border width 2');
48
49 exit_gracefully($pid);
50
51 #####################################################################
52 # 2: check that the borders are present on a workspace with two tiled
53 # windows visible
54 #####################################################################
55
56 $config = <<EOT;
57 # i3 config file (v4)
58 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
59
60 new_window pixel 2
61 new_float pixel 2
62 hide_edge_borders smart
63 EOT
64
65 $pid = launch_with_config($config);
66
67 $tmp = fresh_workspace;
68
69 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
70
71 my $tilewindow = open_window;
72 my $tilewindow2 = open_window;
73
74 $wscontent = get_ws($tmp);
75
76 my @tiled = @{$wscontent->{nodes}};
77 ok(@tiled == 2, 'two tiled container opened');
78 is($tiled[0]->{current_border_width}, 2, 'first tiled current border width set to 2');
79 is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
80 is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
81 is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
82
83 exit_gracefully($pid);
84
85 #####################################################################
86 # 3: check that the borders are hidden on a workspace with one tiled
87 # window visible
88 #####################################################################
89
90 $config = <<EOT;
91 # i3 config file (v4)
92 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
93
94 new_window pixel 2
95 new_float pixel 2
96 hide_edge_borders smart
97 EOT
98
99 $pid = launch_with_config($config);
100
101 $tmp = fresh_workspace;
102
103 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
104
105 $tilewindow = open_window;
106
107 $wscontent = get_ws($tmp);
108
109 @tiled = @{$wscontent->{nodes}};
110 ok(@tiled == 1, 'one tiled container opened');
111 is($tiled[0]->{current_border_width}, 2, 'tiled current border width set to 2');
112 is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*0, 'single tiled border width 0');
113
114 exit_gracefully($pid);
115
116 #####################################################################
117 # 4: check that the borders are present on a workspace with two tiled
118 # windows visible, recursively
119 #####################################################################
120
121 $config = <<EOT;
122 # i3 config file (v4)
123 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
124
125 new_window pixel 2
126 new_float pixel 2
127 hide_edge_borders smart
128 EOT
129
130 $pid = launch_with_config($config);
131
132 $tmp = fresh_workspace;
133
134 ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
135
136 $tilewindow = open_window;
137 $tilewindow2 = open_window;
138 ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
139
140 cmd 'layout tabbed';
141 ok(@{get_ws_content($tmp)} == 1, 'layout tabbed -> back to one container');
142
143 cmd 'focus parent';
144 my $tilewindow3 = open_window;
145 ok(@{get_ws_content($tmp)} == 2, 'after split & new window, two containers');
146
147 $wscontent = get_ws($tmp);
148
149 # Ensure i3’s X11 requests are processed before our inquiry via
150 # $tilewindow->rect:
151 sync_with_i3;
152
153 @tiled = @{$wscontent->{nodes}};
154 ok(@tiled == 2, 'two tiled container opened in another container');
155 is($tiled[0]->{current_border_width}, -1, 'first tiled current border width set to -1');
156 is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
157 is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
158 is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
159
160 exit_gracefully($pid);
161
162 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for our proprietary atom I3_FLOATING_WINDOW to allow
17 # identifying floating windows.
18 # Ticket: #2223
19 use i3test;
20 use X11::XCB qw(:all);
21
22 my ($con);
23
24 sub has_i3_floating_window {
25 sync_with_i3;
26
27 my ($con) = @_;
28 my $cookie = $x->get_property(
29 0,
30 $con->{id},
31 $x->atom(name => 'I3_FLOATING_WINDOW')->id,
32 $x->atom(name => 'CARDINAL')->id,
33 0,
34 1
35 );
36
37 my $reply = $x->get_property_reply($cookie->{sequence});
38 return 0 if $reply->{length} != 1;
39
40 return unpack("L", $reply->{value});
41 }
42
43 ###############################################################################
44 # Toggling floating on a container adds / removes I3_FLOATING_WINDOW.
45 ###############################################################################
46
47 fresh_workspace;
48
49 $con = open_window;
50 is(has_i3_floating_window($con), 0, 'I3_FLOATING_WINDOW is not set');
51
52 cmd 'floating enable';
53 is(has_i3_floating_window($con), 1, 'I3_FLOATING_WINDOW is set');
54
55 cmd 'floating disable';
56 is(has_i3_floating_window($con), 0, 'I3_FLOATING_WINDOW is not set');
57
58 ###############################################################################
59 # A window that is floated when managed has I3_FLOATING_WINDOW set.
60 ###############################################################################
61 #
62 fresh_workspace;
63
64 $con = open_floating_window;
65 is(has_i3_floating_window($con), 1, 'I3_FLOATING_WINDOW is set');
66
67 ###############################################################################
68
69 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test the ipc shutdown event. This event is triggered when the connection to
17 # the ipc is about to shutdown because of a user action such as with a
18 # `restart` or `exit` command. The `change` field indicates why the ipc is
19 # shutting down. It can be either "restart" or "exit".
20 #
21 # Ticket: #2318
22 # Bug still in: 4.12-46-g2123888
23 use i3test;
24
25 # We cannot use events_for in this test as we cannot send events after
26 # issuing the restart/shutdown command.
27
28 my $i3 = i3(get_socket_path());
29 $i3->connect->recv;
30
31 my $cv = AnyEvent->condvar;
32 my $timer = AnyEvent->timer(after => 0.5, interval => 0, cb => sub { $cv->send(0); });
33
34 $i3->subscribe({
35 shutdown => sub {
36 $cv->send(shift);
37 }
38 })->recv;
39
40 cmd 'restart';
41
42 my $e = $cv->recv;
43
44 diag "Event:\n", Dumper($e);
45 ok($e, 'the shutdown event should emit when the ipc is restarted by command');
46 is($e->{change}, 'restart', 'the `change` field should tell the reason for the shutdown');
47
48 # restarting kills the ipc client so we have to make a new one
49 $i3 = i3(get_socket_path());
50 $i3->connect->recv;
51
52 $cv = AnyEvent->condvar;
53 $timer = AnyEvent->timer(after => 0.5, interval => 0, cb => sub { $cv->send(0); });
54
55 $i3->subscribe({
56 shutdown => sub {
57 $cv->send(shift);
58 }
59 })->recv;
60
61 cmd 'exit';
62
63 $e = $cv->recv;
64
65 diag "Event:\n", Dumper($e);
66 ok($e, 'the shutdown event should emit when the ipc is exited by command');
67 is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
68
69 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that one can bind on numpad keys in different numlock states.
17 # Ticket: #2346
18 # Bug still in: 4.12-78-g85bb324
19 use i3test i3_autostart => 0;
20 use i3test::XTEST;
21 use ExtUtils::PkgConfig;
22
23 SKIP: {
24 skip "libxcb-xkb too old (need >= 1.11)", 1 unless
25 ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
26
27 my $config = <<EOT;
28 # i3 config file (v4)
29 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
30
31 # Same key, different numlock states.
32 bindsym Mod2+KP_1 nop KP_1
33 bindsym KP_End nop KP_End
34
35 # Binding which should work with numlock and without.
36 bindsym Mod4+a nop a
37
38 # Binding which should work with numlock and without, see issue #2418.
39 bindsym Escape nop Escape
40
41 # Binding which should work with numlock and without, see issue #2418.
42 bindsym Shift+Escape nop Shift+Escape
43
44 # Binding which should work with numlock and without, see issue #2418.
45 bindsym Mod1+Shift+q nop Mod1+Shift+q
46
47 # Binding which should work with numlock and without, see issue #2559.
48 bindcode 39 nop s
49 EOT
50
51 my $pid = launch_with_config($config);
52
53 is(listen_for_binding(
54 sub {
55 xtest_key_press(87); # KP_End
56 xtest_key_release(87); # KP_End
57 xtest_sync_with_i3;
58 },
59 ),
60 'KP_End',
61 'triggered the "KP_End" keybinding');
62
63 is(listen_for_binding(
64 sub {
65 xtest_key_press(77); # enable Num_Lock
66 xtest_key_release(77); # enable Num_Lock
67 xtest_key_press(87); # KP_1
68 xtest_key_release(87); # KP_1
69 xtest_key_press(77); # disable Num_Lock
70 xtest_key_release(77); # disable Num_Lock
71 xtest_sync_with_i3;
72 },
73 ),
74 'KP_1',
75 'triggered the "KP_1" keybinding');
76
77 is(listen_for_binding(
78 sub {
79 xtest_key_press(133); # Super_L
80 xtest_key_press(38); # a
81 xtest_key_release(38); # a
82 xtest_key_release(133); # Super_L
83 xtest_sync_with_i3;
84 },
85 ),
86 'a',
87 'triggered the "a" keybinding');
88
89 is(listen_for_binding(
90 sub {
91 xtest_key_press(77); # enable Num_Lock
92 xtest_key_release(77); # enable Num_Lock
93 xtest_key_press(133); # Super_L
94 xtest_key_press(38); # a
95 xtest_key_release(38); # a
96 xtest_key_release(133); # Super_L
97 xtest_key_press(77); # disable Num_Lock
98 xtest_key_release(77); # disable Num_Lock
99 xtest_sync_with_i3;
100 },
101 ),
102 'a',
103 'triggered the "a" keybinding');
104
105 is(listen_for_binding(
106 sub {
107 xtest_key_press(9); # Escape
108 xtest_key_release(9); # Escape
109 xtest_sync_with_i3;
110 },
111 ),
112 'Escape',
113 'triggered the "Escape" keybinding');
114
115 is(listen_for_binding(
116 sub {
117 xtest_key_press(77); # enable Num_Lock
118 xtest_key_release(77); # enable Num_Lock
119 xtest_key_press(9); # Escape
120 xtest_key_release(9); # Escape
121 xtest_key_press(77); # disable Num_Lock
122 xtest_key_release(77); # disable Num_Lock
123 xtest_sync_with_i3;
124 },
125 ),
126 'Escape',
127 'triggered the "Escape" keybinding');
128
129 is(listen_for_binding(
130 sub {
131 xtest_key_press(50); # Shift_L
132 xtest_key_press(9); # Escape
133 xtest_key_release(9); # Escape
134 xtest_key_release(50); # Shift_L
135 xtest_sync_with_i3;
136 },
137 ),
138 'Shift+Escape',
139 'triggered the "Escape" keybinding');
140
141 is(listen_for_binding(
142 sub {
143 xtest_key_press(77); # enable Num_Lock
144 xtest_key_release(77); # enable Num_Lock
145 xtest_key_press(50); # Shift_L
146 xtest_key_press(9); # Escape
147 xtest_key_release(9); # Escape
148 xtest_key_release(50); # Shift_L
149 xtest_key_press(77); # disable Num_Lock
150 xtest_key_release(77); # disable Num_Lock
151 xtest_sync_with_i3;
152 },
153 ),
154 'Shift+Escape',
155 'triggered the "Escape" keybinding');
156
157 is(listen_for_binding(
158 sub {
159 xtest_key_press(50); # Shift_L
160 xtest_key_press(64); # Alt_L
161 xtest_key_press(24); # q
162 xtest_key_release(24); # q
163 xtest_key_release(64); # Alt_L
164 xtest_key_release(50); # Shift_L
165 xtest_sync_with_i3;
166 },
167 ),
168 'Mod1+Shift+q',
169 'triggered the "Mod1+Shift+q" keybinding');
170
171 is(listen_for_binding(
172 sub {
173 xtest_key_press(77); # enable Num_Lock
174 xtest_key_release(77); # enable Num_Lock
175 xtest_key_press(50); # Shift_L
176 xtest_key_press(64); # Alt_L
177 xtest_key_press(24); # q
178 xtest_key_release(24); # q
179 xtest_key_release(64); # Alt_L
180 xtest_key_release(50); # Shift_L
181 xtest_key_press(77); # disable Num_Lock
182 xtest_key_release(77); # disable Num_Lock
183 xtest_sync_with_i3;
184 },
185 ),
186 'Mod1+Shift+q',
187 'triggered the "Mod1+Shift+q" keybinding');
188
189 is(listen_for_binding(
190 sub {
191 xtest_key_press(39); # s
192 xtest_key_release(39); # s
193 xtest_sync_with_i3;
194 },
195 ),
196 's',
197 'triggered the "s" keybinding without Num_Lock');
198
199 is(listen_for_binding(
200 sub {
201 xtest_key_press(77); # enable Num_Lock
202 xtest_key_release(77); # enable Num_Lock
203 xtest_key_press(39); # s
204 xtest_key_release(39); # s
205 xtest_key_press(77); # disable Num_Lock
206 xtest_key_release(77); # disable Num_Lock
207 xtest_sync_with_i3;
208 },
209 ),
210 's',
211 'triggered the "s" keybinding with Num_Lock');
212
213 exit_gracefully($pid);
214
215 ################################################################################
216 # Verify bindings for modifiers work
217 ################################################################################
218
219 $config = <<EOT;
220 # i3 config file (v4)
221 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
222
223 bindsym Mod4+Return nop Return
224
225 # Binding which should work with numlock and without, see issue #2559.
226 bindcode --release 133 nop Super_L
227 EOT
228
229 $pid = launch_with_config($config);
230
231 is(listen_for_binding(
232 sub {
233 xtest_key_press(133); # Super_L
234 xtest_key_release(133); # Super_L
235 xtest_sync_with_i3;
236 },
237 ),
238 'Super_L',
239 'triggered the "Super_L" keybinding without Num_Lock');
240
241 is(listen_for_binding(
242 sub {
243 xtest_key_press(77); # enable Num_Lock
244 xtest_key_release(77); # enable Num_Lock
245 xtest_key_press(133); # Super_L
246 xtest_key_release(133); # Super_L
247 xtest_key_press(77); # disable Num_Lock
248 xtest_key_release(77); # disable Num_Lock
249 xtest_sync_with_i3;
250 },
251 ),
252 'Super_L',
253 'triggered the "Super_L" keybinding with Num_Lock');
254
255 is(listen_for_binding(
256 sub {
257 xtest_key_press(133); # Super_L
258 xtest_key_press(36); # Return
259 xtest_key_release(36); # Return
260 xtest_key_release(133); # Super_L
261 xtest_sync_with_i3;
262 },
263 ),
264 'Return',
265 'triggered the "Return" keybinding without Num_Lock');
266
267 is(listen_for_binding(
268 sub {
269 xtest_key_press(77); # enable Num_Lock
270 xtest_key_release(77); # enable Num_Lock
271 xtest_key_press(133); # Super_L
272 xtest_key_press(36); # Return
273 xtest_key_release(36); # Return
274 xtest_key_release(133); # Super_L
275 xtest_key_press(77); # disable Num_Lock
276 xtest_key_release(77); # disable Num_Lock
277 xtest_sync_with_i3;
278 },
279 ),
280 'Return',
281 'triggered the "Return" keybinding with Num_Lock');
282
283 exit_gracefully($pid);
284
285 ################################################################################
286 # Verify the binding is only triggered for KP_End, not KP_1
287 ################################################################################
288
289 $config = <<EOT;
290 # i3 config file (v4)
291 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
292
293 bindsym KP_End nop KP_End
294 bindcode 88 nop KP_Down
295 EOT
296
297 $pid = launch_with_config($config);
298
299 is(listen_for_binding(
300 sub {
301 xtest_key_press(87); # KP_End
302 xtest_key_release(87); # KP_End
303 xtest_sync_with_i3;
304 },
305 ),
306 'KP_End',
307 'triggered the "KP_End" keybinding');
308
309 is(listen_for_binding(
310 sub {
311 xtest_key_press(88); # KP_Down
312 xtest_key_release(88); # KP_Down
313 xtest_sync_with_i3;
314 },
315 ),
316 'KP_Down',
317 'triggered the "KP_Down" keybinding');
318
319 my @unexpected = events_for(
320 sub {
321 xtest_key_press(77); # enable Num_Lock
322 xtest_key_release(77); # enable Num_Lock
323 xtest_key_press(87); # KP_1
324 xtest_key_release(87); # KP_1
325 xtest_key_press(77); # disable Num_Lock
326 xtest_key_release(77); # disable Num_Lock
327 xtest_sync_with_i3;
328 },
329 'binding');
330 is(scalar @unexpected, 0, 'Did not trigger the KP_End keybinding with KP_1');
331
332 my @unexpected2 = events_for(
333 sub {
334 xtest_key_press(77); # enable Num_Lock
335 xtest_key_release(77); # enable Num_Lock
336 xtest_key_press(88); # KP_2
337 xtest_key_release(88); # KP_2
338 xtest_key_press(77); # disable Num_Lock
339 xtest_key_release(77); # disable Num_Lock
340 xtest_sync_with_i3;
341 },
342 'binding');
343
344 is(scalar @unexpected2, 0, 'Did not trigger the KP_Down keybinding with KP_2');
345
346 # TODO: This test does not verify that i3 does _NOT_ grab keycode 87 with Mod2.
347
348 exit_gracefully($pid);
349
350 ################################################################################
351 # Verify mouse bindings are unaffected by NumLock
352 ################################################################################
353
354 $config = <<EOT;
355 # i3 config file (v4)
356 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
357
358 bindsym --whole-window button4 nop button4
359 EOT
360
361 $pid = launch_with_config($config);
362
363 my $win = open_window;
364
365 is(listen_for_binding(
366 sub {
367 xtest_key_press(77); # enable Num_Lock
368 xtest_key_release(77); # enable Num_Lock
369 xtest_button_press(4, 50, 50);
370 xtest_button_release(4, 50, 50);
371 xtest_key_press(77); # disable Num_Lock
372 xtest_key_release(77); # disable Num_Lock
373 xtest_sync_with_i3;
374 },
375 ),
376 'button4',
377 'triggered the button4 keybinding with NumLock');
378
379 is(listen_for_binding(
380 sub {
381 xtest_button_press(4, 50, 50);
382 xtest_button_release(4, 50, 50);
383 xtest_sync_with_i3;
384 },
385 ),
386 'button4',
387 'triggered the button4 keybinding without NumLock');
388
389 exit_gracefully($pid);
390
391 }
392
393 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the swap command.
17 # Ticket: #917
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 for_window[class="mark_A"] mark A
23 for_window[class="mark_B"] mark B
24 EOT
25
26 my ($ws, $ws1, $ws2, $ws3);
27 my ($node, $nodes, $expected_focus, $A, $B, $F);
28 my ($result);
29 my @fullscreen_permutations = ([], ["A"], ["B"], ["A", "B"]);
30 my @urgent;
31
32 ###############################################################################
33 # Invalid con_id should not crash i3
34 # See issue #2895.
35 ###############################################################################
36
37 $ws = fresh_workspace;
38
39 open_window;
40 cmd "swap container with con_id 1";
41
42 does_i3_live;
43 kill_all_windows;
44
45 ###############################################################################
46 # Swap 2 windows in different workspaces using con_id
47 ###############################################################################
48
49 $ws = fresh_workspace;
50 open_window;
51 $A = get_focused($ws);
52
53 $ws = fresh_workspace;
54 open_window;
55
56 cmd "swap container with con_id $A";
57 is(get_focused($ws), $A, 'A is now focused');
58
59 kill_all_windows;
60
61 ###############################################################################
62 # Swap two containers next to each other.
63 # Focus should stay on B because both windows are on the focused workspace.
64 # The focused container is B.
65 #
66 # +---+---+ Layout: H1[ A B ]
67 # | A | B | Focus Stacks:
68 # +---+---+ H1: B, A
69 ###############################################################################
70 $ws = fresh_workspace;
71
72 $A = open_window(wm_class => 'mark_A');
73 $B = open_window(wm_class => 'mark_B');
74 $expected_focus = get_focused($ws);
75
76 cmd '[con_mark=B] swap container with mark A';
77
78 $nodes = get_ws_content($ws);
79 is($nodes->[0]->{window}, $B->{id}, 'B is on the left');
80 is($nodes->[1]->{window}, $A->{id}, 'A is on the right');
81 is(get_focused($ws), $expected_focus, 'B is still focused');
82
83 kill_all_windows;
84
85 ###############################################################################
86 # Swap two containers with different parents.
87 # In this test, the focus head of the left v-split container is A.
88 # The focused container is B.
89 #
90 # +---+---+ Layout: H1[ V1[ A Y ] V2[ X B ] ]
91 # | A | X | Focus Stacks:
92 # +---+---+ H1: V2, V1
93 # | Y | B | V1: A, Y
94 # +---+---+ V2: B, X
95 ###############################################################################
96 $ws = fresh_workspace;
97
98 $A = open_window(wm_class => 'mark_A');
99 $B = open_window(wm_class => 'mark_B');
100 cmd 'split v';
101 open_window;
102 cmd 'move up, focus left';
103 cmd 'split v';
104 open_window;
105 cmd 'focus up, focus right, focus down';
106 $expected_focus = get_focused($ws);
107
108 cmd '[con_mark=B] swap container with mark A';
109
110 $nodes = get_ws_content($ws);
111 is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
112 is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
113 is(get_focused($ws), $expected_focus, 'B is still focused');
114
115 kill_all_windows;
116
117 ###############################################################################
118 # Swap two containers with different parents.
119 # In this test, the focus head of the left v-split container is _not_ A.
120 # The focused container is B.
121 #
122 # +---+---+ Layout: H1[ V1[ A Y ] V2[ X B ] ]
123 # | A | X | Focus Stacks:
124 # +---+---+ H1: V2, V1
125 # | Y | B | V1: Y, A
126 # +---+---+ V2: B, X
127 ###############################################################################
128 $ws = fresh_workspace;
129
130 $A = open_window(wm_class => 'mark_A');
131 $B = open_window(wm_class => 'mark_B');
132 cmd 'split v';
133 open_window;
134 cmd 'move up, focus left';
135 cmd 'split v';
136 open_window;
137 cmd 'focus right, focus down';
138 $expected_focus = get_focused($ws);
139
140 cmd '[con_mark=B] swap container with mark A';
141
142 $nodes = get_ws_content($ws);
143 is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
144 is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
145 is(get_focused($ws), $expected_focus, 'B is still focused');
146
147 kill_all_windows;
148
149 ###############################################################################
150 # Swap two containers with one being on a different workspace.
151 # The focused container is B.
152 #
153 # Layout: O1[ W1[ H1 ] W2[ H2 ] ]
154 # Focus Stacks:
155 # O1: W2, W1
156 #
157 # +---+---+ Layout: H1[ A X ]
158 # | A | X | Focus Stacks:
159 # +---+---+ H1: A, X
160 #
161 # +---+---+ Layout: H2[ Y, B ]
162 # | Y | B | Focus Stacks:
163 # +---+---+ H2: B, Y
164 ###############################################################################
165 for my $fullscreen (@fullscreen_permutations){
166 $ws1 = fresh_workspace;
167 $A = open_window(wm_class => 'mark_A');
168 $expected_focus = get_focused($ws1);
169 open_window;
170 cmd 'focus left';
171
172 $ws2 = fresh_workspace;
173 open_window;
174 $B = open_window(wm_class => 'mark_B');
175
176 my $A_fullscreen = "A" ~~ @$fullscreen || 0;
177 my $B_fullscreen = "B" ~~ @$fullscreen || 0;
178 $A->fullscreen($A_fullscreen);
179 $B->fullscreen($B_fullscreen);
180 sync_with_i3;
181
182 cmd '[con_mark=B] swap container with mark A';
183
184 $nodes = get_ws_content($ws1);
185 $node = $nodes->[0];
186 is($node->{window}, $B->{id}, 'B is on ws1:left');
187 is_num_fullscreen($ws1, $A_fullscreen, 'amount of fullscreen windows in ws1');
188 is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode');
189
190 $nodes = get_ws_content($ws2);
191 $node = $nodes->[1];
192 is($node->{window}, $A->{id}, 'A is on ws2:right');
193 is(get_focused($ws2), $expected_focus, 'A is focused');
194 is_num_fullscreen($ws2, $B_fullscreen, 'amount of fullscreen windows in ws2');
195 is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode');
196
197 kill_all_windows;
198 }
199
200 ###############################################################################
201 # Swap a non-fullscreen window with a fullscreen one in different workspaces.
202 # Layout: O1[ W1[ H1 ] W2[ B ] ]
203 #
204 # +---+---+ Layout: H1[ A F ]
205 # | A | F | Focus Stacks:
206 # +---+---+ H1: F, A
207 #
208 # +---+---+
209 # | B |
210 # +---+---+
211 ###############################################################################
212 $ws1 = fresh_workspace;
213
214 $A = open_window(wm_class => 'mark_A');
215 $F = open_window();
216 $F->fullscreen(1);
217 $expected_focus = get_focused($ws1);
218
219 $ws2 = fresh_workspace;
220 $B = open_window(wm_class => 'mark_B');
221 $B->fullscreen(1);
222 sync_with_i3;
223
224 cmd '[con_mark=B] swap container with mark A';
225
226 $nodes = get_ws_content($ws1);
227 is($nodes->[0]->{window}, $B->{id}, 'B is on ws1:left');
228 is_num_fullscreen($ws1, 1, 'F still fullscreen in ws1');
229 is(get_focused($ws1), $expected_focus, 'F is still focused');
230
231 $nodes = get_ws_content($ws2);
232 is($nodes->[0]->{window}, $A->{id}, 'A is on ws1');
233
234 ###############################################################################
235 # Try a more exotic layout with fullscreen containers.
236 # A and F are fullscreened as a stack of two vertical containers before the
237 # swap is performed.
238 # A is swapped with fullscreened window B which is in another workspace.
239 #
240 # +---+---+ Layout: H1[ X V1[ A F ] ]
241 # | | A | Focus Stacks:
242 # | X +---+ H1: V1, X
243 # | | F | V1: F, A
244 # +---+---+
245 ###############################################################################
246 $ws1 = fresh_workspace;
247
248 open_window;
249 $A = open_window(wm_class => 'mark_A');
250 cmd "split v";
251 open_window;
252 cmd "focus parent";
253 cmd "fullscreen enable";
254 $expected_focus = get_focused($ws1);
255
256 $ws2 = fresh_workspace;
257 $B = open_window(wm_class => 'mark_B');
258 $B->fullscreen(1);
259 sync_with_i3;
260
261 cmd '[con_mark=B] swap container with mark A';
262
263 sync_with_i3;
264 does_i3_live;
265
266 $nodes = get_ws_content($ws1);
267 is($nodes->[1]->{nodes}->[0]->{window}, $B->{id}, 'B is on top right in ws1');
268 is(get_focused($ws1), $expected_focus, 'The container of the stacked windows remains focused in ws1');
269 is_num_fullscreen($ws1, 1, 'Same amount of fullscreen windows in ws1');
270
271 $nodes = get_ws_content($ws2);
272 is($nodes->[0]->{window}, $A->{id}, 'A is on ws2');
273 is_num_fullscreen($ws2, 1, 'A is in fullscreen mode');
274
275 ###############################################################################
276 # Swap two non-focused containers within the same workspace.
277 #
278 # +---+---+ Layout: H1[ V1[ A X ] V2[ F B ] ]
279 # | A | F | Focus Stacks:
280 # +---+---+ H1: V2, V1
281 # | X | B | V1: A, X
282 # +---+---+ V2: F, B
283 ###############################################################################
284 $ws = fresh_workspace;
285
286 $A = open_window(wm_class => 'mark_A');
287 $B = open_window(wm_class => 'mark_B');
288 cmd 'split v';
289 open_window;
290 cmd 'move up, focus left';
291 cmd 'split v';
292 open_window;
293 cmd 'focus up, focus right';
294 $expected_focus = get_focused($ws);
295
296 cmd '[con_mark=B] swap container with mark A';
297
298 $nodes = get_ws_content($ws);
299 is($nodes->[0]->{nodes}->[0]->{window}, $B->{id}, 'B is on the top left');
300 is($nodes->[1]->{nodes}->[1]->{window}, $A->{id}, 'A is on the bottom right');
301 is(get_focused($ws), $expected_focus, 'F is still focused');
302
303 kill_all_windows;
304
305 ###############################################################################
306 # Swap two non-focused containers which are both on different workspaces.
307 #
308 # Layout: O1[ W1[ A ] W2[ B ] W3[ F ] ]
309 # Focus Stacks:
310 # O1: W3, W2, W1
311 #
312 # +---+
313 # | A |
314 # +---+
315 #
316 # +---+
317 # | B |
318 # +---+
319 #
320 # +---+
321 # | F |
322 # +---+
323 ###############################################################################
324 for my $fullscreen (@fullscreen_permutations){
325 $ws1 = fresh_workspace;
326 $A = open_window(wm_class => 'mark_A');
327
328 $ws2 = fresh_workspace;
329 $B = open_window(wm_class => 'mark_B');
330
331 $ws3 = fresh_workspace;
332 open_window;
333 $expected_focus = get_focused($ws3);
334
335 my $A_fullscreen = "A" ~~ @$fullscreen || 0;
336 my $B_fullscreen = "B" ~~ @$fullscreen || 0;
337 $A->fullscreen($A_fullscreen);
338 $B->fullscreen($B_fullscreen);
339 sync_with_i3;
340
341 cmd '[con_mark=B] swap container with mark A';
342
343 $nodes = get_ws_content($ws1);
344 $node = $nodes->[0];
345 is($node->{window}, $B->{id}, 'B is on the first workspace');
346 is_num_fullscreen($ws1, $A_fullscreen, 'amount of fullscreen windows in ws1');
347 is($node->{fullscreen_mode}, $A_fullscreen, 'B got A\'s fullscreen mode');
348
349 $nodes = get_ws_content($ws2);
350 $node = $nodes->[0];
351 is($node->{window}, $A->{id}, 'A is on the second workspace');
352 is_num_fullscreen($ws2, $B_fullscreen, 'amount of fullscreen windows in ws2');
353 is($node->{fullscreen_mode}, $B_fullscreen, 'A got B\'s fullscreen mode');
354
355 is(get_focused($ws3), $expected_focus, 'F is still focused');
356
357 kill_all_windows;
358 }
359
360 ###############################################################################
361 # Swap two non-focused containers with one being on a different workspace.
362 #
363 # Layout: O1[ W1[ A ] W2[ H2 ] ]
364 # Focus Stacks:
365 # O1: W2, W1
366 #
367 # +---+
368 # | A |
369 # +---+
370 #
371 # +---+---+ Layout: H2[ B, F ]
372 # | B | F | Focus Stacks:
373 # +---+---+ H2: F, B
374 #
375 # See issue: #3259
376 ###############################################################################
377
378 for my $fullscreen (0..1){
379 $ws1 = fresh_workspace;
380 $A = open_window(wm_class => 'mark_A');
381
382 $ws2 = fresh_workspace;
383 $B = open_window(wm_class => 'mark_B');
384 open_window;
385 cmd 'fullscreen enable' if $fullscreen;
386 $expected_focus = get_focused($ws2);
387
388 cmd '[con_mark=B] swap container with mark A';
389
390 $nodes = get_ws_content($ws1);
391 is($nodes->[0]->{window}, $B->{id}, 'B is on the first workspace');
392
393 $nodes = get_ws_content($ws2);
394 is($nodes->[0]->{window}, $A->{id}, 'A is on the left of the second workspace');
395 is(get_focused($ws2), $expected_focus, 'F is still focused');
396
397 kill_all_windows;
398 }
399
400 ###############################################################################
401 # 1. A container cannot be swapped with its parent.
402 # 2. A container cannot be swapped with one of its children.
403 #
404 # ↓A↓
405 # +---+---+ Layout: H1[ X V1[ Y B ] ]
406 # | | Y | (with A := V1)
407 # | X +---+
408 # | | B |
409 # +---+---+
410 ###############################################################################
411 $ws = fresh_workspace;
412 open_window;
413 open_window;
414 cmd 'split v';
415 $B = open_window(wm_class => 'mark_B');
416 cmd 'focus parent, mark A, focus child';
417
418 $result = cmd '[con_mark=B] swap container with mark A';
419 is($result->[0]->{success}, 0, 'B cannot be swappd with its parent');
420
421 $result = cmd '[con_mark=A] swap container with mark B';
422 is($result->[0]->{success}, 0, 'A cannot be swappd with one of its children');
423
424 kill_all_windows;
425
426 ###############################################################################
427 # Swapping two containers preserves the geometry of the container they are
428 # being swapped with.
429 #
430 # Before:
431 # +---+-------+
432 # | A | B |
433 # +---+-------+
434 #
435 # After:
436 # +---+-------+
437 # | B | A |
438 # +---+-------+
439 ###############################################################################
440 $ws = fresh_workspace;
441 $A = open_window(wm_class => 'mark_A');
442 $B = open_window(wm_class => 'mark_B');
443 cmd 'resize grow width 0 or 25 ppt';
444
445 # sanity checks
446 $nodes = get_ws_content($ws);
447 cmp_float($nodes->[0]->{percent}, 0.25, 'A has 25% width');
448 cmp_float($nodes->[1]->{percent}, 0.75, 'B has 75% width');
449
450 cmd '[con_mark=B] swap container with mark A';
451
452 $nodes = get_ws_content($ws);
453 cmp_float($nodes->[0]->{percent}, 0.25, 'B has 25% width');
454 cmp_float($nodes->[1]->{percent}, 0.75, 'A has 75% width');
455
456 kill_all_windows;
457
458 ###############################################################################
459 # Swapping containers not sharing the same parent preserves the geometry of
460 # the container they are swapped with.
461 #
462 # Before:
463 # +---+-----+
464 # | A | |
465 # +---+ B |
466 # | | |
467 # | Y +-----+
468 # | | X |
469 # +---+-----+
470 #
471 # After:
472 # +---+-----+
473 # | B | |
474 # +---+ A |
475 # | | |
476 # | Y +-----+
477 # | | X |
478 # +---+-----+
479 ###############################################################################
480 $ws = fresh_workspace;
481
482 $A = open_window(wm_class => 'mark_A');
483 $B = open_window(wm_class => 'mark_B');
484 cmd 'split v';
485 open_window;
486 cmd 'focus up, resize grow height 0 or 25 ppt';
487 cmd 'focus left, split v';
488 open_window;
489 cmd 'resize grow height 0 or 25 ppt';
490
491 # sanity checks
492 $nodes = get_ws_content($ws);
493 cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'A has 25% height');
494 cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'B has 75% height');
495
496 cmd '[con_mark=B] swap container with mark A';
497
498 $nodes = get_ws_content($ws);
499 cmp_float($nodes->[0]->{nodes}->[0]->{percent}, 0.25, 'B has 25% height');
500 cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.75, 'A has 75% height');
501
502 kill_all_windows;
503
504 ###############################################################################
505 # Swapping containers moves the urgency hint correctly.
506 ###############################################################################
507
508 $ws1 = fresh_workspace;
509 $A = open_window(wm_class => 'mark_A');
510 $ws2 = fresh_workspace;
511 $B = open_window(wm_class => 'mark_B');
512 open_window;
513
514 $B->add_hint('urgency');
515 sync_with_i3;
516
517 cmd '[con_mark=B] swap container with mark A';
518
519 @urgent = grep { $_->{urgent} } @{get_ws_content($ws1)};
520 is(@urgent, 1, 'B is marked urgent');
521 is(get_ws($ws1)->{urgent}, 1, 'the first workspace is marked urgent');
522
523 @urgent = grep { $_->{urgent} } @{get_ws_content($ws2)};
524 is(@urgent, 0, 'A is not marked urgent');
525 is(get_ws($ws2)->{urgent}, 0, 'the second workspace is not marked urgent');
526
527 kill_all_windows;
528
529 ###############################################################################
530
531 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: verify layout toggle with invalid parameters does not set
17 # layout to L_DEFAULT, which crashes i3 upon the next IPC message.
18 # Ticket: #2903
19 # Bug still in: 4.14-87-g607e97e6
20 use i3test;
21
22 cmd 'layout toggle 1337 1337';
23
24 fresh_workspace;
25
26 does_i3_live;
27
28 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the focus_follows_mouse setting.
17 use i3test i3_config => <<EOT;
18 # i3 config file (v4)
19 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
20
21 fake-outputs 1000x1000+0+0
22 EOT
23
24 my ($first, $second);
25
26 sub synced_warp_pointer {
27 my ($x_px, $y_px) = @_;
28 sync_with_i3;
29 $x->root->warp_pointer($x_px, $y_px);
30 sync_with_i3;
31 }
32
33 ###################################################################
34 # Test a simple case with 2 windows.
35 ###################################################################
36
37 synced_warp_pointer(600, 600);
38 $first = open_window;
39 $second = open_window;
40 is($x->input_focus, $second->id, 'second window focused');
41
42 synced_warp_pointer(0, 0);
43 is($x->input_focus, $first->id, 'first window focused');
44
45 ###################################################################
46 # Test that focus isn't changed with tabbed windows.
47 ###################################################################
48
49 fresh_workspace;
50 synced_warp_pointer(600, 600);
51 $first = open_window;
52 cmd 'layout tabbed';
53 $second = open_window;
54 is($x->input_focus, $second->id, 'second (tabbed) window focused');
55
56 synced_warp_pointer(0, 0);
57 is($x->input_focus, $second->id, 'second window still focused');
58
59 ###################################################################
60 # Test that floating windows are focused but not raised to the top.
61 # See issue #2990.
62 ###################################################################
63
64 my $ws;
65 my $tmp = fresh_workspace;
66 my ($first_floating, $second_floating);
67
68 synced_warp_pointer(0, 0);
69 $first_floating = open_floating_window(rect => [ 1, 1, 100, 100 ]);
70 $second_floating = open_floating_window(rect => [ 50, 50, 100, 100 ]);
71 $first = open_window;
72
73 is($x->input_focus, $first->id, 'first (tiling) window focused');
74 $ws = get_ws($tmp);
75 is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $second_floating->id, 'second floating on top');
76 is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first_floating->id, 'first floating behind');
77
78 synced_warp_pointer(40, 40);
79 is($x->input_focus, $first_floating->id, 'first floating window focused');
80 $ws = get_ws($tmp);
81 is($ws->{floating_nodes}->[1]->{nodes}->[0]->{window}, $second_floating->id, 'second floating still on top');
82 is($ws->{floating_nodes}->[0]->{nodes}->[0]->{window}, $first_floating->id, 'first floating still behind');
83
84 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that i3 does not crash when opening a floating sticky on one output
17 # and then switching empty workspaces on the other output.
18 # Ticket: #3075
19 # Bug still in: 4.14-191-g9d2d602d
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24 EOT
25
26 # A window on the left output.
27 fresh_workspace(output => 0);
28 open_window;
29 cmd 'sticky enable, floating enable';
30
31 # Switch to the right output and open a new workspace.
32 my $ws = fresh_workspace(output => 1);
33 does_i3_live;
34
35 # Verify results.
36 is(@{get_ws($ws)->{floating_nodes}}, 0, 'workspace in right output is empty');
37 $ws = fresh_workspace(output => 0);
38 is(@{get_ws($ws)->{floating_nodes}}, 1, 'new workspace in left output has the sticky container');
39
40 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verify that the corrent focus stack order is preserved after various
17 # operations.
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21 fake-outputs 1024x768+0+0,1024x768+1024+0
22 EOT
23
24 sub kill_and_confirm_focus {
25 my $focus = shift;
26 my $msg = shift;
27 cmd "kill";
28 sync_with_i3;
29 is($x->input_focus, $focus, $msg);
30 }
31
32 my @windows;
33 my $ws;
34
35 sub focus_windows {
36 for (my $i = $#windows; $i >= 0; $i--) {
37 cmd '[id=' . $windows[$i]->id . '] focus';
38 }
39 }
40
41 sub confirm_focus {
42 my $msg = shift;
43 sync_with_i3;
44 is($x->input_focus, $windows[0]->id, $msg . ': window 0 focused');
45 foreach my $i (1 .. $#windows) {
46 kill_and_confirm_focus($windows[$i]->id, "$msg: window $i focused");
47 }
48 cmd 'kill';
49 @windows = ();
50 }
51
52 #####################################################################
53 # Open 5 windows, focus them in a custom order and then change to
54 # tabbed layout. The focus order should be preserved.
55 #####################################################################
56
57 fresh_workspace;
58
59 $windows[3] = open_window;
60 $windows[1] = open_window;
61 $windows[0] = open_window;
62 $windows[2] = open_window;
63 $windows[4] = open_window;
64 focus_windows;
65
66 cmd 'layout tabbed';
67 confirm_focus('tabbed');
68
69 #####################################################################
70 # Same as above but with stacked.
71 #####################################################################
72
73 fresh_workspace;
74 $windows[3] = open_window;
75 $windows[1] = open_window;
76 $windows[0] = open_window;
77 $windows[2] = open_window;
78 $windows[4] = open_window;
79 focus_windows;
80
81 cmd 'layout stacked';
82 confirm_focus('stacked');
83
84 #####################################################################
85 # Open 4 windows horizontally, move the last one down. The focus
86 # order should be preserved.
87 #####################################################################
88
89 fresh_workspace;
90 $windows[3] = open_window;
91 $windows[2] = open_window;
92 $windows[1] = open_window;
93 $windows[0] = open_window;
94
95 cmd 'move down';
96 confirm_focus('split-h + move');
97
98 #####################################################################
99 # Same as above but with a vertical split.
100 #####################################################################
101
102 fresh_workspace;
103 $windows[3] = open_window;
104 cmd 'split v';
105 $windows[2] = open_window;
106 $windows[1] = open_window;
107 $windows[0] = open_window;
108
109 cmd 'move left';
110 confirm_focus('split-v + move');
111
112 #####################################################################
113 # Test that moving an unfocused container from another output
114 # maintains the correct focus order.
115 #####################################################################
116
117 fresh_workspace(output => 0);
118 $windows[3] = open_window;
119 fresh_workspace(output => 1);
120 $windows[2] = open_window;
121 $windows[1] = open_window;
122 $windows[0] = open_window;
123
124 cmd '[id=' . $windows[3]->id . '] move right';
125 confirm_focus('unfocused move from other output');
126
127 #####################################################################
128 # Test that moving an unfocused container inside its original parent
129 # maintains the correct focus order.
130 #####################################################################
131
132 fresh_workspace;
133 $windows[0] = open_window;
134 $windows[1] = open_window;
135 cmd 'split v';
136 $windows[2] = open_window;
137 $windows[3] = open_window;
138 focus_windows;
139
140 cmd '[id=' . $windows[2]->id . '] move up';
141 confirm_focus('split-v + unfocused move inside parent');
142
143 ######################################################################
144 # Test that moving an unfocused container maintains the correct focus
145 # order.
146 # Layout: H [ A V1 [ B C D ] ]
147 ######################################################################
148
149 fresh_workspace;
150 $windows[3] = open_window;
151 $windows[2] = open_window;
152 cmd 'split v';
153 $windows[1] = open_window;
154 $windows[0] = open_window;
155
156 cmd '[id=' . $windows[3]->id . '] move right';
157 confirm_focus('split-v + unfocused move');
158
159 ######################################################################
160 # Test that moving an unfocused container from inside a split
161 # container to another workspace doesn't focus sibling.
162 ######################################################################
163
164 $ws = fresh_workspace;
165 $windows[0] = open_window;
166 $windows[1] = open_window;
167 cmd 'split v';
168 open_window;
169 cmd 'mark a';
170
171 cmd '[id=' . $windows[0]->id . '] focus';
172 cmd '[con_mark=a] move to workspace ' . get_unused_workspace;
173
174 is(@{get_ws_content($ws)}, 2, 'Sanity check: marked window moved');
175 confirm_focus('Move unfocused window from split container');
176
177 ######################################################################
178 # Moving containers to another workspace puts them on the top of the
179 # focus stack but behind the focused container.
180 ######################################################################
181
182 for my $new_workspace (0 .. 1) {
183 fresh_workspace;
184 $windows[2] = open_window;
185 $windows[1] = open_window;
186 fresh_workspace if $new_workspace;
187 $windows[3] = open_window;
188 $windows[0] = open_window;
189 cmd 'mark target';
190
191 cmd '[id=' . $windows[2]->id . '] move to mark target';
192 cmd '[id=' . $windows[1]->id . '] move to mark target';
193 confirm_focus('\'move to mark\' focus order' . ($new_workspace ? ' when moving containers from other workspace' : ''));
194 }
195
196 ######################################################################
197 # Same but with workspace commands.
198 ######################################################################
199
200 fresh_workspace;
201 $windows[2] = open_window;
202 $windows[1] = open_window;
203 $ws = fresh_workspace;
204 $windows[3] = open_window;
205 $windows[0] = open_window;
206 cmd 'mark target';
207
208 cmd '[id=' . $windows[2]->id . '] move to workspace ' . $ws;
209 cmd '[id=' . $windows[1]->id . '] move to workspace ' . $ws;
210 confirm_focus('\'move to workspace\' focus order when moving containers from other workspace');
211
212 ######################################################################
213 # Test focus order with floating and tiling windows.
214 # See issue: 1975
215 ######################################################################
216
217 fresh_workspace;
218 $windows[2] = open_window;
219 $windows[0] = open_window;
220 $windows[3] = open_floating_window;
221 $windows[1] = open_floating_window;
222 focus_windows;
223
224 confirm_focus('mix of floating and tiling windows');
225
226 ######################################################################
227 # Same but an unfocused tiling window is killed first.
228 ######################################################################
229
230 fresh_workspace;
231 $windows[2] = open_window;
232 $windows[0] = open_window;
233 $windows[3] = open_floating_window;
234 $windows[1] = open_floating_window;
235 focus_windows;
236
237 cmd '[id=' . $windows[1]->id . '] focus';
238 cmd '[id=' . $windows[0]->id . '] kill';
239
240 kill_and_confirm_focus($windows[2]->id, 'window 2 focused after tiling killed');
241 kill_and_confirm_focus($windows[3]->id, 'window 3 focused after tiling killed');
242
243 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies _NET_DESKTOP_NAMES, _NET_CURRENT_DESKTOP and _NET_CURRENT_DESKTOP
17 # are updated properly when closing an inactive workspace container.
18 # See github issue #3126
19
20 use i3test;
21
22 sub get_desktop_names {
23 sync_with_i3;
24
25 my $cookie = $x->get_property(
26 0,
27 $x->get_root_window(),
28 $x->atom(name => '_NET_DESKTOP_NAMES')->id,
29 $x->atom(name => 'UTF8_STRING')->id,
30 0,
31 4096,
32 );
33
34 my $reply = $x->get_property_reply($cookie->{sequence});
35
36 return 0 if $reply->{value_len} == 0;
37
38 # the property is a null-delimited list of utf8 strings ;;
39 return split /\0/, $reply->{value};
40 }
41
42 sub get_num_of_desktops {
43 sync_with_i3;
44
45 my $cookie = $x->get_property(
46 0,
47 $x->get_root_window(),
48 $x->atom(name => '_NET_NUMBER_OF_DESKTOPS')->id,
49 $x->atom(name => 'CARDINAL')->id,
50 0,
51 4,
52 );
53
54 my $reply = $x->get_property_reply($cookie->{sequence});
55
56 return undef if $reply->{value_len} != 1;
57 return undef if $reply->{format} != 32;
58 return undef if $reply->{type} != $x->atom(name => 'CARDINAL')->id,;
59
60 return unpack 'L', $reply->{value};
61 }
62
63 sub get_current_desktop {
64 sync_with_i3;
65
66 my $cookie = $x->get_property(
67 0,
68 $x->get_root_window(),
69 $x->atom(name => '_NET_CURRENT_DESKTOP')->id,
70 $x->atom(name => 'CARDINAL')->id,
71 0,
72 4,
73 );
74
75 my $reply = $x->get_property_reply($cookie->{sequence});
76
77 return undef if $reply->{value_len} != 1;
78 return undef if $reply->{format} != 32;
79 return undef if $reply->{type} != $x->atom(name => 'CARDINAL')->id,;
80
81 return unpack 'L', $reply->{value};
82 }
83
84 cmd 'workspace 0';
85 my $first = open_window;
86
87 cmd 'workspace 1';
88 my $second = open_window;
89
90 cmd 'workspace 2';
91 my $third = open_window;
92
93 # Sanity check
94 is(get_current_desktop, 2);
95 is(get_num_of_desktops, 3);
96 my @actual_names = get_desktop_names;
97 my @expected_names = ('0', '1', '2');
98 is_deeply(\@actual_names, \@expected_names);
99
100 # Kill first window to close a workspace.
101 cmd '[id="' . $second->id . '"] kill';
102
103 is(get_current_desktop, 2, '_NET_CURRENT_DESKTOP should be updated');
104 is(get_num_of_desktops, 2, '_NET_NUMBER_OF_DESKTOPS should be updated');
105 my @actual_names = get_desktop_names;
106 my @expected_names = ('0', '2');
107 is_deeply(\@actual_names, \@expected_names, '_NET_DESKTOP_NAMES should be updated');
108
109
110 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • http://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • http://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • http://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for setting and removing the _NET_WM_STATE_FOCUSED atom properly.
17 # Ticket: #2273
18 use i3test;
19 use X11::XCB qw(:all);
20
21 my ($windowA, $windowB);
22
23 fresh_workspace;
24 $windowA = open_window;
25 ok(is_net_wm_state_focused($windowA), 'a newly opened window that is focused should have _NET_WM_STATE_FOCUSED set');
26
27 $windowB = open_window;
28 ok(!is_net_wm_state_focused($windowA), 'when a another window is focused, the old window should not have _NET_WM_STATE_FOCUSED set');
29 ok(is_net_wm_state_focused($windowB), 'a newly opened window that is focused should have _NET_WM_STATE_FOCUSED set');
30
31 # See issue #3495.
32 cmd 'kill';
33 ok(is_net_wm_state_focused($windowA), 'when the second window is closed, the first window should have _NET_WM_STATE_FOCUSED set');
34
35 fresh_workspace;
36 ok(!is_net_wm_state_focused($windowA), 'when focus moves to the ewmh support window, no window should have _NET_WM_STATE_FOCUSED set');
37
38 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that directional focus gives focus to floating fullscreen containers when
17 # switching workspaces.
18 # Ticket: #3201
19 # Bug still in: 4.15-59-gb849fe3e
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25 EOT
26
27 fresh_workspace(output => 0);
28 my $ws = fresh_workspace(output => 1);
29 open_window;
30 open_floating_window;
31 cmd 'fullscreen enable';
32 my $expected_focus = get_focused($ws);
33
34 cmd 'focus left';
35 cmd 'focus right';
36
37 is (get_focused($ws), $expected_focus, 'floating fullscreen window focused after directional focus');
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test assignments of workspaces to outputs.
17 use i3test i3_autostart => 0;
18
19 ################################################################################
20 # Test initial workspaces.
21 ################################################################################
22
23 my $config = <<EOT;
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25
26 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
27
28 bindsym Mod1+x workspace bindingname
29
30 workspace 9 output doesnotexist
31 workspace special output fake-0
32 workspace 1 output doesnotexist
33 workspace dontusethisname output doesnotexist
34 workspace donotoverride output fake-0
35 workspace 2 output fake-0
36 workspace 3 output fake-0
37 EOT
38
39 my $pid = launch_with_config($config);
40
41 sub check_output {
42 my ($workspace, $output, $msg) = @_;
43 is(get_output_for_workspace($workspace), $output, $msg);
44 }
45
46 check_output('9', '', 'Numbered workspace with a big number that is assigned to output that does not exist is not used');
47 check_output('special', 'fake-0', 'Output gets special workspace because of assignment');
48 check_output('bindingname', 'fake-1', 'Bindings give workspace names');
49 check_output('1', 'fake-2', 'Numbered workspace that is assigned to output that does not exist is used');
50 check_output('2', '', 'Numbered workspace assigned to output with existing workspace is not used');
51 check_output('3', '', 'Numbered workspace assigned to output with existing workspace is not used');
52 check_output('4', 'fake-3', 'First available number that is not assigned to existing output is used');
53 check_output('dontusethisname', '', 'Named workspace that is assigned to output that does not exist is not used');
54 check_output('donotoverride', '', 'Named workspace assigned to already occupied output is not used');
55
56 exit_gracefully($pid);
57
58 ################################################################################
59 # Test multiple assignments
60 ################################################################################
61
62 sub do_test {
63 my ($workspace, $output, $msg) = @_;
64 cmd 'focus output ' . ($output eq 'fake-3' ? 'fake-0' : 'fake-3');
65
66 cmd "workspace $workspace";
67 check_output($workspace, $output, $msg)
68 }
69
70 $config = <<EOT;
71 # i3 config file (v4)
72 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
73
74 fake-outputs 1024x768+0+0P,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
75
76 workspace 1 output fake-0
77 workspace 1 output fake-1 fake-2
78 workspace 2 output fake-3 fake-4 fake-0 fake-1
79 workspace 3 output these outputs do not exist but these do: fake-0 fake-3
80 workspace 4 output whitespace fake-0
81 workspace special output doesnotexist1 doesnotexist2 doesnotexist3
82 EOT
83
84 $pid = launch_with_config($config);
85
86 do_test('1', 'fake-0', 'Multiple assignments do not override a single one');
87 do_test('2', 'fake-3', 'First output out of multiple assignments is used');
88 do_test('3', 'fake-0', 'First existing output is used');
89 do_test('4', 'fake-0', 'Excessive whitespace is ok');
90 do_test('5', 'fake-1', 'Numbered initialization for fake-1');
91 do_test('6', 'fake-2', 'Numbered initialization for fake-2');
92
93 cmd 'focus output fake-0, workspace special';
94 check_output('special', 'fake-0', 'Workspace with only non-existing assigned outputs opened in current output.');
95
96 # Moving assigned workspaces.
97 cmd 'workspace 2, move workspace to output left';
98 check_output('2', 'fake-2', 'Moved assigned workspace up');
99
100 exit_gracefully($pid);
101 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if scrolling the tab bar on a tabbed container works and verifies that
17 # only one window is focused as a result.
18 # Ticket: #3215 (PR)
19 # Bug still in: 4.15-92-g666aa9e0
20 use i3test;
21 use i3test::XTEST;
22
23 sub scroll_down {
24 # button5 = scroll down
25 xtest_button_press(5, 3, 3);
26 xtest_button_release(5, 3, 3);
27 xtest_sync_with_i3;
28 }
29
30 sub scroll_up {
31 # button4 = scroll up
32 xtest_button_press(4, 3, 3);
33 xtest_button_release(4, 3, 3);
34 xtest_sync_with_i3;
35 }
36
37 # Decoration of top left window.
38 $x->root->warp_pointer(3, 3);
39
40 # H [ T [ H [ A B ] C D V [ E F ] ] G ]
41 # Inner horizontal split.
42 open_window;
43 cmd 'layout tabbed';
44 cmd 'splith';
45 my $first = open_window;
46 cmd 'focus parent';
47 # Simple tabs.
48 open_window;
49 my $second_last = open_window;
50 # V-Split container
51 open_window;
52 cmd 'splitv';
53 my $last = open_window;
54 # Second child of the outer horizontal split, next to the tabbed one.
55 my $outside = open_window;
56 cmd 'move right, move right';
57
58 cmd '[id=' . $first->id . '] focus';
59
60 # Scroll from first to last.
61 scroll_down;
62 scroll_down;
63 is($x->input_focus, $second_last->id, 'Sanity check: scrolling');
64 scroll_down;
65 is($x->input_focus, $last->id, 'Last window focused through scrolling');
66 scroll_down;
67 is($x->input_focus, $last->id, 'Scrolling again doesn\'t leave the tabbed container and doesn\'t focus the whole sibling');
68
69 # Scroll from last to first.
70 scroll_up;
71 is($x->input_focus, $second_last->id, 'Scrolling up works');
72 scroll_up;
73 scroll_up;
74 is($x->input_focus, $first->id, 'First window focused through scrolling');
75 scroll_up;
76 is($x->input_focus, $first->id, 'Scrolling again doesn\'t focus the whole sibling');
77
78 # Try scrolling with another window focused
79 cmd '[id=' . $outside->id . '] focus';
80 scroll_up;
81 is($x->input_focus, $first->id, 'Scrolling from outside the tabbed container works');
82
83 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that i3 will not hang if a connected client stops reading from its
17 # subscription socket and that the client is killed after a delay.
18 # Ticket: #2999
19 # Bug still in: 4.15-180-g715cea61
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23 # Set the timeout to 500ms to reduce the duration of this test.
24 ipc_kill_timeout 500
25 EOT
26
27 # Manually connect to i3 so that we can choose to not read events
28 use IO::Socket::UNIX;
29 my $sock = IO::Socket::UNIX->new(Peer => get_socket_path());
30 my $magic = "i3-ipc";
31 my $payload = '["workspace"]';
32 my $message = $magic . pack("LL", length($payload), 2) . $payload;
33 print $sock $message;
34
35 # Constantly switch between 2 workspaces to generate events.
36 fresh_workspace;
37 open_window;
38 fresh_workspace;
39 open_window;
40
41 eval {
42 local $SIG{ALRM} = sub { die "Timeout\n" };
43 # 500 is an arbitrarily large number to make sure that the socket becomes
44 # non-writeable.
45 for (my $i = 0; $i < 500; $i++) {
46 alarm 1;
47 cmd 'workspace back_and_forth';
48 alarm 0;
49 }
50 };
51 ok(!$@, 'i3 didn\'t hang');
52
53 # Wait for connection timeout
54 sleep 1;
55
56 use IO::Select;
57 my $s = IO::Select->new($sock);
58 my $reached_eof = 0;
59 while ($s->can_read(0.05)) {
60 if (read($sock, my $buffer, 100) == 0) {
61 $reached_eof = 1;
62 last;
63 }
64 }
65 ok($reached_eof, 'socket connection closed');
66
67 close $sock;
68 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression test: verify that a scratchpad container that was open in another
17 # workspace and is moved to the current workspace after a 'scratchpad show' is
18 # focused.
19 # Ticket: #3361
20 # Bug still in: 4.15-190-g4b3ff9cd
21 use i3test;
22
23 my $expected_focus = open_window;
24 cmd 'move to scratchpad';
25 cmd 'scratchpad show';
26 my $ws = fresh_workspace;
27 open_window;
28 cmd 'scratchpad show';
29 sync_with_i3;
30 is($x->input_focus, $expected_focus->id, 'scratchpad window brought from other workspace is focused');
31
32 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verify that i3 does not crash when restart is issued while a window with a
17 # title that contains non-UTF8 characters is open.
18 # Ticket: #3156
19 # Bug still in: 4.15-241-g9dc4df81
20 use i3test;
21
22 my $ws = fresh_workspace;
23 open_window(name => "\x{AA} <-- invalid");
24
25 cmd 'restart';
26 does_i3_live;
27
28 # Confirm that the invalid character got replaced with U+FFFD - "REPLACEMENT
29 # CHARACTER"
30 cmd '[title="^' . "\x{fffd}" . ' <-- invalid$"] fullscreen enable';
31 is_num_fullscreen($ws, 1, 'title based criterion works');
32
33 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Regression: moving a container which is the only child of the only child of a
17 # floating container crashes i3.
18 # Ticket: #3556
19 # Bug still in: 4.16-61-g376833db4
20 use i3test;
21
22 my $ws = fresh_workspace;
23 open_window;
24 open_window;
25 cmd 'split v, focus parent, floating toggle, focus child, move right';
26 does_i3_live;
27
28 $ws = get_ws($ws);
29 is(scalar @{$ws->{floating_nodes}}, 0, 'No floating nodes in workspace');
30 is(scalar @{$ws->{nodes}}, 2, 'Two tiling nodes in workspace');
31
32 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the provided X-Server to the t/5??-*.t tests is actually providing
17 # multiple monitors.
18 #
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24 EOT
25
26 my $i3 = i3(get_socket_path());
27
28 ####################
29 # Request tree
30 ####################
31
32 my $tree = $i3->get_tree->recv;
33
34 my @outputs = map { $_->{name} } @{$tree->{nodes}};
35 is_deeply(\@outputs, [ '__i3', 'fake-0', 'fake-1' ],
36 'multi-monitor outputs ok');
37
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that scratchpad windows show up on the proper output.
17 # ticket #596, bug present until up to commit
18 # 89dded044b4fffe78f9d70778748fabb7ac533e9.
19 #
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25 EOT
26
27 ################################################################################
28 # Open a workspace on the second output, put a window to scratchpad, display
29 # it, verify it’s on the same workspace.
30 ################################################################################
31
32 sub verify_scratchpad_on_same_ws {
33 my ($ws) = @_;
34
35 is_num_children($ws, 0, 'no nodes on this ws');
36
37 my $window = open_window;
38
39 is_num_children($ws, 1, 'one nodes on this ws');
40
41 cmd 'move scratchpad';
42
43 is_num_children($ws, 0, 'no nodes on this ws');
44
45 cmd 'scratchpad show';
46 is_num_children($ws, 0, 'no nodes on this ws');
47 is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on this ws');
48 }
49
50 my $second = fresh_workspace(output => 1);
51
52 verify_scratchpad_on_same_ws($second);
53
54 ################################################################################
55 # The same thing, but on the first output.
56 ################################################################################
57
58 my $first = fresh_workspace(output => 0);
59
60 verify_scratchpad_on_same_ws($first);
61
62 ################################################################################
63 # Now open the scratchpad on one output and switch to another.
64 ################################################################################
65
66 sub verify_scratchpad_switch {
67 my ($first, $second) = @_;
68
69 cmd "workspace $first";
70
71 is_num_children($first, 0, 'no nodes on this ws');
72
73 my $window = open_window;
74
75 is_num_children($first, 1, 'one nodes on this ws');
76
77 cmd 'move scratchpad';
78
79 is_num_children($first, 0, 'no nodes on this ws');
80
81 cmd "workspace $second";
82
83 cmd 'scratchpad show';
84 my $ws = get_ws($second);
85 is_num_children($second, 0, 'no nodes on this ws');
86 is(scalar @{$ws->{floating_nodes}}, 1, 'one floating node on this ws');
87
88 # Verify that the coordinates are within bounds.
89 my $srect = $ws->{floating_nodes}->[0]->{rect};
90 my $rect = $ws->{rect};
91 cmd 'nop before bounds check';
92 cmp_ok($srect->{x}, '>=', $rect->{x}, 'x within bounds');
93 cmp_ok($srect->{y}, '>=', $rect->{y}, 'y within bounds');
94 cmp_ok($srect->{x} + $srect->{width}, '<=', $rect->{x} + $rect->{width},
95 'width within bounds');
96 cmp_ok($srect->{y} + $srect->{height}, '<=', $rect->{y} + $rect->{height},
97 'height within bounds');
98 }
99
100 $first = fresh_workspace(output => 0);
101 $second = fresh_workspace(output => 1);
102
103 verify_scratchpad_switch($first, $second);
104
105 $first = fresh_workspace(output => 1);
106 $second = fresh_workspace(output => 0);
107
108 verify_scratchpad_switch($first, $second);
109
110 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies the 'focus output' command works properly.
17
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 fake-outputs 1024x768+0+0,1024x768+1024+0
23 EOT
24 use List::Util qw(first);
25
26 my $tmp = fresh_workspace;
27 my $i3 = i3(get_socket_path());
28
29 ################################################################################
30 # use 'focus output' and verify that focus gets changed appropriately
31 ################################################################################
32
33 sub focused_output {
34 my $tree = $i3->get_tree->recv;
35 my $focused = $tree->{focus}->[0];
36 my $output = first { $_->{id} == $focused } @{$tree->{nodes}};
37 return $output->{name};
38 }
39
40 sync_with_i3;
41 $x->root->warp_pointer(0, 0);
42 sync_with_i3;
43
44 is(focused_output, 'fake-0', 'focus on first output');
45
46 cmd 'focus output right';
47 is(focused_output, 'fake-1', 'focus on second output');
48
49 # focus should wrap when we focus to the right again.
50 cmd 'focus output right';
51 is(focused_output, 'fake-0', 'focus on first output again');
52
53 cmd 'focus output left';
54 is(focused_output, 'fake-1', 'focus back on second output');
55
56 cmd 'focus output left';
57 is(focused_output, 'fake-0', 'focus on first output again');
58
59 cmd 'focus output up';
60 is(focused_output, 'fake-0', 'focus still on first output');
61
62 cmd 'focus output down';
63 is(focused_output, 'fake-0', 'focus still on first output');
64
65 cmd 'focus output fake-1';
66 is(focused_output, 'fake-1', 'focus on second output');
67
68 cmd 'focus output fake-0';
69 is(focused_output, 'fake-0', 'focus on first output');
70
71 ################################################################################
72 # use 'focus output' and verify that i3 does not crash when the currently
73 # focused window is floating and is only partially mapped on an output screen
74 ################################################################################
75
76 is(focused_output, 'fake-0', 'focus on first output');
77
78 my $floating_win = open_window;
79 cmd 'floating toggle';
80 cmd 'move to absolute position -10 -10';
81
82 cmd 'focus output right';
83 is(focused_output, 'fake-1', 'focus on second output');
84
85 cmd 'focus output fake-0';
86 is(focused_output, 'fake-0', 'focus on first output');
87
88 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether 'workspace next_on_output' and the like work correctly.
17 #
18 use List::Util qw(first);
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24 EOT
25
26 ################################################################################
27 # Setup workspaces so that they stay open (with an empty container).
28 ################################################################################
29
30 sync_with_i3;
31 $x->root->warp_pointer(0, 0);
32 sync_with_i3;
33
34 is(focused_ws, '1', 'starting on workspace 1');
35 # ensure workspace 1 stays open
36 open_window;
37
38 cmd 'focus output right';
39 is(focused_ws, '2', 'workspace 2 on second output');
40 # ensure workspace 2 stays open
41 open_window;
42
43 cmd 'focus output right';
44 is(focused_ws, '1', 'back on workspace 1');
45
46 # We don’t use fresh_workspace with named workspaces here since they come last
47 # when using 'workspace next'.
48 cmd 'workspace 5';
49 # ensure workspace 5 stays open
50 open_window;
51
52 ################################################################################
53 # Use workspace next and verify the correct order.
54 ################################################################################
55
56 # The current order should be:
57 # output 1: 1, 5
58 # output 2: 2
59 cmd 'workspace 1';
60 cmd 'workspace next';
61 # We need to sync after changing focus to a different output to wait for the
62 # EnterNotify to be processed, otherwise it will be processed at some point
63 # later in time and mess up our subsequent tests.
64 sync_with_i3;
65
66 is(focused_ws, '2', 'workspace 2 focused');
67 cmd 'workspace next';
68 # We need to sync after changing focus to a different output to wait for the
69 # EnterNotify to be processed, otherwise it will be processed at some point
70 # later in time and mess up our subsequent tests.
71 sync_with_i3;
72
73 is(focused_ws, '5', 'workspace 5 focused');
74
75 ################################################################################
76 # Now try the same with workspace next_on_output.
77 ################################################################################
78
79 cmd 'workspace 1';
80 cmd 'workspace next_on_output';
81 is(focused_ws, '5', 'workspace 5 focused');
82 cmd 'workspace next_on_output';
83 is(focused_ws, '1', 'workspace 1 focused');
84
85 cmd 'workspace prev_on_output';
86 is(focused_ws, '5', 'workspace 5 focused');
87 cmd 'workspace prev_on_output';
88 is(focused_ws, '1', 'workspace 1 focused');
89
90 cmd 'workspace 2';
91 # We need to sync after changing focus to a different output to wait for the
92 # EnterNotify to be processed, otherwise it will be processed at some point
93 # later in time and mess up our subsequent tests.
94 sync_with_i3;
95
96 cmd 'workspace prev_on_output';
97 is(focused_ws, '2', 'workspace 2 focused');
98
99 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether the 'move workspace <ws> to [output] <output>' command works
17 #
18 use List::Util qw(first);
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24
25 bar {
26 # Disable i3bar.
27 i3bar_command :
28 }
29 EOT
30
31 # TODO: get rid of smartmatch in this test
32
33 ################################################################################
34 # Setup workspaces so that they stay open (with an empty container).
35 ################################################################################
36
37 is(focused_ws, '1', 'starting on workspace 1');
38 # ensure workspace 1 stays open
39 open_window;
40
41 cmd 'focus output right';
42 is(focused_ws, '2', 'workspace 2 on second output');
43 # ensure workspace 2 stays open
44 open_window;
45
46 cmd 'focus output right';
47 is(focused_ws, '1', 'back on workspace 1');
48
49 # We don’t use fresh_workspace with named workspaces here since they come last
50 # when using 'workspace next'.
51 cmd 'workspace 5';
52 # ensure workspace 5 stays open
53 open_window;
54
55 ################################################################################
56 # Move a workspace over and verify that it is on the right output.
57 ################################################################################
58
59 # The current order should be:
60 # output 1: 1, 5
61 # output 2: 2
62 cmd 'workspace 5';
63 is(focused_ws, '5', 'workspace 5 focused');
64
65 my ($x0, $x1) = workspaces_per_screen();
66 ok('5' ~~ @$x0, 'workspace 5 on fake-0');
67
68 cmd 'move workspace to output fake-1';
69
70 sub workspaces_per_screen {
71 my $i3 = i3(get_socket_path());
72 my $tree = $i3->get_tree->recv;
73 my @outputs = @{$tree->{nodes}};
74
75 my $fake0 = first { $_->{name} eq 'fake-0' } @outputs;
76 my $fake0_content = first { $_->{type} eq 'con' } @{$fake0->{nodes}};
77
78 my $fake1 = first { $_->{name} eq 'fake-1' } @outputs;
79 my $fake1_content = first { $_->{type} eq 'con' } @{$fake1->{nodes}};
80
81 my @fake0_workspaces = map { $_->{name} } @{$fake0_content->{nodes}};
82 my @fake1_workspaces = map { $_->{name} } @{$fake1_content->{nodes}};
83
84 return \@fake0_workspaces, \@fake1_workspaces;
85 }
86
87 ($x0, $x1) = workspaces_per_screen();
88 ok('5' ~~ @$x1, 'workspace 5 now on fake-1');
89
90 ################################################################################
91 # Verify that a new workspace will be created when moving the last workspace.
92 ################################################################################
93
94 is_deeply($x0, [ '1' ], 'only workspace 1 remaining on fake-0');
95
96 cmd 'workspace 1';
97 cmd 'move workspace to output fake-1';
98
99 ($x0, $x1) = workspaces_per_screen();
100 ok('1' ~~ @$x1, 'workspace 1 now on fake-1');
101 is_deeply($x0, [ '3' ], 'workspace 2 created on fake-0');
102
103 ################################################################################
104 # Verify that 'move workspace to output <direction>' works
105 ################################################################################
106
107 cmd 'workspace 5';
108 cmd 'move workspace to output left';
109
110 ($x0, $x1) = workspaces_per_screen();
111 ok('5' ~~ @$x0, 'workspace 5 back on fake-0');
112
113 # Verify that wrapping works
114 cmd 'move workspace to output left';
115 ($x0, $x1) = workspaces_per_screen();
116 ok('5' ~~ @$x1, 'workspace 5 on fake-1');
117
118 # Put workspace 5 where it should
119 cmd 'move workspace to output left';
120 ($x0, $x1) = workspaces_per_screen();
121 ok('5' ~~ @$x0, 'workspace 5 on fake-0 again');
122
123 ################################################################################
124 # Verify that coordinates of floating windows are fixed correctly when moving a
125 # workspace to a different output.
126 ################################################################################
127
128 cmd 'workspace 5';
129 my $floating_window = open_floating_window;
130
131 my $old_rect = $floating_window->rect;
132
133 cmd 'move workspace to output right';
134
135 my $new_rect = $floating_window->rect;
136
137 isnt($old_rect->{x}, $new_rect->{x}, 'x coordinate changed');
138 is($old_rect->{y}, $new_rect->{y}, 'y coordinate unchanged');
139 is($old_rect->{width}, $new_rect->{width}, 'width unchanged');
140 is($old_rect->{height}, $new_rect->{height}, 'height unchanged');
141
142 ################################################################################
143 # Verify that empty workspaces get cleaned up when moving a different workspace
144 # to that output.
145 ################################################################################
146
147 my $empty_ws = fresh_workspace(output => 0);
148 my $other_output_ws = fresh_workspace(output => 1);
149 cmd 'open';
150
151 ($x0, $x1) = workspaces_per_screen();
152 ok($empty_ws ~~ @$x0, 'empty_ws on fake-0');
153 ok($other_output_ws ~~ @$x1, 'other_output_ws on fake-1');
154
155 cmd 'move workspace to output left';
156
157 ($x0, $x1) = workspaces_per_screen();
158 ok(!($empty_ws ~~ @$x0), 'empty_ws not on fake-0');
159 ok(!($empty_ws ~~ @$x1), 'empty_ws not on fake-1');
160 ok($other_output_ws ~~ @$x0, 'other_output_ws on fake-0');
161
162 ################################################################################
163 # Verify that the special word 'current' can be used for the output.
164 ################################################################################
165
166 my $ws1 = fresh_workspace(output => 1);
167 open_window;
168 cmd 'mark marked';
169
170 my $ws0 = fresh_workspace(output => 0);
171
172 ($x0, $x1) = workspaces_per_screen();
173 ok($ws0 ~~ @$x0, 'ws0 on fake-0');
174 ok($ws1 ~~ @$x1, 'ws1 on fake-1');
175
176 cmd '[con_mark=marked] move workspace to output current';
177
178 ($x0, $x1) = workspaces_per_screen();
179 ok($ws1 ~~ @$x0, 'ws1 on fake-0');
180
181 ################################################################################
182 # Verify that '[class=".*"] move workspace to output' doesn't fail when
183 # containers in the scratchpad are matched.
184 # See issue: #3064.
185 ################################################################################
186 my $__i3_scratch = get_ws('__i3_scratch');
187 is(scalar @{$__i3_scratch->{floating_nodes}}, 0, 'scratchpad is empty');
188
189 $ws0 = fresh_workspace(output => 0);
190 open_window(wm_class => 'a');
191
192 $ws1 = fresh_workspace(output => 1);
193 open_window(wm_class => 'b');
194 my $scratchpad_window = open_window(wm_class => 'c');
195 cmd 'move to scratchpad';
196
197 ($x0, $x1) = workspaces_per_screen();
198 ok($ws0 ~~ @$x0, 'ws0 on fake-0');
199 ok($ws1 ~~ @$x1, 'ws1 on fake-1');
200
201 my $reply = cmd '[class=".*"] move workspace to output fake-1';
202 ok($reply->[0]->{success}, 'move successful');
203
204 ($x0, $x1) = workspaces_per_screen();
205 ok($ws0 ~~ @$x1, 'ws0 on fake-1');
206 ok($ws1 ~~ @$x1, 'ws1 on fake-1');
207
208 $__i3_scratch = get_ws('__i3_scratch');
209 is(scalar @{$__i3_scratch->{floating_nodes}}, 1, 'window still in scratchpad');
210
211 ################################################################################
212
213 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that scratchpad windows don’t move due to floating point caulcation
17 # errors when repeatedly hiding/showing, no matter what display resolution.
18 #
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 683x768+0+0,1024x768+683+0
24 EOT
25
26 sync_with_i3;
27 $x->root->warp_pointer(0, 0);
28 sync_with_i3;
29
30 sub verify_scratchpad_doesnt_move {
31 my ($ws) = @_;
32
33 is_num_children($ws, 0, 'no nodes on this ws');
34
35 my $window = open_window;
36 is_num_children($ws, 1, 'one node on this ws');
37
38 cmd 'move scratchpad';
39 is_num_children($ws, 0, 'no nodes on this ws');
40
41 my $last_x = -1;
42 for (1 .. 20) {
43 cmd 'scratchpad show';
44 is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on this ws');
45
46 # Verify that the coordinates are within bounds.
47 my $content = get_ws($ws);
48 my $srect = $content->{floating_nodes}->[0]->{rect};
49 if ($last_x > -1) {
50 is($srect->{x}, $last_x, 'scratchpad window did not move');
51 }
52 $last_x = $srect->{x};
53 cmd 'scratchpad show';
54 }
55
56 # We need to kill the scratchpad window, otherwise scratchpad show in
57 # subsequent calls of verify_scratchpad_doesnt_move will cycle between all
58 # the windows.
59 cmd 'scratchpad show';
60 cmd 'kill';
61 }
62
63 ################################################################################
64 # test it on the left output first (1366x768)
65 ################################################################################
66
67 my $second = fresh_workspace(output => 0);
68 verify_scratchpad_doesnt_move($second);
69
70 ################################################################################
71 # now on the right output (1024x768)
72 ################################################################################
73
74 sync_with_i3;
75 $x->root->warp_pointer(683 + 10, 0);
76 sync_with_i3;
77
78 my $third = fresh_workspace(output => 1);
79 verify_scratchpad_doesnt_move($third);
80
81 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that focus output right works with monitor setups that don’t line up
17 # on their x/y coordinates.
18 #
19 # ticket #771, bug still present in commit dd743f3b55b2f86d9f1f69ef7952ae8ece4de504
20 #
21 use i3test i3_autostart => 0;
22
23 sub test_focus_left_right {
24 my ($config) = @_;
25
26 my $pid = launch_with_config($config);
27
28 my $i3 = i3(get_socket_path(0));
29
30 sync_with_i3;
31 $x->root->warp_pointer(0, 0);
32 sync_with_i3;
33
34 ############################################################################
35 # Ensure that moving left and right works.
36 ############################################################################
37
38 # First ensure both workspaces have something to focus
39 cmd "workspace 1";
40 my $left_win = open_window;
41
42 cmd "workspace 2";
43 my $right_win = open_window;
44
45 is($x->input_focus, $right_win->id, 'right window focused');
46
47 cmd "focus output left";
48 is($x->input_focus, $left_win->id, 'left window focused');
49
50 cmd "focus output right";
51 is($x->input_focus, $right_win->id, 'right window focused');
52
53 cmd "focus output right";
54 is($x->input_focus, $left_win->id, 'left window focused (wrapping)');
55
56 cmd "focus output left";
57 is($x->input_focus, $right_win->id, 'right window focused (wrapping)');
58
59 ############################################################################
60 # Ensure that moving down/up from S0 doesn’t crash i3 and is a no-op.
61 ############################################################################
62
63 my $second = fresh_workspace(output => 1);
64 my $third_win = open_window;
65
66 cmd "focus output down";
67 is($x->input_focus, $third_win->id, 'right window still focused');
68
69 cmd "focus output up";
70 is($x->input_focus, $third_win->id, 'right window still focused');
71
72 does_i3_live;
73
74 exit_gracefully($pid);
75 }
76
77 # Screen setup looks like this:
78 # +----+
79 # | |--------+
80 # | S1 | S2 |
81 # | |--------+
82 # +----+
83 #
84 my $config = <<EOT;
85 # i3 config file (v4)
86 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
87
88 fake-outputs 1080x1920+0+0,1920x1080+1080+500
89 EOT
90
91 test_focus_left_right($config);
92
93 # Screen setup looks like this:
94 # +----+--------+
95 # | | S2 |
96 # | S1 |--------+
97 # | |
98 # +----+
99 #
100 $config = <<EOT;
101 # i3 config file (v4)
102 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
103
104 fake-outputs 1080x1920+0+0,1920x200+1080+0
105 EOT
106
107 test_focus_left_right($config);
108
109 # Screen setup looks like this:
110 # +----+
111 # | |
112 # | S1 |--------+
113 # | | S2 |
114 # +----+--------+
115 #
116 $config = <<EOT;
117 # i3 config file (v4)
118 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
119
120 fake-outputs 1080x1920+0+0,1920x200+1080+1720
121 EOT
122
123 test_focus_left_right($config);
124
125 # Screen setup looks like this:
126 # +----+ +----+
127 # | | | |
128 # | S1 |--------+ S3 |
129 # | | S2 | |
130 # +----+--------+----+
131 #
132 $config = <<EOT;
133 # i3 config file (v4)
134 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
135
136 fake-outputs 1080x1920+0+0,1920x200+1080+1720,1080x1920+1280+0
137 EOT
138
139 my $pid = launch_with_config($config);
140
141 my $i3 = i3(get_socket_path(0));
142
143 sync_with_i3;
144 $x->root->warp_pointer(0, 0);
145 sync_with_i3;
146
147 ############################################################################
148 # Ensure that focusing right/left works in the expected order.
149 ############################################################################
150
151 sub get_focused_output {
152 my $tree = i3(get_socket_path())->get_tree->recv;
153 my ($focused_id) = @{$tree->{focus}};
154 my ($output) = grep { $_->{id} == $focused_id } @{$tree->{nodes}};
155 return $output->{name};
156 }
157
158 is(get_focused_output(), 'fake-0', 'focus on fake-0');
159
160 cmd 'focus output right';
161 is(get_focused_output(), 'fake-1', 'focus on fake-1');
162
163 cmd 'focus output right';
164 is(get_focused_output(), 'fake-2', 'focus on fake-2');
165
166 cmd 'focus output left';
167 is(get_focused_output(), 'fake-1', 'focus on fake-1');
168
169 cmd 'focus output left';
170 is(get_focused_output(), 'fake-0', 'focus on fake-0');
171
172 cmd 'focus output left';
173 is(get_focused_output(), 'fake-2', 'focus on fake-2 (wrapping)');
174
175 cmd 'focus output right';
176 is(get_focused_output(), 'fake-0', 'focus on fake-0 (wrapping)');
177
178 exit_gracefully($pid);
179
180 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether i3 crashes on cross-output moves with one workspace per output.
17 # Ticket: #827
18 # Bug still in: 4.3-78-g66b389c
19 #
20 use List::Util qw(first);
21 use i3test i3_config => <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24
25 fake-outputs 1024x768+0+0,1024x768+1024+0
26 EOT
27
28 ################################################################################
29 # Setup workspaces so that they stay open (with an empty container).
30 ################################################################################
31
32 is(focused_ws, '1', 'starting on workspace 1');
33
34 cmd 'move workspace to output fake-1';
35
36 does_i3_live;
37
38 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether workspace_layout is properly set after startup.
17 #
18 use List::Util qw(first);
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0
24 workspace_layout tabbed
25 EOT
26
27 ################################################################################
28 # Test that workspace_layout is properly set
29 ################################################################################
30
31 is(focused_ws, '1', 'starting on workspace 1');
32 my $ws = get_ws(1);
33 is($ws->{workspace_layout}, 'tabbed', 'workspace layout is "tabbed"');
34
35 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that switching workspaces via 'focus $dir' never leaves a floating
17 # window focused.
18 #
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+0+768,1024x768+1024+768
24 EOT
25
26 my $s0_ws = fresh_workspace;
27 my $first = open_window;
28 my $second = open_window;
29 my $third = open_window;
30 cmd 'floating toggle';
31
32 # Focus screen 1
33 sync_with_i3;
34 $x->root->warp_pointer(1025, 0);
35 sync_with_i3;
36 my $s1_ws = fresh_workspace;
37
38 my $fourth = open_window;
39
40 # Focus screen 2
41 sync_with_i3;
42 $x->root->warp_pointer(0, 769);
43 sync_with_i3;
44 my $s2_ws = fresh_workspace;
45
46 my $fifth = open_window;
47
48 # Focus screen 3
49 sync_with_i3;
50 $x->root->warp_pointer(1025, 769);
51 sync_with_i3;
52 my $s3_ws = fresh_workspace;
53
54 my $sixth = open_window;
55 my $seventh = open_window;
56 my $eighth = open_window;
57 cmd 'floating toggle';
58
59 # Layout should look like this (windows 3 and 8 are floating):
60 # S0 S1
61 # ┌───┬───┬───────┐
62 # │ ┌─┴─┐ │ │
63 # │1│ 3 │2│ 4 │
64 # │ └─┬─┘ │ │
65 # ├───┴───┼───┬───┤
66 # │ │ ┌─┴─┐ │
67 # │ 5 │6│ 8 │7│
68 # │ │ └─┬─┘ │
69 # └───────┴───┴───┘
70 # S2 S3
71 #
72 ###################################################################
73 # Test that focus (left|down|right|up) doesn't focus floating
74 # windows when moving into horizontally-split workspaces.
75 ###################################################################
76
77 sub reset_focus {
78 my $ws = shift;
79 cmd "workspace $ws; focus floating";
80 }
81
82 cmd "workspace $s1_ws";
83 cmd 'focus left';
84 is($x->input_focus, $second->id, 'second window focused');
85 reset_focus $s0_ws;
86
87 cmd "workspace $s1_ws";
88 cmd 'focus down';
89 is($x->input_focus, $seventh->id, 'seventh window focused');
90 reset_focus $s3_ws;
91
92 cmd "workspace $s2_ws";
93 cmd 'focus right';
94 is($x->input_focus, $seventh->id, 'seventh window focused');
95 reset_focus $s3_ws;
96
97 cmd "workspace $s2_ws";
98 cmd 'focus up';
99 is($x->input_focus, $second->id, 'second window focused');
100 reset_focus $s0_ws;
101
102 ###################################################################
103 # Put the workspaces on screens 0 and 3 into vertical split mode
104 # and test focus (left|down|right|up) again.
105 ###################################################################
106
107 cmd "workspace $s0_ws";
108 is($x->input_focus, $third->id, 'third window focused');
109 cmd 'focus parent';
110 cmd 'focus parent';
111 cmd 'split v';
112 # Focus second or else $first gets to the top of the focus stack.
113 cmd '[id=' . $second->id . '] focus';
114 reset_focus $s0_ws;
115
116 cmd "workspace $s3_ws";
117 is($x->input_focus, $eighth->id, 'eighth window focused');
118 cmd 'focus parent';
119 cmd 'focus parent';
120 cmd 'split v';
121 cmd '[id=' . $sixth->id . '] focus';
122 reset_focus $s3_ws;
123
124 cmd "workspace $s1_ws";
125 cmd 'focus left';
126 is($x->input_focus, $second->id, 'second window focused');
127 reset_focus $s0_ws;
128
129 cmd "workspace $s1_ws";
130 cmd 'focus down';
131 is($x->input_focus, $sixth->id, 'sixth window focused');
132 reset_focus $s3_ws;
133
134 cmd "workspace $s2_ws";
135 cmd 'focus right';
136 is($x->input_focus, $sixth->id, 'sixth window focused');
137
138 cmd "workspace $s2_ws";
139 cmd 'focus up';
140 is($x->input_focus, $second->id, 'second window focused');
141
142 ###################################################################
143 # Test that focus (left|down|right|up), when focusing across
144 # outputs, doesn't focus the next window in the given direction but
145 # the most focused window of the container in the given direction.
146 # In the following layout:
147 # [ WS1*[ ] WS2[ H[ A B* ] ] ]
148 # (where the asterisk denotes the focused container within its
149 # parent) moving right from WS1 should focus B which is focused
150 # inside WS2, not A which is the next window on the right of WS1.
151 # See issue #1160.
152 ###################################################################
153
154 kill_all_windows;
155
156 sync_with_i3;
157 $x->root->warp_pointer(1025, 0); # Second screen.
158 sync_with_i3;
159 $s1_ws = fresh_workspace;
160 $first = open_window;
161 $second = open_window;
162
163 sync_with_i3;
164 $x->root->warp_pointer(0, 0); # First screen.
165 sync_with_i3;
166 $s0_ws = fresh_workspace;
167 open_window;
168 $third = open_window;
169
170 cmd 'focus right';
171 is($x->input_focus, $second->id, 'second window (rightmost) focused');
172 cmd 'focus left';
173 is($x->input_focus, $first->id, 'first window focused');
174 cmd 'focus left';
175 is($x->input_focus, $third->id, 'third window focused');
176
177
178 ###################################################################
179 # Similar but with a tabbed layout.
180 ###################################################################
181
182 cmd 'layout tabbed';
183 $fourth = open_window;
184 cmd 'focus left';
185 is($x->input_focus, $third->id, 'third window (tabbed) focused');
186 cmd "workspace $s1_ws";
187 cmd 'focus left';
188 is($x->input_focus, $third->id, 'third window (tabbed) focused');
189
190
191 ###################################################################
192 # Similar but with a stacked layout on the bottom screen.
193 ###################################################################
194
195 sync_with_i3;
196 $x->root->warp_pointer(0, 769); # Third screen.
197 sync_with_i3;
198 $s2_ws = fresh_workspace;
199 cmd 'layout stacked';
200 $fifth = open_window;
201 $sixth = open_window;
202
203 cmd "workspace $s0_ws";
204 cmd 'focus down';
205 is($x->input_focus, $sixth->id, 'sixth window (stacked) focused');
206
207 ###################################################################
208 # Similar but with a more complex layout.
209 ###################################################################
210
211 sync_with_i3;
212 $x->root->warp_pointer(1025, 769); # Fourth screen.
213 sync_with_i3;
214 $s3_ws = fresh_workspace;
215 open_window;
216 open_window;
217 cmd 'split v';
218 open_window;
219 open_window;
220 cmd 'split h';
221 my $nested = open_window;
222 open_window;
223 cmd 'focus left';
224 is($x->input_focus, $nested->id, 'nested window focused');
225
226 cmd "workspace $s1_ws";
227 cmd 'focus down';
228 is($x->input_focus, $nested->id, 'nested window focused from workspace above');
229
230 cmd "workspace $s2_ws";
231 cmd 'focus right';
232 is($x->input_focus, $nested->id, 'nested window focused from workspace on the left');
233
234 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that ConfigureRequests don’t make windows fall out of the scratchpad.
17 # Ticket: #898
18 # Bug still in: 4.4-15-g770ead6
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24 EOT
25
26 my $left_ws = fresh_workspace(output => 0);
27 my $right_ws = fresh_workspace(output => 1);
28
29 my $window = open_window;
30 cmd 'move scratchpad';
31
32 # Cause a ConfigureRequest by setting the window’s position/size.
33 my ($a, $t) = $window->rect;
34 $window->rect(X11::XCB::Rect->new(x => 0, y => 0, width => $a->width, height => $a->height));
35
36 sync_with_i3;
37
38 my $ws = get_ws($left_ws);
39 is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad after ConfigureRequest');
40 $ws = get_ws($right_ws);
41 is(scalar @{$ws->{floating_nodes}}, 0, 'scratchpad window still in scratchpad after ConfigureRequest');
42
43 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that moving containers wraps across outputs.
17 # E.g. when you have a container on the right output and you move it to the
18 # right, it should appear on the left output.
19 # Bug still in: 4.4-106-g3cd4b8c
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25 EOT
26
27 my $right = fresh_workspace(output => 1);
28 my $left = fresh_workspace(output => 0);
29
30 my $win = open_window;
31
32 is_num_children($left, 1, 'one container on left workspace');
33
34 cmd 'move container to output right';
35 cmd 'focus output right';
36
37 is_num_children($left, 0, 'no containers on left workspace');
38 is_num_children($right, 1, 'one container on right workspace');
39
40 cmd 'move container to output right';
41
42 is_num_children($left, 1, 'one container on left workspace');
43 is_num_children($right, 0, 'no containers on right workspace');
44
45 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether moving workspaces between outputs works correctly.
17 use i3test i3_config => <<EOT;
18 # i3 config file (v4)
19 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
20
21 fake-outputs 1024x768+0+0,1024x768+1024+0
22 EOT
23 use List::Util qw(first);
24
25 sub workspaces_per_screen {
26 my $i3 = i3(get_socket_path());
27 my $tree = $i3->get_tree->recv;
28 my @outputs = @{$tree->{nodes}};
29
30 my $fake0 = first { $_->{name} eq 'fake-0' } @outputs;
31 my $fake0_content = first { $_->{type} eq 'con' } @{$fake0->{nodes}};
32
33 my $fake1 = first { $_->{name} eq 'fake-1' } @outputs;
34 my $fake1_content = first { $_->{type} eq 'con' } @{$fake1->{nodes}};
35
36 my @fake0_workspaces = map { $_->{name} } @{$fake0_content->{nodes}};
37 my @fake1_workspaces = map { $_->{name} } @{$fake1_content->{nodes}};
38
39 return \@fake0_workspaces, \@fake1_workspaces;
40 }
41
42 # Switch to temporary workspaces on both outputs so the numbers are free.
43 my $tmp_right = fresh_workspace(output => 1);
44 my $tmp_left = fresh_workspace(output => 0);
45
46 cmd 'workspace 1';
47 # Keep that workspace open.
48 my $win1 = open_window;
49
50 cmd 'workspace 5';
51 # Keep that workspace open.
52 open_window;
53
54 cmd "workspace $tmp_right";
55 cmd 'workspace 2';
56 # Keep that workspace open.
57 open_window;
58
59 my ($x0, $x1) = workspaces_per_screen();
60 is_deeply($x0, [ '1', '5' ], 'workspace 1 and 5 on fake-0');
61 is_deeply($x1, [ '2' ], 'workspace 2 on fake-1');
62
63 cmd 'workspace 1';
64
65 my ($nodes, $focus) = get_ws_content('1');
66 is($nodes->[0]->{window}, $win1->id, 'window 1 on workspace 1');
67
68 cmd 'move workspace next';
69 cmd '[id="' . $win1->id . '"] focus';
70
71 ($nodes, $focus) = get_ws_content('2');
72 is($nodes->[1]->{window}, $win1->id, 'window 1 on workspace 2 after moving');
73
74 cmd 'move workspace prev';
75 cmd '[id="' . $win1->id . '"] focus';
76
77 ($nodes, $focus) = get_ws_content('1');
78 is($nodes->[0]->{window}, $win1->id, 'window 1 on workspace 1');
79
80 cmd 'move workspace next_on_output';
81 cmd '[id="' . $win1->id . '"] focus';
82
83 ($nodes, $focus) = get_ws_content('5');
84 is($nodes->[1]->{window}, $win1->id, 'window 1 on workspace 5 after moving');
85
86 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ticket: #990
17 # Bug still in: 4.5.1-23-g82b5978
18
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22 fake-outputs 1024x768+0+0,1024x768+1024+0
23 EOT
24
25 my $old_ws = get_ws(focused_ws);
26
27 my $focus = AnyEvent->condvar;
28 my @events = events_for(
29 sub { cmd 'focus output right' },
30 'workspace');
31
32 my $current_ws = get_ws(focused_ws);
33
34 is(scalar @events, 1, 'Received 1 event');
35 is($events[0]->{current}->{id}, $current_ws->{id}, 'Event gave correct current workspace');
36 is($events[0]->{old}->{id}, $old_ws->{id}, 'Event gave correct old workspace');
37
38 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that new workspace names are taken from the config,
17 # then from the first free number starting with 1.
18 #
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24
25 bindsym 1 workspace 1: eggs
26 EOT
27
28 my $i3 = i3(get_socket_path());
29 my $ws = $i3->get_workspaces->recv;
30
31 is($ws->[0]->{name}, '1: eggs', 'new workspace uses config name');
32 is($ws->[1]->{name}, '2', 'naming continues with next free number');
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests if a simple 'move <direction>' command will move containers across outputs.
17 #
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
23
24 workspace left-top output fake-0
25 workspace right-top output fake-1
26 workspace right-bottom output fake-2
27 workspace left-bottom output fake-3
28 EOT
29
30 # Ensure the pointer is at (0, 0) so that we really start on the first
31 # (the left) workspace.
32 $x->root->warp_pointer(0, 0);
33
34 #####################################################################
35 # Try to move a single window across outputs in each direction
36 #####################################################################
37
38 cmd('workspace left-top');
39 my $alone_window = open_window;
40
41 cmd('move right');
42 is(scalar @{get_ws_content('right-top')}, 1, 'moved individual window to right-top workspace');
43
44 cmd('move down');
45 is(scalar @{get_ws_content('right-bottom')}, 1, 'moved individual window to right-bottom workspace');
46
47 cmd('move left');
48 is(scalar @{get_ws_content('left-bottom')}, 1, 'moved individual window to left-bottom workspace');
49
50 cmd('move up');
51 is(scalar @{get_ws_content('left-top')}, 1, 'moved individual window to left-top workspace');
52
53 $alone_window->unmap;
54 wait_for_unmap;
55
56 #####################################################################
57 # Try to move a window on a workspace with two windows across outputs in each
58 # direction
59 #####################################################################
60
61 # from left-top to right-top
62 cmd('workspace left-top');
63 cmd('split h');
64 my $first_window = open_window;
65 my $social_window = open_window( name => 'CORRECT_WINDOW' );
66 cmd('move right');
67 is(scalar @{get_ws_content('right-top')}, 1, 'moved some window to right-top workspace');
68 my $compare_window = shift @{get_ws_content('right-top')};
69 is($compare_window->{window}, $social_window->id, 'moved correct window to right-top workspace');
70 # unmap the first window so we don't confuse it when we move back here
71 $first_window->unmap;
72 wait_for_unmap;
73
74 # from right-top to right-bottom
75 cmd('split v');
76 open_window;
77 # this window opened above - we need to move down twice
78 cmd('focus up; move down; move down');
79 is(scalar @{get_ws_content('right-bottom')}, 1, 'moved some window to right-bottom workspace');
80 $compare_window = shift @{get_ws_content('right-bottom')};
81 is($compare_window->{name}, $social_window->name, 'moved correct window to right-bottom workspace');
82
83 # from right-bottom to left-bottom
84 cmd('split h');
85 open_window;
86 cmd('focus left; move left');
87 is(scalar @{get_ws_content('left-bottom')}, 1, 'moved some window to left-bottom workspace');
88 $compare_window = shift @{get_ws_content('left-bottom')};
89 is($social_window->name, $compare_window->{name}, 'moved correct window to left-bottom workspace');
90
91 # from left-bottom to left-top
92 cmd('split v');
93 open_window;
94 cmd('focus up; move up');
95 is(scalar @{get_ws_content('left-top')}, 1, 'moved some window to left-bottom workspace');
96 $compare_window = shift @{get_ws_content('left-top')};
97 is($social_window->name, $compare_window->{name}, 'moved correct window to left-bottom workspace');
98
99 #####################################################################
100 # Moving a fullscreen container should change its output.
101 #####################################################################
102
103 kill_all_windows;
104
105 cmd 'workspace left-top';
106 open_window;
107 my $fs_window = open_window;
108 open_window;
109
110 cmd '[id=' . $fs_window->id . '] fullscreen enable, move right';
111 is(scalar @{get_ws_content('right-top')}, 1, 'moved fullscreen window to right-top workspace');
112
113 #####################################################################
114 # Moving a global fullscreen container should not change its output.
115 #####################################################################
116
117 kill_all_windows;
118
119 cmd 'workspace left-top';
120 open_window;
121 open_window;
122 open_window;
123
124 cmd 'fullscreen global, move right, fullscreen disable';
125 is(scalar @{get_ws_content('right-top')}, 0, 'global fullscreen window didn\'t change workspace with move');
126
127 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Make sure the command `move <direction>` properly sends the workspace focus
17 # ipc event required for i3bar to be properly updated and redrawn.
18 #
19 # Bug still in: 4.6-195-g34232b8
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25 workspace ws-left output fake-0
26 workspace ws-right output fake-1
27 EOT
28
29 # open two windows on the left output
30 cmd 'workspace ws-left';
31 open_window;
32 open_window;
33
34 sub focus_subtest {
35 my ($cmd, $want) = @_;
36
37 my @events = events_for(
38 sub { cmd $cmd },
39 'workspace');
40
41 my @focus = grep { $_->{change} eq 'focus' } @events;
42 is(scalar @focus, 1, 'Received 1 workspace::focus event');
43 is($focus[0]->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
44 is(@{$focus[0]->{current}->{nodes}}, $want, 'focus event gave the right number of windows on the workspace');
45 }
46
47 # move a window over to the right output
48 subtest 'move right (1)', \&focus_subtest, 'move right', 1;
49
50 # move another window
51 cmd 'workspace ws-left';
52 subtest 'move right (2)', \&focus_subtest, 'move right', 2;
53
54 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that workspace assignment config directives for plain numbers will
17 # assign any workspace of that number to the specified output.
18 # Ticket: #1238
19 # Bug still in: 4.7.2-147-g3760a48
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 workspace 1:override output fake-0
25 workspace 2 output fake-0
26 workspace 1 output fake-1
27 workspace 2:override output fake-1
28 workspace 3 output fake-0
29 workspace 3:override output doesnotexist fake-1
30
31 fake-outputs 1024x768+0+0,1024x768+1024+0
32 EOT
33
34 ################################################################################
35 # Workspace assignments with bare numbers should be interpreted as `workspace
36 # number` config directives. Any workspace beginning with that number should be
37 # assigned to the specified output.
38 ################################################################################
39
40 cmd 'focus output fake-1';
41 cmd 'workspace "2:foo"';
42 is(get_output_for_workspace('2:foo'), 'fake-0',
43 'Workspaces should be assigned by number when the assignment is a plain number')
44 or diag 'Since workspace number 2 is assigned to fake-0, 2:foo should open on fake-0';
45
46 cmd 'focus output fake-0';
47 cmd 'workspace "2:override"';
48 is(get_output_for_workspace('2:override'), 'fake-1',
49 'Workspace assignments by name should override numbered assignments')
50 or diag 'Since workspace "2:override" is assigned by name to fake-1, it should open on fake-1';
51
52 cmd 'focus output fake-1';
53 cmd 'workspace "1:override"';
54 is(get_output_for_workspace('1:override'), 'fake-0',
55 'Assignment rules should not be affected by the order assignments are declared')
56 or diag 'Since workspace "1:override" is assigned by name to fake-0, it should open on fake-0';
57
58 cmd 'focus output fake-1';
59 cmd 'workspace "3:override"';
60 is(get_output_for_workspace('3:override'), 'fake-1',
61 'Assignment rules should not be affected by multiple output assignments')
62 or diag 'Since workspace "3:override" is assigned by name to fake-1, it should open on fake-1';
63
64 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
11 # (unless you are already familiar with Perl)
12
13 use i3test i3_config => <<EOT;
14 # i3 config file (v4)
15 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
16 fake-outputs 1024x768+0+0,1024x768+1024+0
17 mouse_warping none
18 EOT
19
20 # Ensure the pointer is at (0, 0) so that we really start on the first
21 # (the left) workspace.
22 $x->root->warp_pointer(0, 0);
23
24 ######################################################
25 # Open one workspace with one window on both outputs #
26 ######################################################
27
28 # Open window on workspace 1, left output
29 is(focused_ws, '1', 'starting with focus on workspace 1');
30 open_window;
31
32 # Open window on workspace 2, right output
33 cmd 'focus output right';
34 is(focused_ws, '2', 'moved focus to workspace 2');
35 open_window;
36
37 # If mouse_warping is disabled, the pointer has not moved from
38 # position (0, 0) when focus was switched to workspace 2
39 $x->root->warp_pointer(0, 0);
40
41 # Ensure focus is still on workspace 2
42 is(focused_ws, '2', 'warped mouse cursor to (0, 0), focus still in workspace 2');
43
44 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensure that `focus [direction]` will focus an existing floating con when no
17 # tiling con exists on the output in [direction] when focusing across outputs
18 # Bug still in: 4.7.2-204-g893dbae
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 workspace ws_left output fake-0
24 workspace ws_right output fake-1
25
26 mouse_warping none
27
28 fake-outputs 1024x768+0+0,1024x768+1024+0
29 EOT
30
31 cmd 'workspace ws_left';
32 my $win = open_window();
33
34 cmd 'floating enable';
35 cmd 'focus output right';
36 cmd 'focus left';
37
38 is($x->input_focus, $win->id,
39 'Focusing across outputs with `focus [direction]` should focus an existing floating con when no tiling con exists on the output in [direction].');
40
41 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test that the EWMH specified property _NET_DESKTOP_VIEWPORT is updated
17 # properly on the root window. We interpret this as a list of x/y coordinate
18 # pairs for the upper left corner of the respective outputs of the workspaces
19 # Ticket: #1241
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 workspace 0 output fake-0
25 workspace 1 output fake-1
26
27 fake-outputs 1024x768+0+0,1024x768+1024+0
28 EOT
29
30 sub get_desktop_viewport {
31 # Make sure that i3 pushed its changes to X11 before querying.
32 sync_with_i3;
33
34 my $cookie = $x->get_property(
35 0,
36 $x->get_root_window(),
37 $x->atom(name => '_NET_DESKTOP_VIEWPORT')->id,
38 $x->atom(name => 'CARDINAL')->id,
39 0,
40 4096
41 );
42
43 my $reply = $x->get_property_reply($cookie->{sequence});
44
45 return 0 if $reply->{value_len} == 0;
46
47 my $len = $reply->{length};
48
49 return unpack ("L$len", $reply->{value});
50 }
51
52 # initialize the workspaces
53 cmd 'workspace 1';
54 cmd 'workspace 0';
55
56 my @expected_viewport = (0, 0, 1024, 0);
57 my @desktop_viewport = get_desktop_viewport;
58
59 is_deeply(\@desktop_viewport, \@expected_viewport,
60 '_NET_DESKTOP_VIEWPORT should be an array of x/y coordinate pairs for the upper left corner of the respective outputs of the workspaces');
61
62 cmd 'workspace 0';
63 open_window;
64 cmd 'workspace 3';
65
66 @expected_viewport = (0, 0, 0, 0, 1024, 0);
67 @desktop_viewport = get_desktop_viewport;
68
69 is_deeply(\@desktop_viewport, \@expected_viewport,
70 'it should be updated when a new workspace appears');
71
72 cmd 'rename workspace 3 to 2';
73
74 @expected_viewport = (0, 0, 0, 0, 1024, 0);
75 @desktop_viewport = get_desktop_viewport;
76
77 is_deeply(\@desktop_viewport, \@expected_viewport,
78 'it should stay up to date when a workspace is renamed');
79
80 cmd 'workspace 0';
81
82 @expected_viewport = (0, 0, 1024, 0);
83 @desktop_viewport = get_desktop_viewport;
84
85 is_deeply(\@desktop_viewport, \@expected_viewport,
86 'it should be updated when a workspace is emptied');
87
88 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 #
17 # Tests that workspaces are moved to the assigned output if they
18 # are renamed to an assigned name.
19 # Ticket: #1473
20
21 use i3test i3_config => <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25
26 workspace 1 output fake-0
27 workspace 2 output fake-1
28 workspace 3:foo output fake-1
29 workspace baz output fake-1
30 workspace 5 output left
31 workspace 6 output doesnotexist fake-0
32 workspace 7 output fake-1 fake-0
33 EOT
34
35 ##########################################################################
36 # Renaming the workspace to an unassigned name does not move the workspace
37 # (regression test)
38 ##########################################################################
39
40 cmd 'focus output fake-0';
41 cmd 'rename workspace to unassigned';
42 is(get_output_for_workspace('unassigned'), 'fake-0',
43 'Unassigned workspace should stay on its output when being renamed');
44
45 ##########################################################################
46 # Renaming a workspace by number only triggers the assignment
47 ##########################################################################
48
49 cmd 'focus output fake-0';
50 cmd 'rename workspace to 2';
51 is(get_output_for_workspace('2'), 'fake-1',
52 'Renaming the workspace to a number should move it to the assigned output');
53
54 ##########################################################################
55 # Renaming a workspace by number and name triggers the assignment
56 ##########################################################################
57
58 cmd 'focus output fake-0';
59 cmd 'rename workspace to "2:foo"';
60 is(get_output_for_workspace('2:foo'), 'fake-1',
61 'Renaming the workspace to a number and name should move it to the assigned output');
62
63 ##########################################################################
64 # Renaming a workspace by name only triggers the assignment
65 ##########################################################################
66
67 cmd 'focus output fake-0';
68 cmd 'rename workspace to baz';
69 is(get_output_for_workspace('baz'), 'fake-1',
70 'Renaming the workspace to a number and name should move it to the assigned output');
71
72 ##########################################################################
73 # Renaming a workspace so that it is assigned a directional output does
74 # not move the workspace or crash
75 ##########################################################################
76
77 cmd 'focus output fake-0';
78 cmd 'workspace bar';
79 cmd 'rename workspace to 5';
80 is(get_output_for_workspace('5'), 'fake-0',
81 'Renaming the workspace to a workspace assigned to a directional output should not move the workspace');
82
83 ##########################################################################
84 # Renaming an unfocused workspace, triggering an assignment to the output
85 # which holds the currently focused empty workspace should result in the
86 # original workspace replacing the empty one.
87 # See issue #3228.
88 ##########################################################################
89
90 cmd 'workspace baz';
91 cmd 'rename workspace 5 to 2';
92 is(get_output_for_workspace('2'), 'fake-1',
93 'Renaming an unfocused workspace, triggering an assignment to the output which holds the currently focused empty workspace should result in the original workspace replacing the empty one');
94
95 ##########################################################################
96 # Renaming an unfocused empty workspace, triggering an assignment to the
97 # output which holds the currently focused non-empty workspace should
98 # close the empty workspace and not crash i3.
99 # See issue #3248.
100 ##########################################################################
101
102 cmd 'workspace 1';
103 cmd 'workspace 2';
104 open_window;
105 cmd 'rename workspace 1 to baz';
106 is(get_output_for_workspace('baz'), '',
107 'Renaming an unfocused empty workspace, triggering an assignment to the output which holds the currently focused non-empty workspace should close the empty workspace and not crash i3');
108 kill_all_windows;
109
110 ##########################################################################
111 # Renaming a workspace with multiple assignments, where the first output
112 # doesn't exist.
113 ##########################################################################
114
115 cmd 'focus output fake-1';
116 cmd 'rename workspace to 6';
117 is(get_output_for_workspace('6'), 'fake-0',
118 'Renaming the workspace while first target output doesn\'t exist moves it to the second assigned output');
119
120 ##########################################################################
121 # Renaming a workspace with multiple assignments, where both outputs exist
122 # moves it to the first output.
123 ##########################################################################
124
125 cmd 'focus output fake-0';
126 cmd 'rename workspace to 7';
127 is(get_output_for_workspace('7'), 'fake-1',
128 'Renaming a workspace with multiple assignments, where both outputs exist moves it to the first output.');
129
130 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies that 'move position center' moves floating cons to the center of
17 # the appropriate output.
18 # Ticket: #1211
19 # Bug still in: 4.9.1-108-g037cb31
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25
26 workspace left output fake-0
27 workspace right output fake-1
28 EOT
29
30 #####################################################################
31 # Verify that 'move position center' on a floating window does not
32 # move it to another output.
33 #####################################################################
34
35 cmd 'workspace left';
36
37 # Sync in case focus switched outputs due to the workspace change.
38 sync_with_i3;
39
40 my $floating = open_floating_window;
41
42 # Center the window on the left workspace
43 cmd 'move position center';
44 sync_with_i3;
45
46 is(scalar @{get_ws('left')->{floating_nodes}}, 1, 'one floating node on left ws');
47 is(scalar @{get_ws('right')->{floating_nodes}}, 0, 'no floating nodes on right ws');
48
49 # Center the window on the right workspace
50 cmd 'move workspace right; workspace right; move position center';
51 sync_with_i3;
52
53 is(scalar @{get_ws('left')->{floating_nodes}}, 0, 'no floating nodes on left ws');
54 is(scalar @{get_ws('right')->{floating_nodes}}, 1, 'one floating node on right ws');
55
56 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests the behaviour of 'move <direction>' when moving containers across
17 # outputs on workspaces that have non-default layouts.
18 # Ticket: #1603
19 # Bug still in: 4.10.1-40-g0ad097e
20 use List::Util qw(first);
21 use i3test i3_config => <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24
25 fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
26
27 workspace left-top output fake-0
28 workspace right-top output fake-1
29 workspace right-bottom output fake-2
30 workspace left-bottom output fake-3
31
32 workspace_layout stacked
33 EOT
34
35 #####################################################################
36 # Create two windows in the upper left workspace and move them
37 # clockwise around the workspaces until the end up where they began.
38 #####################################################################
39
40 cmd 'workspace left-top';
41
42 my $first = open_window(wm_class => 'first');
43 my $second = open_window(wm_class => 'second');
44
45 is_num_children('left-top', 1, 'one child on left-top');
46 is_num_children('right-top', 0, 'no children on right-top');
47
48 # Move the second window into its own stacked container.
49 cmd 'move right';
50 is_num_children('left-top', 2, 'two children on left-top');
51
52 # Move the second window onto the upper right workspace.
53 cmd 'move right';
54 is_num_children('left-top', 1, 'one child on left-top');
55 is_num_children('right-top', 1, 'one child on right-top');
56
57 # Move the first window onto the upper right workspace.
58 cmd '[class="first"] move right';
59 is_num_children('left-top', 0, 'no children on left-top');
60 is_num_children('right-top', 2, 'two children on right-top');
61
62 # Move the second window onto the lower right workspace.
63 cmd '[class="second"] move down, move down';
64 is_num_children('right-top', 1, 'one child on right-top');
65 is_num_children('right-bottom', 1, 'two children on right-bottom');
66
67 # Move the first window onto the lower right workspace.
68 cmd '[class="first"] move down';
69 is_num_children('right-top', 0, 'no children on right-top');
70 is_num_children('right-bottom', 2, 'two children on right-bottom');
71
72 # Move the second windo onto the lower left workspace.
73 cmd '[class="second"] move left, move left';
74 is_num_children('right-bottom', 1, 'one child on right-bottom');
75 is_num_children('left-bottom', 1, 'one on left-bottom');
76
77 # Move the first window onto the lower left workspace.
78 cmd '[class="first"] move left';
79 is_num_children('right-bottom', 0, 'no children on right-bottom');
80 is_num_children('left-bottom', 2, 'two children on left-bottom');
81
82 # Move the second window onto the upper left workspace.
83 cmd '[class="second"] move up, move up';
84 is_num_children('left-bottom', 1, 'one child on left-bottom');
85 is_num_children('left-top', 1, 'one child on left-top');
86
87 # Move the first window onto the upper left workspace.
88 cmd '[class="first"] move up';
89 is_num_children('left-bottom', 0, 'no children on left-bottom');
90 is_num_children('left-top', 2, 'two children on left-top');
91
92 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ensures that mouse bindings on the i3bar work correctly.
17 # Ticket: #1695
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21 focus_follows_mouse no
22
23 bar {
24 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
25 position top
26
27 bindsym button1 focus left
28 bindsym button2 focus right
29 bindsym button3 focus left
30 bindsym button4 focus right
31 bindsym button5 focus left
32 bindsym --release button6 focus right
33 bindsym button7 focus left
34 bindsym button7 --release focus right
35 }
36 EOT
37 use i3test::XTEST;
38
39 my $i3 = i3(get_socket_path());
40 $i3->connect()->recv;
41 my $ws = fresh_workspace;
42
43 my $cv = AnyEvent->condvar;
44 my $timer = AnyEvent->timer(after => 1, interval => 0, cb => sub { $cv->send(0) });
45 $i3->subscribe({
46 window => sub {
47 my ($event) = @_;
48 if ($event->{change} eq 'focus') {
49 $cv->send($event->{container});
50 }
51 if ($event->{change} eq 'new') {
52 if (defined($event->{container}->{window_properties}->{class}) &&
53 $event->{container}->{window_properties}->{class} eq 'i3bar') {
54 $cv->send($event->{container});
55 }
56 }
57 },
58 })->recv;
59
60 sub i3bar_present {
61 my ($nodes) = @_;
62
63 for my $node (@{$nodes}) {
64 my $props = $node->{window_properties};
65 if (defined($props) && $props->{class} eq 'i3bar') {
66 return $node->{window};
67 }
68 }
69
70 return 0 if !@{$nodes};
71
72 my @children = (map { @{$_->{nodes}} } @{$nodes},
73 map { @{$_->{'floating_nodes'}} } @{$nodes});
74
75 return i3bar_present(\@children);
76 }
77
78 my $i3bar_window = i3bar_present($i3->get_tree->recv->{nodes});
79 if ($i3bar_window) {
80 ok(1, 'i3bar present');
81 } else {
82 my $con = $cv->recv;
83 ok($con, 'i3bar appeared');
84 $i3bar_window = $con->{window};
85 }
86
87 diag('i3bar window = ' . $i3bar_window);
88
89 my $left = open_window;
90 my $right = open_window;
91 sync_with_i3;
92
93 sub focus_subtest {
94 my ($subscribecb, $want, $msg) = @_;
95 my @events = events_for(
96 $subscribecb,
97 'window');
98 my @focus = map { $_->{container}->{window} } grep { $_->{change} eq 'focus' } @events;
99 is_deeply(\@focus, $want, $msg);
100 }
101
102 sub sync {
103 # Ensure XTEST events were sent to i3, which grabs and hence needs to
104 # forward any events to i3bar:
105 xtest_sync_with_i3;
106 # Ensure any pending i3bar IPC messages were handled by i3:
107 xtest_sync_with($i3bar_window);
108 }
109
110 subtest 'button 1 moves focus left', \&focus_subtest,
111 sub {
112 xtest_button_press(1, 3, 3);
113 xtest_button_release(1, 3, 3);
114 sync;
115 },
116 [ $left->{id} ],
117 'button 1 moves focus left';
118
119 subtest 'button 2 moves focus right', \&focus_subtest,
120 sub {
121 xtest_button_press(2, 3, 3);
122 xtest_button_release(2, 3, 3);
123 sync;
124 },
125 [ $right->{id} ],
126 'button 2 moves focus right';
127
128 subtest 'button 3 moves focus left', \&focus_subtest,
129 sub {
130 xtest_button_press(3, 3, 3);
131 xtest_button_release(3, 3, 3);
132 sync;
133 },
134 [ $left->{id} ],
135 'button 3 moves focus left';
136
137 subtest 'button 4 moves focus right', \&focus_subtest,
138 sub {
139 xtest_button_press(4, 3, 3);
140 xtest_button_release(4, 3, 3);
141 sync;
142 },
143 [ $right->{id} ],
144 'button 4 moves focus right';
145
146 subtest 'button 5 moves focus left', \&focus_subtest,
147 sub {
148 xtest_button_press(5, 3, 3);
149 xtest_button_release(5, 3, 3);
150 sync;
151 },
152 [ $left->{id} ],
153 'button 5 moves focus left';
154
155 # Test --release flag with bar bindsym.
156 # See issue: #3068.
157
158 my $old_focus = get_focused($ws);
159 subtest 'button 6 does not move focus while pressed', \&focus_subtest,
160 sub {
161 xtest_button_press(6, 3, 3);
162 sync;
163 },
164 [],
165 'button 6 does not move focus while pressed';
166 is(get_focused($ws), $old_focus, 'focus unchanged');
167
168 subtest 'button 6 release moves focus right', \&focus_subtest,
169 sub {
170 xtest_button_release(6, 3, 3);
171 sync;
172 },
173 [ $right->{id} ],
174 'button 6 release moves focus right';
175
176 # Test same bindsym button with and without --release.
177
178 subtest 'button 7 press moves focus left', \&focus_subtest,
179 sub {
180 xtest_button_press(7, 3, 3);
181 sync;
182 },
183 [ $left->{id} ],
184 'button 7 press moves focus left';
185
186 subtest 'button 7 release moves focus right', \&focus_subtest,
187 sub {
188 xtest_button_release(7, 3, 3);
189 sync;
190 },
191 [ $right->{id} ],
192 'button 7 release moves focus right';
193
194 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Test reconfiguration of dock clients.
17 # Ticket: #1883
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 fake-outputs 1024x768+0+0,1024x768+1024+0
23
24 bar {
25 # Disable i3bar, which is also a dock client.
26 i3bar_command :
27 }
28 EOT
29
30 my ($window, $rect);
31 my (@docks);
32
33 ###############################################################################
34 # 1: Given two screens A and B and a dock client on screen A, when the dock
35 # client is reconfigured to be positioned on screen B, then the client is
36 # moved to the correct position.
37 ###############################################################################
38
39 $window = open_window({
40 window_type => $x->atom(name => '_NET_WM_WINDOW_TYPE_DOCK')
41 });
42
43 $rect = $window->rect;
44 is($rect->x, 0, 'sanity check: dock client is on the left screen');
45
46 $window->rect(X11::XCB::Rect->new(x => 1024, y => 0, width => 1024, height => 30));
47 sync_with_i3;
48
49 @docks = get_dock_clients;
50 is(@docks, 1, 'there is still exactly one dock');
51
52 is($docks[0]->{rect}->{x}, 1024, 'dock client has moved to the other screen');
53
54 ###############################################################################
55
56 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ticket: #1378
17 use i3test;
18 use X11::XCB qw(:all);
19
20 sub get_ewmh_window {
21 my $cookie = $x->get_property(
22 0,
23 $x->get_root_window(),
24 $x->atom(name => '_NET_SUPPORTING_WM_CHECK')->id,
25 $x->atom(name => 'WINDOW')->id,
26 0,
27 4096
28 );
29
30 my $reply = $x->get_property_reply($cookie->{sequence});
31 my $len = $reply->{length};
32 return undef if $len == 0;
33
34 return unpack("L", $reply->{value});
35 }
36
37 my $window = open_window;
38 sync_with_i3;
39 is($x->input_focus, $window->id, 'sanity check: window has input focus');
40 cmd 'kill';
41 sync_with_i3;
42 is($x->input_focus, get_ewmh_window(), 'focus falls back to the EWMH window');
43
44 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether 'workspace next' works correctly.
17 #
18 use List::Util qw(first);
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24 EOT
25
26 sub assert_next {
27 my ($expected) = @_;
28
29 cmd 'workspace next';
30 # We need to sync after changing focus to a different output to wait for the
31 # EnterNotify to be processed, otherwise it will be processed at some point
32 # later in time and mess up our subsequent tests.
33 sync_with_i3;
34
35 is(focused_ws, $expected, "workspace $expected focused");
36 }
37
38 sub assert_prev {
39 my ($expected) = @_;
40
41 cmd 'workspace prev';
42 # We need to sync after changing focus to a different output to wait for the
43 # EnterNotify to be processed, otherwise it will be processed at some point
44 # later in time and mess up our subsequent tests.
45 sync_with_i3;
46
47 is(focused_ws, $expected, "workspace $expected focused");
48 }
49
50 sync_with_i3;
51 $x->root->warp_pointer(0, 0);
52 sync_with_i3;
53
54 ################################################################################
55 # Setup workspaces so that they stay open (with an empty container).
56 # open_window ensures, this
57 #
58 # numbered named
59 # output 1 (left) : 1, 2, 3, 6, 7, B, F, C
60 # output 2 (right): 4, 5, A, D, E
61 #
62 ################################################################################
63
64 cmd 'focus output left';
65 cmd 'workspace A'; open_window;
66 cmd 'workspace D'; open_window;
67 cmd 'workspace 4'; open_window;
68 cmd 'workspace 5'; open_window;
69 cmd 'workspace E'; open_window;
70
71 cmd 'focus output right';
72 cmd 'workspace 1'; open_window;
73 cmd 'workspace 2'; open_window;
74 cmd 'workspace B'; open_window;
75 cmd 'workspace 3'; open_window;
76 cmd 'workspace F'; open_window;
77 cmd 'workspace 6'; open_window;
78 cmd 'workspace C'; open_window;
79 cmd 'workspace 7'; open_window;
80
81 ################################################################################
82 # Use workspace next and verify the correct order.
83 # numbered -> numerical sort
84 # named -> sort by creation time
85 ################################################################################
86 cmd 'workspace 1';
87 is(focused_ws, '1', 'back on workspace 1');
88
89 assert_next('2');
90 assert_next('3');
91 assert_next('4');
92 assert_next('5');
93 assert_next('6');
94 assert_next('7');
95
96 assert_next('B');
97 assert_next('F');
98 assert_next('C');
99 assert_next('A');
100 assert_next('D');
101 assert_next('E');
102 assert_next('1');
103
104 cmd 'workspace 1';
105 is(focused_ws, '1', 'back on workspace 1');
106
107 assert_prev('E');
108 assert_prev('D');
109 assert_prev('A');
110 assert_prev('C');
111 assert_prev('F');
112 assert_prev('B');
113
114 assert_prev('7');
115 assert_prev('6');
116 assert_prev('5');
117 assert_prev('4');
118 assert_prev('3');
119 assert_prev('2');
120 assert_prev('1');
121
122
123 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for _NET_WM_DESKTOP.
17 # Ticket: #2153
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 bar {
23 status_command i3status
24 }
25 EOT
26 use X11::XCB qw(:all);
27
28 ###############################################################################
29
30 sub get_net_wm_desktop {
31 sync_with_i3;
32
33 my ($con) = @_;
34 my $cookie = $x->get_property(
35 0,
36 $con->{id},
37 $x->atom(name => '_NET_WM_DESKTOP')->id,
38 $x->atom(name => 'CARDINAL')->id,
39 0,
40 1
41 );
42
43 my $reply = $x->get_property_reply($cookie->{sequence});
44 return undef if $reply->{length} != 1;
45
46 return unpack("L", $reply->{value});
47 }
48
49 sub send_net_wm_desktop {
50 my ($con, $idx) = @_;
51 my $msg = pack "CCSLLLLLL",
52 X11::XCB::CLIENT_MESSAGE, 32, 0,
53 $con->{id},
54 $x->atom(name => '_NET_WM_DESKTOP')->id,
55 $idx, 0, 0, 0, 0;
56
57 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
58 sync_with_i3;
59 }
60
61 sub open_window_with_net_wm_desktop {
62 my $idx = shift;
63 my $window = open_window(
64 before_map => sub {
65 my ($window) = @_;
66 $x->change_property(
67 PROP_MODE_REPLACE,
68 $window->id,
69 $x->atom(name => '_NET_WM_DESKTOP')->id,
70 $x->atom(name => 'CARDINAL')->id,
71 32, 1,
72 pack('L', $idx),
73 );
74 },
75 dont_map => 1,
76 );
77
78 # We don’t wait for MapNotify and instead sync with i3 so that we don’t need
79 # to encounter the full timeout of 4s when opening a window on a non-visible
80 # workspace.
81 $window->map;
82 sync_with_i3;
83
84 return $window;
85 }
86
87 ###############################################################################
88 # Upon managing a window which does not set _NET_WM_DESKTOP, the property is
89 # set on the window.
90 ###############################################################################
91
92 cmd 'workspace 1';
93 my $con = open_window;
94
95 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set upon managing a window');
96
97 kill_all_windows;
98
99 ###############################################################################
100 # Upon managing a window which sets _NET_WM_DESKTOP, the window is moved to
101 # the specified desktop.
102 ###############################################################################
103
104 cmd 'workspace 0';
105 open_window;
106 cmd 'workspace 1';
107 open_window;
108 cmd 'workspace 2';
109 open_window;
110
111 $con = open_window_with_net_wm_desktop(1);
112
113 is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP still has the correct value');
114 is_num_children('1', 2, 'The window was moved to workspace 1');
115
116 kill_all_windows;
117
118 ###############################################################################
119 # Upon managing a window which sets _NET_WM_DESKTOP to the appropriate value,
120 # the window is made sticky and floating.
121 ###############################################################################
122
123 cmd 'workspace 0';
124 $con = open_window_with_net_wm_desktop(0xFFFFFFFF);
125
126 is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP still has the correct value');
127 is(@{get_ws('0')->{floating_nodes}}, 1, 'The window is floating');
128 ok(get_ws('0')->{floating_nodes}->[0]->{nodes}->[0]->{sticky}, 'The window is sticky');
129
130 kill_all_windows;
131
132 ###############################################################################
133 # _NET_WM_DESKTOP is updated when the window is moved to another workspace
134 # on the same output.
135 ###############################################################################
136
137 cmd 'workspace 0';
138 open_window;
139 cmd 'workspace 1';
140 open_window;
141 cmd 'workspace 0';
142 $con = open_window;
143
144 cmd 'move window to workspace 1';
145
146 is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window');
147
148 kill_all_windows;
149
150 ###############################################################################
151 # _NET_WM_DESKTOP is updated when the floating window is moved to another
152 # workspace on the same output.
153 ###############################################################################
154
155 cmd 'workspace 0';
156 open_window;
157 cmd 'workspace 1';
158 open_window;
159 cmd 'workspace 0';
160 $con = open_window;
161 cmd 'floating enable';
162
163 cmd 'move window to workspace 1';
164
165 is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window');
166
167 kill_all_windows;
168
169 ###############################################################################
170 # _NET_WM_DESKTOP is removed when the window is withdrawn.
171 ###############################################################################
172
173 $con = open_window;
174 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set (sanity check)');
175
176 $con->unmap;
177 wait_for_unmap($con);
178
179 is(get_net_wm_desktop($con), undef, '_NET_WM_DESKTOP is removed');
180
181 kill_all_windows;
182
183 ###############################################################################
184 # A _NET_WM_DESKTOP client message sent to the root window moves a window
185 # to the correct workspace.
186 ###############################################################################
187
188 cmd 'workspace 0';
189 open_window;
190 cmd 'workspace 1';
191 open_window;
192 cmd 'workspace 0';
193
194 $con = open_window;
195 is_num_children('0', 2, 'The window is on workspace 0');
196
197 send_net_wm_desktop($con, 1);
198
199 is_num_children('0', 1, 'The window is no longer on workspace 0');
200 is_num_children('1', 2, 'The window is now on workspace 1');
201 is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated');
202
203 kill_all_windows;
204
205 ###############################################################################
206 # A _NET_WM_DESKTOP client message sent to the root window can make a window
207 # sticky.
208 ###############################################################################
209
210 cmd 'workspace 0';
211 $con = open_window;
212
213 send_net_wm_desktop($con, 0xFFFFFFFF);
214
215 is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated');
216 is(@{get_ws('0')->{floating_nodes}}, 1, 'The window is floating');
217 ok(get_ws('0')->{floating_nodes}->[0]->{nodes}->[0]->{sticky}, 'The window is sticky');
218
219 kill_all_windows;
220
221 ###############################################################################
222 # _NET_WM_DESKTOP is updated when a new workspace with a lower number is
223 # opened and closed.
224 ###############################################################################
225
226 cmd 'workspace 1';
227 $con = open_window;
228 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)');
229
230 cmd 'workspace 0';
231 is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated');
232
233 kill_all_windows;
234
235 ###############################################################################
236 # _NET_WM_DESKTOP is updated when a window is made sticky by command.
237 ###############################################################################
238
239 cmd 'workspace 0';
240 $con = open_window;
241 cmd 'floating enable';
242 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)');
243
244 cmd 'sticky enable';
245 is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated');
246
247 kill_all_windows;
248
249 ###############################################################################
250 # _NET_WM_DESKTOP is updated when a window is made sticky by client message.
251 ###############################################################################
252
253 cmd 'workspace 0';
254 $con = open_window;
255 cmd 'floating enable';
256 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)');
257
258 my $msg = pack "CCSLLLLLL",
259 X11::XCB::CLIENT_MESSAGE, 32, 0,
260 $con->{id},
261 $x->atom(name => '_NET_WM_STATE')->id,
262 1,
263 $x->atom(name => '_NET_WM_STATE_STICKY')->id,
264 0, 0, 0;
265
266 $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
267 sync_with_i3;
268
269 is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated');
270
271 kill_all_windows;
272
273 ###############################################################################
274 # _NET_WM_DESKTOP is updated when a window is moved to the scratchpad.
275 ###############################################################################
276
277 cmd 'workspace 0';
278 $con = open_window;
279 cmd 'floating enable';
280 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)');
281
282 cmd 'move scratchpad';
283 is(get_net_wm_desktop($con), 0xFFFFFFFF, '_NET_WM_DESKTOP is updated');
284
285 cmd 'scratchpad show';
286 is(get_net_wm_desktop($con), 0, '_NET_WM_DESKTOP is set sanity check)');
287
288 kill_all_windows;
289
290 ###############################################################################
291
292 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Ticket: #2229
17 # Bug still in: 4.11-262-geb631ce
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 fake-outputs 400x400+0+0,400x400+400+0
23 workspace_auto_back_and_forth no
24 EOT
25
26 # Set it up such that workspace 3 is on the left output and
27 # workspace 4 is on the right output
28 cmd 'focus output fake-0';
29 open_window;
30 cmd 'workspace 3';
31 cmd 'focus output fake-1';
32 cmd 'workspace 4';
33 open_window;
34
35 cmd 'move workspace to output left';
36
37 ok(!workspace_exists('3'), 'workspace 3 has been closed');
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that fullscreen windows appear on the output indicated by
17 # their geometry
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 fake-outputs 1024x768+0+0,1024x768+1024+0
23 EOT
24 use List::Util qw(first);
25
26 # Helper functions
27 sub fullscreen($) {
28 my ($window) = @_;
29 $window->fullscreen(1);
30 }
31
32 sub find_window {
33 my ($nodes, $id) = @_;
34
35 foreach (@{$nodes}) {
36 return $_ if ($_->{window} // 0) == $id;
37 my $node = find_window($_->{nodes}, $id);
38 return $node if $node;
39 };
40 return undef;
41 }
42
43 # Create two fullscreen windows, each on different output
44 my $orig_rect1 = X11::XCB::Rect->new(x => 0, y => 0, width => 1024, height => 768);
45 my $orig_rect2 = X11::XCB::Rect->new(x => 1024, y => 0, width => 1024, height => 768);
46
47 my $win_on_first_output = open_window(rect => $orig_rect1,
48 before_map => \&fullscreen);
49
50 my $win_on_second_output = open_window(rect => $orig_rect2,
51 before_map => \&fullscreen);
52
53 sync_with_i3;
54
55 # Check that the windows are on the correct output
56 is_deeply(scalar $win_on_first_output->rect, $orig_rect1, "first window spans the first output");
57 is_deeply(scalar $win_on_second_output->rect, $orig_rect2, "second window spans the sencond output");
58
59 # Check that both windows remained fullscreen
60 my $tree = i3(get_socket_path())->get_tree->recv;
61
62 my $node1 = find_window($tree->{nodes}, $win_on_first_output->{id});
63 my $node2 = find_window($tree->{nodes}, $win_on_second_output->{id});
64
65 is($node1->{fullscreen_mode}, 1, "first window is fullscreen");
66 is($node2->{fullscreen_mode}, 1, "second window is fullscreen");
67
68 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for using X resources in the config.
17 # Ticket: #2130
18 use i3test i3_autostart => 0;
19 use X11::XCB qw(PROP_MODE_REPLACE);
20
21 sub get_marks {
22 return i3(get_socket_path())->get_marks->recv;
23 }
24
25 my $config = <<EOT;
26 # i3 config file (v4)
27 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
28
29 # This isn't necessarily what X resources are intended for, but it'll do the
30 # job for the test.
31 set_from_resource \$mark i3wm.mark none
32 for_window [class=worksforme] mark \$mark
33
34 set_from_resource \$othermark i3wm.doesnotexist none
35 for_window [class=doesnotworkforme] mark \$othermark
36
37 EOT
38
39 $x->change_property(
40 PROP_MODE_REPLACE,
41 $x->get_root_window(),
42 $x->atom(name => 'RESOURCE_MANAGER')->id,
43 $x->atom(name => 'STRING')->id,
44 32,
45 length('*mark: works'),
46 '*mark: works');
47 $x->flush;
48
49 my $pid = launch_with_config($config);
50
51 open_window(wm_class => 'worksforme');
52 sync_with_i3;
53 is_deeply(get_marks(), [ 'works' ], 'the resource has loaded correctly');
54
55 cmd 'kill';
56
57 open_window(wm_class => 'doesnotworkforme');
58 sync_with_i3;
59 is_deeply(get_marks(), [ 'none' ], 'the resource fallback was used');
60
61 exit_gracefully($pid);
62
63 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # TODO: Description of this file.
17 # Ticket: #999
18 # Bug still in: 4.13-12-g2ff3d9d
19 use File::Temp qw(tempfile);
20 use i3test i3_autostart => 0;
21
22 my $monitor_name = 'i3-fake-monitor';
23 my $output_name = 'i3-fake-output';
24
25 my $config = <<EOT;
26 # i3 config file (v4)
27 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
28
29 bar {
30 output $output_name
31 }
32 EOT
33
34 my ($outfh, $outname) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1);
35
36 # Prepare a RRGetMonitors reply, see A.2.4 in
37 # https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
38 my $reply = pack('cxSLLLLx[LLL]',
39 1, # reply
40 0, # sequence (will be filled in by inject_randr15)
41 # 60 = length($reply) + length($monitor1)
42 # 32 = minimum X11 reply length
43 (60-32) / 4, # length in words
44 0, # timestamp TODO
45 1, # nmonitors
46 1); # noutputs
47
48 # Manually intern _NET_CURRENT_DESKTOP as $x->atom will not create atoms if
49 # they are not yet interned.
50 my $atom_cookie = $x->intern_atom(0, length($monitor_name), $monitor_name);
51 my $monitor_name_atom = $x->intern_atom_reply($atom_cookie->{sequence})->{atom};
52
53 # MONITORINFO is defined in A.1.1 in
54 # https://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
55 my $monitor1 = pack('LccSssSSLLL',
56 $monitor_name_atom, # name (ATOM)
57 1, # primary
58 1, # automatic
59 1, # ncrtcs
60 0, # x
61 0, # y
62 3840, # width in pixels
63 2160, # height in pixels
64 520, # width in millimeters
65 290, # height in millimeters
66 12345); # output ID #0
67
68 print $outfh $reply;
69 print $outfh $monitor1;
70
71 close($outfh);
72
73 # Prepare a RRGetOutputInfo reply as well; see RRGetOutputInfo in
74 # https://www.x.org/releases/current/doc/randrproto/randrproto.txt
75 ($outfh, my $outname_moninfo) = tempfile('i3-randr15reply-XXXXXX', UNLINK => 1);
76 my $moninfo = pack('cxSLLLx[LLccSSSS]S a* x!4',
77 1, # reply
78 0, # sequence (will be filled in by inject_randr15)
79 # 36 = length($moninfo) (without name and padding)
80 # 32 = minimum X11 reply length
81 ((36 + length($output_name) - 32) + 3) / 4, # length in words
82 0, # timestamp TODO
83 12345, # CRTC
84 length($output_name), # length of name
85 $output_name); # name
86
87 print $outfh $moninfo;
88 close($outfh);
89
90 my $pid = launch_with_config($config,
91 inject_randr15 => $outname,
92 inject_randr15_outputinfo => $outname_moninfo);
93
94 my $tree = i3->get_tree->recv;
95 my @outputs = map { $_->{name} } @{$tree->{nodes}};
96 is_deeply(\@outputs, [ '__i3', $monitor_name ], 'outputs are __i3 and the fake monitor');
97
98 my ($output_data) = grep { $_->{name} eq $monitor_name } @{$tree->{nodes}};
99 is_deeply($output_data->{rect}, {
100 width => 3840,
101 height => 2160,
102 x => 0,
103 y => 0,
104 }, "Fake output at 3840x2160+0+0");
105
106 # Verify that i3 canonicalizes RandR output names to i3 output names
107 # (RandR monitor names) for bar configs
108
109 my $bars = i3->get_bar_config()->recv;
110 is(@$bars, 1, 'one bar configured');
111
112 my $bar_id = shift @$bars;
113
114 my $bar_config = i3->get_bar_config($bar_id)->recv;
115 is_deeply($bar_config->{outputs}, [ $monitor_name ], 'bar_config output name is normalized');
116
117 exit_gracefully($pid);
118
119 ################################################################################
120 # Verify that adding monitors with RandR 1.5 results in i3 outputs.
121 ################################################################################
122
123 # When inject_randr15 is defined but false, fake-xinerama will be turned off,
124 # but inject_randr15 will not actually be used.
125 $pid = launch_with_config($config, inject_randr15 => '');
126
127 $tree = i3->get_tree->recv;
128 @outputs = map { $_->{name} } @{$tree->{nodes}};
129 is_deeply(\@outputs, [ '__i3', 'default' ], 'outputs are __i3 and default');
130
131 SKIP: {
132 skip 'xrandr --setmonitor failed (xrandr too old?)', 1 unless
133 system(q|xrandr --setmonitor up2414q 3840/527x2160/296+1280+0 none|) == 0;
134
135 sync_with_i3;
136
137 $tree = i3->get_tree->recv;
138 @outputs = map { $_->{name} } @{$tree->{nodes}};
139 is_deeply(\@outputs, [ '__i3', 'default', 'up2414q' ], 'outputs are __i3, default and up2414q');
140 }
141
142 exit_gracefully($pid);
143
144 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Verifies i3 doesn’t warp when a new floating window is opened under the cursor
17 # over an unfocused workspace.
18 # Ticket: #2681
19 # Bug still in: 4.13-210-g80c23afa
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0,1024x768+1024+0
25
26 focus_follows_mouse no
27 EOT
28
29 cmd 'focus output fake-0';
30 my $s0_ws = fresh_workspace;
31
32 cmd 'focus output fake-1';
33 my $s1_ws = fresh_workspace;
34 open_window;
35
36 # Move mouse to fake-0
37 sync_with_i3;
38 $x->root->warp_pointer(500, 0);
39 sync_with_i3;
40
41 my $dropdown = open_floating_window;
42 $dropdown->rect(X11::XCB::Rect->new(x => 1, y => 1, width => 100, height => 100));
43 sync_with_i3;
44
45 my $cookie = $x->query_pointer($dropdown->{id});
46 my $reply = $x->query_pointer_reply($cookie->{sequence});
47 cmp_ok($reply->{root_x}, '<', 1024, 'pointer still on fake-0');
48 cmp_ok($reply->{root_y}, '<', 768, 'pointer still on fake-0');
49
50 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests whether 'workspace next' works correctly.
17 #
18 use List::Util qw(first);
19 use i3test i3_config => <<EOT;
20 # i3 config file (v4)
21 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
22
23 fake-outputs 1024x768+0+0,1024x768+1024+0
24 EOT
25
26 sub assert_next {
27 my ($expected) = @_;
28
29 cmd 'workspace next';
30 # We need to sync after changing focus to a different output to wait for the
31 # EnterNotify to be processed, otherwise it will be processed at some point
32 # later in time and mess up our subsequent tests.
33 sync_with_i3;
34
35 is(focused_ws, $expected, "workspace $expected focused");
36 }
37
38 sub assert_prev {
39 my ($expected) = @_;
40
41 cmd 'workspace prev';
42 # We need to sync after changing focus to a different output to wait for the
43 # EnterNotify to be processed, otherwise it will be processed at some point
44 # later in time and mess up our subsequent tests.
45 sync_with_i3;
46
47 is(focused_ws, $expected, "workspace $expected focused");
48 }
49
50 sync_with_i3;
51 $x->root->warp_pointer(0, 0);
52 sync_with_i3;
53
54 ################################################################################
55 # Setup workspaces so that they stay open (with an empty container).
56 # open_window ensures, this
57 #
58 # numbered named
59 # output 1 (left) : 1, 2, 3, 6, 7, B, F, C
60 # output 2 (right): 4, 5, A, D, E
61 #
62 ################################################################################
63
64 cmd 'focus output right';
65 cmd 'workspace A'; open_window;
66 cmd 'workspace D'; open_window;
67 cmd 'workspace 4'; open_window;
68 cmd 'workspace 5'; open_window;
69 cmd 'workspace E'; open_window;
70
71 cmd 'focus output left';
72 cmd 'workspace 1'; open_window;
73 cmd 'workspace 2'; open_window;
74 cmd 'workspace B'; open_window;
75 cmd 'workspace 3'; open_window;
76 cmd 'workspace F'; open_window;
77 cmd 'workspace 6'; open_window;
78 cmd 'workspace C'; open_window;
79 cmd 'workspace 7'; open_window;
80
81 ################################################################################
82 # Use workspace next and verify the correct order.
83 # numbered -> numerical sort
84 # named -> sort by creation time
85 ################################################################################
86 cmd 'workspace 1';
87 is(focused_ws, '1', 'back on workspace 1');
88
89 assert_next('2');
90 assert_next('3');
91 assert_next('4');
92 assert_next('5');
93 assert_next('6');
94 assert_next('7');
95
96 assert_next('B');
97 assert_next('F');
98 assert_next('C');
99 assert_next('A');
100 assert_next('D');
101 assert_next('E');
102 assert_next('1');
103
104 cmd 'workspace 1';
105 is(focused_ws, '1', 'back on workspace 1');
106
107 assert_prev('E');
108 assert_prev('D');
109 assert_prev('A');
110 assert_prev('C');
111 assert_prev('F');
112 assert_prev('B');
113
114 assert_prev('7');
115 assert_prev('6');
116 assert_prev('5');
117 assert_prev('4');
118 assert_prev('3');
119 assert_prev('2');
120 assert_prev('1');
121
122
123 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests for _NET_WM_DESKTOP.
17 # Ticket: #2153
18 use i3test i3_config => <<EOT;
19 # i3 config file (v4)
20 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
21
22 workspace "0" output "fake-0"
23 workspace "1" output "fake-0"
24 workspace "2" output "fake-0"
25 workspace "10" output "fake-1"
26 workspace "11" output "fake-1"
27 workspace "12" output "fake-1"
28
29 fake-outputs 1024x768+0+0,1024x768+1024+0
30 EOT
31 use X11::XCB qw(:all);
32
33 sub get_net_wm_desktop {
34 sync_with_i3;
35
36 my ($con) = @_;
37 my $cookie = $x->get_property(
38 0,
39 $con->{id},
40 $x->atom(name => '_NET_WM_DESKTOP')->id,
41 $x->atom(name => 'CARDINAL')->id,
42 0,
43 1
44 );
45
46 my $reply = $x->get_property_reply($cookie->{sequence});
47 return undef if $reply->{length} != 1;
48
49 return unpack("L", $reply->{value});
50 }
51
52 ###############################################################################
53 # _NET_WM_DESKTOP is updated when the window is moved to another workspace
54 # on another output.
55 ###############################################################################
56
57 cmd 'workspace 0';
58 open_window;
59 cmd 'workspace 10';
60 open_window;
61 cmd 'workspace 0';
62 my $con = open_window;
63
64 cmd 'move window to workspace 10';
65
66 is(get_net_wm_desktop($con), 1, '_NET_WM_DESKTOP is updated when moving the window');
67
68 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that windows inside containers with a single child do not jump
17 # over other containers and erratically move across outputs.
18 # Ticket: #2466
19 # Bug still in: 4.14-63-g75d11820
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 640x480+0+0,640x480+640+0
25
26 workspace left output fake-0
27 workspace right output fake-1
28 EOT
29
30 cmd 'workspace right';
31
32 # ┌───────────────────────────┐ ┌───────────────────────────┐
33 # │ Output 1 │ │ Output 2 - splith │
34 # │ │ │┌───────────┐┌────────────┐│
35 # │ │ ││ ││ splitv ││
36 # │ │ ││ ││╔══════════╗││
37 # │ │ ││ ││║ ║││
38 # │ (empty workspace) │ ││ first ││║ second ║││
39 # │ │ ││ ││║ ║││
40 # │ │ ││ ││║ ║││
41 # │ │ ││ ││╚══════════╝││
42 # │ │ │└───────────┘└────────────┘│
43 # └───────────────────────────┘ └───────────────────────────┘
44 #
45 # Moving "second" left shouldn't cause it to jump over to output 1.
46
47 my $first = open_window;
48 my $second = open_window;
49
50 # Put the second window into its own splitv container
51 cmd 'split v';
52
53 is_num_children('left', 0, 'No children on left');
54 is_num_children('right', 2, 'Two children on right');
55
56 # Move the second window to the left
57 cmd 'move left';
58
59 is_num_children('left', 0, 'Still no children on left');
60 is_num_children('right', 2, 'Still two children on right');
61
62 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that i3bars configured to use the primary output do not have
17 # their output names canonicalized to something other than "primary".
18 # Ticket: #2948
19 # Bug still in: 4.14-93-ga3a7d04a
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 fake-outputs 1024x768+0+0P
25
26 bar {
27 output primary
28 }
29 EOT
30
31 my $bars = i3->get_bar_config()->recv;
32 is(@$bars, 1, 'one bar configured');
33
34 my $bar_id = shift @$bars;
35
36 my $bar_config = i3->get_bar_config($bar_id)->recv;
37 is_deeply($bar_config->{outputs}, [ "primary" ], 'bar_config output is primary');
38
39 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • http://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • http://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • http://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that focus does not wrap when focus_wrapping is disabled in
17 # the configuration.
18 # Ticket: #2352
19 # Bug still in: 4.14-72-g6411130c
20 use i3test i3_config => <<EOT;
21 # i3 config file (v4)
22 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
23
24 focus_wrapping no
25 EOT
26
27 sub test_orientation {
28 my ($orientation, $prev, $next) = @_;
29 my $tmp = fresh_workspace;
30
31 cmd "split $orientation";
32
33 my $win1 = open_window;
34 my $win2 = open_window;
35
36 is($x->input_focus, $win2->id, "Second window focused initially");
37 cmd "focus $prev";
38 is($x->input_focus, $win1->id, "First window focused");
39 cmd "focus $prev";
40 is($x->input_focus, $win1->id, "First window still focused");
41 cmd "focus $next";
42 is($x->input_focus, $win2->id, "Second window focused");
43 cmd "focus $next";
44 is($x->input_focus, $win2->id, "Second window still focused");
45 }
46
47 test_orientation('v', 'up', 'down');
48 test_orientation('h', 'left', 'right');
49
50 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests that the socket file is cleaned up properly after gracefully
17 # shutting down i3 via SIGTERM.
18 # Ticket: #3049
19 use i3test i3_autostart => 0;
20
21 my $config = <<EOT;
22 # i3 config file (v4)
23 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
24 EOT
25
26 my $pid = launch_with_config($config, dont_add_socket_path => 1);
27 my $socket = get_socket_path();
28 ok(-S $socket, "socket $socket exists");
29
30 exit_forcefully($pid, 'TERM');
31
32 ok(!-e $socket, "socket $socket no longer exists");
33
34 done_testing;
0 #!perl
1 # vim:ts=4:sw=4:expandtab
2 #
3 # Please read the following documents before working on tests:
4 # • https://build.i3wm.org/docs/testsuite.html
5 # (or docs/testsuite)
6 #
7 # • https://build.i3wm.org/docs/lib-i3test.html
8 # (alternatively: perldoc ./testcases/lib/i3test.pm)
9 #
10 # • https://build.i3wm.org/docs/ipc.html
11 # (or docs/ipc)
12 #
13 # • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
14 # (unless you are already familiar with Perl)
15 #
16 # Tests resizing tiling containers
17 use i3test;
18
19 ############################################################
20 # resize horizontally
21 ############################################################
22
23 my $tmp = fresh_workspace;
24
25 cmd 'split h';
26
27 my $left = open_window;
28 my $right = open_window;
29
30 diag("left = " . $left->id . ", right = " . $right->id);
31
32 is($x->input_focus, $right->id, 'Right window focused');
33
34 cmd 'resize set 75 ppt 0 ppt';
35
36 my ($nodes, $focus) = get_ws_content($tmp);
37
38 cmp_float($nodes->[0]->{percent}, 0.25, 'left window got only 25%');
39 cmp_float($nodes->[1]->{percent}, 0.75, 'right window got 75%');
40
41 # Same but use the 'width' keyword.
42 cmd 'resize set width 80 ppt';
43
44 ($nodes, $focus) = get_ws_content($tmp);
45
46 cmp_float($nodes->[0]->{percent}, 0.20, 'left window got 20%');
47 cmp_float($nodes->[1]->{percent}, 0.80, 'right window got 80%');
48
49 # Same but with px.
50 cmd 'resize set width 200 px';
51
52 ($nodes, $focus) = get_ws_content($tmp);
53
54 cmp_float($nodes->[1]->{rect}->{width}, 200, 'right window got 200 px');
55
56 ############################################################
57 # resize vertically
58 ############################################################
59
60 $tmp = fresh_workspace;
61
62 cmd 'split v';
63
64 my $top = open_window;
65 my $bottom = open_window;
66
67 diag("top = " . $top->id . ", bottom = " . $bottom->id);
68
69 is($x->input_focus, $bottom->id, 'Bottom window focused');
70
71 cmd 'resize set 0 ppt 75 ppt';
72
73 ($nodes, $focus) = get_ws_content($tmp);
74
75 cmp_float($nodes->[0]->{percent}, 0.25, 'top window got only 25%');
76 cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
77
78 # Same but use the 'height' keyword.
79 cmd 'resize set height 80 ppt';
80
81 ($nodes, $focus) = get_ws_content($tmp);
82
83 cmp_float($nodes->[0]->{percent}, 0.20, 'top window got 20%');
84 cmp_float($nodes->[1]->{percent}, 0.80, 'bottom window got 80%');
85
86 # Same but with px.
87 cmd 'resize set height 200 px';
88
89 ($nodes, $focus) = get_ws_content($tmp);
90
91 cmp_float($nodes->[1]->{rect}->{height}, 200, 'bottom window got 200 px');
92
93 ############################################################
94 # resize horizontally and vertically
95 ############################################################
96
97 $tmp = fresh_workspace;
98
99 cmd 'split h';
100 $left = open_window;
101 my $top_right = open_window;
102 cmd 'split v';
103 my $bottom_right = open_window;
104
105 diag("left = " . $left->id . ", top-right = " . $top_right->id . ", bottom-right = " . $bottom_right->id);
106
107 is($x->input_focus, $bottom_right->id, 'Bottom-right window focused');
108
109 cmd 'resize set 75 ppt 75 ppt';
110
111 ($nodes, $focus) = get_ws_content($tmp);
112
113 cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
114 cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
115 cmp_float($nodes->[1]->{nodes}->[0]->{percent}, 0.25, 'top-right window got 25%');
116 cmp_float($nodes->[1]->{nodes}->[1]->{percent}, 0.75, 'bottom-right window got 75%');
117
118 # Same but with px.
119 cmd 'resize set 155 px 135 px';
120
121 ($nodes, $focus) = get_ws_content($tmp);
122
123 cmp_float($nodes->[1]->{nodes}->[1]->{rect}->{width}, 155, 'bottom-right window got 155 px width');
124 cmp_float($nodes->[1]->{nodes}->[1]->{rect}->{height}, 135, 'bottom-right window got 135 px height');
125
126 # Without specifying mode
127 cmd 'resize set 201 131';
128
129 ($nodes, $focus) = get_ws_content($tmp);
130
131 cmp_float($nodes->[1]->{nodes}->[1]->{rect}->{width}, 201, 'bottom-right window got 201 px width');
132 cmp_float($nodes->[1]->{nodes}->[1]->{rect}->{height}, 131, 'bottom-right window got 131 px height');
133
134 # Mix ppt and px
135 cmd 'resize set 75 ppt 200 px';
136
137 ($nodes, $focus) = get_ws_content($tmp);
138
139 cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
140 cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
141 cmp_float($nodes->[1]->{nodes}->[1]->{rect}->{height}, 200, 'bottom-right window got 200 px height');
142
143 ############################################################
144 # resize from inside a tabbed container
145 ############################################################
146
147 $tmp = fresh_workspace;
148
149 cmd 'split h';
150
151 $left = open_window;
152 my $right1 = open_window;
153
154 cmd 'split h';
155 cmd 'layout tabbed';
156
157 my $right2 = open_window;
158
159 diag("left = " . $left->id . ", right1 = " . $right1->id . ", right2 = " . $right2->id);
160
161 is($x->input_focus, $right2->id, '2nd right window focused');
162
163 cmd 'resize set 75 ppt 0 ppt';
164
165 ($nodes, $focus) = get_ws_content($tmp);
166
167 cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
168 cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
169
170 # Same but with px.
171 cmd 'resize set 155 px';
172
173 ($nodes, $focus) = get_ws_content($tmp);
174
175 cmp_float($nodes->[1]->{rect}->{width}, 155, 'right container got 155 px');
176
177 ############################################################
178 # resize from inside a stacked container
179 ############################################################
180
181 $tmp = fresh_workspace;
182
183 cmd 'split h';
184
185 $left = open_window;
186 $right1 = open_window;
187
188 cmd 'split h';
189 cmd 'layout stacked';
190
191 $right2 = open_window;
192
193 diag("left = " . $left->id . ", right1 = " . $right1->id . ", right2 = " . $right2->id);
194
195 is($x->input_focus, $right2->id, '2nd right window focused');
196
197 cmd 'resize set 75 ppt 0 ppt';
198
199 ($nodes, $focus) = get_ws_content($tmp);
200
201 cmp_float($nodes->[0]->{percent}, 0.25, 'left container got 25%');
202 cmp_float($nodes->[1]->{percent}, 0.75, 'right container got 75%');
203
204 # Same but with px.
205 cmd 'resize set 130 px';
206
207 ($nodes, $focus) = get_ws_content($tmp);
208
209 cmp_float($nodes->[1]->{rect}->{width}, 130, 'right container got 130 px');
210
211 done_testing;
0 #
1 # Valgrind suppression file for i3 testcases
2 #
3 # Format specification:
4 # http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress
5 #
6
7 #
8 # GLib
9 #
10 {
11 Ignore fundamental GType registration
12 Memcheck:Leak
13 ...
14 fun:g_type_register_fundamental
15 ...
16 }
17
18 {
19 Ignore static GType registration
20 Memcheck:Leak
21 match-leak-kinds: possible
22 ...
23 fun:g_type_register_static
24 ...
25 }
26
27 {
28 Ignore GObject init function
29 Memcheck:Leak
30 match-leak-kinds: possible
31 ...
32 obj:/usr/lib/libgobject-2.0*
33 ...
34 fun:call_init.part.0
35 ...
36 }