Merge branch 'next'
This commit is contained in:
commit
2bf7793d4d
|
@ -14,6 +14,7 @@ testcases/_Inline
|
||||||
testcases/inc
|
testcases/inc
|
||||||
testcases/META.yml
|
testcases/META.yml
|
||||||
test.commands_parser
|
test.commands_parser
|
||||||
|
test.config_parser
|
||||||
*.output
|
*.output
|
||||||
*.tab.*
|
*.tab.*
|
||||||
*.yy.c
|
*.yy.c
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -30,7 +30,7 @@ dist: distclean
|
||||||
[ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
|
[ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
|
||||||
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
||||||
mkdir i3-${VERSION}
|
mkdir i3-${VERSION}
|
||||||
cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen common.mk Makefile i3-${VERSION}
|
cp i3-migrate-config-to-v4 generate-command-parser.pl i3-sensible-* i3-dmenu-desktop i3.config.keycodes DEPENDS LICENSE PACKAGE-MAINTAINER RELEASE-NOTES-${VERSION} i3.config i3.xsession.desktop i3.applications.desktop pseudo-doc.doxygen common.mk Makefile i3-${VERSION}
|
||||||
cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs testcases i3-${VERSION}
|
cp -r src libi3 i3-msg i3-nagbar i3-config-wizard i3bar i3-dump-log yajl-fallback include man parser-specs testcases i3-${VERSION}
|
||||||
# Only copy toplevel documentation (important stuff)
|
# Only copy toplevel documentation (important stuff)
|
||||||
mkdir i3-${VERSION}/docs
|
mkdir i3-${VERSION}/docs
|
||||||
|
|
|
@ -22,7 +22,7 @@ We also made the orientation (horizontal/vertical) part of the layout
|
||||||
|
|
||||||
To change a splith container into a splitv container, use either "layout
|
To change a splith container into a splitv container, use either "layout
|
||||||
splitv" or "layout toggle split". The latter command is used in the
|
splitv" or "layout toggle split". The latter command is used in the
|
||||||
default config as mod+l (formerly "layout default"). In case you have
|
default config as mod+e (formerly "layout default"). In case you have
|
||||||
"layout default" in your config file, it is recommended to just replace
|
"layout default" in your config file, it is recommended to just replace
|
||||||
it by "layout toggle split", which will work as "layout default" did
|
it by "layout toggle split", which will work as "layout default" did
|
||||||
before when pressing it once, but toggle between horizontal/vertical
|
before when pressing it once, but toggle between horizontal/vertical
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ Release notes for i3 v4.4 │
|
||||||
|
└──────────────────────────────┘
|
||||||
|
|
||||||
|
This is the i3 v4.4. This version is considered stable. All users of i3 are
|
||||||
|
strongly encouraged to upgrade.
|
||||||
|
|
||||||
|
An important under-the-hood change is that we now use the same parser
|
||||||
|
infrastructure for the configuration file as we do for the commands. This
|
||||||
|
makes maintenance and contributions easier and lets us finally escape the
|
||||||
|
insanity that is bison/flex.
|
||||||
|
|
||||||
|
In case there is a bug and your existing config does not work as expected
|
||||||
|
anymore, try using the --force-old-config-parser-v4.4-only flag when starting
|
||||||
|
i3 and please report a bug. This option will only be present in v4.4, so if
|
||||||
|
you don’t report a bug, you are willingly breaking your own config file.
|
||||||
|
|
||||||
|
Apart from that, there have been several little fixes and additions which make
|
||||||
|
i3 pay more attention to detail, particularly in the floating window area of
|
||||||
|
the code. See the changes/bugfixes list for more information.
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Changes in v4.4 │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
• add i3-dmenu-desktop, a dmenu wrapper which parses application .desktop
|
||||||
|
files and executes them.
|
||||||
|
• also use a custom parser for the config file
|
||||||
|
• i3.xsession.desktop is now standards-compliant
|
||||||
|
• ipc: you can now subscribe to an event called 'mode' (for binding modes)
|
||||||
|
• implement "move container to workspace back_and_forth"
|
||||||
|
• implement delayed urgency hint reset
|
||||||
|
• make "move workspace number" accept a default workspace name after the
|
||||||
|
number
|
||||||
|
• i3bar: allow child to specify start/stop signals to use in hide mode
|
||||||
|
• i3bar: add "urgent" to protocol, it unhides i3bar when in hide mode
|
||||||
|
• make parent of urgent containers also urgent
|
||||||
|
• add descriptive title to split containers (no more "another container")
|
||||||
|
• click to focus: clicking the root window focuses the relevant workspace
|
||||||
|
• display appropriate cursors when resizing or moving floating windows
|
||||||
|
• implement variable border widths for pixel/normal
|
||||||
|
• Implement moving workspaces as if they’re regular containers
|
||||||
|
• Maintain relative positioning when moving floating windows between outputs
|
||||||
|
• Focus the relevant workspace when clicking any container
|
||||||
|
• docs/ipc: remove unnecessary newline
|
||||||
|
• docs/ipc: add a warning to use an existing library
|
||||||
|
• shmlog: remove O_TRUNC flag for shm_open, we truncate on our own
|
||||||
|
• un-fullscreen as needed when moving fullscreen containers
|
||||||
|
• improve startup sequence termination conditions
|
||||||
|
• allow floating cons to be reached using 'focus parent'
|
||||||
|
• grab keys with all permutations of lock and numlock
|
||||||
|
• allow workspace contents to be moved if there are only floating children
|
||||||
|
• allow 'focus <direction>' to move out of non-global fullscreen containers
|
||||||
|
• exit with a proper error message when there are no outputs available
|
||||||
|
• skip floating cons in focus <child|parent> and stop them from being split
|
||||||
|
• focus windows when middle-clicking
|
||||||
|
• skip floating windows in the focus stack when moving through the tree
|
||||||
|
• docs/userguide: use $mod consistently
|
||||||
|
• keycode default config: s/bindcode/bindsym/
|
||||||
|
• implement smart popup_during_fullscreen mode
|
||||||
|
• docs/testsuite: add "installing the dependencies" section
|
||||||
|
• introduce new command to rename focused workspace
|
||||||
|
• libi3: use "pango:" prefix instead of "xft:" to avoid confusion
|
||||||
|
• ipc: add "current" and "old" containers to workspace events
|
||||||
|
• i3bar: add current binding mode indicator
|
||||||
|
• resizing floating windows now obeys the minimum/maximum size
|
||||||
|
• docs/userguide: document new_float option
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Bugfixes │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
• Bugfix: get_output_next() now works with non-aligned RandR setups
|
||||||
|
• Bugfix: close empty workspaces after cross-output move
|
||||||
|
• Bugfix: fix bottom line of tabbed decoration not continuous
|
||||||
|
• Bugfix: use correct coordinates for windows which are opened on a newly
|
||||||
|
created workspace due to assignments
|
||||||
|
• Bugfix: properly react to windows being unmapped before we can reparent
|
||||||
|
• Bugfix: send non-floating window with floating parent to scratchpad
|
||||||
|
• docs/userguide: document how to "un-scratchpad" a window
|
||||||
|
• Bugfix: don’t crash when dragged floating window closes
|
||||||
|
• Bugfix: draw h-split indicator at the correct position
|
||||||
|
• make the resize command honor criteria
|
||||||
|
• Bugfix: with one ws per output, don’t crash on cross-output moves
|
||||||
|
• Bugfix: correctly move floating windows to invisible workspaces
|
||||||
|
cross-output
|
||||||
|
• Bugfix: set workspace_layout in create_workspace_on_output
|
||||||
|
• fix fullscreen focus bug and corresponding test flaw
|
||||||
|
• i3bar: bugfix: don’t send workspace command when at beginning/end of workspace
|
||||||
|
• Bugfix: force rendering when the parent’s orientation changed
|
||||||
|
• Bugfix: fix workspace back_and_forth after displaying a scratchpad window
|
||||||
|
|
||||||
|
┌────────────────────────────┐
|
||||||
|
│ Thanks! │
|
||||||
|
└────────────────────────────┘
|
||||||
|
|
||||||
|
Thanks for testing, bugfixes, discussions and everything I forgot go out to:
|
||||||
|
|
||||||
|
Adrien Schildknecht, aksr, bitonic, chrysn, Conley Moorhous, darkraven, Deiz,
|
||||||
|
Emil Mikulic, Feh, flo, Francesco Mazzoli, hax404, joepd, Kacper Kowalik,
|
||||||
|
Markus, meaneye, Merovius, Michael Walle, moju, Moritz, noxxun, Oliver
|
||||||
|
Kiddle, Pauli Ervi, Pavel Löbl, Piotr, pkordy, Quentin Glidic, Sascha Kruse,
|
||||||
|
Sebastian Ullrich, Simon Elsbrock, slowpoke, strcat, Tblue, Tim, whitequark,
|
||||||
|
xeen, Yaroslav Molochko
|
||||||
|
|
||||||
|
-- Michael Stapelberg, 2012-12-12
|
|
@ -195,6 +195,7 @@ ifeq ($(V),0)
|
||||||
# echo-ing vars
|
# echo-ing vars
|
||||||
V_ASCIIDOC = echo ASCIIDOC $@;
|
V_ASCIIDOC = echo ASCIIDOC $@;
|
||||||
V_POD2HTML = echo POD2HTML $@;
|
V_POD2HTML = echo POD2HTML $@;
|
||||||
|
V_POD2MAN = echo POD2MAN $@;
|
||||||
V_A2X = echo A2X $@;
|
V_A2X = echo A2X $@;
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
#!/usr/bin/env perl
|
#!/usr/bin/env perl
|
||||||
# vim:ts=4:sw=4:expandtab
|
# vim:ts=4:sw=4:expandtab
|
||||||
# renders the layout tree using asymptote
|
# renders the layout tree using asymptote
|
||||||
|
#
|
||||||
|
# ./dump-asy.pl
|
||||||
|
# will render the entire tree
|
||||||
|
# ./dump-asy.pl 'name'
|
||||||
|
# will render the tree starting from the node with the specified name,
|
||||||
|
# e.g. ./dump-asy.pl 2 will render workspace 2 and below
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
@ -9,7 +15,7 @@ use AnyEvent::I3;
|
||||||
use File::Temp;
|
use File::Temp;
|
||||||
use v5.10;
|
use v5.10;
|
||||||
|
|
||||||
my $i3 = i3("/tmp/nestedcons");
|
my $i3 = i3();
|
||||||
|
|
||||||
my $tree = $i3->get_tree->recv;
|
my $tree = $i3->get_tree->recv;
|
||||||
|
|
||||||
|
@ -26,7 +32,13 @@ sub dump_node {
|
||||||
my $w = (defined($n->{window}) ? $n->{window} : "N");
|
my $w = (defined($n->{window}) ? $n->{window} : "N");
|
||||||
my $na = $n->{name};
|
my $na = $n->{name};
|
||||||
$na =~ s/#/\\#/g;
|
$na =~ s/#/\\#/g;
|
||||||
my $name = "($na, $o, $w)";
|
$na =~ s/_/\\_/g;
|
||||||
|
$na =~ s/~/\\textasciitilde{}/g;
|
||||||
|
my $type = 'leaf';
|
||||||
|
if (!defined($n->{window})) {
|
||||||
|
$type = $n->{orientation} . '-split';
|
||||||
|
}
|
||||||
|
my $name = qq|\\"$na\\" ($type)|;
|
||||||
|
|
||||||
print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
|
print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
|
||||||
|
|
||||||
|
@ -36,10 +48,29 @@ sub dump_node {
|
||||||
dump_node($_, $n) for @{$n->{nodes}};
|
dump_node($_, $n) for @{$n->{nodes}};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_node($tree);
|
sub find_node_with_name {
|
||||||
say $tmp "draw(n" . $tree->{id} . ", (0, 0));";
|
my ($node, $name) = @_;
|
||||||
|
|
||||||
|
return $node if ($node->{name} eq $name);
|
||||||
|
for my $child (@{$node->{nodes}}) {
|
||||||
|
my $res = find_node_with_name($child, $name);
|
||||||
|
return $res if defined($res);
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $start = shift;
|
||||||
|
my $root;
|
||||||
|
if ($start) {
|
||||||
|
# Find the specified node in the tree
|
||||||
|
$root = find_node_with_name($tree, $start);
|
||||||
|
} else {
|
||||||
|
$root = $tree;
|
||||||
|
}
|
||||||
|
dump_node($root);
|
||||||
|
say $tmp "draw(n" . $root->{id} . ", (0, 0));";
|
||||||
|
|
||||||
close($tmp);
|
close($tmp);
|
||||||
my $rep = "$tmp";
|
my $rep = "$tmp";
|
||||||
$rep =~ s/asy$/eps/;
|
$rep =~ s/asy$/eps/;
|
||||||
system("cd /tmp && asy $tmp && gv $rep && rm $rep");
|
system("cd /tmp && asy $tmp && gv --scale=-1000 --noresize --widgetless $rep && rm $rep");
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
i3-wm (4.2.1-0) unstable; urgency=low
|
i3-wm (4.4-1) experimental; urgency=low
|
||||||
|
|
||||||
* NOT YET RELEASED
|
* New upstream release
|
||||||
|
|
||||||
-- Michael Stapelberg <michael@stapelberg.de> Fri, 27 Jan 2012 19:34:11 +0000
|
-- Michael Stapelberg <stapelberg@debian.org> Tue, 11 Dec 2012 22:52:56 +0100
|
||||||
|
|
||||||
|
i3-wm (4.3-1) experimental; urgency=low
|
||||||
|
|
||||||
|
* New upstream release
|
||||||
|
|
||||||
|
-- Michael Stapelberg <stapelberg@debian.org> Wed, 19 Sep 2012 18:13:40 +0200
|
||||||
|
|
||||||
i3-wm (4.2-1) unstable; urgency=low
|
i3-wm (4.2-1) unstable; urgency=low
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
docs/debugging.html
|
docs/debugging.html
|
||||||
docs/debugging-release-version.html
|
|
||||||
docs/hacking-howto.html
|
docs/hacking-howto.html
|
||||||
docs/i3bar-protocol.html
|
docs/i3bar-protocol.html
|
||||||
docs/userguide.html
|
docs/userguide.html
|
||||||
|
|
|
@ -8,4 +8,5 @@ man/i3-migrate-config-to-v4.1
|
||||||
man/i3-sensible-pager.1
|
man/i3-sensible-pager.1
|
||||||
man/i3-sensible-editor.1
|
man/i3-sensible-editor.1
|
||||||
man/i3-sensible-terminal.1
|
man/i3-sensible-terminal.1
|
||||||
|
man/i3-dmenu-desktop.1
|
||||||
man/i3bar.1
|
man/i3bar.1
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
Description: list x-terminal-emulator as one of i3-sensible-terminal’s choices
|
||||||
|
Author: Michael Stapelberg <stapelberg@debian.org>
|
||||||
|
Origin: vendor
|
||||||
|
Forwarded: not-needed
|
||||||
|
Last-Update: 2011-12-28
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Index: i3-4.1.1/man/i3-sensible-terminal.man
|
Index: i3-4.1.1/man/i3-sensible-terminal.man
|
||||||
===================================================================
|
===================================================================
|
||||||
--- i3-4.1.1.orig/man/i3-sensible-terminal.man 2011-12-28 23:56:55.487581000 +0100
|
--- i3-4.1.1.orig/man/i3-sensible-terminal.man 2011-12-28 23:56:55.487581000 +0100
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
Index: i3-4.1.1/i3-sensible-terminal
|
Description: i3-sensible-terminal: try x-terminal-emulator first
|
||||||
|
Author: Michael Stapelberg <stapelberg@debian.org>
|
||||||
|
Origin: vendor
|
||||||
|
Forwarded: not-needed
|
||||||
|
Last-Update: 2012-09-19
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Index: i3-4.3/i3-sensible-terminal
|
||||||
===================================================================
|
===================================================================
|
||||||
--- i3-4.1.1.orig/i3-sensible-terminal 2011-12-28 23:51:52.455610236 +0100
|
--- i3-4.3.orig/i3-sensible-terminal 2012-09-19 18:08:09.000000000 +0200
|
||||||
+++ i3-4.1.1/i3-sensible-terminal 2011-12-28 23:52:00.826775027 +0100
|
+++ i3-4.3/i3-sensible-terminal 2012-09-19 18:32:06.393883488 +0200
|
||||||
@@ -4,11 +4,7 @@
|
@@ -4,11 +4,7 @@
|
||||||
#
|
#
|
||||||
# This script tries to exec a terminal emulator by trying some known terminal
|
# This script tries to exec a terminal emulator by trying some known terminal
|
||||||
|
@ -10,8 +18,8 @@ Index: i3-4.1.1/i3-sensible-terminal
|
||||||
-# Distributions/packagers should enhance this script with a
|
-# Distributions/packagers should enhance this script with a
|
||||||
-# distribution-specific mechanism to find the preferred terminal emulator. On
|
-# distribution-specific mechanism to find the preferred terminal emulator. On
|
||||||
-# Debian, there is the x-terminal-emulator symlink for example.
|
-# Debian, there is the x-terminal-emulator symlink for example.
|
||||||
-for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do
|
-for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
|
||||||
+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm; do
|
+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do
|
||||||
if which $terminal > /dev/null 2>&1; then
|
if which $terminal > /dev/null 2>&1; then
|
||||||
exec $terminal "$@"
|
exec $terminal "$@"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -38,7 +38,7 @@ override_dh_auto_build:
|
||||||
$(MAKE) -C docs
|
$(MAKE) -C docs
|
||||||
|
|
||||||
override_dh_installchangelogs:
|
override_dh_installchangelogs:
|
||||||
dh_installchangelogs RELEASE-NOTES-4.2
|
dh_installchangelogs RELEASE-NOTES-4.4
|
||||||
|
|
||||||
override_dh_install:
|
override_dh_install:
|
||||||
$(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
|
$(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
Debugging i3: How To
|
Debugging i3: How To
|
||||||
====================
|
====================
|
||||||
Michael Stapelberg <michael@i3wm.org>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
February 2012
|
December 2012
|
||||||
|
|
||||||
This document describes how to debug i3 suitably for sending us useful bug
|
This document describes how to debug i3 to send us useful bug
|
||||||
reports, even if you have no clue of C programming.
|
reports, even if you have no knowledge of C programming.
|
||||||
|
|
||||||
First of all: Thank you for being interested in debugging i3. It really means
|
Thank you for being interested in debugging i3. It really means
|
||||||
something to us to get your bug fixed. If you have any questions about the
|
something to us to get your bug fixed. If you have any questions about the
|
||||||
debugging and/or need further help, do not hesitate to contact us!
|
process and/or need further help, do not hesitate to contact us!
|
||||||
|
|
||||||
== Verify you are using the latest (development) version
|
== Verify you are using the latest (development) version
|
||||||
|
|
||||||
|
@ -21,25 +21,41 @@ i3 version 4.1.2-248-g51728ba (2012-02-12, branch "next")
|
||||||
|
|
||||||
Your version can look like this:
|
Your version can look like this:
|
||||||
|
|
||||||
4.1.2::
|
4.1.2 (release version)::
|
||||||
You are using a release version. Please
|
You are using a release version. In many cases, bugs are already
|
||||||
upgrade to a development version first, or read
|
fixed in the development version of i3. If they aren’t, we will still ask you
|
||||||
link:debugging-release-version.html[Debugging i3: How To (release version)].
|
to reproduce your error with the most recent development version of i3.
|
||||||
|
Therefore, please upgrade to a development version if you can.
|
||||||
|
|
||||||
4.1.2-248-g51728ba::
|
4.1.2-248-g51728ba (development version)::
|
||||||
Your version is 248 commits newer than 4.1.2, and the git revision of your
|
Your version is 248 commits newer than 4.1.2, and the git revision of your
|
||||||
version is +51728ba+. Go to http://code.i3wm.org/i3/commit/?h=next and see if
|
version is +51728ba+. Go to http://code.i3wm.org/i3/commit/?h=next and see if
|
||||||
the line "commit" starts with the same revision. If so, you are using the
|
the line "commit" starts with the same revision. If so, you are using the
|
||||||
latest version.
|
latest version.
|
||||||
|
|
||||||
Development versions of i3 have several properties which make debugging easier:
|
Development versions of i3 have logging enabled by default and are compiled
|
||||||
|
with debug symbols.
|
||||||
|
|
||||||
1. Shared memory debug logging is enabled by default. You do not have to enable
|
== Enabling logging
|
||||||
logging explicitly.
|
|
||||||
2. Core dumps are enabled by default.
|
If you are using a development version (see previous section), you don’t need
|
||||||
3. If you are using a version from the Debian/Ubuntu autobuilder, it is
|
to do anything -- skip to section 3.
|
||||||
compiled without optimization. Debug symbols are available in the i3-wm-dbg
|
|
||||||
package. When compiling i3 yourself, debug mode is the default.
|
If you are using a release version with a custom +~/.xsession+ (or xinitrc)
|
||||||
|
file, execute i3 with a line like this:
|
||||||
|
|
||||||
|
----------------------------------
|
||||||
|
# Use 25 MiB of RAM for debug logs
|
||||||
|
exec i3 --shmlog-size=26214400
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
If you are *NOT* using an +~/.xsession+ file but you just chose "i3" from the
|
||||||
|
list of sessions in your desktop manager (gdm, lxdm, …), edit
|
||||||
|
+/usr/share/xsessions/i3.desktop+ and replace the +Exec=i3+ line with:
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
Exec=i3 --shmlog-size=26214400
|
||||||
|
------------------------------
|
||||||
|
|
||||||
== Obtaining the debug logfile
|
== Obtaining the debug logfile
|
||||||
|
|
||||||
|
@ -48,35 +64,39 @@ crashed, the logfile provides all information necessary to debug the problem.
|
||||||
|
|
||||||
To save a compressed version of the logfile (suitable for attaching it to a
|
To save a compressed version of the logfile (suitable for attaching it to a
|
||||||
bugreport), use:
|
bugreport), use:
|
||||||
--------------------------------------------------------------------
|
|
||||||
i3-dump-log | gzip -9c > /tmp/i3.log.gz
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
This command does not depend on i3 (it also works when i3 currently displays
|
|
||||||
the crash dialog), but it requires a working X11 connection. When running it
|
|
||||||
from a virtual console (Ctrl-Alt-F1), use:
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
DISPLAY=:0 i3-dump-log | gzip -9c > /tmp/i3.log.gz
|
DISPLAY=:0 i3-dump-log | gzip -9c > /tmp/i3.log.gz
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
This command does not depend on i3 (it also works while i3 displays
|
||||||
|
the crash dialog), but it requires a working X11 connection.
|
||||||
|
|
||||||
|
== Compiling with debug symbols
|
||||||
|
|
||||||
|
To actually get useful backtraces, you should make sure that your version of i3
|
||||||
|
is compiled with debug symbols:
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
$ file `which i3`
|
||||||
|
/usr/bin/i3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
|
||||||
|
linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Notice the +not stripped+, which is the important part. If you have a version
|
||||||
|
which is stripped, please check whether your distribution provides debug
|
||||||
|
symbols (package +i3-wm-dbg+ on Debian for example) or if you can turn off
|
||||||
|
stripping. If nothing helps, please build i3 from source.
|
||||||
|
|
||||||
== Obtaining a backtrace
|
== Obtaining a backtrace
|
||||||
|
|
||||||
When i3 displays its crash dialog, do the following:
|
Once you have made sure that your i3 is compiled with debug symbols and the C
|
||||||
|
debugger +gdb+ is installed on your machine, you can let i3 generate a
|
||||||
|
backtrace in the crash dialog.
|
||||||
|
|
||||||
1. Switch to a virtual console (Ctrl-Alt-F1) or login from a different computer
|
After pressing "b" in the crash dialog, you will get a file called
|
||||||
2. Generate a backtrace (see below)
|
+/tmp/i3-backtrace.%d.%d.txt+ where the first +%d+ is replaced by i3’s process
|
||||||
3. Switch back to the crash dialog (Ctrl-Alt-F7)
|
id (PID) and the second one is incremented each time you generate a backtrace,
|
||||||
4. Restart i3 in-place (you will keep your session), continue working
|
starting at 0.
|
||||||
|
|
||||||
This is how you get a backtrace from a running i3 process:
|
|
||||||
|
|
||||||
-----------------
|
|
||||||
I3PID=$(pidof i3)
|
|
||||||
gdb /proc/$I3PID/exe $I3PID \
|
|
||||||
--batch --quiet \
|
|
||||||
--ex 'backtrace full' > /tmp/i3-backtrace.txt 2>&1
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
== Sending bug reports/debugging on IRC
|
== Sending bug reports/debugging on IRC
|
||||||
|
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
Debugging i3: How To (release version)
|
|
||||||
======================================
|
|
||||||
Michael Stapelberg <michael@i3wm.org>
|
|
||||||
February 2012
|
|
||||||
|
|
||||||
This document describes how to debug i3 suitably for sending us useful bug
|
|
||||||
reports, even if you have no clue of C programming.
|
|
||||||
|
|
||||||
First of all: Thank you for being interested in debugging i3. It really means
|
|
||||||
something to us to get your bug fixed. If you have any questions about the
|
|
||||||
debugging and/or need further help, do not hesitate to contact us!
|
|
||||||
|
|
||||||
NOTE: This document is for the release version of i3. If you are using a
|
|
||||||
development version, please see link:debugging.html[Debugging i3: How To]
|
|
||||||
instead.
|
|
||||||
|
|
||||||
== Consider using the development version
|
|
||||||
|
|
||||||
This document is for the release version of i3. In many cases, bugs are already
|
|
||||||
fixed in the development version of i3. If they aren’t, we will still ask you
|
|
||||||
to reproduce your error with the most recent development version of i3.
|
|
||||||
Therefore, please upgrade to a development version and continue reading at
|
|
||||||
link:debugging.html[Debugging i3: How To].
|
|
||||||
|
|
||||||
If you absolutely cannot upgrade to a development version of i3, you may
|
|
||||||
continue reading this document.
|
|
||||||
|
|
||||||
== Enabling logging
|
|
||||||
|
|
||||||
i3 logs useful information to stdout. To have a clearly defined place where log
|
|
||||||
files will be saved, you should redirect stdout and stderr in your
|
|
||||||
+~/.xsession+. While you’re at it, putting each run of i3 in a separate log
|
|
||||||
file with date/time in its filename is a good idea to not get confused about
|
|
||||||
the different log files later on.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
exec /usr/bin/i3 >~/i3log-$(date +'%F-%k-%M-%S') 2>&1
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
To enable verbose output and all levels of debug output (required when
|
|
||||||
attaching logfiles to bugreports), add the parameters +-V -d all+, like this:
|
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
exec /usr/bin/i3 -V -d all >~/i3log-$(date +'%F-%k-%M-%S') 2>&1
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
== Enabling core dumps
|
|
||||||
|
|
||||||
When i3 crashes, often you have the chance of getting a 'core dump' (an image
|
|
||||||
of the memory of the i3 process which can be loaded into a debugger). To get a
|
|
||||||
core dump, you have to make sure that the user limit for core dump files is set
|
|
||||||
high enough. Many systems ship with a default value which even forbids core
|
|
||||||
dumps completely. To disable the limit completely and thus enable core dumps,
|
|
||||||
use the following command (in your +~/.xsession+, before starting i3):
|
|
||||||
|
|
||||||
-------------------
|
|
||||||
ulimit -c unlimited
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Furthermore, to easily recognize core dumps and allow multiple of them, you
|
|
||||||
should set a custom core dump filename pattern, using a command like the
|
|
||||||
following:
|
|
||||||
|
|
||||||
---------------------------------------------
|
|
||||||
sudo sysctl -w kernel.core_pattern=core.%e.%p
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
This will generate files which have the executable’s file name (%e) and the
|
|
||||||
process id (%p) in it. You can save this setting across reboots using
|
|
||||||
+/etc/sysctl.conf+.
|
|
||||||
|
|
||||||
== Compiling with debug symbols
|
|
||||||
|
|
||||||
To actually get useful core dumps, you should make sure that your version of i3
|
|
||||||
is compiled with debug symbols, that is, that the symbols are not stripped
|
|
||||||
during the build process. You can check whether your executable contains
|
|
||||||
symbols by issuing the following command:
|
|
||||||
|
|
||||||
----------------
|
|
||||||
file $(which i3)
|
|
||||||
----------------
|
|
||||||
|
|
||||||
You should get an output like this:
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
/usr/bin/i3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
|
|
||||||
linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
Notice the +not stripped+, which is the important part. If you have a version
|
|
||||||
which is stripped, please have a look if your distribution provides debug
|
|
||||||
symbols (package +i3-wm-dbg+ on Debian for example) or if you can turn off
|
|
||||||
stripping. If nothing helps, please build i3 from source.
|
|
||||||
|
|
||||||
== Generating a backtrace
|
|
||||||
|
|
||||||
Once you have made sure that your i3 is compiled with debug symbols and that
|
|
||||||
core dumps are enabled, you can start making sense out of the core dumps.
|
|
||||||
|
|
||||||
Because the core dump depends on the original executable (and its debug
|
|
||||||
symbols), please do this as soon as you encounter the problem. If you
|
|
||||||
re-compile i3, your core dump might be useless afterwards.
|
|
||||||
|
|
||||||
Please install +gdb+, a debugger for C. No worries, you don’t need to learn it
|
|
||||||
now. Start gdb using the following command (replacing the actual name of the
|
|
||||||
core dump of course):
|
|
||||||
|
|
||||||
----------------------------
|
|
||||||
gdb $(which i3) core.i3.3849
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Then, generate a backtrace using:
|
|
||||||
|
|
||||||
--------------
|
|
||||||
backtrace full
|
|
||||||
--------------
|
|
||||||
|
|
||||||
== Sending bug reports/debugging on IRC
|
|
||||||
|
|
||||||
When sending bug reports, please paste the relevant part of the log (if in
|
|
||||||
doubt, please send us rather too much information than too less) and the whole
|
|
||||||
backtrace (if there was a core dump).
|
|
||||||
|
|
||||||
When debugging with us in IRC, be prepared to use a so called nopaste service
|
|
||||||
such as http://nopaste.info or http://pastebin.com because pasting large
|
|
||||||
amounts of text in IRC sometimes leads to incomplete lines (servers have line
|
|
||||||
length limitations) or flood kicks.
|
|
|
@ -5,8 +5,7 @@ ASCIIDOC = asciidoc
|
||||||
I3POD2HTML = ./docs/i3-pod2html
|
I3POD2HTML = ./docs/i3-pod2html
|
||||||
|
|
||||||
ASCIIDOC_NOTOC_TARGETS = \
|
ASCIIDOC_NOTOC_TARGETS = \
|
||||||
docs/debugging.html \
|
docs/debugging.html
|
||||||
docs/debugging-release-version.html
|
|
||||||
|
|
||||||
ASCIIDOC_TOC_TARGETS = \
|
ASCIIDOC_TOC_TARGETS = \
|
||||||
docs/hacking-howto.html \
|
docs/hacking-howto.html \
|
||||||
|
|
|
@ -44,10 +44,15 @@ understand the old protocol version, but in order to use the new one, you need
|
||||||
to provide the correct version. The header block is terminated by a newline and
|
to provide the correct version. The header block is terminated by a newline and
|
||||||
consists of a single JSON hash:
|
consists of a single JSON hash:
|
||||||
|
|
||||||
*Example*:
|
*Minimal example*:
|
||||||
----------------
|
------------------------------
|
||||||
{ "version": 1 }
|
{ "version": 1 }
|
||||||
----------------
|
------------------------------
|
||||||
|
|
||||||
|
*All features example*:
|
||||||
|
------------------------------
|
||||||
|
{ "version": 1, "stop_signal": 10, "cont_signal": 12 }
|
||||||
|
------------------------------
|
||||||
|
|
||||||
(Note that before i3 v4.3 the precise format had to be +{"version":1}+,
|
(Note that before i3 v4.3 the precise format had to be +{"version":1}+,
|
||||||
byte-for-byte.)
|
byte-for-byte.)
|
||||||
|
@ -93,6 +98,19 @@ You can find an example of a shell script which can be used as your
|
||||||
+status_command+ in the bar configuration at
|
+status_command+ in the bar configuration at
|
||||||
http://code.stapelberg.de/git/i3/tree/contrib/trivial-bar-script.sh?h=next
|
http://code.stapelberg.de/git/i3/tree/contrib/trivial-bar-script.sh?h=next
|
||||||
|
|
||||||
|
=== Header in detail
|
||||||
|
|
||||||
|
version::
|
||||||
|
The version number (as an integer) of the i3bar protocol you will use.
|
||||||
|
stop_signal::
|
||||||
|
Specify to i3bar the signal (as an integer) to send to stop your
|
||||||
|
processing.
|
||||||
|
The default value (if none is specified) is SIGSTOP.
|
||||||
|
cont_signal::
|
||||||
|
Specify to i3bar the signal (as an integer)to send to continue your
|
||||||
|
processing.
|
||||||
|
The default value (if none is specified) is SIGCONT.
|
||||||
|
|
||||||
=== Blocks in detail
|
=== Blocks in detail
|
||||||
|
|
||||||
full_text::
|
full_text::
|
||||||
|
@ -116,6 +134,16 @@ color::
|
||||||
when it is associated.
|
when it is associated.
|
||||||
Colors are specified in hex (like in HTML), starting with a leading
|
Colors are specified in hex (like in HTML), starting with a leading
|
||||||
hash sign. For example, +#ff0000+ means red.
|
hash sign. For example, +#ff0000+ means red.
|
||||||
|
min_width::
|
||||||
|
The minimum width (in pixels) of the block. If the content of the
|
||||||
|
+full_text+ key take less space than the specified min_width, the block
|
||||||
|
will be padded to the left and/or the right side, according to the +align+
|
||||||
|
key. This is useful when you want to prevent the whole status line to shift
|
||||||
|
when value take more or less space between each iteration.
|
||||||
|
align::
|
||||||
|
Align text on the +center+ (default), +right+ or +left+ of the block, when
|
||||||
|
the minimum width of the latter, specified by the +min_width+ key, is not
|
||||||
|
reached.
|
||||||
name and instance::
|
name and instance::
|
||||||
Every block should have a unique +name+ (string) entry so that it can
|
Every block should have a unique +name+ (string) entry so that it can
|
||||||
be easily identified in scripts which process the output. i3bar
|
be easily identified in scripts which process the output. i3bar
|
||||||
|
@ -148,6 +176,8 @@ An example of a block which uses all possible entries follows:
|
||||||
"full_text": "E: 10.0.0.1 (1000 Mbit/s)",
|
"full_text": "E: 10.0.0.1 (1000 Mbit/s)",
|
||||||
"short_text": "10.0.0.1",
|
"short_text": "10.0.0.1",
|
||||||
"color": "#00ff00",
|
"color": "#00ff00",
|
||||||
|
"min_width": 300,
|
||||||
|
"align": "right",
|
||||||
"urgent": false,
|
"urgent": false,
|
||||||
"name": "ethernet",
|
"name": "ethernet",
|
||||||
"instance": "eth0"
|
"instance": "eth0"
|
||||||
|
|
53
docs/ipc
53
docs/ipc
|
@ -1,7 +1,7 @@
|
||||||
IPC interface (interprocess communication)
|
IPC interface (interprocess communication)
|
||||||
==========================================
|
==========================================
|
||||||
Michael Stapelberg <michael@i3wm.org>
|
Michael Stapelberg <michael@i3wm.org>
|
||||||
August 2012
|
October 2012
|
||||||
|
|
||||||
This document describes how to interface with i3 from a separate process. This
|
This document describes how to interface with i3 from a separate process. This
|
||||||
is useful for example to remote-control i3 (to write test cases for example) or
|
is useful for example to remote-control i3 (to write test cases for example) or
|
||||||
|
@ -19,6 +19,13 @@ calling +i3 --get-socketpath+.
|
||||||
All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
|
All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
|
||||||
X11 property, stored on the X11 root window.
|
X11 property, stored on the X11 root window.
|
||||||
|
|
||||||
|
[WARNING]
|
||||||
|
.Use an existing library!
|
||||||
|
There are existing libraries for many languages. You can have a look at
|
||||||
|
<<libraries>> or search the web if your language of choice is not mentioned.
|
||||||
|
Usually, it is not necessary to implement low-level communication with i3
|
||||||
|
directly.
|
||||||
|
|
||||||
== Establishing a connection
|
== Establishing a connection
|
||||||
|
|
||||||
To establish a connection, simply open the IPC socket. The following code
|
To establish a connection, simply open the IPC socket. The following code
|
||||||
|
@ -82,7 +89,7 @@ So, a typical message could look like this:
|
||||||
Or, as a hexdump:
|
Or, as a hexdump:
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
00000000 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex|
|
00000000 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex|
|
||||||
00000010 69 74 0a |it.|
|
00000010 69 74 |it|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
To generate and send such a message, you could use the following code in Perl:
|
To generate and send such a message, you could use the following code in Perl:
|
||||||
|
@ -274,6 +281,8 @@ name (string)::
|
||||||
border (string)::
|
border (string)::
|
||||||
Can be either "normal", "none" or "1pixel", dependending on the
|
Can be either "normal", "none" or "1pixel", dependending on the
|
||||||
container’s border style.
|
container’s border style.
|
||||||
|
current_border_width (integer)::
|
||||||
|
Number of pixels of the border width.
|
||||||
layout (string)::
|
layout (string)::
|
||||||
Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
|
Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
|
||||||
"output".
|
"output".
|
||||||
|
@ -610,6 +619,8 @@ workspace (0)::
|
||||||
output (1)::
|
output (1)::
|
||||||
Sent when RandR issues a change notification (of either screens,
|
Sent when RandR issues a change notification (of either screens,
|
||||||
outputs, CRTCs or output properties).
|
outputs, CRTCs or output properties).
|
||||||
|
mode (2)::
|
||||||
|
Sent whenever i3 changes its binding mode.
|
||||||
|
|
||||||
*Example:*
|
*Example:*
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
@ -635,9 +646,29 @@ This event consists of a single serialized map containing a property
|
||||||
+change (string)+ which indicates the type of the change ("focus", "init",
|
+change (string)+ which indicates the type of the change ("focus", "init",
|
||||||
"empty", "urgent").
|
"empty", "urgent").
|
||||||
|
|
||||||
|
Moreover, when the change is "focus", an +old (object)+ and a +current
|
||||||
|
(object)+ properties will be present with the previous and current
|
||||||
|
workspace respectively. When the first switch occurs (when i3 focuses
|
||||||
|
the workspace visible at the beginning) there is no previous
|
||||||
|
workspace, and the +old+ property will be set to +null+. Also note
|
||||||
|
that if the previous is empty it will get destroyed when switching,
|
||||||
|
but will still be present in the "old" property.
|
||||||
|
|
||||||
*Example:*
|
*Example:*
|
||||||
---------------------
|
---------------------
|
||||||
{ "change": "focus" }
|
{
|
||||||
|
"change": "focus",
|
||||||
|
"current": {
|
||||||
|
"id": 28489712,
|
||||||
|
"type":4,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
"old": {
|
||||||
|
"id": 28489715,
|
||||||
|
"type": 4,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
=== output event
|
=== output event
|
||||||
|
@ -651,7 +682,21 @@ This event consists of a single serialized map containing a property
|
||||||
{ "change": "unspecified" }
|
{ "change": "unspecified" }
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
== See also
|
=== mode event
|
||||||
|
|
||||||
|
This event consists of a single serialized map containing a property
|
||||||
|
+change (string)+ which holds the name of current mode in use. The name
|
||||||
|
is the same as specified in config when creating a mode. The default
|
||||||
|
mode is simply named default.
|
||||||
|
|
||||||
|
*Example:*
|
||||||
|
---------------------------
|
||||||
|
{ "change": "default" }
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
== See also (existing libraries)
|
||||||
|
|
||||||
|
[[libraries]]
|
||||||
|
|
||||||
For some languages, libraries are available (so you don’t have to implement
|
For some languages, libraries are available (so you don’t have to implement
|
||||||
all this on your own). This list names some (if you wrote one, please let me
|
all this on your own). This list names some (if you wrote one, please let me
|
||||||
|
|
|
@ -63,6 +63,35 @@ For several reasons, the i3 testsuite has been implemented in Perl:
|
||||||
|
|
||||||
Please do not start programming language flamewars at this point.
|
Please do not start programming language flamewars at this point.
|
||||||
|
|
||||||
|
=== Installing the dependencies
|
||||||
|
|
||||||
|
As usual with Perl programs, the testsuite ships with a +Makefile.PL+.
|
||||||
|
This file specifies which Perl modules the testsuite depends on and can be used
|
||||||
|
to install all of them.
|
||||||
|
|
||||||
|
Perl modules are distributed via CPAN, and there is the official, standard CPAN
|
||||||
|
client, simply called +cpan+. It comes with every Perl installation and can be
|
||||||
|
used to install the testsuite. Many users prefer to use the more modern
|
||||||
|
+cpanminus+ instead, though (because it asks no questions and just works):
|
||||||
|
|
||||||
|
.Installing testsuite dependencies using cpanminus (preferred)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
$ cd ~/i3/testcases
|
||||||
|
$ sudo apt-get install cpanminus
|
||||||
|
$ sudo cpanm .
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
If you don’t want to use cpanminus for some reason, the same works with cpan:
|
||||||
|
|
||||||
|
.Installing testsuite dependencies using cpan
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
$ cd ~/i3/testcases
|
||||||
|
$ sudo cpan .
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
In case you don’t have root permissions, you can also install into your home
|
||||||
|
directory, see http://michael.stapelberg.de/cpan/
|
||||||
|
|
||||||
=== Mechanisms
|
=== Mechanisms
|
||||||
|
|
||||||
==== Script: complete-run
|
==== Script: complete-run
|
||||||
|
|
260
docs/userguide
260
docs/userguide
|
@ -12,28 +12,28 @@ contact us on IRC (preferred) or post your question(s) on the mailing list.
|
||||||
For the "too long; didn’t read" people, here is an overview of the default
|
For the "too long; didn’t read" people, here is an overview of the default
|
||||||
keybindings (click to see the full size image):
|
keybindings (click to see the full size image):
|
||||||
|
|
||||||
*Keys to use with mod (alt):*
|
*Keys to use with $mod (Alt):*
|
||||||
|
|
||||||
image:keyboard-layer1.png["Keys to use with mod (alt)",width=600,link="keyboard-layer1.png"]
|
image:keyboard-layer1.png["Keys to use with $mod (Alt)",width=600,link="keyboard-layer1.png"]
|
||||||
|
|
||||||
*Keys to use with Shift+mod:*
|
*Keys to use with Shift+$mod:*
|
||||||
|
|
||||||
image:keyboard-layer2.png["Keys to use with Shift+mod",width=600,link="keyboard-layer2.png"]
|
image:keyboard-layer2.png["Keys to use with Shift+$mod",width=600,link="keyboard-layer2.png"]
|
||||||
|
|
||||||
The red keys are the modifiers you need to press (by default), the blue keys
|
The red keys are the modifiers you need to press (by default), the blue keys
|
||||||
are your homerow.
|
are your homerow.
|
||||||
|
|
||||||
== Using i3
|
== Using i3
|
||||||
|
|
||||||
Throughout this guide, the keyword +mod+ will be used to refer to the
|
Throughout this guide, the keyword +$mod+ will be used to refer to the
|
||||||
configured modifier. This is the alt key (Mod1) by default, with windows (Mod4)
|
configured modifier. This is the Alt key (Mod1) by default, with windows (Mod4)
|
||||||
being a popular alternative.
|
being a popular alternative.
|
||||||
|
|
||||||
=== Opening terminals and moving around
|
=== Opening terminals and moving around
|
||||||
|
|
||||||
One very basic operation is opening a new terminal. By default, the keybinding
|
One very basic operation is opening a new terminal. By default, the keybinding
|
||||||
for this is mod+Enter, that is Alt+Enter in the default configuration. By
|
for this is $mod+Enter, that is Alt+Enter in the default configuration. By
|
||||||
pressing mod+Enter, a new terminal will be opened. It will fill the whole
|
pressing $mod+Enter, a new terminal will be opened. It will fill the whole
|
||||||
space available on your screen.
|
space available on your screen.
|
||||||
|
|
||||||
image:single_terminal.png[Single terminal]
|
image:single_terminal.png[Single terminal]
|
||||||
|
@ -48,9 +48,9 @@ image:two_terminals.png[Two terminals]
|
||||||
To move the focus between the two terminals, you can use the direction keys
|
To move the focus between the two terminals, you can use the direction keys
|
||||||
which you may know from the editor +vi+. However, in i3, your homerow is used
|
which you may know from the editor +vi+. However, in i3, your homerow is used
|
||||||
for these keys (in +vi+, the keys are shifted to the left by one for
|
for these keys (in +vi+, the keys are shifted to the left by one for
|
||||||
compatibility with most keyboard layouts). Therefore, +mod+J+ is left, +mod+K+
|
compatibility with most keyboard layouts). Therefore, +$mod+J+ is left, +$mod+K+
|
||||||
is down, +mod+L+ is up and `mod+;` is right. So, to switch between the
|
is down, +$mod+L+ is up and `$mod+;` is right. So, to switch between the
|
||||||
terminals, use +mod+K+ or +mod+L+. Of course, you can also use the arrow keys.
|
terminals, use +$mod+K+ or +$mod+L+. Of course, you can also use the arrow keys.
|
||||||
|
|
||||||
At the moment, your workspace is split (it contains two terminals) in a
|
At the moment, your workspace is split (it contains two terminals) in a
|
||||||
specific direction (horizontal by default). Every window can be split
|
specific direction (horizontal by default). Every window can be split
|
||||||
|
@ -61,8 +61,8 @@ windows.
|
||||||
|
|
||||||
TODO: picture of the tree
|
TODO: picture of the tree
|
||||||
|
|
||||||
To split a window vertically, press +mod+v+ before you create the new window.
|
To split a window vertically, press +$mod+v+ before you create the new window.
|
||||||
To split it horizontally, press +mod+h+.
|
To split it horizontally, press +$mod+h+.
|
||||||
|
|
||||||
=== Changing the container layout
|
=== Changing the container layout
|
||||||
|
|
||||||
|
@ -80,15 +80,15 @@ tabbed::
|
||||||
The same principle as +stacking+, but the list of windows at the top is only
|
The same principle as +stacking+, but the list of windows at the top is only
|
||||||
a single line which is vertically split.
|
a single line which is vertically split.
|
||||||
|
|
||||||
To switch modes, press +mod+e+ for splith/splitv (it toggles), +mod+s+ for
|
To switch modes, press +$mod+e+ for splith/splitv (it toggles), +$mod+s+ for
|
||||||
stacking and +mod+w+ for tabbed.
|
stacking and +$mod+w+ for tabbed.
|
||||||
|
|
||||||
image:modes.png[Container modes]
|
image:modes.png[Container modes]
|
||||||
|
|
||||||
=== Toggling fullscreen mode for a window
|
=== Toggling fullscreen mode for a window
|
||||||
|
|
||||||
To display a window in fullscreen mode or to go out of fullscreen mode again,
|
To display a window in fullscreen mode or to go out of fullscreen mode again,
|
||||||
press +mod+f+.
|
press +$mod+f+.
|
||||||
|
|
||||||
There is also a global fullscreen mode in i3 in which the client will span all
|
There is also a global fullscreen mode in i3 in which the client will span all
|
||||||
available outputs (the command is +fullscreen global+).
|
available outputs (the command is +fullscreen global+).
|
||||||
|
@ -96,7 +96,7 @@ available outputs (the command is +fullscreen global+).
|
||||||
=== Opening other applications
|
=== Opening other applications
|
||||||
|
|
||||||
Aside from opening applications from a terminal, you can also use the handy
|
Aside from opening applications from a terminal, you can also use the handy
|
||||||
+dmenu+ which is opened by pressing +mod+d+ by default. Just type the name
|
+dmenu+ which is opened by pressing +$mod+d+ by default. Just type the name
|
||||||
(or a part of it) of the application which you want to open. The corresponding
|
(or a part of it) of the application which you want to open. The corresponding
|
||||||
application has to be in your +$PATH+ for this to work.
|
application has to be in your +$PATH+ for this to work.
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ create a keybinding for starting the application directly. See the section
|
||||||
|
|
||||||
If an application does not provide a mechanism for closing (most applications
|
If an application does not provide a mechanism for closing (most applications
|
||||||
provide a menu, the escape key or a shortcut like +Control+W+ to close), you
|
provide a menu, the escape key or a shortcut like +Control+W+ to close), you
|
||||||
can press +mod+Shift+q+ to kill a window. For applications which support
|
can press +$mod+Shift+q+ to kill a window. For applications which support
|
||||||
the WM_DELETE protocol, this will correctly close the application (saving
|
the WM_DELETE protocol, this will correctly close the application (saving
|
||||||
any modifications or doing other cleanup). If the application doesn’t support
|
any modifications or doing other cleanup). If the application doesn’t support
|
||||||
the WM_DELETE protocol your X server will kill the window and the behaviour
|
the WM_DELETE protocol your X server will kill the window and the behaviour
|
||||||
|
@ -118,7 +118,7 @@ depends on the application.
|
||||||
|
|
||||||
Workspaces are an easy way to group a set of windows. By default, you are on
|
Workspaces are an easy way to group a set of windows. By default, you are on
|
||||||
the first workspace, as the bar on the bottom left indicates. To switch to
|
the first workspace, as the bar on the bottom left indicates. To switch to
|
||||||
another workspace, press +mod+num+ where +num+ is the number of the workspace
|
another workspace, press +$mod+num+ where +num+ is the number of the workspace
|
||||||
you want to use. If the workspace does not exist yet, it will be created.
|
you want to use. If the workspace does not exist yet, it will be created.
|
||||||
|
|
||||||
A common paradigm is to put the web browser on one workspace, communication
|
A common paradigm is to put the web browser on one workspace, communication
|
||||||
|
@ -132,7 +132,7 @@ focus to that screen.
|
||||||
|
|
||||||
=== Moving windows to workspaces
|
=== Moving windows to workspaces
|
||||||
|
|
||||||
To move a window to another workspace, simply press +mod+Shift+num+ where
|
To move a window to another workspace, simply press +$mod+Shift+num+ where
|
||||||
+num+ is (like when switching workspaces) the number of the target workspace.
|
+num+ is (like when switching workspaces) the number of the target workspace.
|
||||||
Similarly to switching workspaces, the target workspace will be created if
|
Similarly to switching workspaces, the target workspace will be created if
|
||||||
it does not yet exist.
|
it does not yet exist.
|
||||||
|
@ -148,11 +148,11 @@ columns/rows with your keyboard.
|
||||||
=== Restarting i3 inplace
|
=== Restarting i3 inplace
|
||||||
|
|
||||||
To restart i3 inplace (and thus get into a clean state if there is a bug, or
|
To restart i3 inplace (and thus get into a clean state if there is a bug, or
|
||||||
to upgrade to a newer version of i3) you can use +mod+Shift+r+.
|
to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
|
||||||
|
|
||||||
=== Exiting i3
|
=== Exiting i3
|
||||||
|
|
||||||
To cleanly exit i3 without killing your X server, you can use +mod+Shift+e+.
|
To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
|
||||||
|
|
||||||
=== Floating
|
=== Floating
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ paradigm but can be useful for some corner cases like "Save as" dialog
|
||||||
windows, or toolbar windows (GIMP or similar). Those windows usually set the
|
windows, or toolbar windows (GIMP or similar). Those windows usually set the
|
||||||
appropriate hint and are opened in floating mode by default.
|
appropriate hint and are opened in floating mode by default.
|
||||||
|
|
||||||
You can toggle floating mode for a window by pressing +mod+Shift+Space+. By
|
You can toggle floating mode for a window by pressing +$mod+Shift+Space+. By
|
||||||
dragging the window’s titlebar with your mouse you can move the window
|
dragging the window’s titlebar with your mouse you can move the window
|
||||||
around. By grabbing the borders and moving them you can resize the window. You
|
around. By grabbing the borders and moving them you can resize the window. You
|
||||||
can also do that by using the <<floating_modifier>>.
|
can also do that by using the <<floating_modifier>>.
|
||||||
|
@ -202,7 +202,7 @@ orientation (horizontal, vertical or unspecified) and the orientation depends
|
||||||
on the layout the container is in (vertical for splitv and stacking, horizontal
|
on the layout the container is in (vertical for splitv and stacking, horizontal
|
||||||
for splith and tabbed). So, in our example with the workspace, the default
|
for splith and tabbed). So, in our example with the workspace, the default
|
||||||
layout of the workspace +Container+ is splith (most monitors are widescreen
|
layout of the workspace +Container+ is splith (most monitors are widescreen
|
||||||
nowadays). If you change the layout to splitv (+mod+l+ in the default config)
|
nowadays). If you change the layout to splitv (+$mod+l+ in the default config)
|
||||||
and *then* open two terminals, i3 will configure your windows like this:
|
and *then* open two terminals, i3 will configure your windows like this:
|
||||||
|
|
||||||
image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"]
|
image::tree-shot2.png["shot2",title="Vertical Workspace Orientation"]
|
||||||
|
@ -212,8 +212,8 @@ Let’s assume you have two terminals on a workspace (with splith layout, that i
|
||||||
horizontal orientation), focus is on the right terminal. Now you want to open
|
horizontal orientation), focus is on the right terminal. Now you want to open
|
||||||
another terminal window below the current one. If you would just open a new
|
another terminal window below the current one. If you would just open a new
|
||||||
terminal window, it would show up to the right due to the splith layout.
|
terminal window, it would show up to the right due to the splith layout.
|
||||||
Instead, press +mod+v+ to split the container with the splitv layout (to
|
Instead, press +$mod+v+ to split the container with the splitv layout (to
|
||||||
open a +Horizontal Split Container+, use +mod+h+). Now you can open a new
|
open a +Horizontal Split Container+, use +$mod+h+). Now you can open a new
|
||||||
terminal and it will open below the current one:
|
terminal and it will open below the current one:
|
||||||
|
|
||||||
image::tree-layout1.png["Layout",float="right"]
|
image::tree-layout1.png["Layout",float="right"]
|
||||||
|
@ -248,7 +248,7 @@ single workspace on which you open three terminal windows. All these terminal
|
||||||
windows are directly attached to one node inside i3’s layout tree, the
|
windows are directly attached to one node inside i3’s layout tree, the
|
||||||
workspace node. By default, the workspace node’s orientation is +horizontal+.
|
workspace node. By default, the workspace node’s orientation is +horizontal+.
|
||||||
|
|
||||||
Now you move one of these terminals down (+mod+k+ by default). The workspace
|
Now you move one of these terminals down (+$mod+k+ by default). The workspace
|
||||||
node’s orientation will be changed to +vertical+. The terminal window you moved
|
node’s orientation will be changed to +vertical+. The terminal window you moved
|
||||||
down is directly attached to the workspace and appears on the bottom of the
|
down is directly attached to the workspace and appears on the bottom of the
|
||||||
screen. A new (horizontal) container was created to accomodate the other two
|
screen. A new (horizontal) container was created to accomodate the other two
|
||||||
|
@ -316,13 +316,15 @@ and fall back to a working font.
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
------------------------------
|
------------------------------
|
||||||
font <X core font description>
|
font <X core font description>
|
||||||
font xft:<a FreeType font description>
|
font pango:[family list] [style options] [size]
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
font xft:DejaVu Sans Mono 10
|
font pango:DejaVu Sans Mono 10
|
||||||
|
font pango:DejaVu Sans Mono, Terminus Bold Semi-Condensed 11
|
||||||
|
font pango:Terminus 11x
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
[[keybindings]]
|
[[keybindings]]
|
||||||
|
@ -362,10 +364,10 @@ bindcode [--release] [Modifiers+]keycode command
|
||||||
*Examples*:
|
*Examples*:
|
||||||
--------------------------------
|
--------------------------------
|
||||||
# Fullscreen
|
# Fullscreen
|
||||||
bindsym mod+f fullscreen
|
bindsym $mod+f fullscreen
|
||||||
|
|
||||||
# Restart
|
# Restart
|
||||||
bindsym mod+Shift+r restart
|
bindsym $mod+Shift+r restart
|
||||||
|
|
||||||
# Notebook-specific hotkeys
|
# Notebook-specific hotkeys
|
||||||
bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh
|
bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh
|
||||||
|
@ -479,11 +481,13 @@ workspace_layout tabbed
|
||||||
=== Border style for new windows
|
=== Border style for new windows
|
||||||
|
|
||||||
This option determines which border style new windows will have. The default is
|
This option determines which border style new windows will have. The default is
|
||||||
"normal".
|
"normal". Note that new_float applies only to windows which are starting out as
|
||||||
|
floating windows, e.g. dialog windows.
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
new_window <normal|1pixel|none>
|
new_window <normal|1pixel|none|pixel>
|
||||||
|
new_float <normal|1pixel|none|pixel>
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
|
@ -491,6 +495,19 @@ new_window <normal|1pixel|none>
|
||||||
new_window 1pixel
|
new_window 1pixel
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
The "normal" and "pixel" border styles support an optional border width in
|
||||||
|
pixels:
|
||||||
|
|
||||||
|
*Example*:
|
||||||
|
---------------------
|
||||||
|
# The same as new_window none
|
||||||
|
new_window pixel 0
|
||||||
|
|
||||||
|
# A 3 px border
|
||||||
|
new_window pixel 3
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
=== Hiding vertical borders
|
=== Hiding vertical borders
|
||||||
|
|
||||||
You can hide vertical borders adjacent to the screen edges using
|
You can hide vertical borders adjacent to the screen edges using
|
||||||
|
@ -790,21 +807,23 @@ focus_follows_mouse no
|
||||||
When you are in fullscreen mode, some applications still open popup windows
|
When you are in fullscreen mode, some applications still open popup windows
|
||||||
(take Xpdf for example). This is because these applications may not be aware
|
(take Xpdf for example). This is because these applications may not be aware
|
||||||
that they are in fullscreen mode (they do not check the corresponding hint).
|
that they are in fullscreen mode (they do not check the corresponding hint).
|
||||||
There are two things which are possible to do in this situation:
|
There are three things which are possible to do in this situation:
|
||||||
|
|
||||||
1. Just ignore the popup (don’t map it). This won’t interrupt you while you are
|
1. Display the popup if it belongs to the fullscreen application only. This is
|
||||||
|
the default and should be reasonable behavior for most users.
|
||||||
|
2. Just ignore the popup (don’t map it). This won’t interrupt you while you are
|
||||||
in fullscreen. However, some apps might react badly to this (deadlock until
|
in fullscreen. However, some apps might react badly to this (deadlock until
|
||||||
you go out of fullscreen).
|
you go out of fullscreen).
|
||||||
2. Leave fullscreen mode. This is the default.
|
3. Leave fullscreen mode.
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
popup_during_fullscreen <ignore|leave_fullscreen>
|
popup_during_fullscreen <smart|ignore|leave_fullscreen>
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
------------------------------
|
------------------------------
|
||||||
popup_during_fullscreen ignore
|
popup_during_fullscreen smart
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
=== Focus wrapping
|
=== Focus wrapping
|
||||||
|
@ -862,7 +881,7 @@ This configuration directive enables automatic +workspace back_and_forth+ (see
|
||||||
|
|
||||||
For instance: Assume you are on workspace "1: www" and switch to "2: IM" using
|
For instance: Assume you are on workspace "1: www" and switch to "2: IM" using
|
||||||
mod+2 because somebody sent you a message. You don’t need to remember where you
|
mod+2 because somebody sent you a message. You don’t need to remember where you
|
||||||
came from now, you can just press mod+2 again to switch back to "1: www".
|
came from now, you can just press $mod+2 again to switch back to "1: www".
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@ -874,6 +893,30 @@ workspace_auto_back_and_forth <yes|no>
|
||||||
workspace_auto_back_and_forth yes
|
workspace_auto_back_and_forth yes
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
=== Delaying urgency hint reset on workspace change
|
||||||
|
|
||||||
|
If an application on another workspace sets an urgency hint, switching to this
|
||||||
|
workspace may lead to immediate focus of the application, which also means the
|
||||||
|
window decoration color would be immediately resetted to +client.focused+. This
|
||||||
|
may make it unnecessarily hard to tell which window originally raised the
|
||||||
|
event.
|
||||||
|
|
||||||
|
In order to prevent this, you can tell i3 to delay resetting the urgency state
|
||||||
|
by a certain time using the +force_display_urgency_hint+ directive. Setting the
|
||||||
|
value to 0 disables this feature.
|
||||||
|
|
||||||
|
The default is 500ms.
|
||||||
|
|
||||||
|
*Syntax*:
|
||||||
|
---------------------------------------
|
||||||
|
force_display_urgency_hint <timeout> ms
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
*Example*:
|
||||||
|
---------------------------------
|
||||||
|
force_display_urgency_hint 500 ms
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
== Configuring i3bar
|
== Configuring i3bar
|
||||||
|
|
||||||
The bar at the bottom of your monitor is drawn by a separate process called
|
The bar at the bottom of your monitor is drawn by a separate process called
|
||||||
|
@ -1079,7 +1122,7 @@ font <font>
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
bar {
|
bar {
|
||||||
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
font xft:DejaVu Sans Mono 10
|
font pango:DejaVu Sans Mono 10
|
||||||
}
|
}
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1170,7 +1213,7 @@ the following keybinding:
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
bindsym mod+x move container to workspace 3; workspace 3
|
bindsym $mod+x move container to workspace 3; workspace 3
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
[[command_criteria]]
|
[[command_criteria]]
|
||||||
|
@ -1182,10 +1225,10 @@ which have the class Firefox, use:
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
------------------------------------
|
------------------------------------
|
||||||
bindsym mod+x [class="Firefox"] kill
|
bindsym $mod+x [class="Firefox"] kill
|
||||||
|
|
||||||
# same thing, but case-insensitive
|
# same thing, but case-insensitive
|
||||||
bindsym mod+x [class="(?i)firefox"] kill
|
bindsym $mod+x [class="(?i)firefox"] kill
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
The criteria which are currently implemented are:
|
The criteria which are currently implemented are:
|
||||||
|
@ -1231,10 +1274,10 @@ exec [--no-startup-id] command
|
||||||
*Example*:
|
*Example*:
|
||||||
------------------------------
|
------------------------------
|
||||||
# Start the GIMP
|
# Start the GIMP
|
||||||
bindsym mod+g exec gimp
|
bindsym $mod+g exec gimp
|
||||||
|
|
||||||
# Start the terminal emulator urxvt which is not yet startup-notification-aware
|
# Start the terminal emulator urxvt which is not yet startup-notification-aware
|
||||||
bindsym mod+Return exec --no-startup-id urxvt
|
bindsym $mod+Return exec --no-startup-id urxvt
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
The +--no-startup-id+ parameter disables startup-notification support for this
|
The +--no-startup-id+ parameter disables startup-notification support for this
|
||||||
|
@ -1265,8 +1308,8 @@ split <vertical|horizontal>
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
------------------------------
|
------------------------------
|
||||||
bindsym mod+v split vertical
|
bindsym $mod+v split vertical
|
||||||
bindsym mod+h split horizontal
|
bindsym $mod+h split horizontal
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
=== Manipulating layout
|
=== Manipulating layout
|
||||||
|
@ -1287,21 +1330,21 @@ layout toggle [split|all]
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
--------------
|
--------------
|
||||||
bindsym mod+s layout stacking
|
bindsym $mod+s layout stacking
|
||||||
bindsym mod+l layout toggle split
|
bindsym $mod+l layout toggle split
|
||||||
bindsym mod+w layout tabbed
|
bindsym $mod+w layout tabbed
|
||||||
|
|
||||||
# Toggle between stacking/tabbed/split:
|
# Toggle between stacking/tabbed/split:
|
||||||
bindsym mod+x layout toggle
|
bindsym $mod+x layout toggle
|
||||||
|
|
||||||
# Toggle between stacking/tabbed/splith/splitv:
|
# Toggle between stacking/tabbed/splith/splitv:
|
||||||
bindsym mod+x layout toggle all
|
bindsym $mod+x layout toggle all
|
||||||
|
|
||||||
# Toggle fullscreen
|
# Toggle fullscreen
|
||||||
bindsym mod+f fullscreen
|
bindsym $mod+f fullscreen
|
||||||
|
|
||||||
# Toggle floating/tiling
|
# Toggle floating/tiling
|
||||||
bindsym mod+t floating toggle
|
bindsym $mod+t floating toggle
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
=== Focusing/Moving containers
|
=== Focusing/Moving containers
|
||||||
|
@ -1343,36 +1386,36 @@ relevant for floating containers. The default amount is 10 pixels.
|
||||||
*Examples*:
|
*Examples*:
|
||||||
----------------------
|
----------------------
|
||||||
# Focus container on the left, bottom, top, right:
|
# Focus container on the left, bottom, top, right:
|
||||||
bindsym mod+j focus left
|
bindsym $mod+j focus left
|
||||||
bindsym mod+k focus down
|
bindsym $mod+k focus down
|
||||||
bindsym mod+l focus up
|
bindsym $mod+l focus up
|
||||||
bindsym mod+semicolon focus right
|
bindsym $mod+semicolon focus right
|
||||||
|
|
||||||
# Focus parent container
|
# Focus parent container
|
||||||
bindsym mod+u focus parent
|
bindsym $mod+u focus parent
|
||||||
|
|
||||||
# Focus last floating/tiling container
|
# Focus last floating/tiling container
|
||||||
bindsym mod+g focus mode_toggle
|
bindsym $mod+g focus mode_toggle
|
||||||
|
|
||||||
# Focus the output right to the current one
|
# Focus the output right to the current one
|
||||||
bindsym mod+x focus output right
|
bindsym $mod+x focus output right
|
||||||
|
|
||||||
# Focus the big output
|
# Focus the big output
|
||||||
bindsym mod+x focus output HDMI-2
|
bindsym $mod+x focus output HDMI-2
|
||||||
|
|
||||||
# Move container to the left, bottom, top, right:
|
# Move container to the left, bottom, top, right:
|
||||||
bindsym mod+j move left
|
bindsym $mod+j move left
|
||||||
bindsym mod+k move down
|
bindsym $mod+k move down
|
||||||
bindsym mod+l move up
|
bindsym $mod+l move up
|
||||||
bindsym mod+semicolon move right
|
bindsym $mod+semicolon move right
|
||||||
|
|
||||||
# Move container, but make floating containers
|
# Move container, but make floating containers
|
||||||
# move more than the default
|
# move more than the default
|
||||||
bindsym mod+j move left 20 px
|
bindsym $mod+j move left 20 px
|
||||||
|
|
||||||
# Move floating container to the center
|
# Move floating container to the center
|
||||||
# of all outputs
|
# of all outputs
|
||||||
bindsym mod+c move absolute position center
|
bindsym $mod+c move absolute position center
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
=== Changing (named) workspaces/moving to workspaces
|
=== Changing (named) workspaces/moving to workspaces
|
||||||
|
@ -1395,38 +1438,40 @@ RandR output.
|
||||||
|
|
||||||
[[back_and_forth]]
|
[[back_and_forth]]
|
||||||
To switch back to the previously focused workspace, use +workspace
|
To switch back to the previously focused workspace, use +workspace
|
||||||
back_and_forth+.
|
back_and_forth+; likewise, you can move containers to the previously focused
|
||||||
|
workspace using +move container to workspace back_and_forth+.
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
workspace <next|prev|next_on_output|prev_on_output>
|
workspace <next|prev|next_on_output|prev_on_output>
|
||||||
workspace back_and_forth
|
workspace back_and_forth
|
||||||
workspace <name>
|
workspace <name>
|
||||||
workspace number <number>
|
workspace number <name>
|
||||||
|
|
||||||
move [window|container] [to] workspace <name>
|
move [window|container] [to] workspace <name>
|
||||||
move [window|container] [to] workspace number <number>
|
move [window|container] [to] workspace number <name>
|
||||||
move [window|container] [to] workspace <prev|next|current>
|
move [window|container] [to] workspace <prev|next|current>
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
-------------------------
|
-------------------------
|
||||||
bindsym mod+1 workspace 1
|
bindsym $mod+1 workspace 1
|
||||||
bindsym mod+2 workspace 2
|
bindsym $mod+2 workspace 2
|
||||||
...
|
...
|
||||||
|
|
||||||
bindsym mod+Shift+1 move container to workspace 1
|
bindsym $mod+Shift+1 move container to workspace 1
|
||||||
bindsym mod+Shift+2 move container to workspace 2
|
bindsym $mod+Shift+2 move container to workspace 2
|
||||||
...
|
...
|
||||||
|
|
||||||
# switch between the current and the previously focused one
|
# switch between the current and the previously focused one
|
||||||
bindsym mod+b workspace back_and_forth
|
bindsym $mod+b workspace back_and_forth
|
||||||
|
bindsym $mod+Shift+b move container to workspace back_and_forth
|
||||||
|
|
||||||
# move the whole workspace to the next output
|
# move the whole workspace to the next output
|
||||||
bindsym mod+x move workspace to output right
|
bindsym $mod+x move workspace to output right
|
||||||
|
|
||||||
# move firefox to current workspace
|
# move firefox to current workspace
|
||||||
bindsym mod+F1 [class="Firefox"] move workspace current
|
bindsym $mod+F1 [class="Firefox"] move workspace current
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
==== Named workspaces
|
==== Named workspaces
|
||||||
|
@ -1436,7 +1481,7 @@ workspace command, you can use an arbitrary name:
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
-------------------------
|
-------------------------
|
||||||
bindsym mod+1 workspace mail
|
bindsym $mod+1 workspace mail
|
||||||
...
|
...
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -1445,8 +1490,8 @@ number, like this:
|
||||||
|
|
||||||
*Example*:
|
*Example*:
|
||||||
-------------------------
|
-------------------------
|
||||||
bindsym mod+1 workspace 1: mail
|
bindsym $mod+1 workspace 1: mail
|
||||||
bindsym mod+2 workspace 2: www
|
bindsym $mod+2 workspace 2: www
|
||||||
...
|
...
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -1456,25 +1501,36 @@ workspaces are ordered the way they appeared. When they start with a number, i3
|
||||||
will order them numerically. Also, you will be able to use +workspace number 1+
|
will order them numerically. Also, you will be able to use +workspace number 1+
|
||||||
to switch to the workspace which begins with number 1, regardless of which name
|
to switch to the workspace which begins with number 1, regardless of which name
|
||||||
it has. This is useful in case you are changing the workspace’s name
|
it has. This is useful in case you are changing the workspace’s name
|
||||||
dynamically.
|
dynamically. To combine both commands you can use +workspace number 1: mail+ to
|
||||||
|
specify a default name if there's currently no workspace starting with a "1".
|
||||||
|
|
||||||
==== Renaming workspaces
|
==== Renaming workspaces
|
||||||
|
|
||||||
You can rename workspaces. This might be useful to start with the default
|
You can rename workspaces. This might be useful to start with the default
|
||||||
numbered workspaces, do your work, and rename the workspaces afterwards to
|
numbered workspaces, do your work, and rename the workspaces afterwards to
|
||||||
reflect what’s actually on them.
|
reflect what’s actually on them. You can also omit the old name to rename
|
||||||
|
the currently focused workspace. This is handy if you wan't to use the
|
||||||
|
rename command with +i3-input+.
|
||||||
|
|
||||||
*Syntax*:
|
*Syntax*:
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
rename workspace <old_name> to <new_name>
|
rename workspace <old_name> to <new_name>
|
||||||
|
rename workspace to <new_name>
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
i3-msg 'rename workspace 5 to 6'
|
i3-msg 'rename workspace 5 to 6'
|
||||||
i3-msg 'rename workspace 1 to "1: www"'
|
i3-msg 'rename workspace 1 to "1: www"'
|
||||||
i3-msg 'rename workspace "1: www" to "10: www"'
|
i3-msg 'rename workspace "1: www" to "10: www"'
|
||||||
------------------------------------------------
|
i3-msg 'rename workspace to "2: mail"
|
||||||
|
bindsym $mod+r exec i3-input -F 'rename workspace to %s' -P 'New name: '
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=== Moving workspaces to a different screen
|
||||||
|
|
||||||
|
See <<move_to_outputs>> for how to move a container/workspace to a different
|
||||||
|
RandR output.
|
||||||
|
|
||||||
=== Moving containers/workspaces to RandR outputs
|
=== Moving containers/workspaces to RandR outputs
|
||||||
|
|
||||||
|
@ -1494,10 +1550,10 @@ move workspace to output <<left|right|down|up>|<output>>
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
# Move the current workspace to the next output
|
# Move the current workspace to the next output
|
||||||
# (effectively toggles when you only have two outputs)
|
# (effectively toggles when you only have two outputs)
|
||||||
bindsym mod+x move workspace to output right
|
bindsym $mod+x move workspace to output right
|
||||||
|
|
||||||
# Put this window on the presentation output.
|
# Put this window on the presentation output.
|
||||||
bindsym mod+x move container to output VGA1
|
bindsym $mod+x move container to output VGA1
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
[[resizingconfig]]
|
[[resizingconfig]]
|
||||||
|
@ -1548,7 +1604,7 @@ mode "resize" {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Enter resize mode
|
# Enter resize mode
|
||||||
bindsym mod+r mode "resize"
|
bindsym $mod+r mode "resize"
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
=== Jumping to specific windows
|
=== Jumping to specific windows
|
||||||
|
@ -1569,7 +1625,7 @@ with criteria for that.
|
||||||
*Examples*:
|
*Examples*:
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
# Get me to the next open VIM instance
|
# Get me to the next open VIM instance
|
||||||
bindsym mod+a [class="urxvt" title="VIM"] focus
|
bindsym $mod+a [class="urxvt" title="VIM"] focus
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
=== VIM-like marks (mark/goto)
|
=== VIM-like marks (mark/goto)
|
||||||
|
@ -1605,10 +1661,10 @@ TODO: make i3-input replace %s
|
||||||
*Examples*:
|
*Examples*:
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
# Read 1 character and mark the current window with this character
|
# Read 1 character and mark the current window with this character
|
||||||
bindsym mod+m exec i3-input -p 'mark ' -l 1 -P 'Mark: '
|
bindsym $mod+m exec i3-input -p 'mark ' -l 1 -P 'Mark: '
|
||||||
|
|
||||||
# Read 1 character and go to the window with the character
|
# Read 1 character and go to the window with the character
|
||||||
bindsym mod+g exec i3-input -p 'goto ' -l 1 -P 'Goto: '
|
bindsym $mod+g exec i3-input -p 'goto ' -l 1 -P 'Goto: '
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
Alternatively, if you do not want to mess with +i3-input+, you could create
|
Alternatively, if you do not want to mess with +i3-input+, you could create
|
||||||
|
@ -1625,9 +1681,9 @@ There is also +border toggle+ which will toggle the different border styles.
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
----------------------------
|
----------------------------
|
||||||
bindsym mod+t border normal
|
bindsym $mod+t border normal
|
||||||
bindsym mod+y border 1pixel
|
bindsym $mod+y border 1pixel
|
||||||
bindsym mod+u border none
|
bindsym $mod+u border none
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
[[stack-limit]]
|
[[stack-limit]]
|
||||||
|
@ -1672,9 +1728,9 @@ however you don’t need to (simply killing your X session is fine as well).
|
||||||
|
|
||||||
*Examples*:
|
*Examples*:
|
||||||
----------------------------
|
----------------------------
|
||||||
bindsym mod+Shift+r restart
|
bindsym $mod+Shift+r restart
|
||||||
bindsym mod+Shift+w reload
|
bindsym $mod+Shift+w reload
|
||||||
bindsym mod+Shift+e exit
|
bindsym $mod+Shift+e exit
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
=== Scratchpad
|
=== Scratchpad
|
||||||
|
@ -1685,7 +1741,9 @@ invisible until you show it again. There is no way to open that workspace.
|
||||||
Instead, when using +scratchpad show+, the window will be shown again, as a
|
Instead, when using +scratchpad show+, the window will be shown again, as a
|
||||||
floating window, centered on your current workspace (using +scratchpad show+ on
|
floating window, centered on your current workspace (using +scratchpad show+ on
|
||||||
a visible scratchpad window will make it hidden again, so you can have a
|
a visible scratchpad window will make it hidden again, so you can have a
|
||||||
keybinding to toggle).
|
keybinding to toggle). Note that this is just a normal floating window, so if
|
||||||
|
you want to "remove it from scratchpad", you can simple make it tiling again
|
||||||
|
(+floating toggle+).
|
||||||
|
|
||||||
As the name indicates, this is useful for having a window with your favorite
|
As the name indicates, this is useful for having a window with your favorite
|
||||||
editor always at hand. However, you can also use this for other permanently
|
editor always at hand. However, you can also use this for other permanently
|
||||||
|
@ -1702,10 +1760,10 @@ scratchpad show
|
||||||
*Examples*:
|
*Examples*:
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
# Make the currently focused window a scratchpad
|
# Make the currently focused window a scratchpad
|
||||||
bindsym mod+Shift+minus move scratchpad
|
bindsym $mod+Shift+minus move scratchpad
|
||||||
|
|
||||||
# Show the first scratchpad window
|
# Show the first scratchpad window
|
||||||
bindsym mod+minus scratchpad show
|
bindsym $mod+minus scratchpad show
|
||||||
|
|
||||||
# Show the sup-mail scratchpad window, if any.
|
# Show the sup-mail scratchpad window, if any.
|
||||||
bindsym mod4+s [title="^Sup ::"] scratchpad show
|
bindsym mod4+s [title="^Sup ::"] scratchpad show
|
||||||
|
@ -1818,6 +1876,8 @@ have more than one monitor:
|
||||||
3. If you have many workspaces on many monitors, it might get hard to keep
|
3. If you have many workspaces on many monitors, it might get hard to keep
|
||||||
track of which window you put where. Thus, you can use vim-like marks to
|
track of which window you put where. Thus, you can use vim-like marks to
|
||||||
quickly switch between windows. See <<vim_like_marks>>.
|
quickly switch between windows. See <<vim_like_marks>>.
|
||||||
|
4. For information on how to move existing workspaces between monitors,
|
||||||
|
see <<_moving_containers_workspaces_to_randr_outputs>>.
|
||||||
|
|
||||||
== i3 and the rest of your software world
|
== i3 and the rest of your software world
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,18 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
|
use Getopt::Long;
|
||||||
use v5.10;
|
use v5.10;
|
||||||
|
|
||||||
|
my $input = '';
|
||||||
|
my $prefix = '';
|
||||||
|
my $result = GetOptions(
|
||||||
|
'input=s' => \$input,
|
||||||
|
'prefix=s' => \$prefix
|
||||||
|
);
|
||||||
|
|
||||||
|
die qq|Input file "$input" does not exist!| unless -e $input;
|
||||||
|
|
||||||
# reads in a whole file
|
# reads in a whole file
|
||||||
sub slurp {
|
sub slurp {
|
||||||
open my $fh, '<', shift;
|
open my $fh, '<', shift;
|
||||||
|
@ -24,8 +34,6 @@ sub slurp {
|
||||||
# Stores the different states.
|
# Stores the different states.
|
||||||
my %states;
|
my %states;
|
||||||
|
|
||||||
# XXX: don’t hardcode input and output
|
|
||||||
my $input = '../parser-specs/commands.spec';
|
|
||||||
my @raw_lines = split("\n", slurp($input));
|
my @raw_lines = split("\n", slurp($input));
|
||||||
my @lines;
|
my @lines;
|
||||||
|
|
||||||
|
@ -101,24 +109,30 @@ for my $line (@lines) {
|
||||||
# Second step: Generate the enum values for all states.
|
# Second step: Generate the enum values for all states.
|
||||||
|
|
||||||
# It is important to keep the order the same, so we store the keys once.
|
# It is important to keep the order the same, so we store the keys once.
|
||||||
my @keys = keys %states;
|
# We sort descendingly by length to be able to replace occurences of the state
|
||||||
|
# name even when one state’s name is included in another one’s (like FOR_WINDOW
|
||||||
|
# is in FOR_WINDOW_COMMAND).
|
||||||
|
my @keys = sort { length($b) <=> length($a) } keys %states;
|
||||||
|
|
||||||
open(my $enumfh, '>', 'GENERATED_enums.h');
|
open(my $enumfh, '>', "GENERATED_${prefix}_enums.h");
|
||||||
|
|
||||||
# XXX: we might want to have a way to do this without a trailing comma, but gcc
|
# XXX: we might want to have a way to do this without a trailing comma, but gcc
|
||||||
# seems to eat it.
|
# seems to eat it.
|
||||||
|
my %statenum;
|
||||||
say $enumfh 'typedef enum {';
|
say $enumfh 'typedef enum {';
|
||||||
my $cnt = 0;
|
my $cnt = 0;
|
||||||
for my $state (@keys, '__CALL') {
|
for my $state (@keys, '__CALL') {
|
||||||
say $enumfh " $state = $cnt,";
|
say $enumfh " $state = $cnt,";
|
||||||
|
$statenum{$state} = $cnt;
|
||||||
$cnt++;
|
$cnt++;
|
||||||
}
|
}
|
||||||
say $enumfh '} cmdp_state;';
|
say $enumfh '} cmdp_state;';
|
||||||
close($enumfh);
|
close($enumfh);
|
||||||
|
|
||||||
# Third step: Generate the call function.
|
# Third step: Generate the call function.
|
||||||
open(my $callfh, '>', 'GENERATED_call.h');
|
open(my $callfh, '>', "GENERATED_${prefix}_call.h");
|
||||||
say $callfh 'static void GENERATED_call(const int call_identifier, struct CommandResult *result) {';
|
my $resultname = uc(substr($prefix, 0, 1)) . substr($prefix, 1) . 'Result';
|
||||||
|
say $callfh "static void GENERATED_call(const int call_identifier, struct $resultname *result) {";
|
||||||
say $callfh ' switch (call_identifier) {';
|
say $callfh ' switch (call_identifier) {';
|
||||||
my $call_id = 0;
|
my $call_id = 0;
|
||||||
for my $state (@keys) {
|
for my $state (@keys) {
|
||||||
|
@ -132,13 +146,23 @@ for my $state (@keys) {
|
||||||
$next_state ||= 'INITIAL';
|
$next_state ||= 'INITIAL';
|
||||||
my $fmt = $cmd;
|
my $fmt = $cmd;
|
||||||
# Replace the references to identified literals (like $workspace) with
|
# Replace the references to identified literals (like $workspace) with
|
||||||
# calls to get_string().
|
# calls to get_string(). Also replaces state names (like FOR_WINDOW)
|
||||||
|
# with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.).
|
||||||
|
$cmd =~ s/$_/$statenum{$_}/g for @keys;
|
||||||
$cmd =~ s/\$([a-z_]+)/get_string("$1")/g;
|
$cmd =~ s/\$([a-z_]+)/get_string("$1")/g;
|
||||||
# Used only for debugging/testing.
|
$cmd =~ s/\&([a-z_]+)/get_long("$1")/g;
|
||||||
|
# For debugging/testing, we print the call using printf() and thus need
|
||||||
|
# to generate a format string. The format uses %d for <number>s,
|
||||||
|
# literal numbers or state IDs and %s for NULL, <string>s and literal
|
||||||
|
# strings.
|
||||||
|
$fmt =~ s/$_/%d/g for @keys;
|
||||||
$fmt =~ s/\$([a-z_]+)/%s/g;
|
$fmt =~ s/\$([a-z_]+)/%s/g;
|
||||||
|
$fmt =~ s/\&([a-z_]+)/%ld/g;
|
||||||
$fmt =~ s/"([a-z0-9_]+)"/%s/g;
|
$fmt =~ s/"([a-z0-9_]+)"/%s/g;
|
||||||
|
$fmt =~ s/(?:-?|\b)[0-9]+\b/%d/g;
|
||||||
|
|
||||||
say $callfh " case $call_id:";
|
say $callfh " case $call_id:";
|
||||||
|
say $callfh " result->next_state = $next_state;";
|
||||||
say $callfh '#ifndef TEST_PARSER';
|
say $callfh '#ifndef TEST_PARSER';
|
||||||
my $real_cmd = $cmd;
|
my $real_cmd = $cmd;
|
||||||
if ($real_cmd =~ /\(\)/) {
|
if ($real_cmd =~ /\(\)/) {
|
||||||
|
@ -152,9 +176,14 @@ for my $state (@keys) {
|
||||||
$cmd =~ s/[^(]+\(//;
|
$cmd =~ s/[^(]+\(//;
|
||||||
$cmd =~ s/\)$//;
|
$cmd =~ s/\)$//;
|
||||||
$cmd = ", $cmd" if length($cmd) > 0;
|
$cmd = ", $cmd" if length($cmd) > 0;
|
||||||
|
$cmd =~ s/, NULL//g;
|
||||||
say $callfh qq| fprintf(stderr, "$fmt\\n"$cmd);|;
|
say $callfh qq| fprintf(stderr, "$fmt\\n"$cmd);|;
|
||||||
|
# The cfg_criteria functions have side-effects which are important for
|
||||||
|
# testing. They are implemented as stubs in the test parser code.
|
||||||
|
if ($real_cmd =~ /^cfg_criteria/) {
|
||||||
|
say $callfh qq| $real_cmd;|;
|
||||||
|
}
|
||||||
say $callfh '#endif';
|
say $callfh '#endif';
|
||||||
say $callfh " state = $next_state;";
|
|
||||||
say $callfh " break;";
|
say $callfh " break;";
|
||||||
$token->{next_state} = "call $call_id";
|
$token->{next_state} = "call $call_id";
|
||||||
$call_id++;
|
$call_id++;
|
||||||
|
@ -162,17 +191,18 @@ for my $state (@keys) {
|
||||||
}
|
}
|
||||||
say $callfh ' default:';
|
say $callfh ' default:';
|
||||||
say $callfh ' printf("BUG in the parser. state = %d\n", call_identifier);';
|
say $callfh ' printf("BUG in the parser. state = %d\n", call_identifier);';
|
||||||
|
say $callfh ' assert(false);';
|
||||||
say $callfh ' }';
|
say $callfh ' }';
|
||||||
say $callfh '}';
|
say $callfh '}';
|
||||||
close($callfh);
|
close($callfh);
|
||||||
|
|
||||||
# Fourth step: Generate the token datastructures.
|
# Fourth step: Generate the token datastructures.
|
||||||
|
|
||||||
open(my $tokfh, '>', 'GENERATED_tokens.h');
|
open(my $tokfh, '>', "GENERATED_${prefix}_tokens.h");
|
||||||
|
|
||||||
for my $state (@keys) {
|
for my $state (@keys) {
|
||||||
my $tokens = $states{$state};
|
my $tokens = $states{$state};
|
||||||
say $tokfh 'cmdp_token tokens_' . $state . '[' . scalar @$tokens . '] = {';
|
say $tokfh 'static cmdp_token tokens_' . $state . '[' . scalar @$tokens . '] = {';
|
||||||
for my $token (@$tokens) {
|
for my $token (@$tokens) {
|
||||||
my $call_identifier = 0;
|
my $call_identifier = 0;
|
||||||
my $token_name = $token->{token};
|
my $token_name = $token->{token};
|
||||||
|
@ -192,7 +222,7 @@ for my $state (@keys) {
|
||||||
say $tokfh '};';
|
say $tokfh '};';
|
||||||
}
|
}
|
||||||
|
|
||||||
say $tokfh 'cmdp_token_ptr tokens[' . scalar @keys . '] = {';
|
say $tokfh 'static cmdp_token_ptr tokens[' . scalar @keys . '] = {';
|
||||||
for my $state (@keys) {
|
for my $state (@keys) {
|
||||||
my $tokens = $states{$state};
|
my $tokens = $states{$state};
|
||||||
say $tokfh ' { tokens_' . $state . ', ' . scalar @$tokens . ' },';
|
say $tokfh ' { tokens_' . $state . ', ' . scalar @$tokens . ' },';
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* For systems without getline, fall back to fgetln */
|
/* For systems without getline, fall back to fgetln */
|
||||||
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 800000) || defined(__OpenBSD__)
|
#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 800000)
|
||||||
#define USE_FGETLN
|
#define USE_FGETLN
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
/* Defining this macro before including stdio.h is necessary in order to have
|
/* Defining this macro before including stdio.h is necessary in order to have
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef _XCB_H
|
#ifndef I3_XCB_H
|
||||||
#define _XCB_H
|
#define I3_XCB_H
|
||||||
|
|
||||||
/* from X11/keysymdef.h */
|
/* from X11/keysymdef.h */
|
||||||
#define XCB_NUM_LOCK 0xff7f
|
#define XCB_NUM_LOCK 0xff7f
|
||||||
|
|
|
@ -0,0 +1,467 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
# vim:ts=4:sw=4:expandtab
|
||||||
|
#
|
||||||
|
# © 2012 Michael Stapelberg
|
||||||
|
#
|
||||||
|
# No dependencies except for perl ≥ v5.10
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Data::Dumper;
|
||||||
|
use IPC::Open2;
|
||||||
|
use POSIX qw(locale_h);
|
||||||
|
use File::Find;
|
||||||
|
use File::Basename qw(basename);
|
||||||
|
use File::Temp qw(tempfile);
|
||||||
|
use Getopt::Long;
|
||||||
|
use Pod::Usage;
|
||||||
|
use v5.10;
|
||||||
|
use utf8;
|
||||||
|
use open ':encoding(utf8)';
|
||||||
|
|
||||||
|
binmode STDOUT, ':utf8';
|
||||||
|
binmode STDERR, ':utf8';
|
||||||
|
|
||||||
|
# reads in a whole file
|
||||||
|
sub slurp {
|
||||||
|
open(my $fh, '<', shift) or die "$!";
|
||||||
|
local $/;
|
||||||
|
<$fh>;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $entry_type = 'both';
|
||||||
|
my $dmenu_cmd = 'dmenu -i';
|
||||||
|
my $result = GetOptions(
|
||||||
|
'dmenu=s' => \$dmenu_cmd,
|
||||||
|
'entry-type=s' => \$entry_type,
|
||||||
|
'version' => sub {
|
||||||
|
say "dmenu-desktop 1.2 © 2012 Michael Stapelberg";
|
||||||
|
exit 0;
|
||||||
|
},
|
||||||
|
'help' => sub {
|
||||||
|
pod2usage(-exitval => 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
die "Could not parse command line options" unless $result;
|
||||||
|
|
||||||
|
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
# ┃ Convert LC_MESSAGES into an ordered list of suffixes to search for in the ┃
|
||||||
|
# ┃ .desktop files (e.g. “Name[de_DE@euro]” for LC_MESSAGES=de_DE.UTF-8@euro ┃
|
||||||
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
# For details on how the transformation of LC_MESSAGES to a list of keys that
|
||||||
|
# should be looked up works, refer to “Localized values for keys” of the
|
||||||
|
# “Desktop Entry Specification”:
|
||||||
|
# http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html
|
||||||
|
my $lc_messages = setlocale(LC_MESSAGES);
|
||||||
|
|
||||||
|
# Ignore the encoding (e.g. .UTF-8)
|
||||||
|
$lc_messages =~ s/\.[^@]+//g;
|
||||||
|
|
||||||
|
my @suffixes = ($lc_messages);
|
||||||
|
|
||||||
|
# _COUNTRY and @MODIFIER are present
|
||||||
|
if ($lc_messages =~ /_[^@]+@/) {
|
||||||
|
my $no_modifier = $lc_messages;
|
||||||
|
$no_modifier =~ s/@.*//g;
|
||||||
|
push @suffixes, $no_modifier;
|
||||||
|
|
||||||
|
my $no_country = $lc_messages;
|
||||||
|
$no_country =~ s/_[^@]+//g;
|
||||||
|
push @suffixes, $no_country;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Strip _COUNTRY and @MODIFIER if present
|
||||||
|
$lc_messages =~ s/[_@].*//g;
|
||||||
|
push @suffixes, $lc_messages;
|
||||||
|
|
||||||
|
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
# ┃ Read all .desktop files and store the values in which we are interested. ┃
|
||||||
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
my %desktops;
|
||||||
|
# See http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
|
||||||
|
my $xdg_data_home = $ENV{XDG_DATA_HOME};
|
||||||
|
$xdg_data_home = $ENV{HOME} . '/.local/share' if
|
||||||
|
!defined($xdg_data_home) ||
|
||||||
|
$xdg_data_home eq '' ||
|
||||||
|
! -d $xdg_data_home;
|
||||||
|
|
||||||
|
my $xdg_data_dirs = $ENV{XDG_DATA_DIRS};
|
||||||
|
$xdg_data_dirs = '/usr/local/share/:/usr/share/' if
|
||||||
|
!defined($xdg_data_dirs) ||
|
||||||
|
$xdg_data_dirs eq '';
|
||||||
|
|
||||||
|
my @searchdirs = ("$xdg_data_home/applications/");
|
||||||
|
for my $dir (split(':', $xdg_data_dirs)) {
|
||||||
|
push @searchdirs, "$dir/applications/";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup the paths, maybe some application does not cope with double slashes
|
||||||
|
# (the field code %k is replaced with the .desktop file location).
|
||||||
|
@searchdirs = map { s,//,/,g; $_ } @searchdirs;
|
||||||
|
|
||||||
|
# To avoid errors by File::Find’s find(), only pass existing directories.
|
||||||
|
@searchdirs = grep { -d $_ } @searchdirs;
|
||||||
|
|
||||||
|
find(
|
||||||
|
{
|
||||||
|
wanted => sub {
|
||||||
|
return unless substr($_, -1 * length('.desktop')) eq '.desktop';
|
||||||
|
my $relative = $File::Find::name;
|
||||||
|
|
||||||
|
# + 1 for the trailing /, which is missing in ::topdir.
|
||||||
|
substr($relative, 0, length($File::Find::topdir) + 1) = '';
|
||||||
|
|
||||||
|
# Don’t overwrite files with the same relative path, we search in
|
||||||
|
# descending order of importance.
|
||||||
|
return if exists($desktops{$relative});
|
||||||
|
|
||||||
|
$desktops{$relative} = $File::Find::name;
|
||||||
|
},
|
||||||
|
no_chdir => 1,
|
||||||
|
},
|
||||||
|
@searchdirs
|
||||||
|
);
|
||||||
|
|
||||||
|
my %apps;
|
||||||
|
|
||||||
|
for my $file (values %desktops) {
|
||||||
|
my $base = basename($file);
|
||||||
|
|
||||||
|
# _ is an invalid character for a key, so we can use it for our own keys.
|
||||||
|
$apps{$base}->{_Location} = $file;
|
||||||
|
|
||||||
|
# Extract all “Name” and “Exec” keys from the [Desktop Entry] group
|
||||||
|
# and store them in $apps{$base}.
|
||||||
|
my %names;
|
||||||
|
my @lines = split("\n", slurp($file));
|
||||||
|
for my $line (@lines) {
|
||||||
|
my $first = substr($line, 0, 1);
|
||||||
|
next if $line eq '' || $first eq '#';
|
||||||
|
next unless ($line eq '[Desktop Entry]' ..
|
||||||
|
($first eq '[' &&
|
||||||
|
substr($line, -1) eq ']' &&
|
||||||
|
$line ne '[Desktop Entry]'));
|
||||||
|
next if $first eq '[';
|
||||||
|
|
||||||
|
my ($key, $value) = ($line =~ /^
|
||||||
|
(
|
||||||
|
[A-Za-z0-9-]+ # the spec specifies these as valid key characters
|
||||||
|
(?:\[[^]]+\])? # possibly, there as a locale suffix
|
||||||
|
)
|
||||||
|
\s* = \s* # whitespace around = should be ignored
|
||||||
|
(.*) # no restrictions on the values
|
||||||
|
$/x);
|
||||||
|
|
||||||
|
if ($key =~ /^Name/) {
|
||||||
|
$names{$key} = $value;
|
||||||
|
} elsif ($key eq 'Exec' ||
|
||||||
|
$key eq 'TryExec' ||
|
||||||
|
$key eq 'Type') {
|
||||||
|
$apps{$base}->{$key} = $value;
|
||||||
|
} elsif ($key eq 'NoDisplay' ||
|
||||||
|
$key eq 'Hidden' ||
|
||||||
|
$key eq 'StartupNotify' ||
|
||||||
|
$key eq 'Terminal') {
|
||||||
|
# Values of type boolean must either be string true or false,
|
||||||
|
# see “Possible value types”:
|
||||||
|
# http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html
|
||||||
|
$apps{$base}->{$key} = ($value eq 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for my $suffix (@suffixes) {
|
||||||
|
next unless exists($names{"Name[$suffix]"});
|
||||||
|
$apps{$base}->{Name} = $names{"Name[$suffix]"};
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fallback to unlocalized “Name”.
|
||||||
|
$apps{$base}->{Name} = $names{Name} unless exists($apps{$base}->{Name});
|
||||||
|
}
|
||||||
|
|
||||||
|
# %apps now looks like this:
|
||||||
|
#
|
||||||
|
# %apps = {
|
||||||
|
# 'evince.desktop' => {
|
||||||
|
# 'Exec' => 'evince %U',
|
||||||
|
# 'Name' => 'Dokumentenbetrachter',
|
||||||
|
# '_Location' => '/usr/share/applications/evince.desktop'
|
||||||
|
# },
|
||||||
|
# 'gedit.desktop' => {
|
||||||
|
# 'Exec' => 'gedit %U',
|
||||||
|
# 'Name' => 'gedit',
|
||||||
|
# '_Location' => '/usr/share/applications/gedit.desktop'
|
||||||
|
# }
|
||||||
|
# };
|
||||||
|
|
||||||
|
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
# ┃ Turn %apps inside out to provide Name → filename lookup. ┃
|
||||||
|
# ┃ The Name is what we display in dmenu later. ┃
|
||||||
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
my %choices;
|
||||||
|
for my $app (keys %apps) {
|
||||||
|
my $name = $apps{$app}->{Name};
|
||||||
|
|
||||||
|
# Don’t try to use .desktop files which don’t have Type=application
|
||||||
|
next if (!exists($apps{$app}->{Type}) ||
|
||||||
|
$apps{$app}->{Type} ne 'Application');
|
||||||
|
|
||||||
|
# Don’t offer apps which have NoDisplay == true or Hidden == true.
|
||||||
|
# See http://wiki.xfce.org/howto/customize-menu#hide_menu_entries
|
||||||
|
# for the difference between NoDisplay and Hidden.
|
||||||
|
next if (exists($apps{$app}->{NoDisplay}) && $apps{$app}->{NoDisplay}) ||
|
||||||
|
(exists($apps{$app}->{Hidden}) && $apps{$app}->{Hidden});
|
||||||
|
|
||||||
|
if (exists($apps{$app}->{TryExec})) {
|
||||||
|
my $tryexec = $apps{$app}->{TryExec};
|
||||||
|
if (substr($tryexec, 0, 1) eq '/') {
|
||||||
|
# Skip if absolute path is not executable.
|
||||||
|
next unless -x $tryexec;
|
||||||
|
} else {
|
||||||
|
# Search in $PATH for the executable.
|
||||||
|
my $found = 0;
|
||||||
|
for my $path (split(':', $ENV{PATH})) {
|
||||||
|
next unless -x "$path/$tryexec";
|
||||||
|
$found = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
next unless $found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry_type eq 'name' || $entry_type eq 'both') {
|
||||||
|
if (exists($choices{$name})) {
|
||||||
|
# There are two .desktop files which contain the same “Name” value.
|
||||||
|
# I’m not sure if that is allowed to happen, but we disambiguate the
|
||||||
|
# situation by appending “ (2)”, “ (3)”, etc. to the name.
|
||||||
|
#
|
||||||
|
# An example of this happening is exo-file-manager.desktop and
|
||||||
|
# thunar-settings.desktop, both of which contain “Name=File Manager”.
|
||||||
|
my $inc = 2;
|
||||||
|
$inc++ while exists($choices{"$name ($inc)"});
|
||||||
|
$name = "$name ($inc)";
|
||||||
|
}
|
||||||
|
|
||||||
|
$choices{$name} = $app;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($entry_type eq 'command' || $entry_type eq 'both') {
|
||||||
|
my ($command) = split(' ', $apps{$app}->{Exec});
|
||||||
|
$choices{basename($command)} = $app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# %choices now looks like this:
|
||||||
|
#
|
||||||
|
# %choices = {
|
||||||
|
# 'Dokumentenbetrachter' => 'evince.desktop',
|
||||||
|
# 'gedit' => 'gedit.desktop'
|
||||||
|
# };
|
||||||
|
|
||||||
|
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
# ┃ Run dmenu to ask the user for her choice ┃
|
||||||
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
# open2 will just make dmenu’s STDERR go to our own STDERR.
|
||||||
|
my ($dmenu_out, $dmenu_in);
|
||||||
|
my $pid = open2($dmenu_out, $dmenu_in, $dmenu_cmd);
|
||||||
|
binmode $dmenu_in, ':utf8';
|
||||||
|
binmode $dmenu_out, ':utf8';
|
||||||
|
|
||||||
|
# Feed dmenu the possible choices.
|
||||||
|
say $dmenu_in $_ for sort keys %choices;
|
||||||
|
close($dmenu_in);
|
||||||
|
|
||||||
|
waitpid($pid, 0);
|
||||||
|
my $status = ($? >> 8);
|
||||||
|
|
||||||
|
# Pass on dmenu’s exit status if there was an error.
|
||||||
|
exit $status unless $status == 0;
|
||||||
|
|
||||||
|
my $choice = <$dmenu_out>;
|
||||||
|
my $app;
|
||||||
|
# Exact match: the user chose “Avidemux (GTK+)”
|
||||||
|
if (exists($choices{$choice})) {
|
||||||
|
$app = $apps{$choices{$choice}};
|
||||||
|
$choice = '';
|
||||||
|
} else {
|
||||||
|
# Not an exact match: the user entered “Avidemux (GTK+) ~/movie.mp4”
|
||||||
|
for my $possibility (keys %choices) {
|
||||||
|
next unless substr($choice, 0, length($possibility)) eq $possibility;
|
||||||
|
$app = $apps{$choices{$possibility}};
|
||||||
|
substr($choice, 0, length($possibility)) = '';
|
||||||
|
# Remove whitespace separating the entry and arguments.
|
||||||
|
$choice =~ s/^\s//g;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
if (!defined($app)) {
|
||||||
|
die "Invalid input: “$choice” does not match any application.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
# ┃ Make i3 start the chosen application. ┃
|
||||||
|
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
|
||||||
|
my $name = $app->{Name};
|
||||||
|
my $exec = $app->{Exec};
|
||||||
|
my $location = $app->{_Location};
|
||||||
|
|
||||||
|
# Quote as described by “The Exec key”:
|
||||||
|
# http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html
|
||||||
|
sub quote {
|
||||||
|
my ($str) = @_;
|
||||||
|
$str =~ s/("|`|\$|\\)/\\$1/g;
|
||||||
|
$str = qq|"$str"| if $str ne "";
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
$choice = quote($choice);
|
||||||
|
$location = quote($location);
|
||||||
|
|
||||||
|
# Remove deprecated field codes, as the spec dictates.
|
||||||
|
$exec =~ s/%[dDnNvm]//g;
|
||||||
|
|
||||||
|
# Replace filename field codes with the rest of the command line.
|
||||||
|
# Note that we assume the user uses precisely one file name,
|
||||||
|
# not multiple file names.
|
||||||
|
$exec =~ s/%[fF]/$choice/g;
|
||||||
|
|
||||||
|
# If the program works with URLs,
|
||||||
|
# we assume the user provided a URL instead of a filename.
|
||||||
|
# As per the spec, there must be at most one of %f, %u, %F or %U present.
|
||||||
|
$exec =~ s/%[uU]/$choice/g;
|
||||||
|
|
||||||
|
# The translated name of the application.
|
||||||
|
$exec =~ s/%c/$name/g;
|
||||||
|
|
||||||
|
# XXX: Icons are not implemented. Is the complexity (looking up the path if
|
||||||
|
# only a name is given) actually worth it?
|
||||||
|
#$exec =~ s/%i/--icon $icon/g;
|
||||||
|
|
||||||
|
# location of .desktop file
|
||||||
|
$exec =~ s/%k/$location/g;
|
||||||
|
|
||||||
|
# Literal % characters are represented as %%.
|
||||||
|
$exec =~ s/%%/%/g;
|
||||||
|
|
||||||
|
my $nosn = '';
|
||||||
|
my $cmd;
|
||||||
|
if (exists($app->{Terminal}) && $app->{Terminal}) {
|
||||||
|
# For applications which specify “Terminal=true” (e.g. htop.desktop),
|
||||||
|
# we need to create a temporary script that contains the full command line
|
||||||
|
# as the syntax for starting commands with arguments varies from terminal
|
||||||
|
# emulator to terminal emulator.
|
||||||
|
# Then, we launch that script with i3-sensible-terminal.
|
||||||
|
my ($fh, $filename) = tempfile();
|
||||||
|
binmode($fh, ':utf8');
|
||||||
|
say $fh <<EOT;
|
||||||
|
#!/bin/sh
|
||||||
|
rm $filename
|
||||||
|
exec $exec
|
||||||
|
EOT
|
||||||
|
close($fh);
|
||||||
|
chmod 0755, $filename;
|
||||||
|
|
||||||
|
$cmd = qq|exec i3-sensible-terminal -e "$filename"|;
|
||||||
|
} else {
|
||||||
|
# i3 executes applications by passing the argument to i3’s “exec” command
|
||||||
|
# as-is to $SHELL -c. The i3 parser supports quoted strings: When a string
|
||||||
|
# starts with a double quote ("), everything is parsed as-is until the next
|
||||||
|
# double quote which is NOT preceded by a backslash (\).
|
||||||
|
#
|
||||||
|
# Therefore, we escape all double quotes (") by replacing them with \"
|
||||||
|
$exec =~ s/"/\\"/g;
|
||||||
|
|
||||||
|
if (exists($app->{StartupNotify}) && !$app->{StartupNotify}) {
|
||||||
|
$nosn = '--no-startup-id';
|
||||||
|
}
|
||||||
|
$cmd = qq|exec $nosn "$exec"|;
|
||||||
|
}
|
||||||
|
|
||||||
|
system('i3-msg', $cmd) == 0 or die "Could not launch i3-msg: $?";
|
||||||
|
|
||||||
|
=encoding utf-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
i3-dmenu-desktop - run .desktop files with dmenu
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
i3-dmenu-desktop [--dmenu='dmenu -i'] [--entry-type=both]
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
i3-dmenu-desktop is a script which extracts the (localized) name from
|
||||||
|
application .desktop files, offers the user a choice via dmenu(1) and then
|
||||||
|
starts the chosen application via i3 (for startup notification support).
|
||||||
|
The advantage of using .desktop files instead of dmenu_run(1) is that dmenu_run
|
||||||
|
offers B<all> binaries in your $PATH, including non-interactive utilities like
|
||||||
|
"sed". Also, .desktop files contain a proper name, information about whether
|
||||||
|
the application runs in a terminal and whether it supports startup
|
||||||
|
notifications.
|
||||||
|
|
||||||
|
The .desktop files are searched in $XDG_DATA_HOME/applications (by default
|
||||||
|
$HOME/.local/share/applications) and in the "applications" subdirectory of each
|
||||||
|
entry of $XDG_DATA_DIRS (by default /usr/local/share/:/usr/share/).
|
||||||
|
|
||||||
|
Files with the same name in $XDG_DATA_HOME/applications take precedence over
|
||||||
|
files in $XDG_DATA_DIRS, so that you can overwrite parts of the system-wide
|
||||||
|
.desktop files by copying them to your local directory and making changes.
|
||||||
|
|
||||||
|
i3-dmenu-desktop displays the "Name" value in the localized version depending
|
||||||
|
on LC_MESSAGES as specified in the Desktop Entry Specification.
|
||||||
|
|
||||||
|
You can pass a filename or URL (%f/%F and %u/%U field codes in the .desktop
|
||||||
|
file respectively) by appending it to the name of the application. E.g., if you
|
||||||
|
want to launch "GNU Emacs 24" with the patch /tmp/foobar.txt, you would type
|
||||||
|
"emacs", press TAB, type " /tmp/foobar.txt" and press ENTER.
|
||||||
|
|
||||||
|
.desktop files with Terminal=true are started using i3-sensible-terminal(1).
|
||||||
|
|
||||||
|
.desktop files with NoDisplay=true or Hidden=true are skipped.
|
||||||
|
|
||||||
|
UTF-8 is supported, of course, but dmenu does not support displaying all
|
||||||
|
glyphs. E.g., xfce4-terminal.desktop's Name[fi]=Pääte will be displayed just
|
||||||
|
fine, but not its Name[ru]=Терминал.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<--dmenu=command>
|
||||||
|
|
||||||
|
Execute command instead of 'dmenu -i'. This option can be used to pass custom
|
||||||
|
parameters to dmenu, or to make i3-dmenu-desktop start a custom (patched?)
|
||||||
|
version of dmenu.
|
||||||
|
|
||||||
|
=item B<--entry-type=type>
|
||||||
|
|
||||||
|
Display the (localized) "Name" (type = name) or the command (type = command) or
|
||||||
|
both (type = both) in dmenu.
|
||||||
|
|
||||||
|
Examples are "GNU Image Manipulation Program" (type = name), "gimp" (type =
|
||||||
|
command) and both (type = both).
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 VERSION
|
||||||
|
|
||||||
|
Version 1.2
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Michael Stapelberg, C<< <michael at i3wm.org> >>
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright 2012 Michael Stapelberg.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the BSD license.
|
||||||
|
|
||||||
|
=cut
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef _I3_INPUT
|
#ifndef I3_INPUT
|
||||||
#define _I3_INPUT
|
#define I3_INPUT
|
||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef _I3_NAGBAR
|
#ifndef I3_NAGBAR
|
||||||
#define _I3_NAGBAR
|
#define I3_NAGBAR
|
||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
# The font above is very space-efficient, that is, it looks good, sharp and
|
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||||
# clear in small sizes. However, if you need a lot of unicode glyphs or
|
# clear in small sizes. However, if you need a lot of unicode glyphs or
|
||||||
# right-to-left text rendering, you should instead use pango for rendering and
|
# right-to-left text rendering, you should instead use pango for rendering and
|
||||||
# chose an xft font, such as:
|
# chose a FreeType font, such as:
|
||||||
# font xft:DejaVu Sans Mono 10
|
# font pango:DejaVu Sans Mono 10
|
||||||
|
|
||||||
# use Mouse+Mod1 to drag floating windows to their wanted position
|
# use Mouse+Mod1 to drag floating windows to their wanted position
|
||||||
floating_modifier Mod1
|
floating_modifier Mod1
|
||||||
|
@ -29,6 +29,10 @@ bindsym Mod1+Shift+q kill
|
||||||
|
|
||||||
# start dmenu (a program launcher)
|
# start dmenu (a program launcher)
|
||||||
bindsym Mod1+d exec dmenu_run
|
bindsym Mod1+d exec dmenu_run
|
||||||
|
# There also is the (new) i3-dmenu-desktop which only displays applications
|
||||||
|
# shipping a .desktop file. It is a wrapper around dmenu, so you need that
|
||||||
|
# installed.
|
||||||
|
# bindsym Mod1+d exec --no-startup-id i3-dmenu-desktop
|
||||||
|
|
||||||
# change focus
|
# change focus
|
||||||
bindsym Mod1+j focus left
|
bindsym Mod1+j focus left
|
||||||
|
|
|
@ -16,8 +16,8 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
# The font above is very space-efficient, that is, it looks good, sharp and
|
# The font above is very space-efficient, that is, it looks good, sharp and
|
||||||
# clear in small sizes. However, if you need a lot of unicode glyphs or
|
# clear in small sizes. However, if you need a lot of unicode glyphs or
|
||||||
# right-to-left text rendering, you should instead use pango for rendering and
|
# right-to-left text rendering, you should instead use pango for rendering and
|
||||||
# chose an xft font, such as:
|
# chose a FreeType font, such as:
|
||||||
# font xft:DejaVu Sans Mono 10
|
# font pango:DejaVu Sans Mono 10
|
||||||
|
|
||||||
# Use Mouse+$mod to drag floating windows to their wanted position
|
# Use Mouse+$mod to drag floating windows to their wanted position
|
||||||
floating_modifier $mod
|
floating_modifier $mod
|
||||||
|
@ -30,6 +30,10 @@ bindcode $mod+Shift+24 kill
|
||||||
|
|
||||||
# start dmenu (a program launcher)
|
# start dmenu (a program launcher)
|
||||||
bindcode $mod+40 exec dmenu_run
|
bindcode $mod+40 exec dmenu_run
|
||||||
|
# There also is the (new) i3-dmenu-desktop which only displays applications
|
||||||
|
# shipping a .desktop file. It is a wrapper around dmenu, so you need that
|
||||||
|
# installed.
|
||||||
|
# bindsym $mod+d exec --no-startup-id i3-dmenu-desktop
|
||||||
|
|
||||||
# change focus
|
# change focus
|
||||||
bindcode $mod+44 focus left
|
bindcode $mod+44 focus left
|
||||||
|
@ -79,7 +83,7 @@ bindcode $mod+65 focus mode_toggle
|
||||||
bindcode $mod+38 focus parent
|
bindcode $mod+38 focus parent
|
||||||
|
|
||||||
# focus the child container
|
# focus the child container
|
||||||
#bindcode $mod+d focus child
|
#bindsym $mod+d focus child
|
||||||
|
|
||||||
# switch to workspace
|
# switch to workspace
|
||||||
bindcode $mod+10 workspace 1
|
bindcode $mod+10 workspace 1
|
||||||
|
|
|
@ -10,8 +10,31 @@
|
||||||
#ifndef CHILD_H_
|
#ifndef CHILD_H_
|
||||||
#define CHILD_H_
|
#define CHILD_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define STDIN_CHUNK_SIZE 1024
|
#define STDIN_CHUNK_SIZE 1024
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version number is an uint32_t to avoid machines with different sizes of
|
||||||
|
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
||||||
|
* even an int8_t, but still…
|
||||||
|
*/
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
bool stopped;
|
||||||
|
/**
|
||||||
|
* The signal requested by the client to inform it of the hidden state of i3bar
|
||||||
|
*/
|
||||||
|
int stop_signal;
|
||||||
|
/**
|
||||||
|
* The signal requested by the client to inform it of theun hidden state of i3bar
|
||||||
|
*/
|
||||||
|
int cont_signal;
|
||||||
|
} i3bar_child;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start a child-process with the specified command and reroute stdin.
|
* Start a child-process with the specified command and reroute stdin.
|
||||||
* We actually start a $SHELL to execute the command so we don't have to care
|
* We actually start a $SHELL to execute the command so we don't have to care
|
||||||
|
|
|
@ -27,16 +27,28 @@ struct rect_t {
|
||||||
int h;
|
int h;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ALIGN_LEFT,
|
||||||
|
ALIGN_CENTER,
|
||||||
|
ALIGN_RIGHT
|
||||||
|
} blockalign_t;
|
||||||
|
|
||||||
/* This data structure represents one JSON dictionary, multiple of these make
|
/* This data structure represents one JSON dictionary, multiple of these make
|
||||||
* up one status line. */
|
* up one status line. */
|
||||||
struct status_block {
|
struct status_block {
|
||||||
i3String *full_text;
|
i3String *full_text;
|
||||||
|
|
||||||
char *color;
|
char *color;
|
||||||
|
uint32_t min_width;
|
||||||
|
blockalign_t align;
|
||||||
|
|
||||||
/* The amount of pixels necessary to render this block. This variable is
|
bool urgent;
|
||||||
|
|
||||||
|
/* The amount of pixels necessary to render this block. These variables are
|
||||||
* only temporarily used in refresh_statusline(). */
|
* only temporarily used in refresh_statusline(). */
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
|
uint32_t x_offset;
|
||||||
|
uint32_t x_append;
|
||||||
|
|
||||||
TAILQ_ENTRY(status_block) blocks;
|
TAILQ_ENTRY(status_block) blocks;
|
||||||
};
|
};
|
||||||
|
@ -48,10 +60,11 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head;
|
||||||
#include "outputs.h"
|
#include "outputs.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "workspaces.h"
|
#include "workspaces.h"
|
||||||
|
#include "mode.h"
|
||||||
#include "trayclients.h"
|
#include "trayclients.h"
|
||||||
#include "xcb.h"
|
#include "xcb.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "libi3.h"
|
#include "libi3.h"
|
||||||
#include "determine_json_version.h"
|
#include "parse_json_header.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* vim:ts=4:sw=4:expandtab
|
|
||||||
*
|
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
|
||||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
|
||||||
*
|
|
||||||
* determine_json_version.c: Determines the JSON protocol version based on the
|
|
||||||
* first line of input from a child program.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef DETERMINE_JSON_VERSION_H_
|
|
||||||
#define DETERMINE_JSON_VERSION_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determines the JSON i3bar protocol version from the given buffer. In case
|
|
||||||
* the buffer does not contain valid JSON, or no version field is found, this
|
|
||||||
* function returns -1. The amount of bytes consumed by parsing the header is
|
|
||||||
* returned in *consumed (if non-NULL).
|
|
||||||
*
|
|
||||||
* The return type is an int32_t to avoid machines with different sizes of
|
|
||||||
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
|
||||||
* even an int8_t, but still…
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* mode.c: Handle mode-event and show current binding mode in the bar
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef MODE_H_
|
||||||
|
#define MODE_H_
|
||||||
|
|
||||||
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* Name of current binding mode and its render width */
|
||||||
|
struct mode {
|
||||||
|
i3String *name;
|
||||||
|
int width;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct mode mode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start parsing the received json-string
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_mode_json(char *json);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* parse_json_header.c: Parse the JSON protocol header to determine
|
||||||
|
* protocol version and features.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef PARSE_JSON_HEADER_H_
|
||||||
|
#define PARSE_JSON_HEADER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the JSON protocol header to determine protocol version and features.
|
||||||
|
* In case the buffer does not contain a valid header (invalid JSON, or no
|
||||||
|
* version field found), the 'correct' field of the returned header is set to
|
||||||
|
* false. The amount of bytes consumed by parsing the header is returned in
|
||||||
|
* *consumed (if non-NULL).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed);
|
||||||
|
|
||||||
|
#endif
|
|
@ -16,6 +16,8 @@
|
||||||
#undef MIN
|
#undef MIN
|
||||||
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
#define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0)
|
||||||
|
|
||||||
/* Securely free p */
|
/* Securely free p */
|
||||||
#define FREE(p) do { \
|
#define FREE(p) do { \
|
||||||
if (p != NULL) { \
|
if (p != NULL) { \
|
||||||
|
|
|
@ -110,7 +110,7 @@ void reconfig_windows(void);
|
||||||
* Render the bars, with buttons and statusline
|
* Render the bars, with buttons and statusline
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void draw_bars(void);
|
void draw_bars(bool force_unhide);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
* Redraw the bars, i.e. simply copy the buffer to the barwindow
|
||||||
|
@ -118,4 +118,10 @@ void draw_bars(void);
|
||||||
*/
|
*/
|
||||||
void redraw_bars(void);
|
void redraw_bars(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the current binding mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void set_current_mode(struct mode *mode);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,19 +25,20 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
/* Global variables for child_*() */
|
/* Global variables for child_*() */
|
||||||
pid_t child_pid;
|
i3bar_child child = { 0 };
|
||||||
|
|
||||||
/* stdin- and sigchild-watchers */
|
/* stdin- and sigchild-watchers */
|
||||||
ev_io *stdin_io;
|
ev_io *stdin_io;
|
||||||
ev_child *child_sig;
|
ev_child *child_sig;
|
||||||
|
|
||||||
/* JSON parser for stdin */
|
/* JSON parser for stdin */
|
||||||
bool first_line = true;
|
|
||||||
bool plaintext = false;
|
|
||||||
yajl_callbacks callbacks;
|
yajl_callbacks callbacks;
|
||||||
yajl_handle parser;
|
yajl_handle parser;
|
||||||
|
|
||||||
typedef struct parser_ctx {
|
typedef struct parser_ctx {
|
||||||
|
/* True if one of the parsed blocks was urgent */
|
||||||
|
bool has_urgent;
|
||||||
|
|
||||||
/* A copy of the last JSON map key. */
|
/* A copy of the last JSON map key. */
|
||||||
char *last_map_key;
|
char *last_map_key;
|
||||||
|
|
||||||
|
@ -69,6 +70,8 @@ void cleanup(void) {
|
||||||
ev_child_stop(main_loop, child_sig);
|
ev_child_stop(main_loop, child_sig);
|
||||||
FREE(child_sig);
|
FREE(child_sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&child, 0, sizeof(i3bar_child));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -109,6 +112,14 @@ static int stdin_map_key(void *context, const unsigned char *key, unsigned int l
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stdin_boolean(void *context, int val) {
|
||||||
|
parser_ctx *ctx = context;
|
||||||
|
if (strcasecmp(ctx->last_map_key, "urgent") == 0) {
|
||||||
|
ctx->block.urgent = val;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#if YAJL_MAJOR >= 2
|
#if YAJL_MAJOR >= 2
|
||||||
static int stdin_string(void *context, const unsigned char *val, size_t len) {
|
static int stdin_string(void *context, const unsigned char *val, size_t len) {
|
||||||
#else
|
#else
|
||||||
|
@ -121,6 +132,27 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le
|
||||||
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
if (strcasecmp(ctx->last_map_key, "color") == 0) {
|
||||||
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
sasprintf(&(ctx->block.color), "%.*s", len, val);
|
||||||
}
|
}
|
||||||
|
if (strcasecmp(ctx->last_map_key, "align") == 0) {
|
||||||
|
if (len == strlen("left") && !strncmp((const char*)val, "left", strlen("left"))) {
|
||||||
|
ctx->block.align = ALIGN_LEFT;
|
||||||
|
} else if (len == strlen("right") && !strncmp((const char*)val, "right", strlen("right"))) {
|
||||||
|
ctx->block.align = ALIGN_RIGHT;
|
||||||
|
} else {
|
||||||
|
ctx->block.align = ALIGN_CENTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int stdin_integer(void *context, long long val) {
|
||||||
|
#else
|
||||||
|
static int stdin_integer(void *context, long val) {
|
||||||
|
#endif
|
||||||
|
parser_ctx *ctx = context;
|
||||||
|
if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
|
||||||
|
ctx->block.min_width = (uint32_t)val;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +164,8 @@ static int stdin_end_map(void *context) {
|
||||||
* i3bar doesn’t crash and the user gets an annoying message. */
|
* i3bar doesn’t crash and the user gets an annoying message. */
|
||||||
if (!new_block->full_text)
|
if (!new_block->full_text)
|
||||||
new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)");
|
new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)");
|
||||||
|
if (new_block->urgent)
|
||||||
|
ctx->has_urgent = true;
|
||||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -148,11 +182,10 @@ static int stdin_end_array(void *context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callbalk for stdin. We read a line from stdin and store the result
|
* Helper function to read stdin
|
||||||
* in statusline
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) {
|
||||||
int fd = watcher->fd;
|
int fd = watcher->fd;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
int rec = 0;
|
int rec = 0;
|
||||||
|
@ -173,8 +206,9 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
/* end of file, kill the watcher */
|
/* end of file, kill the watcher */
|
||||||
ELOG("stdin: received EOF\n");
|
ELOG("stdin: received EOF\n");
|
||||||
cleanup();
|
cleanup();
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
return;
|
*ret_buffer_len = -1;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
rec += n;
|
rec += n;
|
||||||
|
|
||||||
|
@ -185,51 +219,94 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
}
|
}
|
||||||
if (*buffer == '\0') {
|
if (*buffer == '\0') {
|
||||||
FREE(buffer);
|
FREE(buffer);
|
||||||
return;
|
rec = -1;
|
||||||
}
|
}
|
||||||
|
*ret_buffer_len = rec;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char *json_input = buffer;
|
static void read_flat_input(char *buffer, int length) {
|
||||||
if (first_line) {
|
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
||||||
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
|
/* Clear the old buffer if any. */
|
||||||
/* Detect whether this is JSON or plain text. */
|
I3STRING_FREE(first->full_text);
|
||||||
unsigned int consumed = 0;
|
/* Remove the trailing newline and terminate the string at the same
|
||||||
/* At the moment, we don’t care for the version. This might change
|
* time. */
|
||||||
* in the future, but for now, we just discard it. */
|
if (buffer[length-1] == '\n' || buffer[length-1] == '\r')
|
||||||
plaintext = (determine_json_version(buffer, buffer_len, &consumed) == -1);
|
buffer[length-1] = '\0';
|
||||||
if (plaintext) {
|
else buffer[length] = '\0';
|
||||||
/* In case of plaintext, we just add a single block and change its
|
first->full_text = i3string_from_utf8(buffer);
|
||||||
* full_text pointer later. */
|
}
|
||||||
struct status_block *new_block = scalloc(sizeof(struct status_block));
|
|
||||||
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
static bool read_json_input(unsigned char *input, int length) {
|
||||||
} else {
|
yajl_status status = yajl_parse(parser, input, length);
|
||||||
json_input += consumed;
|
bool has_urgent = false;
|
||||||
rec -= consumed;
|
|
||||||
}
|
|
||||||
first_line = false;
|
|
||||||
}
|
|
||||||
if (!plaintext) {
|
|
||||||
yajl_status status = yajl_parse(parser, json_input, rec);
|
|
||||||
#if YAJL_MAJOR >= 2
|
#if YAJL_MAJOR >= 2
|
||||||
if (status != yajl_status_ok) {
|
if (status != yajl_status_ok) {
|
||||||
#else
|
#else
|
||||||
if (status != yajl_status_ok && status != yajl_status_insufficient_data) {
|
if (status != yajl_status_ok && status != yajl_status_insufficient_data) {
|
||||||
#endif
|
#endif
|
||||||
fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
|
fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
|
||||||
status, rec, json_input);
|
status, length, input);
|
||||||
|
} else if (parser_context.has_urgent) {
|
||||||
|
has_urgent = true;
|
||||||
}
|
}
|
||||||
|
return has_urgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callbalk for stdin. We read a line from stdin and store the result
|
||||||
|
* in statusline
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
|
int rec;
|
||||||
|
unsigned char *buffer = get_buffer(watcher, &rec);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return;
|
||||||
|
bool has_urgent = false;
|
||||||
|
if (child.version > 0) {
|
||||||
|
has_urgent = read_json_input(buffer, rec);
|
||||||
} else {
|
} else {
|
||||||
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
read_flat_input((char*)buffer, rec);
|
||||||
/* Clear the old buffer if any. */
|
|
||||||
I3STRING_FREE(first->full_text);
|
|
||||||
/* Remove the trailing newline and terminate the string at the same
|
|
||||||
* time. */
|
|
||||||
if (buffer[rec-1] == '\n' || buffer[rec-1] == '\r')
|
|
||||||
buffer[rec-1] = '\0';
|
|
||||||
else buffer[rec] = '\0';
|
|
||||||
first->full_text = i3string_from_utf8((const char *)buffer);
|
|
||||||
}
|
}
|
||||||
free(buffer);
|
free(buffer);
|
||||||
draw_bars();
|
draw_bars(has_urgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callbalk for stdin first line. We read the first line to detect
|
||||||
|
* whether this is JSON or plain text
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
|
int rec;
|
||||||
|
unsigned char *buffer = get_buffer(watcher, &rec);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return;
|
||||||
|
DLOG("Detecting input type based on buffer *%.*s*\n", rec, buffer);
|
||||||
|
/* Detect whether this is JSON or plain text. */
|
||||||
|
unsigned int consumed = 0;
|
||||||
|
/* At the moment, we don’t care for the version. This might change
|
||||||
|
* in the future, but for now, we just discard it. */
|
||||||
|
parse_json_header(&child, buffer, rec, &consumed);
|
||||||
|
if (child.version > 0) {
|
||||||
|
/* If hide-on-modifier is set, we start of by sending the
|
||||||
|
* child a SIGSTOP, because the bars aren't mapped at start */
|
||||||
|
if (config.hide_on_modifier) {
|
||||||
|
stop_child();
|
||||||
|
}
|
||||||
|
read_json_input(buffer + consumed, rec - consumed);
|
||||||
|
} else {
|
||||||
|
/* In case of plaintext, we just add a single block and change its
|
||||||
|
* full_text pointer later. */
|
||||||
|
struct status_block *new_block = scalloc(sizeof(struct status_block));
|
||||||
|
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
|
||||||
|
read_flat_input((char*)buffer, rec);
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
ev_io_stop(main_loop, stdin_io);
|
||||||
|
ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
|
||||||
|
ev_io_start(main_loop, stdin_io);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -240,7 +317,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
||||||
*/
|
*/
|
||||||
void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
|
void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
|
||||||
ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
|
ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
|
||||||
child_pid,
|
child.pid,
|
||||||
watcher->rstatus);
|
watcher->rstatus);
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
@ -255,7 +332,9 @@ void start_child(char *command) {
|
||||||
/* Allocate a yajl parser which will be used to parse stdin. */
|
/* Allocate a yajl parser which will be used to parse stdin. */
|
||||||
memset(&callbacks, '\0', sizeof(yajl_callbacks));
|
memset(&callbacks, '\0', sizeof(yajl_callbacks));
|
||||||
callbacks.yajl_map_key = stdin_map_key;
|
callbacks.yajl_map_key = stdin_map_key;
|
||||||
|
callbacks.yajl_boolean = stdin_boolean;
|
||||||
callbacks.yajl_string = stdin_string;
|
callbacks.yajl_string = stdin_string;
|
||||||
|
callbacks.yajl_integer = stdin_integer;
|
||||||
callbacks.yajl_start_array = stdin_start_array;
|
callbacks.yajl_start_array = stdin_start_array;
|
||||||
callbacks.yajl_end_array = stdin_end_array;
|
callbacks.yajl_end_array = stdin_end_array;
|
||||||
callbacks.yajl_start_map = stdin_start_map;
|
callbacks.yajl_start_map = stdin_start_map;
|
||||||
|
@ -268,14 +347,13 @@ void start_child(char *command) {
|
||||||
parser = yajl_alloc(&callbacks, NULL, &parser_context);
|
parser = yajl_alloc(&callbacks, NULL, &parser_context);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
child_pid = 0;
|
|
||||||
if (command != NULL) {
|
if (command != NULL) {
|
||||||
int fd[2];
|
int fd[2];
|
||||||
if (pipe(fd) == -1)
|
if (pipe(fd) == -1)
|
||||||
err(EXIT_FAILURE, "pipe(fd)");
|
err(EXIT_FAILURE, "pipe(fd)");
|
||||||
|
|
||||||
child_pid = fork();
|
child.pid = fork();
|
||||||
switch (child_pid) {
|
switch (child.pid) {
|
||||||
case -1:
|
case -1:
|
||||||
ELOG("Couldn't fork(): %s\n", strerror(errno));
|
ELOG("Couldn't fork(): %s\n", strerror(errno));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
@ -298,12 +376,6 @@ void start_child(char *command) {
|
||||||
|
|
||||||
dup2(fd[0], STDIN_FILENO);
|
dup2(fd[0], STDIN_FILENO);
|
||||||
|
|
||||||
/* If hide-on-modifier is set, we start of by sending the
|
|
||||||
* child a SIGSTOP, because the bars aren't mapped at start */
|
|
||||||
if (config.hide_on_modifier) {
|
|
||||||
stop_child();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,12 +384,12 @@ void start_child(char *command) {
|
||||||
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
stdin_io = smalloc(sizeof(ev_io));
|
stdin_io = smalloc(sizeof(ev_io));
|
||||||
ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
|
ev_io_init(stdin_io, &stdin_io_first_line_cb, STDIN_FILENO, EV_READ);
|
||||||
ev_io_start(main_loop, stdin_io);
|
ev_io_start(main_loop, stdin_io);
|
||||||
|
|
||||||
/* We must cleanup, if the child unexpectedly terminates */
|
/* We must cleanup, if the child unexpectedly terminates */
|
||||||
child_sig = smalloc(sizeof(ev_child));
|
child_sig = smalloc(sizeof(ev_child));
|
||||||
ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
|
ev_child_init(child_sig, &child_sig_cb, child.pid, 0);
|
||||||
ev_child_start(main_loop, child_sig);
|
ev_child_start(main_loop, child_sig);
|
||||||
|
|
||||||
atexit(kill_child_at_exit);
|
atexit(kill_child_at_exit);
|
||||||
|
@ -328,9 +400,10 @@ void start_child(char *command) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child_at_exit(void) {
|
void kill_child_at_exit(void) {
|
||||||
if (child_pid != 0) {
|
if (child.pid > 0) {
|
||||||
kill(child_pid, SIGCONT);
|
if (child.cont_signal > 0 && child.stopped)
|
||||||
kill(child_pid, SIGTERM);
|
kill(child.pid, child.cont_signal);
|
||||||
|
kill(child.pid, SIGTERM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,12 +413,12 @@ void kill_child_at_exit(void) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void kill_child(void) {
|
void kill_child(void) {
|
||||||
if (child_pid != 0) {
|
if (child.pid > 0) {
|
||||||
kill(child_pid, SIGCONT);
|
if (child.cont_signal > 0 && child.stopped)
|
||||||
kill(child_pid, SIGTERM);
|
kill(child.pid, child.cont_signal);
|
||||||
|
kill(child.pid, SIGTERM);
|
||||||
int status;
|
int status;
|
||||||
waitpid(child_pid, &status, 0);
|
waitpid(child.pid, &status, 0);
|
||||||
child_pid = 0;
|
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,8 +428,9 @@ void kill_child(void) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void stop_child(void) {
|
void stop_child(void) {
|
||||||
if (child_pid != 0) {
|
if (child.stop_signal > 0 && !child.stopped) {
|
||||||
kill(child_pid, SIGSTOP);
|
child.stopped = true;
|
||||||
|
kill(child.pid, child.stop_signal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +439,8 @@ void stop_child(void) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cont_child(void) {
|
void cont_child(void) {
|
||||||
if (child_pid != 0) {
|
if (child.cont_signal > 0 && child.stopped) {
|
||||||
kill(child_pid, SIGCONT);
|
child.stopped = false;
|
||||||
|
kill(child.pid, child.cont_signal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
* vim:ts=4:sw=4:expandtab
|
|
||||||
*
|
|
||||||
* i3bar - an xcb-based status- and ws-bar for i3
|
|
||||||
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
|
||||||
*
|
|
||||||
* determine_json_version.c: Determines the JSON protocol version based on the
|
|
||||||
* first line of input from a child program.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <err.h>
|
|
||||||
#include <ev.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <yajl/yajl_common.h>
|
|
||||||
#include <yajl/yajl_parse.h>
|
|
||||||
#include <yajl/yajl_version.h>
|
|
||||||
|
|
||||||
static bool version_key;
|
|
||||||
static int32_t version_number;
|
|
||||||
|
|
||||||
#if YAJL_MAJOR >= 2
|
|
||||||
static int version_integer(void *ctx, long long val) {
|
|
||||||
#else
|
|
||||||
static int version_integer(void *ctx, long val) {
|
|
||||||
#endif
|
|
||||||
if (version_key)
|
|
||||||
version_number = (uint32_t)val;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if YAJL_MAJOR >= 2
|
|
||||||
static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
|
||||||
#else
|
|
||||||
static int version_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
|
||||||
#endif
|
|
||||||
version_key = (stringlen == strlen("version") &&
|
|
||||||
strncmp((const char*)stringval, "version", strlen("version")) == 0);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static yajl_callbacks version_callbacks = {
|
|
||||||
NULL, /* null */
|
|
||||||
NULL, /* boolean */
|
|
||||||
&version_integer,
|
|
||||||
NULL, /* double */
|
|
||||||
NULL, /* number */
|
|
||||||
NULL, /* string */
|
|
||||||
NULL, /* start_map */
|
|
||||||
&version_map_key,
|
|
||||||
NULL, /* end_map */
|
|
||||||
NULL, /* start_array */
|
|
||||||
NULL /* end_array */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determines the JSON i3bar protocol version from the given buffer. In case
|
|
||||||
* the buffer does not contain valid JSON, or no version field is found, this
|
|
||||||
* function returns -1. The amount of bytes consumed by parsing the header is
|
|
||||||
* returned in *consumed (if non-NULL).
|
|
||||||
*
|
|
||||||
* The return type is an int32_t to avoid machines with different sizes of
|
|
||||||
* 'int' to allow different values here. It’s highly unlikely we ever exceed
|
|
||||||
* even an int8_t, but still…
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int32_t determine_json_version(const unsigned char *buffer, int length, unsigned int *consumed) {
|
|
||||||
#if YAJL_MAJOR >= 2
|
|
||||||
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);
|
|
||||||
/* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
|
|
||||||
* yajl 2, we need to be explicit. */
|
|
||||||
yajl_config(handle, yajl_allow_trailing_garbage, 1);
|
|
||||||
#else
|
|
||||||
yajl_parser_config parse_conf = { 0, 0 };
|
|
||||||
|
|
||||||
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
version_key = false;
|
|
||||||
version_number = -1;
|
|
||||||
|
|
||||||
yajl_status state = yajl_parse(handle, buffer, length);
|
|
||||||
if (state != yajl_status_ok) {
|
|
||||||
version_number = -1;
|
|
||||||
if (consumed != NULL)
|
|
||||||
*consumed = 0;
|
|
||||||
} else {
|
|
||||||
if (consumed != NULL)
|
|
||||||
*consumed = yajl_get_bytes_consumed(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
yajl_free(handle);
|
|
||||||
|
|
||||||
return version_number;
|
|
||||||
}
|
|
|
@ -42,7 +42,7 @@ void got_command_reply(char *reply) {
|
||||||
void got_workspace_reply(char *reply) {
|
void got_workspace_reply(char *reply) {
|
||||||
DLOG("Got Workspace-Data!\n");
|
DLOG("Got Workspace-Data!\n");
|
||||||
parse_workspaces_json(reply);
|
parse_workspaces_json(reply);
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -71,7 +71,7 @@ void got_output_reply(char *reply) {
|
||||||
kick_tray_clients(o_walk);
|
kick_tray_clients(o_walk);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -138,10 +138,22 @@ void got_output_event(char *event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called, when a mode-event arrives (i3 changed binding mode).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void got_mode_event(char *event) {
|
||||||
|
DLOG("Got Mode Event!\n");
|
||||||
|
parse_mode_json(event);
|
||||||
|
draw_bars(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Data-structure to easily call the reply-handlers later */
|
/* Data-structure to easily call the reply-handlers later */
|
||||||
handler_t event_handlers[] = {
|
handler_t event_handlers[] = {
|
||||||
&got_workspace_event,
|
&got_workspace_event,
|
||||||
&got_output_event
|
&got_output_event,
|
||||||
|
&got_mode_event
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -297,8 +309,8 @@ void destroy_connection(void) {
|
||||||
*/
|
*/
|
||||||
void subscribe_events(void) {
|
void subscribe_events(void) {
|
||||||
if (config.disable_ws) {
|
if (config.disable_ws) {
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\" ]");
|
||||||
} else {
|
} else {
|
||||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\" ]");
|
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"workspace\", \"output\", \"mode\" ]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* mode.c: Handle mode-event and show current binding mode in the bar
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <yajl/yajl_parse.h>
|
||||||
|
#include <yajl/yajl_version.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/* A datatype to pass through the callbacks to save the state */
|
||||||
|
struct mode_json_params {
|
||||||
|
char *json;
|
||||||
|
char *cur_key;
|
||||||
|
mode *mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a string (change)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
|
||||||
|
#else
|
||||||
|
static int mode_string_cb(void *params_, const unsigned char *val, unsigned int len) {
|
||||||
|
#endif
|
||||||
|
struct mode_json_params *params = (struct mode_json_params*) params_;
|
||||||
|
|
||||||
|
if (!strcmp(params->cur_key, "change")) {
|
||||||
|
|
||||||
|
/* Save the name */
|
||||||
|
params->mode->name = i3string_from_utf8_with_length((const char *)val, len);
|
||||||
|
/* Save its rendered width */
|
||||||
|
params->mode->width = predict_text_width(params->mode->name);
|
||||||
|
|
||||||
|
DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name));
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a key.
|
||||||
|
*
|
||||||
|
* Essentially we just save it in the parsing-state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
|
||||||
|
#else
|
||||||
|
static int mode_map_key_cb(void *params_, const unsigned char *keyVal, unsigned int keyLen) {
|
||||||
|
#endif
|
||||||
|
struct mode_json_params *params = (struct mode_json_params*) params_;
|
||||||
|
FREE(params->cur_key);
|
||||||
|
|
||||||
|
params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
|
||||||
|
strncpy(params->cur_key, (const char*) keyVal, keyLen);
|
||||||
|
params->cur_key[keyLen] = '\0';
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A datastructure to pass all these callbacks to yajl */
|
||||||
|
yajl_callbacks mode_callbacks = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&mode_string_cb,
|
||||||
|
NULL,
|
||||||
|
&mode_map_key_cb,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start parsing the received json-string
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_mode_json(char *json) {
|
||||||
|
/* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
|
||||||
|
* JSON in chunks */
|
||||||
|
struct mode_json_params params;
|
||||||
|
|
||||||
|
mode binding;
|
||||||
|
|
||||||
|
params.cur_key = NULL;
|
||||||
|
params.json = json;
|
||||||
|
params.mode = &binding;
|
||||||
|
|
||||||
|
yajl_handle handle;
|
||||||
|
yajl_status state;
|
||||||
|
|
||||||
|
#if YAJL_MAJOR < 2
|
||||||
|
yajl_parser_config parse_conf = { 0, 0 };
|
||||||
|
|
||||||
|
handle = yajl_alloc(&mode_callbacks, &parse_conf, NULL, (void*) ¶ms);
|
||||||
|
#else
|
||||||
|
handle = yajl_alloc(&mode_callbacks, NULL, (void*) ¶ms);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
state = yajl_parse(handle, (const unsigned char*) json, strlen(json));
|
||||||
|
|
||||||
|
/* FIXME: Propper errorhandling for JSON-parsing */
|
||||||
|
switch (state) {
|
||||||
|
case yajl_status_ok:
|
||||||
|
break;
|
||||||
|
case yajl_status_client_canceled:
|
||||||
|
#if YAJL_MAJOR < 2
|
||||||
|
case yajl_status_insufficient_data:
|
||||||
|
#endif
|
||||||
|
case yajl_status_error:
|
||||||
|
ELOG("Could not parse mode-event!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't want to indicate default binding mode */
|
||||||
|
if (strcmp("default", i3string_as_utf8(params.mode->name)) == 0)
|
||||||
|
I3STRING_FREE(params.mode->name);
|
||||||
|
|
||||||
|
/* Set the new binding mode */
|
||||||
|
set_current_mode(&binding);
|
||||||
|
|
||||||
|
yajl_free(handle);
|
||||||
|
|
||||||
|
FREE(params.cur_key);
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3bar - an xcb-based status- and ws-bar for i3
|
||||||
|
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* parse_json_header.c: Parse the JSON protocol header to determine
|
||||||
|
* protocol version and features.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <ev.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <yajl/yajl_common.h>
|
||||||
|
#include <yajl/yajl_parse.h>
|
||||||
|
#include <yajl/yajl_version.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
static enum {
|
||||||
|
KEY_VERSION,
|
||||||
|
KEY_STOP_SIGNAL,
|
||||||
|
KEY_CONT_SIGNAL,
|
||||||
|
NO_KEY
|
||||||
|
} current_key;
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int header_integer(void *ctx, long long val) {
|
||||||
|
#else
|
||||||
|
static int header_integer(void *ctx, long val) {
|
||||||
|
#endif
|
||||||
|
i3bar_child *child = ctx;
|
||||||
|
|
||||||
|
switch (current_key) {
|
||||||
|
case KEY_VERSION:
|
||||||
|
child->version = val;
|
||||||
|
break;
|
||||||
|
case KEY_STOP_SIGNAL:
|
||||||
|
child->stop_signal = val;
|
||||||
|
break;
|
||||||
|
case KEY_CONT_SIGNAL:
|
||||||
|
child->cont_signal = val;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECK_KEY(name) (stringlen == strlen(name) && \
|
||||||
|
STARTS_WITH((const char*)stringval, stringlen, name))
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
static int header_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
|
||||||
|
#else
|
||||||
|
static int header_map_key(void *ctx, const unsigned char *stringval, unsigned int stringlen) {
|
||||||
|
#endif
|
||||||
|
if (CHECK_KEY("version")) {
|
||||||
|
current_key = KEY_VERSION;
|
||||||
|
} else if (CHECK_KEY("stop_signal")) {
|
||||||
|
current_key = KEY_STOP_SIGNAL;
|
||||||
|
} else if (CHECK_KEY("cont_signal")) {
|
||||||
|
current_key = KEY_CONT_SIGNAL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static yajl_callbacks version_callbacks = {
|
||||||
|
NULL, /* null */
|
||||||
|
NULL, /* boolean */
|
||||||
|
&header_integer,
|
||||||
|
NULL, /* double */
|
||||||
|
NULL, /* number */
|
||||||
|
NULL, /* string */
|
||||||
|
NULL, /* start_map */
|
||||||
|
&header_map_key,
|
||||||
|
NULL, /* end_map */
|
||||||
|
NULL, /* start_array */
|
||||||
|
NULL /* end_array */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void child_init(i3bar_child *child) {
|
||||||
|
child->version = 0;
|
||||||
|
child->stop_signal = SIGSTOP;
|
||||||
|
child->cont_signal = SIGCONT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the JSON protocol header to determine protocol version and features.
|
||||||
|
* In case the buffer does not contain a valid header (invalid JSON, or no
|
||||||
|
* version field found), the 'correct' field of the returned header is set to
|
||||||
|
* false. The amount of bytes consumed by parsing the header is returned in
|
||||||
|
* *consumed (if non-NULL).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void parse_json_header(i3bar_child *child, const unsigned char *buffer, int length, unsigned int *consumed) {
|
||||||
|
child_init(child);
|
||||||
|
|
||||||
|
current_key = NO_KEY;
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
yajl_handle handle = yajl_alloc(&version_callbacks, NULL, child);
|
||||||
|
/* Allow trailing garbage. yajl 1 always behaves that way anyways, but for
|
||||||
|
* yajl 2, we need to be explicit. */
|
||||||
|
yajl_config(handle, yajl_allow_trailing_garbage, 1);
|
||||||
|
#else
|
||||||
|
yajl_parser_config parse_conf = { 0, 0 };
|
||||||
|
|
||||||
|
yajl_handle handle = yajl_alloc(&version_callbacks, &parse_conf, NULL, child);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
yajl_status state = yajl_parse(handle, buffer, length);
|
||||||
|
if (state != yajl_status_ok) {
|
||||||
|
child_init(child);
|
||||||
|
if (consumed != NULL)
|
||||||
|
*consumed = 0;
|
||||||
|
} else {
|
||||||
|
if (consumed != NULL)
|
||||||
|
*consumed = yajl_get_bytes_consumed(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
yajl_free(handle);
|
||||||
|
}
|
125
i3bar/src/xcb.c
125
i3bar/src/xcb.c
|
@ -74,6 +74,9 @@ ev_check *xcb_chk;
|
||||||
ev_io *xcb_io;
|
ev_io *xcb_io;
|
||||||
ev_io *xkb_io;
|
ev_io *xkb_io;
|
||||||
|
|
||||||
|
/* The name of current binding mode */
|
||||||
|
static mode binding;
|
||||||
|
|
||||||
/* The parsed colors */
|
/* The parsed colors */
|
||||||
struct xcb_colors_t {
|
struct xcb_colors_t {
|
||||||
uint32_t bar_fg;
|
uint32_t bar_fg;
|
||||||
|
@ -120,10 +123,31 @@ void refresh_statusline(void) {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
block->width = predict_text_width(block->full_text);
|
block->width = predict_text_width(block->full_text);
|
||||||
|
|
||||||
|
/* Compute offset and append for text aligment in min_width. */
|
||||||
|
if (block->min_width <= block->width) {
|
||||||
|
block->x_offset = 0;
|
||||||
|
block->x_append = 0;
|
||||||
|
} else {
|
||||||
|
uint32_t padding_width = block->min_width - block->width;
|
||||||
|
switch (block->align) {
|
||||||
|
case ALIGN_LEFT:
|
||||||
|
block->x_append = padding_width;
|
||||||
|
break;
|
||||||
|
case ALIGN_RIGHT:
|
||||||
|
block->x_offset = padding_width;
|
||||||
|
break;
|
||||||
|
case ALIGN_CENTER:
|
||||||
|
block->x_offset = padding_width / 2;
|
||||||
|
block->x_append = padding_width / 2 + padding_width % 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If this is not the last block, add some pixels for a separator. */
|
/* If this is not the last block, add some pixels for a separator. */
|
||||||
if (TAILQ_NEXT(block, blocks) != NULL)
|
if (TAILQ_NEXT(block, blocks) != NULL)
|
||||||
block->width += 9;
|
block->width += 9;
|
||||||
statusline_width += block->width;
|
statusline_width += block->width + block->x_offset + block->x_append;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the statusline is bigger than our screen we need to make sure that
|
/* If the statusline is bigger than our screen we need to make sure that
|
||||||
|
@ -144,8 +168,8 @@ void refresh_statusline(void) {
|
||||||
|
|
||||||
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
||||||
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
||||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x, 0, block->width);
|
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 0, block->width);
|
||||||
x += block->width;
|
x += block->width + block->x_offset + block->x_append;
|
||||||
|
|
||||||
if (TAILQ_NEXT(block, blocks) != NULL) {
|
if (TAILQ_NEXT(block, blocks) != NULL) {
|
||||||
/* This is not the last block, draw a separator. */
|
/* This is not the last block, draw a separator. */
|
||||||
|
@ -302,16 +326,24 @@ void handle_button(xcb_button_press_event_t *event) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
/* Mouse wheel down. We select the next ws */
|
/* Mouse wheel up. We select the previous ws, if any.
|
||||||
if (cur_ws != TAILQ_FIRST(walk->workspaces)) {
|
* If there is no more workspace, don’t even send the workspace
|
||||||
|
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
||||||
|
* up on the wrong workspace. */
|
||||||
|
if (cur_ws == TAILQ_FIRST(walk->workspaces))
|
||||||
|
return;
|
||||||
|
|
||||||
cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
|
cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
/* Mouse wheel up. We select the previos ws */
|
/* Mouse wheel down. We select the next ws, if any.
|
||||||
if (cur_ws != TAILQ_LAST(walk->workspaces, ws_head)) {
|
* If there is no more workspace, don’t even send the workspace
|
||||||
|
* command, otherwise (with workspace auto_back_and_forth) we’d end
|
||||||
|
* up on the wrong workspace. */
|
||||||
|
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
|
||||||
|
return;
|
||||||
|
|
||||||
cur_ws = TAILQ_NEXT(cur_ws, tailq);
|
cur_ws = TAILQ_NEXT(cur_ws, tailq);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +563,7 @@ static void handle_client_message(xcb_client_message_event_t* event) {
|
||||||
/* Trigger an update to copy the statusline text to the appropriate
|
/* Trigger an update to copy the statusline text to the appropriate
|
||||||
* position */
|
* position */
|
||||||
configure_trayclients();
|
configure_trayclients();
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -559,7 +591,7 @@ static void handle_unmap_notify(xcb_unmap_notify_event_t* event) {
|
||||||
|
|
||||||
/* Trigger an update, we now have more space for the statusline */
|
/* Trigger an update, we now have more space for the statusline */
|
||||||
configure_trayclients();
|
configure_trayclients();
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -624,13 +656,13 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
|
||||||
xcb_unmap_window(xcb_connection, trayclient->win);
|
xcb_unmap_window(xcb_connection, trayclient->win);
|
||||||
trayclient->mapped = map_it;
|
trayclient->mapped = map_it;
|
||||||
configure_trayclients();
|
configure_trayclients();
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
} else if (!trayclient->mapped && map_it) {
|
} else if (!trayclient->mapped && map_it) {
|
||||||
/* need to map the window */
|
/* need to map the window */
|
||||||
xcb_map_window(xcb_connection, trayclient->win);
|
xcb_map_window(xcb_connection, trayclient->win);
|
||||||
trayclient->mapped = map_it;
|
trayclient->mapped = map_it;
|
||||||
configure_trayclients();
|
configure_trayclients();
|
||||||
draw_bars();
|
draw_bars(false);
|
||||||
}
|
}
|
||||||
free(xembedr);
|
free(xembedr);
|
||||||
}
|
}
|
||||||
|
@ -1398,12 +1430,15 @@ void reconfig_windows(void) {
|
||||||
* Render the bars, with buttons and statusline
|
* Render the bars, with buttons and statusline
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void draw_bars(void) {
|
void draw_bars(bool unhide) {
|
||||||
DLOG("Drawing Bars...\n");
|
DLOG("Drawing Bars...\n");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
refresh_statusline();
|
refresh_statusline();
|
||||||
|
|
||||||
|
static char *last_urgent_ws = NULL;
|
||||||
|
bool walks_away = true;
|
||||||
|
|
||||||
i3_output *outputs_walk;
|
i3_output *outputs_walk;
|
||||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||||
if (!outputs_walk->active) {
|
if (!outputs_walk->active) {
|
||||||
|
@ -1460,8 +1495,6 @@ void draw_bars(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
i3_ws *ws_walk;
|
i3_ws *ws_walk;
|
||||||
static char *last_urgent_ws = NULL;
|
|
||||||
bool has_urgent = false, walks_away = true;
|
|
||||||
|
|
||||||
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
|
||||||
DLOG("Drawing Button for WS %s at x = %d, len = %d\n", i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
|
DLOG("Drawing Button for WS %s at x = %d, len = %d\n", i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
|
||||||
|
@ -1486,13 +1519,11 @@ void draw_bars(void) {
|
||||||
fg_color = colors.urgent_ws_fg;
|
fg_color = colors.urgent_ws_fg;
|
||||||
bg_color = colors.urgent_ws_bg;
|
bg_color = colors.urgent_ws_bg;
|
||||||
border_color = colors.urgent_ws_border;
|
border_color = colors.urgent_ws_border;
|
||||||
has_urgent = true;
|
unhide = true;
|
||||||
if (!ws_walk->focused) {
|
if (!ws_walk->focused) {
|
||||||
FREE(last_urgent_ws);
|
FREE(last_urgent_ws);
|
||||||
last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name));
|
last_urgent_ws = sstrdup(i3string_as_utf8(ws_walk->name));
|
||||||
}
|
}
|
||||||
/* The urgent-hint should get noticed, so we unhide the bars shortly */
|
|
||||||
unhide_bars();
|
|
||||||
}
|
}
|
||||||
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
|
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
|
||||||
uint32_t vals_border[] = { border_color, border_color };
|
uint32_t vals_border[] = { border_color, border_color };
|
||||||
|
@ -1520,16 +1551,56 @@ void draw_bars(void) {
|
||||||
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
||||||
draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
|
draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, ws_walk->name_width);
|
||||||
i += 10 + ws_walk->name_width + 1;
|
i += 10 + ws_walk->name_width + 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_urgent && !mod_pressed && walks_away) {
|
if (binding.name) {
|
||||||
FREE(last_urgent_ws);
|
|
||||||
hide_bars();
|
uint32_t fg_color = colors.urgent_ws_fg;
|
||||||
|
uint32_t bg_color = colors.urgent_ws_bg;
|
||||||
|
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
|
||||||
|
|
||||||
|
uint32_t vals_border[] = { colors.urgent_ws_border, colors.urgent_ws_border };
|
||||||
|
xcb_change_gc(xcb_connection,
|
||||||
|
outputs_walk->bargc,
|
||||||
|
mask,
|
||||||
|
vals_border);
|
||||||
|
xcb_rectangle_t rect_border = { i, 0, binding.width + 10, font.height + 4 };
|
||||||
|
xcb_poly_fill_rectangle(xcb_connection,
|
||||||
|
outputs_walk->buffer,
|
||||||
|
outputs_walk->bargc,
|
||||||
|
1,
|
||||||
|
&rect_border);
|
||||||
|
|
||||||
|
uint32_t vals[] = { bg_color, bg_color };
|
||||||
|
xcb_change_gc(xcb_connection,
|
||||||
|
outputs_walk->bargc,
|
||||||
|
mask,
|
||||||
|
vals);
|
||||||
|
xcb_rectangle_t rect = { i + 1, 1, binding.width + 8, font.height + 2 };
|
||||||
|
xcb_poly_fill_rectangle(xcb_connection,
|
||||||
|
outputs_walk->buffer,
|
||||||
|
outputs_walk->bargc,
|
||||||
|
1,
|
||||||
|
&rect);
|
||||||
|
|
||||||
|
set_font_colors(outputs_walk->bargc, fg_color, bg_color);
|
||||||
|
draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 2, binding.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mod_pressed) {
|
||||||
|
if (unhide) {
|
||||||
|
/* The urgent-hint should get noticed, so we unhide the bars shortly */
|
||||||
|
unhide_bars();
|
||||||
|
} else if (walks_away) {
|
||||||
|
FREE(last_urgent_ws);
|
||||||
|
hide_bars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
redraw_bars();
|
redraw_bars();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1554,3 +1625,13 @@ void redraw_bars(void) {
|
||||||
xcb_flush(xcb_connection);
|
xcb_flush(xcb_connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the current binding mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void set_current_mode(struct mode *current) {
|
||||||
|
I3STRING_FREE(binding.name);
|
||||||
|
binding = *current;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
* compile-time.
|
* compile-time.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _ALL_H
|
#ifndef I3_ALL_H
|
||||||
#define _ALL_H
|
#define I3_ALL_H
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
@ -79,6 +79,8 @@
|
||||||
#include "scratchpad.h"
|
#include "scratchpad.h"
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "commands_parser.h"
|
#include "commands_parser.h"
|
||||||
|
#include "config_directives.h"
|
||||||
|
#include "config_parser.h"
|
||||||
#include "fake_outputs.h"
|
#include "fake_outputs.h"
|
||||||
#include "display_version.h"
|
#include "display_version.h"
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* assignments.c: Assignments for specific windows (for_window).
|
* assignments.c: Assignments for specific windows (for_window).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _ASSIGNMENTS_H
|
#ifndef I3_ASSIGNMENTS_H
|
||||||
#define _ASSIGNMENTS_H
|
#define I3_ASSIGNMENTS_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the list of assignments for the given window and runs all matching
|
* Checks the list of assignments for the given window and runs all matching
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* click.c: Button press (mouse click) events.
|
* click.c: Button press (mouse click) events.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _CLICK_H
|
#ifndef I3_CLICK_H
|
||||||
#define _CLICK_H
|
#define I3_CLICK_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The button press X callback. This function determines whether the floating
|
* The button press X callback. This function determines whether the floating
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* cmdparse.y: the parser for commands you send to i3 (or bind on keys)
|
* cmdparse.y: the parser for commands you send to i3 (or bind on keys)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _CMDPARSE_H
|
#ifndef I3_CMDPARSE_H
|
||||||
#define _CMDPARSE_H
|
#define I3_CMDPARSE_H
|
||||||
|
|
||||||
char *parse_cmd(const char *new);
|
char *parse_cmd(const char *new);
|
||||||
|
|
||||||
|
|
|
@ -7,26 +7,14 @@
|
||||||
* commands.c: all command functions (see commands_parser.c)
|
* commands.c: all command functions (see commands_parser.c)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _COMMANDS_H
|
#ifndef I3_COMMANDS_H
|
||||||
#define _COMMANDS_H
|
#define I3_COMMANDS_H
|
||||||
|
|
||||||
#include "commands_parser.h"
|
#include "commands_parser.h"
|
||||||
|
|
||||||
/** The beginning of the prototype for every cmd_ function. */
|
/** The beginning of the prototype for every cmd_ function. */
|
||||||
#define I3_CMD Match *current_match, struct CommandResult *cmd_output
|
#define I3_CMD Match *current_match, struct CommandResult *cmd_output
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper data structure for an operation window (window on which the operation
|
|
||||||
* will be performed). Used to build the TAILQ owindows.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef struct owindow {
|
|
||||||
Con *con;
|
|
||||||
TAILQ_ENTRY(owindow) owindows;
|
|
||||||
} owindow;
|
|
||||||
|
|
||||||
typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the specified 'Match' data structure and the initial state of
|
* Initializes the specified 'Match' data structure and the initial state of
|
||||||
* commands.c for matching target windows of a command.
|
* commands.c for matching target windows of a command.
|
||||||
|
@ -55,6 +43,12 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue);
|
||||||
*/
|
*/
|
||||||
void cmd_move_con_to_workspace(I3_CMD, char *which);
|
void cmd_move_con_to_workspace(I3_CMD, char *which);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of 'move [window|container] [to] workspace back_and_forth'.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cmd_move_con_to_workspace_back_and_forth(I3_CMD);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of 'move [window|container] [to] workspace <name>'.
|
* Implementation of 'move [window|container] [to] workspace <name>'.
|
||||||
*
|
*
|
||||||
|
@ -77,7 +71,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
|
||||||
* Implementation of 'border normal|none|1pixel|toggle'.
|
* Implementation of 'border normal|none|1pixel|toggle'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_border(I3_CMD, char *border_style_str);
|
void cmd_border(I3_CMD, char *border_style_str, char *border_width);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of 'nop <comment>'.
|
* Implementation of 'nop <comment>'.
|
||||||
|
@ -152,7 +146,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name);
|
||||||
void cmd_split(I3_CMD, char *direction);
|
void cmd_split(I3_CMD, char *direction);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementaiton of 'kill [window|client]'.
|
* Implementation of 'kill [window|client]'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_kill(I3_CMD, char *kill_mode_str);
|
void cmd_kill(I3_CMD, char *kill_mode_str);
|
||||||
|
@ -212,25 +206,25 @@ void cmd_layout(I3_CMD, char *layout_str);
|
||||||
void cmd_layout_toggle(I3_CMD, char *toggle_mode);
|
void cmd_layout_toggle(I3_CMD, char *toggle_mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementaiton of 'exit'.
|
* Implementation of 'exit'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_exit(I3_CMD);
|
void cmd_exit(I3_CMD);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementaiton of 'reload'.
|
* Implementation of 'reload'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_reload(I3_CMD);
|
void cmd_reload(I3_CMD);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementaiton of 'restart'.
|
* Implementation of 'restart'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_restart(I3_CMD);
|
void cmd_restart(I3_CMD);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementaiton of 'open'.
|
* Implementation of 'open'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_open(I3_CMD);
|
void cmd_open(I3_CMD);
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* commands.c: all command functions (see commands_parser.c)
|
* commands.c: all command functions (see commands_parser.c)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _COMMANDS_PARSER_H
|
#ifndef I3_COMMANDS_PARSER_H
|
||||||
#define _COMMANDS_PARSER_H
|
#define I3_COMMANDS_PARSER_H
|
||||||
|
|
||||||
#include <yajl/yajl_gen.h>
|
#include <yajl/yajl_gen.h>
|
||||||
|
|
||||||
|
@ -27,6 +27,11 @@ struct CommandResult {
|
||||||
|
|
||||||
/* Whether the command requires calling tree_render. */
|
/* Whether the command requires calling tree_render. */
|
||||||
bool needs_tree_render;
|
bool needs_tree_render;
|
||||||
|
|
||||||
|
/* The next state to transition to. Passed to the function so that we can
|
||||||
|
* determine the next state as a result of a function call, like
|
||||||
|
* cfg_criteria_pop_state() does. */
|
||||||
|
int next_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CommandResult *parse_command(const char *input);
|
struct CommandResult *parse_command(const char *input);
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
* …).
|
* …).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _CON_H
|
#ifndef I3_CON_H
|
||||||
#define _CON_H
|
#define I3_CON_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new container (and attach it to the given parent, if not NULL).
|
* Create a new container (and attach it to the given parent, if not NULL).
|
||||||
|
@ -33,6 +33,18 @@ void con_focus(Con *con);
|
||||||
*/
|
*/
|
||||||
bool con_is_leaf(Con *con);
|
bool con_is_leaf(Con *con);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if a container should be considered split.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_is_split(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this node has regular or floating children.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_has_children(Con *con);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this node accepts a window (if the node swallows windows,
|
* Returns true if this node accepts a window (if the node swallows windows,
|
||||||
* it might already have swallowed enough and cannot hold any more).
|
* it might already have swallowed enough and cannot hold any more).
|
||||||
|
@ -66,6 +78,12 @@ Con *con_parent_with_orientation(Con *con, orientation_t orientation);
|
||||||
*/
|
*/
|
||||||
Con *con_get_fullscreen_con(Con *con, int fullscreen_mode);
|
Con *con_get_fullscreen_con(Con *con, int fullscreen_mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the container is internal, such as __i3_scratch
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_is_internal(Con *con);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the node is floating.
|
* Returns true if the node is floating.
|
||||||
*
|
*
|
||||||
|
@ -244,7 +262,7 @@ int con_border_style(Con *con);
|
||||||
* floating window.
|
* floating window.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void con_set_border_style(Con *con, int border_style);
|
void con_set_border_style(Con *con, int border_style, int border_width);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function changes the layout of a given container. Use it to handle
|
* This function changes the layout of a given container. Use it to handle
|
||||||
|
@ -293,4 +311,23 @@ Rect con_minimum_size(Con *con);
|
||||||
*/
|
*/
|
||||||
bool con_fullscreen_permits_focusing(Con *con);
|
bool con_fullscreen_permits_focusing(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given container has an urgent child.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_has_urgent_child(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make all parent containers urgent if con is urgent or clear the urgent flag
|
||||||
|
* of all parent containers if there are no more urgent children left.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void con_update_parents_urgency(Con *con);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a string representing the subtree under con.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *con_get_tree_representation(Con *con);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
* mode).
|
* mode).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _CONFIG_H
|
#ifndef I3_CONFIG_H
|
||||||
#define _CONFIG_H
|
#define I3_CONFIG_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
@ -24,6 +24,8 @@ extern char *current_configpath;
|
||||||
extern Config config;
|
extern Config config;
|
||||||
extern SLIST_HEAD(modes_head, Mode) modes;
|
extern SLIST_HEAD(modes_head, Mode) modes;
|
||||||
extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
|
extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
|
||||||
|
/* defined in src/cfgparse.y */
|
||||||
|
extern bool force_old_config_parser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used during the config file lexing/parsing to keep the state of the lexer
|
* Used during the config file lexing/parsing to keep the state of the lexer
|
||||||
|
@ -98,6 +100,7 @@ struct Config {
|
||||||
int default_layout;
|
int default_layout;
|
||||||
int container_stack_limit;
|
int container_stack_limit;
|
||||||
int container_stack_limit_value;
|
int container_stack_limit_value;
|
||||||
|
int default_border_width;
|
||||||
|
|
||||||
/** Default orientation for new containers */
|
/** Default orientation for new containers */
|
||||||
int default_orientation;
|
int default_orientation;
|
||||||
|
@ -149,6 +152,13 @@ struct Config {
|
||||||
* between two workspaces. */
|
* between two workspaces. */
|
||||||
bool workspace_auto_back_and_forth;
|
bool workspace_auto_back_and_forth;
|
||||||
|
|
||||||
|
/** By default, urgency is cleared immediately when switching to another
|
||||||
|
* workspace leads to focusing the con with the urgency hint. When having
|
||||||
|
* multiple windows on that workspace, the user needs to guess which
|
||||||
|
* application raised the event. To prevent this, the reset of the urgency
|
||||||
|
* flag can be delayed using an urgency timer. */
|
||||||
|
float workspace_urgency_timer;
|
||||||
|
|
||||||
/** The default border style for new windows. */
|
/** The default border style for new windows. */
|
||||||
border_style_t default_border;
|
border_style_t default_border;
|
||||||
|
|
||||||
|
@ -181,8 +191,15 @@ struct Config {
|
||||||
|
|
||||||
/** What should happen when a new popup is opened during fullscreen mode */
|
/** What should happen when a new popup is opened during fullscreen mode */
|
||||||
enum {
|
enum {
|
||||||
PDF_LEAVE_FULLSCREEN = 0,
|
/* display (and focus) the popup when it belongs to the fullscreen
|
||||||
PDF_IGNORE = 1
|
* window only. */
|
||||||
|
PDF_SMART = 0,
|
||||||
|
|
||||||
|
/* leave fullscreen mode unconditionally */
|
||||||
|
PDF_LEAVE_FULLSCREEN = 1,
|
||||||
|
|
||||||
|
/* just ignore the popup, that is, don’t map it */
|
||||||
|
PDF_IGNORE = 2,
|
||||||
} popup_during_fullscreen;
|
} popup_during_fullscreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* config_directives.h: all config storing functions (see config_parser.c)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef I3_CONFIG_DIRECTIVES_H
|
||||||
|
#define I3_CONFIG_DIRECTIVES_H
|
||||||
|
|
||||||
|
#include "config_parser.h"
|
||||||
|
|
||||||
|
/** The beginning of the prototype for every cfg_ function. */
|
||||||
|
#define I3_CFG Match *current_match, struct ConfigResult *result
|
||||||
|
|
||||||
|
/* Defines a configuration function, that is, anything that can be called by
|
||||||
|
* using 'call cfg_foo()' in parser-specs/.*.spec. Useful so that we don’t need
|
||||||
|
* to repeat the definition all the time. */
|
||||||
|
#define CFGFUN(name, ...) \
|
||||||
|
void cfg_ ## name (I3_CFG, ## __VA_ARGS__ )
|
||||||
|
|
||||||
|
/* The following functions are called by the config parser, see
|
||||||
|
* parser-specs/config.spec. They get the parsed parameters and store them in
|
||||||
|
* our data structures, e.g. cfg_font gets a font name and stores it in
|
||||||
|
* config.font.
|
||||||
|
*
|
||||||
|
* Since they are so similar, individual comments were omitted. */
|
||||||
|
|
||||||
|
CFGFUN(criteria_init, int _state);
|
||||||
|
CFGFUN(criteria_add, const char *ctype, const char *cvalue);
|
||||||
|
CFGFUN(criteria_pop_state);
|
||||||
|
|
||||||
|
CFGFUN(font, const char *font);
|
||||||
|
CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command);
|
||||||
|
CFGFUN(for_window, const char *command);
|
||||||
|
CFGFUN(floating_minimum_size, const long width, const long height);
|
||||||
|
CFGFUN(floating_maximum_size, const long width, const long height);
|
||||||
|
CFGFUN(default_orientation, const char *orientation);
|
||||||
|
CFGFUN(workspace_layout, const char *layout);
|
||||||
|
CFGFUN(workspace_back_and_forth, const char *value);
|
||||||
|
CFGFUN(focus_follows_mouse, const char *value);
|
||||||
|
CFGFUN(force_focus_wrapping, const char *value);
|
||||||
|
CFGFUN(force_xinerama, const char *value);
|
||||||
|
CFGFUN(fake_outputs, const char *outputs);
|
||||||
|
CFGFUN(force_display_urgency_hint, const long duration_ms);
|
||||||
|
CFGFUN(hide_edge_borders, const char *borders);
|
||||||
|
CFGFUN(assign, const char *workspace);
|
||||||
|
CFGFUN(ipc_socket, const char *path);
|
||||||
|
CFGFUN(restart_state, const char *path);
|
||||||
|
CFGFUN(popup_during_fullscreen, const char *value);
|
||||||
|
CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator);
|
||||||
|
CFGFUN(color_single, const char *colorclass, const char *color);
|
||||||
|
CFGFUN(floating_modifier, const char *modifiers);
|
||||||
|
CFGFUN(new_window, const char *windowtype, const char *border, const long width);
|
||||||
|
CFGFUN(workspace, const char *workspace, const char *output);
|
||||||
|
CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
|
||||||
|
|
||||||
|
CFGFUN(enter_mode, const char *mode);
|
||||||
|
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
|
||||||
|
|
||||||
|
CFGFUN(bar_font, const char *font);
|
||||||
|
CFGFUN(bar_mode, const char *mode);
|
||||||
|
CFGFUN(bar_output, const char *output);
|
||||||
|
CFGFUN(bar_verbose, const char *verbose);
|
||||||
|
CFGFUN(bar_modifier, const char *modifier);
|
||||||
|
CFGFUN(bar_position, const char *position);
|
||||||
|
CFGFUN(bar_i3bar_command, const char *i3bar_command);
|
||||||
|
CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text);
|
||||||
|
CFGFUN(bar_socket_path, const char *socket_path);
|
||||||
|
CFGFUN(bar_tray_output, const char *output);
|
||||||
|
CFGFUN(bar_color_single, const char *colorclass, const char *color);
|
||||||
|
CFGFUN(bar_status_command, const char *command);
|
||||||
|
CFGFUN(bar_workspace_buttons, const char *value);
|
||||||
|
CFGFUN(bar_finish);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* config_parser.h: config parser-related definitions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef I3_CONFIG_PARSER_H
|
||||||
|
#define I3_CONFIG_PARSER_H
|
||||||
|
|
||||||
|
#include <yajl/yajl_gen.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The result of a parse_config call. Currently unused, but the JSON output
|
||||||
|
* will be useful in the future when we implement a config parsing IPC command.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct ConfigResult {
|
||||||
|
/* The JSON generator to append a reply to. */
|
||||||
|
yajl_gen json_gen;
|
||||||
|
|
||||||
|
/* The next state to transition to. Passed to the function so that we can
|
||||||
|
* determine the next state as a result of a function call, like
|
||||||
|
* cfg_criteria_pop_state() does. */
|
||||||
|
int next_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConfigResult *parse_config(const char *input, struct context *context);
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,8 +7,8 @@
|
||||||
* include/data.h: This file defines all data structures used by i3
|
* include/data.h: This file defines all data structures used by i3
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _DATA_H
|
#ifndef I3_DATA_H
|
||||||
#define _DATA_H
|
#define I3_DATA_H
|
||||||
|
|
||||||
#define SN_API_NOT_YET_FROZEN 1
|
#define SN_API_NOT_YET_FROZEN 1
|
||||||
#include <libsn/sn-launcher.h>
|
#include <libsn/sn-launcher.h>
|
||||||
|
@ -55,7 +55,7 @@ typedef struct Window i3Window;
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
|
||||||
typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t;
|
typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t;
|
||||||
typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_1PIXEL = 2 } border_style_t;
|
typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_PIXEL = 2 } border_style_t;
|
||||||
|
|
||||||
/** parameter to specify whether tree_close() and x_window_kill() should kill
|
/** parameter to specify whether tree_close() and x_window_kill() should kill
|
||||||
* only this specific window or the whole X11 client */
|
* only this specific window or the whole X11 client */
|
||||||
|
@ -441,8 +441,6 @@ struct Assignment {
|
||||||
*/
|
*/
|
||||||
struct Con {
|
struct Con {
|
||||||
bool mapped;
|
bool mapped;
|
||||||
/** whether this is a split container or not */
|
|
||||||
bool split;
|
|
||||||
enum {
|
enum {
|
||||||
CT_ROOT = 0,
|
CT_ROOT = 0,
|
||||||
CT_OUTPUT = 1,
|
CT_OUTPUT = 1,
|
||||||
|
@ -486,6 +484,7 @@ struct Con {
|
||||||
|
|
||||||
/* the x11 border pixel attribute */
|
/* the x11 border pixel attribute */
|
||||||
int border_width;
|
int border_width;
|
||||||
|
int current_border_width;
|
||||||
|
|
||||||
/* minimum increment size specified for the window (in pixels) */
|
/* minimum increment size specified for the window (in pixels) */
|
||||||
int width_increment;
|
int width_increment;
|
||||||
|
@ -497,6 +496,9 @@ struct Con {
|
||||||
* inside this container (if any) sets the urgency hint, for example. */
|
* inside this container (if any) sets the urgency hint, for example. */
|
||||||
bool urgent;
|
bool urgent;
|
||||||
|
|
||||||
|
/* timer used for disabling urgency */
|
||||||
|
struct ev_timer *urgency_timer;
|
||||||
|
|
||||||
/* ids/pixmap/graphics context for the frame window */
|
/* ids/pixmap/graphics context for the frame window */
|
||||||
xcb_window_t frame;
|
xcb_window_t frame;
|
||||||
xcb_pixmap_t pixmap;
|
xcb_pixmap_t pixmap;
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* events. This code is from xcb-util.
|
* events. This code is from xcb-util.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _DEBUG_H
|
#ifndef I3_DEBUG_H
|
||||||
#define _DEBUG_H
|
#define I3_DEBUG_H
|
||||||
|
|
||||||
int handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e);
|
int handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e);
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* display_version.c: displays the running i3 version, runs as part of
|
* display_version.c: displays the running i3 version, runs as part of
|
||||||
* i3 --moreversion.
|
* i3 --moreversion.
|
||||||
*/
|
*/
|
||||||
#ifndef _DISPLAY_VERSION_H
|
#ifndef I3_DISPLAY_VERSION_H
|
||||||
#define _DISPLAY_VERSION_H
|
#define I3_DISPLAY_VERSION_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects to i3 to find out the currently running version. Useful since it
|
* Connects to i3 to find out the currently running version. Useful since it
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* ewmh.c: Get/set certain EWMH properties easily.
|
* ewmh.c: Get/set certain EWMH properties easily.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _EWMH_C
|
#ifndef I3_EWMH_C
|
||||||
#define _EWMH_C
|
#define I3_EWMH_C
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates _NET_CURRENT_DESKTOP with the current desktop number.
|
* Updates _NET_CURRENT_DESKTOP with the current desktop number.
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* which don’t support multi-monitor in a useful way) and for our testsuite.
|
* which don’t support multi-monitor in a useful way) and for our testsuite.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _FAKE_OUTPUTS_H
|
#ifndef I3_FAKE_OUTPUTS_H
|
||||||
#define _FAKE_OUTPUTS_H
|
#define I3_FAKE_OUTPUTS_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates outputs according to the given specification.
|
* Creates outputs according to the given specification.
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* floating.c: Floating windows.
|
* floating.c: Floating windows.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _FLOATING_H
|
#ifndef I3_FLOATING_H
|
||||||
#define _FLOATING_H
|
#define I3_FLOATING_H
|
||||||
|
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
|
||||||
|
@ -99,6 +99,14 @@ void floating_drag_window(Con *con, const xcb_button_press_event_t *event);
|
||||||
*/
|
*/
|
||||||
void floating_resize_window(Con *con, const bool proportional, const xcb_button_press_event_t *event);
|
void floating_resize_window(Con *con, const bool proportional, const xcb_button_press_event_t *event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a floating window is created or resized.
|
||||||
|
* This function resizes the window if its size is higher or lower than the
|
||||||
|
* configured maximum/minimum size, respectively.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void floating_check_size(Con *floating_con);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/**
|
/**
|
||||||
* Changes focus in the given direction for floating clients.
|
* Changes focus in the given direction for floating clients.
|
||||||
|
@ -134,8 +142,8 @@ void floating_toggle_hide(xcb_connection_t *conn, Workspace *workspace);
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void drag_pointer(Con *con, const xcb_button_press_event_t *event,
|
void drag_pointer(Con *con, const xcb_button_press_event_t *event,
|
||||||
xcb_window_t confine_to, border_t border, callback_t callback,
|
xcb_window_t confine_to, border_t border, int cursor,
|
||||||
const void *extra);
|
callback_t callback, const void *extra);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repositions the CT_FLOATING_CON to have the coordinates specified by
|
* Repositions the CT_FLOATING_CON to have the coordinates specified by
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* …).
|
* …).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _HANDLERS_H
|
#ifndef I3_HANDLERS_H
|
||||||
#define _HANDLERS_H
|
#define I3_HANDLERS_H
|
||||||
|
|
||||||
#include <xcb/randr.h>
|
#include <xcb/randr.h>
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* i3.h: global variables that are used all over i3.
|
* i3.h: global variables that are used all over i3.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _I3_H
|
#ifndef I3_I3_H
|
||||||
#define _I3_H
|
#define I3_I3_H
|
||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* for the IPC interface to i3 (see docs/ipc for more information).
|
* for the IPC interface to i3 (see docs/ipc for more information).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _I3_IPC_H
|
#ifndef I3_I3_IPC_H
|
||||||
#define _I3_IPC_H
|
#define I3_I3_IPC_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Messages from clients to i3
|
* Messages from clients to i3
|
||||||
|
@ -84,4 +84,7 @@
|
||||||
/* The output event will be triggered upon changes in the output list */
|
/* The output event will be triggered upon changes in the output list */
|
||||||
#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1)
|
#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1)
|
||||||
|
|
||||||
|
/* The output event will be triggered upon mode changes */
|
||||||
|
#define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
|
* ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _IPC_H
|
#ifndef I3_IPC_H
|
||||||
#define _IPC_H
|
#define I3_IPC_H
|
||||||
|
|
||||||
#include <ev.h>
|
#include <ev.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* key_press.c: key press handler
|
* key_press.c: key press handler
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _KEY_PRESS_H
|
#ifndef I3_KEY_PRESS_H
|
||||||
#define _KEY_PRESS_H
|
#define I3_KEY_PRESS_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There was a key press. We compare this key code with our bindings table and pass
|
* There was a key press. We compare this key code with our bindings table and pass
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* as i3-msg, i3-config-wizard, …
|
* as i3-msg, i3-config-wizard, …
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _LIBI3_H
|
#ifndef I3_LIBI3_H
|
||||||
#define _LIBI3_H
|
#define I3_LIBI3_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* restart.
|
* restart.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _LOAD_LAYOUT_H
|
#ifndef I3_LOAD_LAYOUT_H
|
||||||
#define _LOAD_LAYOUT_H
|
#define I3_LOAD_LAYOUT_H
|
||||||
|
|
||||||
void tree_append_json(const char *filename);
|
void tree_append_json(const char *filename);
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* log.c: Logging functions.
|
* log.c: Logging functions.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _LOG_H
|
#ifndef I3_LOG_H
|
||||||
#define _LOG_H
|
#define I3_LOG_H
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* manage.c: Initially managing new windows (or existing ones on restart).
|
* manage.c: Initially managing new windows (or existing ones on restart).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _MANAGE_H
|
#ifndef I3_MANAGE_H
|
||||||
#define _MANAGE_H
|
#define I3_MANAGE_H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
* match_matches_window() to find the windows affected by this command.
|
* match_matches_window() to find the windows affected by this command.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _MATCH_H
|
#ifndef I3_MATCH_H
|
||||||
#define _MATCH_H
|
#define I3_MATCH_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes the Match data structure. This function is necessary because the
|
* Initializes the Match data structure. This function is necessary because the
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* move.c: Moving containers into some direction.
|
* move.c: Moving containers into some direction.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _MOVE_H
|
#ifndef I3_MOVE_H
|
||||||
#define _MOVE_H
|
#define I3_MOVE_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
|
* Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* output.c: Output (monitor) related functions.
|
* output.c: Output (monitor) related functions.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _OUTPUT_H
|
#ifndef I3_OUTPUT_H
|
||||||
#define _OUTPUT_H
|
#define I3_OUTPUT_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the output container below the given output container.
|
* Returns the output container below the given output container.
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
* (take your time to read it completely, it answers all questions).
|
* (take your time to read it completely, it answers all questions).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _RANDR_H
|
#ifndef I3_RANDR_H
|
||||||
#define _RANDR_H
|
#define I3_RANDR_H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include <xcb/randr.h>
|
#include <xcb/randr.h>
|
||||||
|
@ -18,6 +18,11 @@
|
||||||
TAILQ_HEAD(outputs_head, xoutput);
|
TAILQ_HEAD(outputs_head, xoutput);
|
||||||
extern struct outputs_head outputs;
|
extern struct outputs_head outputs;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CLOSEST_OUTPUT = 0,
|
||||||
|
FARTHEST_OUTPUT = 1
|
||||||
|
} output_close_far_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have just established a connection to the X server and need the initial
|
* We have just established a connection to the X server and need the initial
|
||||||
* XRandR information to setup workspaces for each screen.
|
* XRandR information to setup workspaces for each screen.
|
||||||
|
@ -96,6 +101,6 @@ Output *get_output_most(direction_t direction, Output *current);
|
||||||
* Gets the output which is the next one in the given direction.
|
* Gets the output which is the next one in the given direction.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Output *get_output_next(direction_t direction, Output *current);
|
Output *get_output_next(direction_t direction, Output *current, output_close_far_t close_far);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* regex.c: Interface to libPCRE (perl compatible regular expressions).
|
* regex.c: Interface to libPCRE (perl compatible regular expressions).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _REGEX_H
|
#ifndef I3_REGEX_H
|
||||||
#define _REGEX_H
|
#define I3_REGEX_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new 'regex' struct containing the given pattern and a PCRE
|
* Creates a new 'regex' struct containing the given pattern and a PCRE
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* various rects. Needs to be pushed to X11 (see x.c) to be visible.
|
* various rects. Needs to be pushed to X11 (see x.c) to be visible.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _RENDER_H
|
#ifndef I3_RENDER_H
|
||||||
#define _RENDER_H
|
#define I3_RENDER_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Renders" the given container (and its children), meaning that all rects are
|
* "Renders" the given container (and its children), meaning that all rects are
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* resize.c: Interactive resizing.
|
* resize.c: Interactive resizing.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _RESIZE_H
|
#ifndef I3_RESIZE_H
|
||||||
#define _RESIZE_H
|
#define I3_RESIZE_H
|
||||||
|
|
||||||
int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
|
int resize_graphical_handler(Con *first, Con *second, orientation_t orientation, const xcb_button_press_event_t *event);
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* scratchpad.c: Scratchpad functions (TODO: more description)
|
* scratchpad.c: Scratchpad functions (TODO: more description)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _SCRATCHPAD_H
|
#ifndef I3_SCRATCHPAD_H
|
||||||
#define _SCRATCHPAD_H
|
#define I3_SCRATCHPAD_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the specified window to the __i3_scratch workspace, making it floating
|
* Moves the specified window to the __i3_scratch workspace, making it floating
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* default (ringbuffer for storing the debug log).
|
* default (ringbuffer for storing the debug log).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _I3_SHMLOG_H
|
#ifndef I3_I3_SHMLOG_H
|
||||||
#define _I3_SHMLOG_H
|
#define I3_I3_SHMLOG_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
* to restart inplace).
|
* to restart inplace).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _SIGHANDLER_H
|
#ifndef I3_SIGHANDLER_H
|
||||||
#define _SIGHANDLER_H
|
#define I3_SIGHANDLER_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup signal handlers to safely handle SIGSEGV and SIGFPE
|
* Setup signal handlers to safely handle SIGSEGV and SIGFPE
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
* the appropriate workspace.
|
* the appropriate workspace.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _STARTUP_H
|
#ifndef I3_STARTUP_H
|
||||||
#define _STARTUP_H
|
#define I3_STARTUP_H
|
||||||
|
|
||||||
#define SN_API_NOT_YET_FROZEN 1
|
#define SN_API_NOT_YET_FROZEN 1
|
||||||
#include <libsn/sn-monitor.h>
|
#include <libsn/sn-monitor.h>
|
||||||
|
@ -31,12 +31,27 @@
|
||||||
*/
|
*/
|
||||||
void start_application(const char *command, bool no_startup_id);
|
void start_application(const char *command, bool no_startup_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a startup sequence, ignoring whether its timeout has elapsed.
|
||||||
|
* Useful when e.g. a window is moved between workspaces and its children
|
||||||
|
* shouldn't spawn on the original workspace.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void startup_sequence_delete(struct Startup_Sequence *sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by libstartup-notification when something happens
|
* Called by libstartup-notification when something happens
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void startup_monitor_event(SnMonitorEvent *event, void *userdata);
|
void startup_monitor_event(SnMonitorEvent *event, void *userdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
|
||||||
|
xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the given window belongs to a startup notification by checking if
|
* Checks if the given window belongs to a startup notification by checking if
|
||||||
* the _NET_STARTUP_ID property is set on the window (or on its leader, if it’s
|
* the _NET_STARTUP_ID property is set on the window (or on its leader, if it’s
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* tree.c: Everything that primarily modifies the layout tree data structure.
|
* tree.c: Everything that primarily modifies the layout tree data structure.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _TREE_H
|
#ifndef I3_TREE_H
|
||||||
#define _TREE_H
|
#define I3_TREE_H
|
||||||
|
|
||||||
extern Con *croot;
|
extern Con *croot;
|
||||||
/* TODO: i am not sure yet how much access to the focused container should
|
/* TODO: i am not sure yet how much access to the focused container should
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* also libi3).
|
* also libi3).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _UTIL_H
|
#ifndef I3_UTIL_H
|
||||||
#define _UTIL_H
|
#define I3_UTIL_H
|
||||||
|
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* window.c: Updates window attributes (X11 hints/properties).
|
* window.c: Updates window attributes (X11 hints/properties).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _WINDOW_H
|
#ifndef I3_WINDOW_H
|
||||||
#define _WINDOW_H
|
#define I3_WINDOW_H
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the WM_CLASS (consisting of the class and instance) for the
|
* Updates the WM_CLASS (consisting of the class and instance) for the
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* workspaces.
|
* workspaces.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _WORKSPACE_H
|
#ifndef I3_WORKSPACE_H
|
||||||
#define _WORKSPACE_H
|
#define I3_WORKSPACE_H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
@ -95,6 +95,12 @@ Con* workspace_prev_on_output(void);
|
||||||
*/
|
*/
|
||||||
void workspace_back_and_forth(void);
|
void workspace_back_and_forth(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previously focused workspace con, or NULL if unavailable.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Con *workspace_back_and_forth_get(void);
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/**
|
/**
|
||||||
|
@ -168,4 +174,11 @@ void ws_force_orientation(Con *ws, orientation_t orientation);
|
||||||
*/
|
*/
|
||||||
Con *workspace_attach_to(Con *ws);
|
Con *workspace_attach_to(Con *ws);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new container and re-parents all of children from the given
|
||||||
|
* workspace into it.
|
||||||
|
*
|
||||||
|
* The container inherits the layout from the workspace.
|
||||||
|
*/
|
||||||
|
Con *workspace_encapsulate(Con *ws);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
* render.c). Basically a big state machine.
|
* render.c). Basically a big state machine.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _X_H
|
#ifndef I3_X_H
|
||||||
#define _X_H
|
#define I3_X_H
|
||||||
|
|
||||||
/** Stores the X11 window ID of the currently focused window */
|
/** Stores the X11 window ID of the currently focused window */
|
||||||
extern xcb_window_t focused_id;
|
extern xcb_window_t focused_id;
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* xcb.c: Helper functions for easier usage of XCB
|
* xcb.c: Helper functions for easier usage of XCB
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _XCB_H
|
#ifndef I3_XCB_H
|
||||||
#define _XCB_H
|
#define I3_XCB_H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "xcursor.h"
|
#include "xcursor.h"
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
* older versions.
|
* older versions.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _XCB_COMPAT_H
|
#ifndef I3_XCB_COMPAT_H
|
||||||
#define _XCB_COMPAT_H
|
#define I3_XCB_COMPAT_H
|
||||||
|
|
||||||
#define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t
|
#define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t
|
||||||
#define xcb_icccm_get_wm_protocols xcb_get_wm_protocols
|
#define xcb_icccm_get_wm_protocols xcb_get_wm_protocols
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* xcursor.c: libXcursor support for themed cursors.
|
* xcursor.c: libXcursor support for themed cursors.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _XCURSOR_CURSOR_H
|
#ifndef I3_XCURSOR_CURSOR_H
|
||||||
#define _XCURSOR_CURSOR_H
|
#define I3_XCURSOR_CURSOR_H
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
@ -16,7 +16,12 @@ enum xcursor_cursor_t {
|
||||||
XCURSOR_CURSOR_POINTER = 0,
|
XCURSOR_CURSOR_POINTER = 0,
|
||||||
XCURSOR_CURSOR_RESIZE_HORIZONTAL,
|
XCURSOR_CURSOR_RESIZE_HORIZONTAL,
|
||||||
XCURSOR_CURSOR_RESIZE_VERTICAL,
|
XCURSOR_CURSOR_RESIZE_VERTICAL,
|
||||||
|
XCURSOR_CURSOR_TOP_LEFT_CORNER,
|
||||||
|
XCURSOR_CURSOR_TOP_RIGHT_CORNER,
|
||||||
|
XCURSOR_CURSOR_BOTTOM_LEFT_CORNER,
|
||||||
|
XCURSOR_CURSOR_BOTTOM_RIGHT_CORNER,
|
||||||
XCURSOR_CURSOR_WATCH,
|
XCURSOR_CURSOR_WATCH,
|
||||||
|
XCURSOR_CURSOR_MOVE,
|
||||||
XCURSOR_CURSOR_MAX
|
XCURSOR_CURSOR_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
* driver which does not support RandR in 2011 *sigh*.
|
* driver which does not support RandR in 2011 *sigh*.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifndef _XINERAMA_H
|
#ifndef I3_XINERAMA_H
|
||||||
#define _XINERAMA_H
|
#define I3_XINERAMA_H
|
||||||
|
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* vim:ts=4:sw=4:expandtab
|
||||||
|
*
|
||||||
|
* i3 - an improved dynamic tiling window manager
|
||||||
|
* © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
*
|
||||||
|
* yajl_utils.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef I3_YAJL_UTILS_H
|
||||||
|
#define I3_YAJL_UTILS_H
|
||||||
|
|
||||||
|
#include <yajl/yajl_gen.h>
|
||||||
|
#include <yajl/yajl_parse.h>
|
||||||
|
#include <yajl/yajl_version.h>
|
||||||
|
|
||||||
|
/* Shorter names for all those yajl_gen_* functions */
|
||||||
|
#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
|
||||||
|
#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
|
||||||
|
|
||||||
|
#if YAJL_MAJOR >= 2
|
||||||
|
#define ygenalloc() yajl_gen_alloc(NULL)
|
||||||
|
#define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, client)
|
||||||
|
typedef size_t ylength;
|
||||||
|
#else
|
||||||
|
#define ygenalloc() yajl_gen_alloc(NULL, NULL);
|
||||||
|
#define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, NULL, client)
|
||||||
|
typedef unsigned int ylength;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -142,7 +142,11 @@ i3Font load_font(const char *pattern, const bool fallback) {
|
||||||
|
|
||||||
#if PANGO_SUPPORT
|
#if PANGO_SUPPORT
|
||||||
/* Try to load a pango font if specified */
|
/* Try to load a pango font if specified */
|
||||||
if (strlen(pattern) > strlen("xft:") && !strncmp(pattern, "xft:", strlen("xft:"))) {
|
if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) {
|
||||||
|
pattern += strlen("pango:");
|
||||||
|
if (load_pango_font(&font, pattern))
|
||||||
|
return font;
|
||||||
|
} else if (strlen(pattern) > strlen("xft:") && !strncmp(pattern, "xft:", strlen("xft:"))) {
|
||||||
pattern += strlen("xft:");
|
pattern += strlen("xft:");
|
||||||
if (load_pango_font(&font, pattern))
|
if (load_pango_font(&font, pattern))
|
||||||
return font;
|
return font;
|
||||||
|
|
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
||||||
<refentrytitle>{mantitle}</refentrytitle>
|
<refentrytitle>{mantitle}</refentrytitle>
|
||||||
<manvolnum>{manvolnum}</manvolnum>
|
<manvolnum>{manvolnum}</manvolnum>
|
||||||
<refmiscinfo class="source">i3</refmiscinfo>
|
<refmiscinfo class="source">i3</refmiscinfo>
|
||||||
<refmiscinfo class="version">4.3</refmiscinfo>
|
<refmiscinfo class="version">4.4</refmiscinfo>
|
||||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||||
</refmeta>
|
</refmeta>
|
||||||
<refnamediv>
|
<refnamediv>
|
||||||
|
|
|
@ -24,7 +24,8 @@ workspaces.
|
||||||
|
|
||||||
get_outputs::
|
get_outputs::
|
||||||
Gets the current outputs. The reply will be a JSON-encoded list of outputs (see
|
Gets the current outputs. The reply will be a JSON-encoded list of outputs (see
|
||||||
the reply section).
|
the reply section of docs/ipc, e.g. at
|
||||||
|
http://i3wm.org/docs/ipc.html#_receiving_replies_from_i3).
|
||||||
|
|
||||||
get_tree::
|
get_tree::
|
||||||
Gets the layout tree. i3 uses a tree as data structure which includes every
|
Gets the layout tree. i3 uses a tree as data structure which includes every
|
||||||
|
|
15
man/man.mk
15
man/man.mk
|
@ -1,10 +1,12 @@
|
||||||
DISTCLEAN_TARGETS += clean-mans
|
DISTCLEAN_TARGETS += clean-mans
|
||||||
|
|
||||||
A2X = a2x
|
A2X = a2x
|
||||||
|
POD2MAN = pod2man
|
||||||
|
|
||||||
A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f man/asciidoc.conf" $(A2X_FLAGS) $<
|
A2X_MAN_CALL = $(V_A2X)$(A2X) -f manpage --asciidoc-opts="-f man/asciidoc.conf" $(A2X_FLAGS) $<
|
||||||
|
POD2MAN_CALL = $(V_POD2MAN)$(POD2MAN) --utf8 $< > $@
|
||||||
|
|
||||||
MANS_1 = \
|
MANS_ASCIIDOC = \
|
||||||
man/i3.1 \
|
man/i3.1 \
|
||||||
man/i3bar.1 \
|
man/i3bar.1 \
|
||||||
man/i3-msg.1 \
|
man/i3-msg.1 \
|
||||||
|
@ -17,14 +19,21 @@ MANS_1 = \
|
||||||
man/i3-sensible-terminal.1 \
|
man/i3-sensible-terminal.1 \
|
||||||
man/i3-dump-log.1
|
man/i3-dump-log.1
|
||||||
|
|
||||||
|
MANS_POD = \
|
||||||
|
man/i3-dmenu-desktop.1
|
||||||
|
|
||||||
MANS = \
|
MANS = \
|
||||||
$(MANS_1)
|
$(MANS_ASCIIDOC) \
|
||||||
|
$(MANS_POD)
|
||||||
|
|
||||||
mans: $(MANS)
|
mans: $(MANS)
|
||||||
|
|
||||||
$(MANS_1): %.1: %.man man/asciidoc.conf
|
$(MANS_ASCIIDOC): %.1: %.man man/asciidoc.conf
|
||||||
$(A2X_MAN_CALL)
|
$(A2X_MAN_CALL)
|
||||||
|
|
||||||
|
$(MANS_POD): %.1: i3-dmenu-desktop
|
||||||
|
$(POD2MAN_CALL)
|
||||||
|
|
||||||
clean-mans:
|
clean-mans:
|
||||||
for file in $(notdir $(MANS)); \
|
for file in $(notdir $(MANS)); \
|
||||||
do \
|
do \
|
||||||
|
|
|
@ -61,10 +61,20 @@ state EXEC:
|
||||||
command = string
|
command = string
|
||||||
-> call cmd_exec($nosn, $command)
|
-> call cmd_exec($nosn, $command)
|
||||||
|
|
||||||
# border normal|none|1pixel|toggle
|
# border normal|none|1pixel|toggle|1pixel
|
||||||
state BORDER:
|
state BORDER:
|
||||||
border_style = 'normal', 'none', '1pixel', 'toggle'
|
border_style = 'normal', 'pixel'
|
||||||
-> call cmd_border($border_style)
|
-> BORDER_WIDTH
|
||||||
|
border_style = 'none', 'toggle'
|
||||||
|
-> call cmd_border($border_style, "0")
|
||||||
|
border_style = '1pixel'
|
||||||
|
-> call cmd_border($border_style, "1")
|
||||||
|
|
||||||
|
state BORDER_WIDTH:
|
||||||
|
end
|
||||||
|
-> call cmd_border($border_style, "2")
|
||||||
|
border_width = word
|
||||||
|
-> call cmd_border($border_style, $border_width)
|
||||||
|
|
||||||
# layout default|stacked|stacking|tabbed|splitv|splith
|
# layout default|stacked|stacking|tabbed|splitv|splith
|
||||||
# layout toggle [split|all]
|
# layout toggle [split|all]
|
||||||
|
@ -185,17 +195,30 @@ state RESIZE_TILING_OR:
|
||||||
-> call cmd_resize($way, $direction, $resize_px, $resize_ppt)
|
-> call cmd_resize($way, $direction, $resize_px, $resize_ppt)
|
||||||
|
|
||||||
# rename workspace <name> to <name>
|
# rename workspace <name> to <name>
|
||||||
|
# rename workspace to <name>
|
||||||
state RENAME:
|
state RENAME:
|
||||||
'workspace'
|
'workspace'
|
||||||
-> RENAME_WORKSPACE
|
-> RENAME_WORKSPACE
|
||||||
|
|
||||||
state RENAME_WORKSPACE:
|
state RENAME_WORKSPACE:
|
||||||
|
old_name = 'to'
|
||||||
|
-> RENAME_WORKSPACE_LIKELY_TO
|
||||||
old_name = word
|
old_name = word
|
||||||
-> RENAME_WORKSPACE_TO
|
-> RENAME_WORKSPACE_TO
|
||||||
|
|
||||||
|
state RENAME_WORKSPACE_LIKELY_TO:
|
||||||
|
'to'
|
||||||
|
-> RENAME_WORKSPACE_NEW_NAME
|
||||||
|
new_name = word
|
||||||
|
-> call cmd_rename_workspace(NULL, $new_name)
|
||||||
|
|
||||||
state RENAME_WORKSPACE_TO:
|
state RENAME_WORKSPACE_TO:
|
||||||
'to'
|
'to'
|
||||||
->
|
-> RENAME_WORKSPACE_NEW_NAME
|
||||||
|
|
||||||
|
state RENAME_WORKSPACE_NEW_NAME:
|
||||||
|
end
|
||||||
|
-> call cmd_rename_workspace(NULL, "to")
|
||||||
new_name = string
|
new_name = string
|
||||||
-> call cmd_rename_workspace($old_name, $new_name)
|
-> call cmd_rename_workspace($old_name, $new_name)
|
||||||
|
|
||||||
|
@ -243,6 +266,8 @@ state MOVE_WORKSPACE:
|
||||||
-> MOVE_WORKSPACE_TO_OUTPUT
|
-> MOVE_WORKSPACE_TO_OUTPUT
|
||||||
workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current'
|
workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current'
|
||||||
-> call cmd_move_con_to_workspace($workspace)
|
-> call cmd_move_con_to_workspace($workspace)
|
||||||
|
'back_and_forth'
|
||||||
|
-> call cmd_move_con_to_workspace_back_and_forth()
|
||||||
'number'
|
'number'
|
||||||
-> MOVE_WORKSPACE_NUMBER
|
-> MOVE_WORKSPACE_NUMBER
|
||||||
workspace = string
|
workspace = string
|
||||||
|
|
|
@ -0,0 +1,446 @@
|
||||||
|
# vim:ts=2:sw=2:expandtab
|
||||||
|
#
|
||||||
|
# i3 - an improved dynamic tiling window manager
|
||||||
|
# © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
||||||
|
#
|
||||||
|
# parser-specs/config.spec: Specification file for generate-command-parser.pl
|
||||||
|
# which will generate the appropriate header files for our C parser.
|
||||||
|
#
|
||||||
|
# Use :source highlighting.vim in vim to get syntax highlighting
|
||||||
|
# for this file.
|
||||||
|
|
||||||
|
# TODO: should we implement an include statement for the criteria part so we DRY?
|
||||||
|
|
||||||
|
state INITIAL:
|
||||||
|
# We have an end token here for all the commands which just call some
|
||||||
|
# function without using an explicit 'end' token.
|
||||||
|
end ->
|
||||||
|
error ->
|
||||||
|
'#' -> IGNORE_LINE
|
||||||
|
'set' -> IGNORE_LINE
|
||||||
|
bindtype = 'bindsym', 'bindcode', 'bind' -> BINDING
|
||||||
|
'bar' -> BARBRACE
|
||||||
|
'font' -> FONT
|
||||||
|
'mode' -> MODENAME
|
||||||
|
'floating_minimum_size' -> FLOATING_MINIMUM_SIZE_WIDTH
|
||||||
|
'floating_maximum_size' -> FLOATING_MAXIMUM_SIZE_WIDTH
|
||||||
|
'floating_modifier' -> FLOATING_MODIFIER
|
||||||
|
'default_orientation' -> DEFAULT_ORIENTATION
|
||||||
|
'workspace_layout' -> WORKSPACE_LAYOUT
|
||||||
|
windowtype = 'new_window', 'new_float' -> NEW_WINDOW
|
||||||
|
'hide_edge_borders' -> HIDE_EDGE_BORDERS
|
||||||
|
'for_window' -> FOR_WINDOW
|
||||||
|
'assign' -> ASSIGN
|
||||||
|
'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE
|
||||||
|
'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING
|
||||||
|
'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA
|
||||||
|
'workspace_auto_back_and_forth' -> WORKSPACE_BACK_AND_FORTH
|
||||||
|
'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS
|
||||||
|
'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT
|
||||||
|
'workspace' -> WORKSPACE
|
||||||
|
'ipc_socket', 'ipc-socket' -> IPC_SOCKET
|
||||||
|
'restart_state' -> RESTART_STATE
|
||||||
|
'popup_during_fullscreen' -> POPUP_DURING_FULLSCREEN
|
||||||
|
exectype = 'exec_always', 'exec' -> EXEC
|
||||||
|
colorclass = 'client.background'
|
||||||
|
-> COLOR_SINGLE
|
||||||
|
colorclass = 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent'
|
||||||
|
-> COLOR_BORDER
|
||||||
|
|
||||||
|
# We ignore comments and 'set' lines (variables).
|
||||||
|
state IGNORE_LINE:
|
||||||
|
end, string
|
||||||
|
-> INITIAL
|
||||||
|
|
||||||
|
# floating_minimum_size <width> x <height>
|
||||||
|
state FLOATING_MINIMUM_SIZE_WIDTH:
|
||||||
|
width = number
|
||||||
|
-> FLOATING_MINIMUM_SIZE_X
|
||||||
|
|
||||||
|
state FLOATING_MINIMUM_SIZE_X:
|
||||||
|
'x'
|
||||||
|
-> FLOATING_MINIMUM_SIZE_HEIGHT
|
||||||
|
|
||||||
|
state FLOATING_MINIMUM_SIZE_HEIGHT:
|
||||||
|
height = number
|
||||||
|
-> call cfg_floating_minimum_size(&width, &height)
|
||||||
|
|
||||||
|
# floating_maximum_size <width> x <height>
|
||||||
|
state FLOATING_MAXIMUM_SIZE_WIDTH:
|
||||||
|
width = number
|
||||||
|
-> FLOATING_MAXIMUM_SIZE_X
|
||||||
|
|
||||||
|
state FLOATING_MAXIMUM_SIZE_X:
|
||||||
|
'x'
|
||||||
|
-> FLOATING_MAXIMUM_SIZE_HEIGHT
|
||||||
|
|
||||||
|
state FLOATING_MAXIMUM_SIZE_HEIGHT:
|
||||||
|
height = number
|
||||||
|
-> call cfg_floating_maximum_size(&width, &height)
|
||||||
|
|
||||||
|
# floating_modifier <modifier>
|
||||||
|
state FLOATING_MODIFIER:
|
||||||
|
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl'
|
||||||
|
->
|
||||||
|
'+'
|
||||||
|
->
|
||||||
|
end
|
||||||
|
-> call cfg_floating_modifier($modifiers)
|
||||||
|
|
||||||
|
# default_orientation <horizontal|vertical|auto>
|
||||||
|
state DEFAULT_ORIENTATION:
|
||||||
|
orientation = 'horizontal', 'vertical', 'auto'
|
||||||
|
-> call cfg_default_orientation($orientation)
|
||||||
|
|
||||||
|
# workspace_layout <default|stacking|tabbed>
|
||||||
|
state WORKSPACE_LAYOUT:
|
||||||
|
layout = 'default', 'stacking', 'stacked', 'tabbed'
|
||||||
|
-> call cfg_workspace_layout($layout)
|
||||||
|
|
||||||
|
# new_window <normal|1pixel|none>
|
||||||
|
# new_float <normal|1pixel|none>
|
||||||
|
# TODO: new_float is not in the userguide yet
|
||||||
|
# TODO: pixel is not in the userguide yet
|
||||||
|
state NEW_WINDOW:
|
||||||
|
border = 'normal', 'pixel'
|
||||||
|
-> NEW_WINDOW_PIXELS
|
||||||
|
border = '1pixel', 'none'
|
||||||
|
-> call cfg_new_window($windowtype, $border, -1)
|
||||||
|
|
||||||
|
state NEW_WINDOW_PIXELS:
|
||||||
|
end
|
||||||
|
-> call cfg_new_window($windowtype, $border, 2)
|
||||||
|
width = number
|
||||||
|
-> NEW_WINDOW_PIXELS_PX
|
||||||
|
|
||||||
|
state NEW_WINDOW_PIXELS_PX:
|
||||||
|
'px'
|
||||||
|
->
|
||||||
|
end
|
||||||
|
-> call cfg_new_window($windowtype, $border, &width)
|
||||||
|
|
||||||
|
# hide_edge_borders <none|vertical|horizontal|both>
|
||||||
|
# also hide_edge_borders <bool> for compatibility
|
||||||
|
state HIDE_EDGE_BORDERS:
|
||||||
|
hide_borders = 'none', 'vertical', 'horizontal', 'both'
|
||||||
|
-> call cfg_hide_edge_borders($hide_borders)
|
||||||
|
hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active'
|
||||||
|
-> call cfg_hide_edge_borders($hide_borders)
|
||||||
|
|
||||||
|
# for_window <criteria> command
|
||||||
|
state FOR_WINDOW:
|
||||||
|
'['
|
||||||
|
-> call cfg_criteria_init(FOR_WINDOW_COMMAND); CRITERIA
|
||||||
|
|
||||||
|
state FOR_WINDOW_COMMAND:
|
||||||
|
command = string
|
||||||
|
-> call cfg_for_window($command)
|
||||||
|
|
||||||
|
# assign <criteria> [→] workspace
|
||||||
|
state ASSIGN:
|
||||||
|
'['
|
||||||
|
-> call cfg_criteria_init(ASSIGN_WORKSPACE); CRITERIA
|
||||||
|
|
||||||
|
state ASSIGN_WORKSPACE:
|
||||||
|
'→'
|
||||||
|
->
|
||||||
|
workspace = string
|
||||||
|
-> call cfg_assign($workspace)
|
||||||
|
|
||||||
|
# Criteria: Used by for_window and assign.
|
||||||
|
state CRITERIA:
|
||||||
|
ctype = 'class' -> CRITERION
|
||||||
|
ctype = 'instance' -> CRITERION
|
||||||
|
ctype = 'window_role' -> CRITERION
|
||||||
|
ctype = 'con_id' -> CRITERION
|
||||||
|
ctype = 'id' -> CRITERION
|
||||||
|
ctype = 'con_mark' -> CRITERION
|
||||||
|
ctype = 'title' -> CRITERION
|
||||||
|
ctype = 'urgent' -> CRITERION
|
||||||
|
']'
|
||||||
|
-> call cfg_criteria_pop_state()
|
||||||
|
|
||||||
|
state CRITERION:
|
||||||
|
'=' -> CRITERION_STR
|
||||||
|
|
||||||
|
state CRITERION_STR:
|
||||||
|
cvalue = word
|
||||||
|
-> call cfg_criteria_add($ctype, $cvalue); CRITERIA
|
||||||
|
|
||||||
|
# focus_follows_mouse bool
|
||||||
|
state FOCUS_FOLLOWS_MOUSE:
|
||||||
|
value = word
|
||||||
|
-> call cfg_focus_follows_mouse($value)
|
||||||
|
|
||||||
|
# force_focus_wrapping
|
||||||
|
state FORCE_FOCUS_WRAPPING:
|
||||||
|
value = word
|
||||||
|
-> call cfg_force_focus_wrapping($value)
|
||||||
|
|
||||||
|
# force_xinerama
|
||||||
|
state FORCE_XINERAMA:
|
||||||
|
value = word
|
||||||
|
-> call cfg_force_xinerama($value)
|
||||||
|
|
||||||
|
# workspace_back_and_forth
|
||||||
|
state WORKSPACE_BACK_AND_FORTH:
|
||||||
|
value = word
|
||||||
|
-> call cfg_workspace_back_and_forth($value)
|
||||||
|
|
||||||
|
|
||||||
|
# fake_outputs (for testcases)
|
||||||
|
state FAKE_OUTPUTS:
|
||||||
|
outputs = string
|
||||||
|
-> call cfg_fake_outputs($outputs)
|
||||||
|
|
||||||
|
# force_display_urgency_hint <timeout> ms
|
||||||
|
state FORCE_DISPLAY_URGENCY_HINT:
|
||||||
|
duration_ms = number
|
||||||
|
-> FORCE_DISPLAY_URGENCY_HINT_MS
|
||||||
|
|
||||||
|
state FORCE_DISPLAY_URGENCY_HINT_MS:
|
||||||
|
'ms'
|
||||||
|
->
|
||||||
|
end
|
||||||
|
-> call cfg_force_display_urgency_hint(&duration_ms)
|
||||||
|
|
||||||
|
# workspace <workspace> output <output>
|
||||||
|
state WORKSPACE:
|
||||||
|
workspace = word
|
||||||
|
-> WORKSPACE_OUTPUT
|
||||||
|
|
||||||
|
state WORKSPACE_OUTPUT:
|
||||||
|
'output'
|
||||||
|
-> WORKSPACE_OUTPUT_STR
|
||||||
|
|
||||||
|
state WORKSPACE_OUTPUT_STR:
|
||||||
|
output = string
|
||||||
|
-> call cfg_workspace($workspace, $output)
|
||||||
|
|
||||||
|
# ipc-socket <path>
|
||||||
|
state IPC_SOCKET:
|
||||||
|
path = string
|
||||||
|
-> call cfg_ipc_socket($path)
|
||||||
|
|
||||||
|
# restart_state <path> (for testcases)
|
||||||
|
state RESTART_STATE:
|
||||||
|
path = string
|
||||||
|
-> call cfg_restart_state($path)
|
||||||
|
|
||||||
|
# popup_during_fullscreen
|
||||||
|
state POPUP_DURING_FULLSCREEN:
|
||||||
|
value = 'ignore', 'leave_fullscreen'
|
||||||
|
-> call cfg_popup_during_fullscreen($value)
|
||||||
|
|
||||||
|
# client.background <hexcolor>
|
||||||
|
state COLOR_SINGLE:
|
||||||
|
color = word
|
||||||
|
-> call cfg_color_single($colorclass, $color)
|
||||||
|
|
||||||
|
# colorclass border background text indicator
|
||||||
|
state COLOR_BORDER:
|
||||||
|
border = word
|
||||||
|
-> COLOR_BACKGROUND
|
||||||
|
|
||||||
|
state COLOR_BACKGROUND:
|
||||||
|
background = word
|
||||||
|
-> COLOR_TEXT
|
||||||
|
|
||||||
|
state COLOR_TEXT:
|
||||||
|
text = word
|
||||||
|
-> COLOR_INDICATOR
|
||||||
|
|
||||||
|
state COLOR_INDICATOR:
|
||||||
|
indicator = word
|
||||||
|
-> call cfg_color($colorclass, $border, $background, $text, $indicator)
|
||||||
|
end
|
||||||
|
-> call cfg_color($colorclass, $border, $background, $text, NULL)
|
||||||
|
|
||||||
|
# <exec|exec_always> [--no-startup-id] command
|
||||||
|
state EXEC:
|
||||||
|
no_startup_id = '--no-startup-id'
|
||||||
|
->
|
||||||
|
command = string
|
||||||
|
-> call cfg_exec($exectype, $no_startup_id, $command)
|
||||||
|
|
||||||
|
# font font
|
||||||
|
state FONT:
|
||||||
|
font = string
|
||||||
|
-> call cfg_font($font)
|
||||||
|
|
||||||
|
# bindsym/bindcode
|
||||||
|
state BINDING:
|
||||||
|
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch'
|
||||||
|
->
|
||||||
|
'+'
|
||||||
|
->
|
||||||
|
key = word
|
||||||
|
-> BINDCOMMAND
|
||||||
|
|
||||||
|
state BINDCOMMAND:
|
||||||
|
release = '--release'
|
||||||
|
->
|
||||||
|
command = string
|
||||||
|
-> call cfg_binding($bindtype, $modifiers, $key, $release, $command)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Mode configuration
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
state MODENAME:
|
||||||
|
modename = word
|
||||||
|
-> call cfg_enter_mode($modename); MODEBRACE
|
||||||
|
|
||||||
|
state MODEBRACE:
|
||||||
|
end
|
||||||
|
->
|
||||||
|
'{'
|
||||||
|
-> MODE
|
||||||
|
|
||||||
|
state MODE:
|
||||||
|
end ->
|
||||||
|
error ->
|
||||||
|
'#' -> MODE_IGNORE_LINE
|
||||||
|
'set' -> MODE_IGNORE_LINE
|
||||||
|
bindtype = 'bindsym', 'bindcode', 'bind'
|
||||||
|
-> MODE_BINDING
|
||||||
|
'}'
|
||||||
|
-> INITIAL
|
||||||
|
|
||||||
|
# We ignore comments and 'set' lines (variables).
|
||||||
|
state MODE_IGNORE_LINE:
|
||||||
|
end, string
|
||||||
|
-> MODE
|
||||||
|
|
||||||
|
state MODE_BINDING:
|
||||||
|
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch'
|
||||||
|
->
|
||||||
|
'+'
|
||||||
|
->
|
||||||
|
key = word
|
||||||
|
-> MODE_BINDCOMMAND
|
||||||
|
|
||||||
|
state MODE_BINDCOMMAND:
|
||||||
|
release = '--release'
|
||||||
|
->
|
||||||
|
command = string
|
||||||
|
-> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $command); MODE
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Bar configuration (i3bar)
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
state BARBRACE:
|
||||||
|
end
|
||||||
|
->
|
||||||
|
'{'
|
||||||
|
-> BAR
|
||||||
|
|
||||||
|
state BAR:
|
||||||
|
end ->
|
||||||
|
error ->
|
||||||
|
'#' -> BAR_IGNORE_LINE
|
||||||
|
'set' -> BAR_IGNORE_LINE
|
||||||
|
'i3bar_command' -> BAR_BAR_COMMAND
|
||||||
|
'status_command' -> BAR_STATUS_COMMAND
|
||||||
|
'socket_path' -> BAR_SOCKET_PATH
|
||||||
|
'mode' -> BAR_MODE
|
||||||
|
'modifier' -> BAR_MODIFIER
|
||||||
|
'position' -> BAR_POSITION
|
||||||
|
'output' -> BAR_OUTPUT
|
||||||
|
'tray_output' -> BAR_TRAY_OUTPUT
|
||||||
|
'font' -> BAR_FONT
|
||||||
|
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
|
||||||
|
'verbose' -> BAR_VERBOSE
|
||||||
|
'colors' -> BAR_COLORS_BRACE
|
||||||
|
'}'
|
||||||
|
-> call cfg_bar_finish(); INITIAL
|
||||||
|
|
||||||
|
# We ignore comments and 'set' lines (variables).
|
||||||
|
state BAR_IGNORE_LINE:
|
||||||
|
end, string
|
||||||
|
-> BAR
|
||||||
|
|
||||||
|
state BAR_BAR_COMMAND:
|
||||||
|
command = string
|
||||||
|
-> call cfg_bar_i3bar_command($command); BAR
|
||||||
|
|
||||||
|
state BAR_STATUS_COMMAND:
|
||||||
|
command = string
|
||||||
|
-> call cfg_bar_status_command($command); BAR
|
||||||
|
|
||||||
|
state BAR_SOCKET_PATH:
|
||||||
|
path = string
|
||||||
|
-> call cfg_bar_socket_path($path); BAR
|
||||||
|
|
||||||
|
state BAR_MODE:
|
||||||
|
mode = 'dock', 'hide'
|
||||||
|
-> call cfg_bar_mode($mode); BAR
|
||||||
|
|
||||||
|
state BAR_MODIFIER:
|
||||||
|
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
|
||||||
|
-> call cfg_bar_modifier($modifier); BAR
|
||||||
|
|
||||||
|
state BAR_POSITION:
|
||||||
|
position = 'top', 'bottom'
|
||||||
|
-> call cfg_bar_position($position); BAR
|
||||||
|
|
||||||
|
state BAR_OUTPUT:
|
||||||
|
output = string
|
||||||
|
-> call cfg_bar_output($output); BAR
|
||||||
|
|
||||||
|
state BAR_TRAY_OUTPUT:
|
||||||
|
output = string
|
||||||
|
-> call cfg_bar_tray_output($output); BAR
|
||||||
|
|
||||||
|
state BAR_FONT:
|
||||||
|
font = string
|
||||||
|
-> call cfg_bar_font($font); BAR
|
||||||
|
|
||||||
|
state BAR_WORKSPACE_BUTTONS:
|
||||||
|
value = word
|
||||||
|
-> call cfg_bar_workspace_buttons($value); BAR
|
||||||
|
|
||||||
|
state BAR_VERBOSE:
|
||||||
|
value = word
|
||||||
|
-> call cfg_bar_verbose($value); BAR
|
||||||
|
|
||||||
|
state BAR_COLORS_BRACE:
|
||||||
|
end
|
||||||
|
->
|
||||||
|
'{'
|
||||||
|
-> BAR_COLORS
|
||||||
|
|
||||||
|
state BAR_COLORS:
|
||||||
|
end ->
|
||||||
|
'#' -> BAR_COLORS_IGNORE_LINE
|
||||||
|
'set' -> BAR_COLORS_IGNORE_LINE
|
||||||
|
colorclass = 'background', 'statusline'
|
||||||
|
-> BAR_COLORS_SINGLE
|
||||||
|
colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace'
|
||||||
|
-> BAR_COLORS_BORDER
|
||||||
|
'}'
|
||||||
|
-> BAR
|
||||||
|
|
||||||
|
# We ignore comments and 'set' lines (variables).
|
||||||
|
state BAR_COLORS_IGNORE_LINE:
|
||||||
|
end, string
|
||||||
|
-> BAR_COLORS
|
||||||
|
|
||||||
|
state BAR_COLORS_SINGLE:
|
||||||
|
color = word
|
||||||
|
-> call cfg_bar_color_single($colorclass, $color); BAR_COLORS
|
||||||
|
|
||||||
|
state BAR_COLORS_BORDER:
|
||||||
|
border = word
|
||||||
|
-> BAR_COLORS_BACKGROUND
|
||||||
|
|
||||||
|
state BAR_COLORS_BACKGROUND:
|
||||||
|
background = word
|
||||||
|
-> BAR_COLORS_TEXT
|
||||||
|
|
||||||
|
state BAR_COLORS_TEXT:
|
||||||
|
end
|
||||||
|
-> call cfg_bar_color($colorclass, $border, $background, NULL); BAR_COLORS
|
||||||
|
text = word
|
||||||
|
-> call cfg_bar_color($colorclass, $border, $background, $text); BAR_COLORS
|
|
@ -9,7 +9,7 @@ syntax match i3specComment /#.*/
|
||||||
highlight link i3specComment Comment
|
highlight link i3specComment Comment
|
||||||
|
|
||||||
syntax region i3specLiteral start=/'/ end=/'/
|
syntax region i3specLiteral start=/'/ end=/'/
|
||||||
syntax keyword i3specToken string word end
|
syntax keyword i3specToken string word number end
|
||||||
highlight link i3specLiteral String
|
highlight link i3specLiteral String
|
||||||
highlight link i3specToken String
|
highlight link i3specToken String
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ EOL (\r?\n)
|
||||||
%s OUTPUT_COND
|
%s OUTPUT_COND
|
||||||
%s FOR_WINDOW_COND
|
%s FOR_WINDOW_COND
|
||||||
%s EAT_WHITESPACE
|
%s EAT_WHITESPACE
|
||||||
|
%s BORDER_WIDTH
|
||||||
|
|
||||||
%x BUFFER_LINE
|
%x BUFFER_LINE
|
||||||
%x BAR
|
%x BAR
|
||||||
|
@ -171,6 +172,7 @@ EOL (\r?\n)
|
||||||
}
|
}
|
||||||
<ASSIGN_TARGET_COND>[ \t]*→[ \t]* { BEGIN(WANT_STRING); }
|
<ASSIGN_TARGET_COND>[ \t]*→[ \t]* { BEGIN(WANT_STRING); }
|
||||||
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
|
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
|
||||||
|
<BORDER_WIDTH>[^\n][0-9]+ { printf("Border width set to: %s\n", yytext); yylval.number = atoi(yytext); return NUMBER;}
|
||||||
<EXEC>--no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; }
|
<EXEC>--no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; }
|
||||||
<EXEC>. { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
<EXEC>. { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
||||||
<OPTRELEASE>--release { printf("--release\n"); yy_pop_state(); return TOK_RELEASE; }
|
<OPTRELEASE>--release { printf("--release\n"); yy_pop_state(); return TOK_RELEASE; }
|
||||||
|
@ -200,9 +202,10 @@ auto { return TOK_AUTO; }
|
||||||
workspace_layout { return TOK_WORKSPACE_LAYOUT; }
|
workspace_layout { return TOK_WORKSPACE_LAYOUT; }
|
||||||
new_window { return TOKNEWWINDOW; }
|
new_window { return TOKNEWWINDOW; }
|
||||||
new_float { return TOKNEWFLOAT; }
|
new_float { return TOKNEWFLOAT; }
|
||||||
normal { return TOK_NORMAL; }
|
normal { yy_push_state(BORDER_WIDTH); return TOK_NORMAL; }
|
||||||
none { return TOK_NONE; }
|
none { return TOK_NONE; }
|
||||||
1pixel { return TOK_1PIXEL; }
|
1pixel { return TOK_1PIXEL; }
|
||||||
|
pixel { yy_push_state(BORDER_WIDTH); return TOK_PIXEL; }
|
||||||
hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; }
|
hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; }
|
||||||
both { return TOK_BOTH; }
|
both { return TOK_BOTH; }
|
||||||
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
|
focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; }
|
||||||
|
@ -212,6 +215,8 @@ force-xinerama { return TOK_FORCE_XINERAMA; }
|
||||||
fake_outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
|
fake_outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
|
||||||
fake-outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
|
fake-outputs { WS_STRING; return TOK_FAKE_OUTPUTS; }
|
||||||
workspace_auto_back_and_forth { return TOK_WORKSPACE_AUTO_BAF; }
|
workspace_auto_back_and_forth { return TOK_WORKSPACE_AUTO_BAF; }
|
||||||
|
force_display_urgency_hint { return TOK_WORKSPACE_URGENCY_TIMER; }
|
||||||
|
ms { return TOK_TIME_MS; }
|
||||||
workspace_bar { return TOKWORKSPACEBAR; }
|
workspace_bar { return TOKWORKSPACEBAR; }
|
||||||
popup_during_fullscreen { return TOK_POPUP_DURING_FULLSCREEN; }
|
popup_during_fullscreen { return TOK_POPUP_DURING_FULLSCREEN; }
|
||||||
ignore { return TOK_IGNORE; }
|
ignore { return TOK_IGNORE; }
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
|
bool force_old_config_parser = false;
|
||||||
|
|
||||||
static pid_t configerror_pid = -1;
|
static pid_t configerror_pid = -1;
|
||||||
|
|
||||||
static Match current_match;
|
static Match current_match;
|
||||||
|
@ -108,6 +110,7 @@ static int detect_version(char *buf) {
|
||||||
strncasecmp(bind, "focus down", strlen("focus down")) == 0 ||
|
strncasecmp(bind, "focus down", strlen("focus down")) == 0 ||
|
||||||
strncasecmp(bind, "border normal", strlen("border normal")) == 0 ||
|
strncasecmp(bind, "border normal", strlen("border normal")) == 0 ||
|
||||||
strncasecmp(bind, "border 1pixel", strlen("border 1pixel")) == 0 ||
|
strncasecmp(bind, "border 1pixel", strlen("border 1pixel")) == 0 ||
|
||||||
|
strncasecmp(bind, "border pixel", strlen("border pixel")) == 0 ||
|
||||||
strncasecmp(bind, "border borderless", strlen("border borderless")) == 0 ||
|
strncasecmp(bind, "border borderless", strlen("border borderless")) == 0 ||
|
||||||
strncasecmp(bind, "--no-startup-id", strlen("--no-startup-id")) == 0 ||
|
strncasecmp(bind, "--no-startup-id", strlen("--no-startup-id")) == 0 ||
|
||||||
strncasecmp(bind, "bar", strlen("bar")) == 0) {
|
strncasecmp(bind, "bar", strlen("bar")) == 0) {
|
||||||
|
@ -624,16 +627,21 @@ void parse_file(const char *f) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now lex/parse it */
|
|
||||||
yy_scan_string(new);
|
|
||||||
|
|
||||||
context = scalloc(sizeof(struct context));
|
context = scalloc(sizeof(struct context));
|
||||||
context->filename = f;
|
context->filename = f;
|
||||||
|
|
||||||
|
if (force_old_config_parser) {
|
||||||
|
/* now lex/parse it */
|
||||||
|
yy_scan_string(new);
|
||||||
if (yyparse() != 0) {
|
if (yyparse() != 0) {
|
||||||
fprintf(stderr, "Could not parse configfile\n");
|
fprintf(stderr, "Could not parse configfile\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
struct ConfigResult *config_output = parse_config(new, context);
|
||||||
|
yajl_gen_free(config_output->json_gen);
|
||||||
|
}
|
||||||
|
|
||||||
check_for_duplicate_bindings(context);
|
check_for_duplicate_bindings(context);
|
||||||
|
|
||||||
|
@ -668,6 +676,7 @@ void parse_file(const char *f) {
|
||||||
start_configerror_nagbar(f);
|
start_configerror_nagbar(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (force_old_config_parser)
|
||||||
yylex_destroy();
|
yylex_destroy();
|
||||||
FREE(context->line_copy);
|
FREE(context->line_copy);
|
||||||
free(context);
|
free(context);
|
||||||
|
@ -728,6 +737,7 @@ void parse_file(const char *f) {
|
||||||
%token <color> TOKCOLOR
|
%token <color> TOKCOLOR
|
||||||
%token TOKARROW "→"
|
%token TOKARROW "→"
|
||||||
%token TOKMODE "mode"
|
%token TOKMODE "mode"
|
||||||
|
%token TOK_TIME_MS "ms"
|
||||||
%token TOK_BAR "bar"
|
%token TOK_BAR "bar"
|
||||||
%token TOK_ORIENTATION "default_orientation"
|
%token TOK_ORIENTATION "default_orientation"
|
||||||
%token TOK_HORIZ "horizontal"
|
%token TOK_HORIZ "horizontal"
|
||||||
|
@ -738,6 +748,7 @@ void parse_file(const char *f) {
|
||||||
%token TOKNEWFLOAT "new_float"
|
%token TOKNEWFLOAT "new_float"
|
||||||
%token TOK_NORMAL "normal"
|
%token TOK_NORMAL "normal"
|
||||||
%token TOK_NONE "none"
|
%token TOK_NONE "none"
|
||||||
|
%token TOK_PIXEL "pixel"
|
||||||
%token TOK_1PIXEL "1pixel"
|
%token TOK_1PIXEL "1pixel"
|
||||||
%token TOK_HIDE_EDGE_BORDERS "hide_edge_borders"
|
%token TOK_HIDE_EDGE_BORDERS "hide_edge_borders"
|
||||||
%token TOK_BOTH "both"
|
%token TOK_BOTH "both"
|
||||||
|
@ -746,6 +757,7 @@ void parse_file(const char *f) {
|
||||||
%token TOK_FORCE_XINERAMA "force_xinerama"
|
%token TOK_FORCE_XINERAMA "force_xinerama"
|
||||||
%token TOK_FAKE_OUTPUTS "fake_outputs"
|
%token TOK_FAKE_OUTPUTS "fake_outputs"
|
||||||
%token TOK_WORKSPACE_AUTO_BAF "workspace_auto_back_and_forth"
|
%token TOK_WORKSPACE_AUTO_BAF "workspace_auto_back_and_forth"
|
||||||
|
%token TOK_WORKSPACE_URGENCY_TIMER "force_display_urgency_hint"
|
||||||
%token TOKWORKSPACEBAR "workspace_bar"
|
%token TOKWORKSPACEBAR "workspace_bar"
|
||||||
%token TOK_DEFAULT "default"
|
%token TOK_DEFAULT "default"
|
||||||
%token TOK_STACKING "stacking"
|
%token TOK_STACKING "stacking"
|
||||||
|
@ -816,9 +828,11 @@ void parse_file(const char *f) {
|
||||||
%type <number> bar_mode_mode
|
%type <number> bar_mode_mode
|
||||||
%type <number> bar_modifier_modifier
|
%type <number> bar_modifier_modifier
|
||||||
%type <number> optional_no_startup_id
|
%type <number> optional_no_startup_id
|
||||||
|
%type <number> optional_border_width
|
||||||
%type <number> optional_release
|
%type <number> optional_release
|
||||||
%type <string> command
|
%type <string> command
|
||||||
%type <string> word_or_number
|
%type <string> word_or_number
|
||||||
|
%type <string> duration
|
||||||
%type <string> qstring_or_number
|
%type <string> qstring_or_number
|
||||||
%type <string> optional_workspace_name
|
%type <string> optional_workspace_name
|
||||||
%type <string> workspace_name
|
%type <string> workspace_name
|
||||||
|
@ -848,6 +862,7 @@ line:
|
||||||
| force_focus_wrapping
|
| force_focus_wrapping
|
||||||
| force_xinerama
|
| force_xinerama
|
||||||
| fake_outputs
|
| fake_outputs
|
||||||
|
| force_display_urgency_hint
|
||||||
| workspace_back_and_forth
|
| workspace_back_and_forth
|
||||||
| workspace_bar
|
| workspace_bar
|
||||||
| workspace
|
| workspace
|
||||||
|
@ -1052,6 +1067,11 @@ word_or_number:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
duration:
|
||||||
|
NUMBER { sasprintf(&$$, "%d", $1); }
|
||||||
|
| NUMBER TOK_TIME_MS { sasprintf(&$$, "%d", $1); }
|
||||||
|
;
|
||||||
|
|
||||||
mode:
|
mode:
|
||||||
TOKMODE QUOTEDSTRING '{' modelines '}'
|
TOKMODE QUOTEDSTRING '{' modelines '}'
|
||||||
{
|
{
|
||||||
|
@ -1471,9 +1491,27 @@ new_float:
|
||||||
;
|
;
|
||||||
|
|
||||||
border_style:
|
border_style:
|
||||||
TOK_NORMAL { $$ = BS_NORMAL; }
|
TOK_NORMAL optional_border_width
|
||||||
| TOK_NONE { $$ = BS_NONE; }
|
{
|
||||||
| TOK_1PIXEL { $$ = BS_1PIXEL; }
|
/* FIXME: the whole border_style thing actually screws up when new_float is used because it overwrites earlier values :-/ */
|
||||||
|
config.default_border_width = $2;
|
||||||
|
$$ = BS_NORMAL;
|
||||||
|
}
|
||||||
|
| TOK_1PIXEL
|
||||||
|
{
|
||||||
|
config.default_border_width = 1;
|
||||||
|
$$ = BS_PIXEL;
|
||||||
|
}
|
||||||
|
| TOK_NONE
|
||||||
|
{
|
||||||
|
config.default_border_width = 0;
|
||||||
|
$$ = BS_NONE;
|
||||||
|
}
|
||||||
|
| TOK_PIXEL optional_border_width
|
||||||
|
{
|
||||||
|
config.default_border_width = $2;
|
||||||
|
$$ = BS_PIXEL;
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
bool:
|
bool:
|
||||||
|
@ -1548,6 +1586,14 @@ workspace_back_and_forth:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
force_display_urgency_hint:
|
||||||
|
TOK_WORKSPACE_URGENCY_TIMER duration
|
||||||
|
{
|
||||||
|
DLOG("workspace urgency_timer = %f\n", atoi($2) / 1000.0);
|
||||||
|
config.workspace_urgency_timer = atoi($2) / 1000.0;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
workspace_bar:
|
workspace_bar:
|
||||||
TOKWORKSPACEBAR bool
|
TOKWORKSPACEBAR bool
|
||||||
{
|
{
|
||||||
|
@ -1736,6 +1782,11 @@ exec_always:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
optional_border_width:
|
||||||
|
/* empty */ { $$ = 2; } // 2 pixels is the default value for any type of border
|
||||||
|
| NUMBER { $$ = $1; }
|
||||||
|
;
|
||||||
|
|
||||||
optional_no_startup_id:
|
optional_no_startup_id:
|
||||||
/* empty */ { $$ = false; }
|
/* empty */ { $$ = false; }
|
||||||
| TOK_NO_STARTUP_ID { $$ = true; }
|
| TOK_NO_STARTUP_ID { $$ = true; }
|
||||||
|
|
47
src/click.c
47
src/click.c
|
@ -179,6 +179,22 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
||||||
DLOG("--> OUTCOME = %p\n", con);
|
DLOG("--> OUTCOME = %p\n", con);
|
||||||
DLOG("type = %d, name = %s\n", con->type, con->name);
|
DLOG("type = %d, name = %s\n", con->type, con->name);
|
||||||
|
|
||||||
|
/* Any click in a workspace should focus that workspace. If the
|
||||||
|
* workspace is on another output we need to do a workspace_show in
|
||||||
|
* order for i3bar (and others) to notice the change in workspace. */
|
||||||
|
Con *ws = con_get_workspace(con);
|
||||||
|
Con *focused_workspace = con_get_workspace(focused);
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
|
ws = TAILQ_FIRST(&(output_get_content(con_get_output(con))->focus_head));
|
||||||
|
if (!ws)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ws != focused_workspace)
|
||||||
|
workspace_show(ws);
|
||||||
|
focused_id = XCB_NONE;
|
||||||
|
|
||||||
/* don’t handle dockarea cons, they must not be focused */
|
/* don’t handle dockarea cons, they must not be focused */
|
||||||
if (con->parent->type == CT_DOCKAREA)
|
if (con->parent->type == CT_DOCKAREA)
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -207,21 +223,13 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2: focus this con. If the workspace is on another output we need to
|
/* 2: focus this con. */
|
||||||
* do a workspace_show in order for i3bar (and others) to notice the
|
|
||||||
* change in workspace. */
|
|
||||||
Con *ws = con_get_workspace(con);
|
|
||||||
Con *focused_workspace = con_get_workspace(focused);
|
|
||||||
|
|
||||||
if (ws != focused_workspace)
|
|
||||||
workspace_show(ws);
|
|
||||||
focused_id = XCB_NONE;
|
|
||||||
con_focus(con);
|
con_focus(con);
|
||||||
|
|
||||||
/* 3: For floating containers, we also want to raise them on click.
|
/* 3: For floating containers, we also want to raise them on click.
|
||||||
* We will skip handling events on floating cons in fullscreen mode */
|
* We will skip handling events on floating cons in fullscreen mode */
|
||||||
Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL);
|
Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL);
|
||||||
if (floatingcon != NULL && fs == NULL) {
|
if (floatingcon != NULL && fs != con) {
|
||||||
floating_raise_con(floatingcon);
|
floating_raise_con(floatingcon);
|
||||||
|
|
||||||
/* 4: floating_modifier plus left mouse button drags */
|
/* 4: floating_modifier plus left mouse button drags */
|
||||||
|
@ -309,6 +317,25 @@ int handle_button_press(xcb_button_press_event_t *event) {
|
||||||
return route_click(con, event, mod_pressed, CLICK_INSIDE);
|
return route_click(con, event, mod_pressed, CLICK_INSIDE);
|
||||||
|
|
||||||
if (!(con = con_by_frame_id(event->event))) {
|
if (!(con = con_by_frame_id(event->event))) {
|
||||||
|
/* If the root window is clicked, find the relevant output from the
|
||||||
|
* click coordinates and focus the output's active workspace. */
|
||||||
|
if (event->event == root) {
|
||||||
|
Con *output, *ws;
|
||||||
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
||||||
|
if (con_is_internal(output) ||
|
||||||
|
!rect_contains(output->rect, event->event_x, event->event_y))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ws = TAILQ_FIRST(&(output_get_content(output)->focus_head));
|
||||||
|
if (ws != con_get_workspace(focused)) {
|
||||||
|
workspace_show(ws);
|
||||||
|
tree_render();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ELOG("Clicked into unknown window?!\n");
|
ELOG("Clicked into unknown window?!\n");
|
||||||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
||||||
xcb_flush(conn);
|
xcb_flush(conn);
|
||||||
|
|
237
src/commands.c
237
src/commands.c
|
@ -38,7 +38,6 @@
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static owindows_head owindows;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true if a is definitely greater than b (using the given epsilon)
|
* Returns true if a is definitely greater than b (using the given epsilon)
|
||||||
|
@ -57,19 +56,19 @@ static Output *get_output_from_string(Output *current_output, const char *output
|
||||||
Output *output;
|
Output *output;
|
||||||
|
|
||||||
if (strcasecmp(output_str, "left") == 0) {
|
if (strcasecmp(output_str, "left") == 0) {
|
||||||
output = get_output_next(D_LEFT, current_output);
|
output = get_output_next(D_LEFT, current_output, CLOSEST_OUTPUT);
|
||||||
if (!output)
|
if (!output)
|
||||||
output = get_output_most(D_RIGHT, current_output);
|
output = get_output_most(D_RIGHT, current_output);
|
||||||
} else if (strcasecmp(output_str, "right") == 0) {
|
} else if (strcasecmp(output_str, "right") == 0) {
|
||||||
output = get_output_next(D_RIGHT, current_output);
|
output = get_output_next(D_RIGHT, current_output, CLOSEST_OUTPUT);
|
||||||
if (!output)
|
if (!output)
|
||||||
output = get_output_most(D_LEFT, current_output);
|
output = get_output_most(D_LEFT, current_output);
|
||||||
} else if (strcasecmp(output_str, "up") == 0) {
|
} else if (strcasecmp(output_str, "up") == 0) {
|
||||||
output = get_output_next(D_UP, current_output);
|
output = get_output_next(D_UP, current_output, CLOSEST_OUTPUT);
|
||||||
if (!output)
|
if (!output)
|
||||||
output = get_output_most(D_DOWN, current_output);
|
output = get_output_most(D_DOWN, current_output);
|
||||||
} else if (strcasecmp(output_str, "down") == 0) {
|
} else if (strcasecmp(output_str, "down") == 0) {
|
||||||
output = get_output_next(D_DOWN, current_output);
|
output = get_output_next(D_DOWN, current_output, CLOSEST_OUTPUT);
|
||||||
if (!output)
|
if (!output)
|
||||||
output = get_output_most(D_UP, current_output);
|
output = get_output_most(D_UP, current_output);
|
||||||
} else output = get_output_by_name(output_str);
|
} else output = get_output_by_name(output_str);
|
||||||
|
@ -99,6 +98,29 @@ static bool maybe_back_and_forth(struct CommandResult *cmd_output, char *name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the passed workspace unless it is the current one and auto back and
|
||||||
|
* forth is enabled, in which case the back_and_forth workspace is returned.
|
||||||
|
*/
|
||||||
|
static Con *maybe_auto_back_and_forth_workspace(Con *workspace) {
|
||||||
|
Con *current, *baf;
|
||||||
|
|
||||||
|
if (!config.workspace_auto_back_and_forth)
|
||||||
|
return workspace;
|
||||||
|
|
||||||
|
current = con_get_workspace(focused);
|
||||||
|
|
||||||
|
if (current == workspace) {
|
||||||
|
baf = workspace_back_and_forth_get();
|
||||||
|
if (baf != NULL) {
|
||||||
|
DLOG("Substituting workspace with back_and_forth, as it is focused.\n");
|
||||||
|
return baf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return workspace;
|
||||||
|
}
|
||||||
|
|
||||||
// This code is commented out because we might recycle it for popping up error
|
// This code is commented out because we might recycle it for popping up error
|
||||||
// messages on parser errors.
|
// messages on parser errors.
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -199,6 +221,20 @@ void cmd_MIGRATION_start_nagbar(void) {
|
||||||
* Criteria functions.
|
* Criteria functions.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper data structure for an operation window (window on which the operation
|
||||||
|
* will be performed). Used to build the TAILQ owindows.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef struct owindow {
|
||||||
|
Con *con;
|
||||||
|
TAILQ_ENTRY(owindow) owindows;
|
||||||
|
} owindow;
|
||||||
|
|
||||||
|
typedef TAILQ_HEAD(owindows_head, owindow) owindows_head;
|
||||||
|
|
||||||
|
static owindows_head owindows;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializes the specified 'Match' data structure and the initial state of
|
* Initializes the specified 'Match' data structure and the initial state of
|
||||||
* commands.c for matching target windows of a command.
|
* commands.c for matching target windows of a command.
|
||||||
|
@ -365,7 +401,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
||||||
* when criteria was specified but didn't match any window or
|
* when criteria was specified but didn't match any window or
|
||||||
* when criteria wasn't specified and we don't have any window focused. */
|
* when criteria wasn't specified and we don't have any window focused. */
|
||||||
if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
|
if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
|
||||||
(match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
|
(match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
|
||||||
|
!con_has_children(focused))) {
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -400,6 +437,38 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
||||||
ysuccess(true);
|
ysuccess(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of 'move [window|container] [to] workspace back_and_forth'.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
|
||||||
|
owindow *current;
|
||||||
|
Con *ws;
|
||||||
|
|
||||||
|
ws = workspace_back_and_forth_get();
|
||||||
|
|
||||||
|
if (ws == NULL) {
|
||||||
|
y(map_open);
|
||||||
|
ystr("success");
|
||||||
|
y(bool, false);
|
||||||
|
ystr("error");
|
||||||
|
ystr("No workspace was previously active.");
|
||||||
|
y(map_close);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
|
con_move_to_workspace(current->con, ws, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_output->needs_tree_render = true;
|
||||||
|
// XXX: default reply for now, make this a better reply
|
||||||
|
ysuccess(true);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of 'move [window|container] [to] workspace <name>'.
|
* Implementation of 'move [window|container] [to] workspace <name>'.
|
||||||
*
|
*
|
||||||
|
@ -421,9 +490,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
|
||||||
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
!con_has_children(focused)) {
|
||||||
ELOG("No window to move, you have focused a workspace.\n");
|
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -432,6 +500,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
||||||
/* get the workspace */
|
/* get the workspace */
|
||||||
Con *ws = workspace_get(name, NULL);
|
Con *ws = workspace_get(name, NULL);
|
||||||
|
|
||||||
|
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
|
@ -445,7 +515,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of 'move [window|container] [to] workspace number <number>'.
|
* Implementation of 'move [window|container] [to] workspace number <name>'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
|
@ -455,7 +525,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
* when criteria was specified but didn't match any window or
|
* when criteria was specified but didn't match any window or
|
||||||
* when criteria wasn't specified and we don't have any window focused. */
|
* when criteria wasn't specified and we don't have any window focused. */
|
||||||
if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
|
if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
|
||||||
(match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
|
(match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
|
||||||
|
!con_has_children(focused))) {
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -469,8 +540,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
if (parsed_num == LONG_MIN ||
|
if (parsed_num == LONG_MIN ||
|
||||||
parsed_num == LONG_MAX ||
|
parsed_num == LONG_MAX ||
|
||||||
parsed_num < 0 ||
|
parsed_num < 0 ||
|
||||||
*endptr != '\0') {
|
endptr == which) {
|
||||||
LOG("Could not parse \"%s\" as a number.\n", which);
|
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||||
y(map_open);
|
y(map_open);
|
||||||
ystr("success");
|
ystr("success");
|
||||||
y(bool, false);
|
y(bool, false);
|
||||||
|
@ -489,6 +560,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
workspace = workspace_get(which, NULL);
|
workspace = workspace_get(which, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workspace = maybe_auto_back_and_forth_workspace(workspace);
|
||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
|
@ -503,23 +576,35 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
||||||
|
|
||||||
static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
|
static void cmd_resize_floating(I3_CMD, char *way, char *direction, Con *floating_con, int px) {
|
||||||
LOG("floating resize\n");
|
LOG("floating resize\n");
|
||||||
|
Rect old_rect = floating_con->rect;
|
||||||
|
|
||||||
if (strcmp(direction, "up") == 0) {
|
if (strcmp(direction, "up") == 0) {
|
||||||
floating_con->rect.y -= px;
|
|
||||||
floating_con->rect.height += px;
|
floating_con->rect.height += px;
|
||||||
} else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
|
} else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
|
||||||
floating_con->rect.height += px;
|
floating_con->rect.height += px;
|
||||||
} else if (strcmp(direction, "left") == 0) {
|
} else if (strcmp(direction, "left") == 0) {
|
||||||
floating_con->rect.x -= px;
|
|
||||||
floating_con->rect.width += px;
|
floating_con->rect.width += px;
|
||||||
} else {
|
} else {
|
||||||
floating_con->rect.width += px;
|
floating_con->rect.width += px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
floating_check_size(floating_con);
|
||||||
|
|
||||||
|
/* Did we actually resize anything or did the size constraints prevent us?
|
||||||
|
* If we could not resize, exit now to not move the window. */
|
||||||
|
if (memcmp(&old_rect, &(floating_con->rect), sizeof(Rect)) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strcmp(direction, "up") == 0) {
|
||||||
|
floating_con->rect.y -= px;
|
||||||
|
} else if (strcmp(direction, "left") == 0) {
|
||||||
|
floating_con->rect.x -= px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int ppt) {
|
static bool cmd_resize_tiling_direction(I3_CMD, Con *current, char *way, char *direction, int ppt) {
|
||||||
LOG("tiling resize\n");
|
LOG("tiling resize\n");
|
||||||
/* get the appropriate current container (skip stacked/tabbed cons) */
|
/* get the appropriate current container (skip stacked/tabbed cons) */
|
||||||
Con *current = focused;
|
|
||||||
Con *other = NULL;
|
Con *other = NULL;
|
||||||
double percentage = 0;
|
double percentage = 0;
|
||||||
while (current->parent->layout == L_STACKED ||
|
while (current->parent->layout == L_STACKED ||
|
||||||
|
@ -599,10 +684,9 @@ static bool cmd_resize_tiling_direction(I3_CMD, char *way, char *direction, int
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cmd_resize_tiling_width_height(I3_CMD, char *way, char *direction, int ppt) {
|
static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char *direction, int ppt) {
|
||||||
LOG("width/height resize\n");
|
LOG("width/height resize\n");
|
||||||
/* get the appropriate current container (skip stacked/tabbed cons) */
|
/* get the appropriate current container (skip stacked/tabbed cons) */
|
||||||
Con *current = focused;
|
|
||||||
while (current->parent->layout == L_STACKED ||
|
while (current->parent->layout == L_STACKED ||
|
||||||
current->parent->layout == L_TABBED)
|
current->parent->layout == L_TABBED)
|
||||||
current = current->parent;
|
current = current->parent;
|
||||||
|
@ -697,19 +781,24 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
|
||||||
ppt *= -1;
|
ppt *= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANDLE_EMPTY_MATCH;
|
||||||
|
|
||||||
|
owindow *current;
|
||||||
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
Con *floating_con;
|
Con *floating_con;
|
||||||
if ((floating_con = con_inside_floating(focused))) {
|
if ((floating_con = con_inside_floating(current->con))) {
|
||||||
cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
|
cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
|
||||||
} else {
|
} else {
|
||||||
if (strcmp(direction, "width") == 0 ||
|
if (strcmp(direction, "width") == 0 ||
|
||||||
strcmp(direction, "height") == 0) {
|
strcmp(direction, "height") == 0) {
|
||||||
if (!cmd_resize_tiling_width_height(current_match, cmd_output, way, direction, ppt))
|
if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt))
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (!cmd_resize_tiling_direction(current_match, cmd_output, way, direction, ppt))
|
if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
// XXX: default reply for now, make this a better reply
|
// XXX: default reply for now, make this a better reply
|
||||||
|
@ -717,11 +806,11 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of 'border normal|none|1pixel|toggle'.
|
* Implementation of 'border normal|none|1pixel|toggle|pixel'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_border(I3_CMD, char *border_style_str) {
|
void cmd_border(I3_CMD, char *border_style_str, char *border_width ) {
|
||||||
DLOG("border style should be changed to %s\n", border_style_str);
|
DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width);
|
||||||
owindow *current;
|
owindow *current;
|
||||||
|
|
||||||
HANDLE_EMPTY_MATCH;
|
HANDLE_EMPTY_MATCH;
|
||||||
|
@ -729,23 +818,39 @@ void cmd_border(I3_CMD, char *border_style_str) {
|
||||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||||
int border_style = current->con->border_style;
|
int border_style = current->con->border_style;
|
||||||
|
char *end;
|
||||||
|
int tmp_border_width = -1;
|
||||||
|
tmp_border_width = strtol(border_width, &end, 10);
|
||||||
|
if (end == border_width) {
|
||||||
|
/* no valid digits found */
|
||||||
|
tmp_border_width = -1;
|
||||||
|
}
|
||||||
if (strcmp(border_style_str, "toggle") == 0) {
|
if (strcmp(border_style_str, "toggle") == 0) {
|
||||||
border_style++;
|
border_style++;
|
||||||
border_style %= 3;
|
border_style %= 3;
|
||||||
|
if (border_style == BS_NORMAL)
|
||||||
|
tmp_border_width = 2;
|
||||||
|
else if (border_style == BS_NONE)
|
||||||
|
tmp_border_width = 0;
|
||||||
|
else if (border_style == BS_PIXEL)
|
||||||
|
tmp_border_width = 1;
|
||||||
} else {
|
} else {
|
||||||
if (strcmp(border_style_str, "normal") == 0)
|
if (strcmp(border_style_str, "normal") == 0)
|
||||||
border_style = BS_NORMAL;
|
border_style = BS_NORMAL;
|
||||||
else if (strcmp(border_style_str, "none") == 0)
|
else if (strcmp(border_style_str, "pixel") == 0)
|
||||||
|
border_style = BS_PIXEL;
|
||||||
|
else if (strcmp(border_style_str, "1pixel") == 0){
|
||||||
|
border_style = BS_PIXEL;
|
||||||
|
tmp_border_width = 1;
|
||||||
|
} else if (strcmp(border_style_str, "none") == 0)
|
||||||
border_style = BS_NONE;
|
border_style = BS_NONE;
|
||||||
else if (strcmp(border_style_str, "1pixel") == 0)
|
|
||||||
border_style = BS_1PIXEL;
|
|
||||||
else {
|
else {
|
||||||
ELOG("BUG: called with border_style=%s\n", border_style_str);
|
ELOG("BUG: called with border_style=%s\n", border_style_str);
|
||||||
ysuccess(false);
|
ysuccess(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
con_set_border_style(current->con, border_style);
|
con_set_border_style(current->con, border_style, tmp_border_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
|
@ -807,7 +912,7 @@ void cmd_workspace(I3_CMD, char *which) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of 'workspace number <number>'
|
* Implementation of 'workspace number <name>'
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_workspace_number(I3_CMD, char *which) {
|
void cmd_workspace_number(I3_CMD, char *which) {
|
||||||
|
@ -818,8 +923,8 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
||||||
if (parsed_num == LONG_MIN ||
|
if (parsed_num == LONG_MIN ||
|
||||||
parsed_num == LONG_MAX ||
|
parsed_num == LONG_MAX ||
|
||||||
parsed_num < 0 ||
|
parsed_num < 0 ||
|
||||||
*endptr != '\0') {
|
endptr == which) {
|
||||||
LOG("Could not parse \"%s\" as a number.\n", which);
|
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||||
y(map_open);
|
y(map_open);
|
||||||
ystr("success");
|
ystr("success");
|
||||||
y(bool, false);
|
y(bool, false);
|
||||||
|
@ -838,8 +943,6 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
|
LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
|
||||||
ysuccess(true);
|
ysuccess(true);
|
||||||
/* terminate the which string after the endposition of the number */
|
|
||||||
*endptr = '\0';
|
|
||||||
workspace_show_by_name(which);
|
workspace_show_by_name(which);
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
return;
|
return;
|
||||||
|
@ -949,13 +1052,13 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
|
||||||
|
|
||||||
// TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
|
// TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
|
||||||
if (strcasecmp(name, "up") == 0)
|
if (strcasecmp(name, "up") == 0)
|
||||||
output = get_output_next(D_UP, current_output);
|
output = get_output_next(D_UP, current_output, CLOSEST_OUTPUT);
|
||||||
else if (strcasecmp(name, "down") == 0)
|
else if (strcasecmp(name, "down") == 0)
|
||||||
output = get_output_next(D_DOWN, current_output);
|
output = get_output_next(D_DOWN, current_output, CLOSEST_OUTPUT);
|
||||||
else if (strcasecmp(name, "left") == 0)
|
else if (strcasecmp(name, "left") == 0)
|
||||||
output = get_output_next(D_LEFT, current_output);
|
output = get_output_next(D_LEFT, current_output, CLOSEST_OUTPUT);
|
||||||
else if (strcasecmp(name, "right") == 0)
|
else if (strcasecmp(name, "right") == 0)
|
||||||
output = get_output_next(D_RIGHT, current_output);
|
output = get_output_next(D_RIGHT, current_output, CLOSEST_OUTPUT);
|
||||||
else
|
else
|
||||||
output = get_output_by_name(name);
|
output = get_output_by_name(name);
|
||||||
|
|
||||||
|
@ -1042,8 +1145,12 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
||||||
Con *content = output_get_content(output->con);
|
Con *content = output_get_content(output->con);
|
||||||
LOG("got output %p with content %p\n", output, content);
|
LOG("got output %p with content %p\n", output, content);
|
||||||
|
|
||||||
|
Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
|
||||||
|
LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
|
||||||
|
|
||||||
Con *ws = con_get_workspace(current->con);
|
Con *ws = con_get_workspace(current->con);
|
||||||
LOG("should move workspace %p / %s\n", ws, ws->name);
|
LOG("should move workspace %p / %s\n", ws, ws->name);
|
||||||
|
bool workspace_was_visible = workspace_is_visible(ws);
|
||||||
|
|
||||||
if (con_num_children(ws->parent) == 1) {
|
if (con_num_children(ws->parent) == 1) {
|
||||||
LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
|
LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
|
||||||
|
@ -1078,9 +1185,9 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
||||||
/* notify the IPC listeners */
|
/* notify the IPC listeners */
|
||||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
||||||
}
|
}
|
||||||
|
DLOG("Detaching\n");
|
||||||
|
|
||||||
/* detach from the old output and attach to the new output */
|
/* detach from the old output and attach to the new output */
|
||||||
bool workspace_was_visible = workspace_is_visible(ws);
|
|
||||||
Con *old_content = ws->parent;
|
Con *old_content = ws->parent;
|
||||||
con_detach(ws);
|
con_detach(ws);
|
||||||
if (workspace_was_visible) {
|
if (workspace_was_visible) {
|
||||||
|
@ -1102,6 +1209,22 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
|
||||||
/* Focus the moved workspace on the destination output. */
|
/* Focus the moved workspace on the destination output. */
|
||||||
workspace_show(ws);
|
workspace_show(ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NB: We cannot simply work with previously_visible_ws since it might
|
||||||
|
* have been cleaned up by workspace_show() already, depending on the
|
||||||
|
* focus order/number of other workspaces on the output.
|
||||||
|
* Instead, we loop through the available workspaces and only work with
|
||||||
|
* previously_visible_ws if we still find it. */
|
||||||
|
TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
|
||||||
|
if (ws != previously_visible_ws)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Call the on_remove_child callback of the workspace which previously
|
||||||
|
* was visible on the destination output. Since it is no longer
|
||||||
|
* visible, it might need to get cleaned up. */
|
||||||
|
CALL(previously_visible_ws, on_remove_child);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_output->needs_tree_render = true;
|
cmd_output->needs_tree_render = true;
|
||||||
|
@ -1132,7 +1255,7 @@ void cmd_split(I3_CMD, char *direction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementaiton of 'kill [window|client]'.
|
* Implementation of 'kill [window|client]'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_kill(I3_CMD, char *kill_mode_str) {
|
void cmd_kill(I3_CMD, char *kill_mode_str) {
|
||||||
|
@ -1187,14 +1310,6 @@ void cmd_exec(I3_CMD, char *nosn, char *command) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_focus_direction(I3_CMD, char *direction) {
|
void cmd_focus_direction(I3_CMD, char *direction) {
|
||||||
if (focused &&
|
|
||||||
focused->type != CT_WORKSPACE &&
|
|
||||||
focused->fullscreen_mode != CF_NONE) {
|
|
||||||
LOG("Cannot change focus while in fullscreen mode.\n");
|
|
||||||
ysuccess(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DLOG("direction = *%s*\n", direction);
|
DLOG("direction = *%s*\n", direction);
|
||||||
|
|
||||||
if (strcmp(direction, "left") == 0)
|
if (strcmp(direction, "left") == 0)
|
||||||
|
@ -1221,14 +1336,6 @@ void cmd_focus_direction(I3_CMD, char *direction) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_focus_window_mode(I3_CMD, char *window_mode) {
|
void cmd_focus_window_mode(I3_CMD, char *window_mode) {
|
||||||
if (focused &&
|
|
||||||
focused->type != CT_WORKSPACE &&
|
|
||||||
focused->fullscreen_mode != CF_NONE) {
|
|
||||||
LOG("Cannot change focus while in fullscreen mode.\n");
|
|
||||||
ysuccess(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DLOG("window_mode = %s\n", window_mode);
|
DLOG("window_mode = %s\n", window_mode);
|
||||||
|
|
||||||
Con *ws = con_get_workspace(focused);
|
Con *ws = con_get_workspace(focused);
|
||||||
|
@ -1478,7 +1585,7 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementaiton of 'exit'.
|
* Implementation of 'exit'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_exit(I3_CMD) {
|
void cmd_exit(I3_CMD) {
|
||||||
|
@ -1490,7 +1597,7 @@ void cmd_exit(I3_CMD) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementaiton of 'reload'.
|
* Implementation of 'reload'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_reload(I3_CMD) {
|
void cmd_reload(I3_CMD) {
|
||||||
|
@ -1507,7 +1614,7 @@ void cmd_reload(I3_CMD) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementaiton of 'restart'.
|
* Implementation of 'restart'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_restart(I3_CMD) {
|
void cmd_restart(I3_CMD) {
|
||||||
|
@ -1519,7 +1626,7 @@ void cmd_restart(I3_CMD) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementaiton of 'open'.
|
* Implementation of 'open'.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_open(I3_CMD) {
|
void cmd_open(I3_CMD) {
|
||||||
|
@ -1708,16 +1815,24 @@ void cmd_scratchpad_show(I3_CMD) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implementation of 'rename workspace <name> to <name>'
|
* Implementation of 'rename workspace [<name>] to <name>'
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
|
||||||
|
if (old_name) {
|
||||||
LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
|
LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
|
||||||
|
} else {
|
||||||
|
LOG("Renaming current workspace to \"%s\"\n", new_name);
|
||||||
|
}
|
||||||
|
|
||||||
Con *output, *workspace = NULL;
|
Con *output, *workspace = NULL;
|
||||||
|
if (old_name) {
|
||||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
||||||
GREP_FIRST(workspace, output_get_content(output),
|
GREP_FIRST(workspace, output_get_content(output),
|
||||||
!strcasecmp(child->name, old_name));
|
!strcasecmp(child->name, old_name));
|
||||||
|
} else {
|
||||||
|
workspace = con_get_workspace(focused);
|
||||||
|
}
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
// TODO: we should include the old workspace name here and use yajl for
|
// TODO: we should include the old workspace name here and use yajl for
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
* input parser-specs/commands.spec.
|
* input parser-specs/commands.spec.
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include "GENERATED_enums.h"
|
#include "GENERATED_command_enums.h"
|
||||||
|
|
||||||
typedef struct token {
|
typedef struct token {
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -63,7 +63,7 @@ typedef struct tokenptr {
|
||||||
int n;
|
int n;
|
||||||
} cmdp_token_ptr;
|
} cmdp_token_ptr;
|
||||||
|
|
||||||
#include "GENERATED_tokens.h"
|
#include "GENERATED_command_tokens.h"
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* The (small) stack where identified literals are stored during the parsing
|
* The (small) stack where identified literals are stored during the parsing
|
||||||
|
@ -182,7 +182,7 @@ static Match current_match;
|
||||||
static struct CommandResult subcommand_output;
|
static struct CommandResult subcommand_output;
|
||||||
static struct CommandResult command_output;
|
static struct CommandResult command_output;
|
||||||
|
|
||||||
#include "GENERATED_call.h"
|
#include "GENERATED_command_call.h"
|
||||||
|
|
||||||
|
|
||||||
static void next_state(const cmdp_token *token) {
|
static void next_state(const cmdp_token *token) {
|
||||||
|
@ -190,6 +190,7 @@ static void next_state(const cmdp_token *token) {
|
||||||
subcommand_output.json_gen = command_output.json_gen;
|
subcommand_output.json_gen = command_output.json_gen;
|
||||||
subcommand_output.needs_tree_render = false;
|
subcommand_output.needs_tree_render = false;
|
||||||
GENERATED_call(token->extra.call_identifier, &subcommand_output);
|
GENERATED_call(token->extra.call_identifier, &subcommand_output);
|
||||||
|
state = subcommand_output.next_state;
|
||||||
/* If any subcommand requires a tree_render(), we need to make the
|
/* If any subcommand requires a tree_render(), we need to make the
|
||||||
* whole parser result request a tree_render(). */
|
* whole parser result request a tree_render(). */
|
||||||
if (subcommand_output.needs_tree_render)
|
if (subcommand_output.needs_tree_render)
|
||||||
|
|
319
src/con.c
319
src/con.c
|
@ -28,6 +28,20 @@ char *colors[] = {
|
||||||
|
|
||||||
static void con_on_remove_child(Con *con);
|
static void con_on_remove_child(Con *con);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* force parent split containers to be redrawn
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void con_force_split_parents_redraw(Con *con) {
|
||||||
|
Con *parent = con;
|
||||||
|
|
||||||
|
while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
|
||||||
|
if (!con_is_leaf(parent))
|
||||||
|
FREE(parent->deco_render_params);
|
||||||
|
parent = parent->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a new container (and attach it to the given parent, if not NULL).
|
* Create a new container (and attach it to the given parent, if not NULL).
|
||||||
* This function initializes the data structures and creates the appropriate
|
* This function initializes the data structures and creates the appropriate
|
||||||
|
@ -41,6 +55,7 @@ Con *con_new(Con *parent, i3Window *window) {
|
||||||
new->type = CT_CON;
|
new->type = CT_CON;
|
||||||
new->window = window;
|
new->window = window;
|
||||||
new->border_style = config.default_border;
|
new->border_style = config.default_border;
|
||||||
|
new->current_border_width = -1;
|
||||||
static int cnt = 0;
|
static int cnt = 0;
|
||||||
DLOG("opening window %d\n", cnt);
|
DLOG("opening window %d\n", cnt);
|
||||||
|
|
||||||
|
@ -162,6 +177,7 @@ add_to_focus_head:
|
||||||
* This way, we have the option to insert Cons without having
|
* This way, we have the option to insert Cons without having
|
||||||
* to focus them. */
|
* to focus them. */
|
||||||
TAILQ_INSERT_TAIL(focus_head, con, focused);
|
TAILQ_INSERT_TAIL(focus_head, con, focused);
|
||||||
|
con_force_split_parents_redraw(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -169,6 +185,7 @@ add_to_focus_head:
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void con_detach(Con *con) {
|
void con_detach(Con *con) {
|
||||||
|
con_force_split_parents_redraw(con);
|
||||||
if (con->type == CT_FLOATING_CON) {
|
if (con->type == CT_FLOATING_CON) {
|
||||||
TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows);
|
TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows);
|
||||||
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
||||||
|
@ -195,8 +212,14 @@ void con_focus(Con *con) {
|
||||||
con_focus(con->parent);
|
con_focus(con->parent);
|
||||||
|
|
||||||
focused = con;
|
focused = con;
|
||||||
if (con->urgent) {
|
/* We can't blindly reset non-leaf containers since they might have
|
||||||
|
* other urgent children. Therefore we only reset leafs and propagate
|
||||||
|
* the changes upwards via con_update_parents_urgency() which does proper
|
||||||
|
* checks before resetting the urgency.
|
||||||
|
*/
|
||||||
|
if (con->urgent && con_is_leaf(con)) {
|
||||||
con->urgent = false;
|
con->urgent = false;
|
||||||
|
con_update_parents_urgency(con);
|
||||||
workspace_update_urgent_flag(con_get_workspace(con));
|
workspace_update_urgent_flag(con_get_workspace(con));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,6 +232,32 @@ bool con_is_leaf(Con *con) {
|
||||||
return TAILQ_EMPTY(&(con->nodes_head));
|
return TAILQ_EMPTY(&(con->nodes_head));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this node has regular or floating children.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_has_children(Con *con) {
|
||||||
|
return (!con_is_leaf(con) || !TAILQ_EMPTY(&(con->floating_head)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if a container should be considered split.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_is_split(Con *con) {
|
||||||
|
if (con_is_leaf(con))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (con->layout) {
|
||||||
|
case L_DOCKAREA:
|
||||||
|
case L_OUTPUT:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true if this node accepts a window (if the node swallows windows,
|
* Returns true if this node accepts a window (if the node swallows windows,
|
||||||
* it might already have swallowed enough and cannot hold any more).
|
* it might already have swallowed enough and cannot hold any more).
|
||||||
|
@ -219,7 +268,7 @@ bool con_accepts_window(Con *con) {
|
||||||
if (con->type == CT_WORKSPACE)
|
if (con->type == CT_WORKSPACE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (con->split) {
|
if (con_is_split(con)) {
|
||||||
DLOG("container %p does not accept windows, it is a split container.\n", con);
|
DLOG("container %p does not accept windows, it is a split container.\n", con);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -337,6 +386,14 @@ Con *con_get_fullscreen_con(Con *con, int fullscreen_mode) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the container is internal, such as __i3_scratch
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_is_internal(Con *con) {
|
||||||
|
return (con->name[0] == '_' && con->name[1] == '_');
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true if the node is floating.
|
* Returns true if the node is floating.
|
||||||
*
|
*
|
||||||
|
@ -576,11 +633,6 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
|
void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
|
||||||
if (con->type == CT_WORKSPACE) {
|
|
||||||
DLOG("Moving workspaces is not yet implemented.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prevent moving if this would violate the fullscreen focus restrictions. */
|
/* Prevent moving if this would violate the fullscreen focus restrictions. */
|
||||||
if (!con_fullscreen_permits_focusing(workspace)) {
|
if (!con_fullscreen_permits_focusing(workspace)) {
|
||||||
LOG("Cannot move out of a fullscreen container");
|
LOG("Cannot move out of a fullscreen container");
|
||||||
|
@ -598,6 +650,25 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (con->type == CT_WORKSPACE) {
|
||||||
|
/* Re-parent all of the old workspace's floating windows. */
|
||||||
|
Con *child;
|
||||||
|
while (!TAILQ_EMPTY(&(source_ws->floating_head))) {
|
||||||
|
child = TAILQ_FIRST(&(source_ws->floating_head));
|
||||||
|
con_move_to_workspace(child, workspace, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there are no non-floating children, ignore the workspace. */
|
||||||
|
if (con_is_leaf(con))
|
||||||
|
return;
|
||||||
|
|
||||||
|
con = workspace_encapsulate(con);
|
||||||
|
if (con == NULL) {
|
||||||
|
ELOG("Workspace failed to move its contents into a container!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Save the current workspace. So we can call workspace_show() by the end
|
/* Save the current workspace. So we can call workspace_show() by the end
|
||||||
* of this function. */
|
* of this function. */
|
||||||
Con *current_ws = con_get_workspace(focused);
|
Con *current_ws = con_get_workspace(focused);
|
||||||
|
@ -655,6 +726,14 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If moving a fullscreen container and the destination already has a
|
||||||
|
* fullscreen window on it, un-fullscreen the target's fullscreen con. */
|
||||||
|
Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
|
||||||
|
if (con->fullscreen_mode != CF_NONE && fullscreen != NULL) {
|
||||||
|
con_toggle_fullscreen(fullscreen, CF_OUTPUT);
|
||||||
|
fullscreen = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
DLOG("Re-attaching container to %p / %s\n", next, next->name);
|
DLOG("Re-attaching container to %p / %s\n", next, next->name);
|
||||||
/* 5: re-attach the con to the parent of this focused container */
|
/* 5: re-attach the con to the parent of this focused container */
|
||||||
Con *parent = con->parent;
|
Con *parent = con->parent;
|
||||||
|
@ -671,13 +750,15 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
* invisible.
|
* invisible.
|
||||||
* We don’t focus the con for i3 pseudo workspaces like __i3_scratch and
|
* We don’t focus the con for i3 pseudo workspaces like __i3_scratch and
|
||||||
* we don’t focus when there is a fullscreen con on that workspace. */
|
* we don’t focus when there is a fullscreen con on that workspace. */
|
||||||
if ((workspace->name[0] != '_' || workspace->name[1] != '_') &&
|
if (!con_is_internal(workspace) && !fullscreen) {
|
||||||
con_get_fullscreen_con(workspace, CF_OUTPUT) == NULL) {
|
/* We need to save the focused workspace on the output in case the
|
||||||
/* We need to save focus on workspace level and restore it afterwards.
|
* new workspace is hidden and it's necessary to immediately switch
|
||||||
* Otherwise, we might focus a different workspace without actually
|
* back to the originally-focused workspace. */
|
||||||
* switching workspaces. */
|
|
||||||
Con *old_focus = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
|
Con *old_focus = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
|
||||||
con_focus(con_descend_focused(con));
|
con_focus(con_descend_focused(con));
|
||||||
|
|
||||||
|
/* Restore focus if the output's focused workspace has changed. */
|
||||||
|
if (con_get_workspace(focused) != old_focus)
|
||||||
con_focus(old_focus);
|
con_focus(old_focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,8 +767,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
* don’t want to focus invisible workspaces */
|
* don’t want to focus invisible workspaces */
|
||||||
if (source_output != dest_output &&
|
if (source_output != dest_output &&
|
||||||
workspace_is_visible(workspace) &&
|
workspace_is_visible(workspace) &&
|
||||||
workspace->name[0] != '_' &&
|
!con_is_internal(workspace)) {
|
||||||
workspace->name[1] != '_') {
|
|
||||||
DLOG("Moved to a different output, focusing target\n");
|
DLOG("Moved to a different output, focusing target\n");
|
||||||
} else {
|
} else {
|
||||||
/* Descend focus stack in case focus_next is a workspace which can
|
/* Descend focus stack in case focus_next is a workspace which can
|
||||||
|
@ -701,6 +781,38 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
||||||
con_focus(con_descend_focused(focus_next));
|
con_focus(con_descend_focused(focus_next));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If anything within the container is associated with a startup sequence,
|
||||||
|
* delete it so child windows won't be created on the old workspace. */
|
||||||
|
struct Startup_Sequence *sequence;
|
||||||
|
xcb_get_property_cookie_t cookie;
|
||||||
|
xcb_get_property_reply_t *startup_id_reply;
|
||||||
|
|
||||||
|
if (!con_is_leaf(con)) {
|
||||||
|
Con *child;
|
||||||
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
|
if (!child->window)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cookie = xcb_get_property(conn, false, child->window->id,
|
||||||
|
A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
|
||||||
|
startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
|
||||||
|
|
||||||
|
sequence = startup_sequence_get(child->window, startup_id_reply, true);
|
||||||
|
if (sequence != NULL)
|
||||||
|
startup_sequence_delete(sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con->window) {
|
||||||
|
cookie = xcb_get_property(conn, false, con->window->id,
|
||||||
|
A__NET_STARTUP_ID, XCB_GET_PROPERTY_TYPE_ANY, 0, 512);
|
||||||
|
startup_id_reply = xcb_get_property_reply(conn, cookie, NULL);
|
||||||
|
|
||||||
|
sequence = startup_sequence_get(con->window, startup_id_reply, true);
|
||||||
|
if (sequence != NULL)
|
||||||
|
startup_sequence_delete(sequence);
|
||||||
|
}
|
||||||
|
|
||||||
CALL(parent, on_remove_child);
|
CALL(parent, on_remove_child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,6 +1005,7 @@ Con *con_descend_tiling_focused(Con *con) {
|
||||||
*/
|
*/
|
||||||
Con *con_descend_direction(Con *con, direction_t direction) {
|
Con *con_descend_direction(Con *con, direction_t direction) {
|
||||||
Con *most = NULL;
|
Con *most = NULL;
|
||||||
|
Con *current;
|
||||||
int orientation = con_orientation(con);
|
int orientation = con_orientation(con);
|
||||||
DLOG("con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
|
DLOG("con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
|
||||||
if (direction == D_LEFT || direction == D_RIGHT) {
|
if (direction == D_LEFT || direction == D_RIGHT) {
|
||||||
|
@ -906,7 +1019,12 @@ Con *con_descend_direction(Con *con, direction_t direction) {
|
||||||
/* Wrong orientation. We use the last focused con. Within that con,
|
/* Wrong orientation. We use the last focused con. Within that con,
|
||||||
* we recurse to chose the left/right con or at least the last
|
* we recurse to chose the left/right con or at least the last
|
||||||
* focused one. */
|
* focused one. */
|
||||||
most = TAILQ_FIRST(&(con->focus_head));
|
TAILQ_FOREACH(current, &(con->focus_head), focused) {
|
||||||
|
if (current->type != CT_FLOATING_CON) {
|
||||||
|
most = current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* If the con has no orientation set, it’s not a split container
|
/* If the con has no orientation set, it’s not a split container
|
||||||
* but a container with a client window, so stop recursing */
|
* but a container with a client window, so stop recursing */
|
||||||
|
@ -925,7 +1043,12 @@ Con *con_descend_direction(Con *con, direction_t direction) {
|
||||||
/* Wrong orientation. We use the last focused con. Within that con,
|
/* Wrong orientation. We use the last focused con. Within that con,
|
||||||
* we recurse to chose the top/bottom con or at least the last
|
* we recurse to chose the top/bottom con or at least the last
|
||||||
* focused one. */
|
* focused one. */
|
||||||
most = TAILQ_FIRST(&(con->focus_head));
|
TAILQ_FOREACH(current, &(con->focus_head), focused) {
|
||||||
|
if (current->type != CT_FLOATING_CON) {
|
||||||
|
most = current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* If the con has no orientation set, it’s not a split container
|
/* If the con has no orientation set, it’s not a split container
|
||||||
* but a container with a client window, so stop recursing */
|
* but a container with a client window, so stop recursing */
|
||||||
|
@ -946,52 +1069,38 @@ Con *con_descend_direction(Con *con, direction_t direction) {
|
||||||
*/
|
*/
|
||||||
Rect con_border_style_rect(Con *con) {
|
Rect con_border_style_rect(Con *con) {
|
||||||
adjacent_t borders_to_hide = ADJ_NONE;
|
adjacent_t borders_to_hide = ADJ_NONE;
|
||||||
|
int border_width = con->current_border_width;
|
||||||
|
DLOG("The border width for con is set to: %d\n", con->current_border_width);
|
||||||
Rect result;
|
Rect result;
|
||||||
|
if (con->current_border_width < 0)
|
||||||
|
border_width = config.default_border_width;
|
||||||
|
DLOG("Effective border width is set to: %d\n", border_width);
|
||||||
/* Shortcut to avoid calling con_adjacent_borders() on dock containers. */
|
/* Shortcut to avoid calling con_adjacent_borders() on dock containers. */
|
||||||
int border_style = con_border_style(con);
|
int border_style = con_border_style(con);
|
||||||
if (border_style == BS_NONE)
|
if (border_style == BS_NONE)
|
||||||
return (Rect){ 0, 0, 0, 0 };
|
return (Rect){ 0, 0, 0, 0 };
|
||||||
borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
||||||
switch (border_style) {
|
if (border_style == BS_NORMAL) {
|
||||||
case BS_NORMAL:
|
result = (Rect){border_width, 0 , -(2 * border_width), -(border_width)};
|
||||||
result = (Rect){2, 0, -(2 * 2), -2};
|
} else {
|
||||||
|
result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
|
||||||
|
}
|
||||||
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
||||||
result.x -= 2;
|
result.x -= border_width;
|
||||||
result.width += 2;
|
result.width += border_width;
|
||||||
}
|
}
|
||||||
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
||||||
result.width += 2;
|
result.width += border_width;
|
||||||
|
}
|
||||||
|
if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE && (border_style != BS_NORMAL)) {
|
||||||
|
result.y -= border_width;
|
||||||
|
result.height += border_width;
|
||||||
}
|
}
|
||||||
/* With normal borders we never hide the upper border */
|
|
||||||
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
||||||
result.height += 2;
|
result.height += border_width;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
case BS_1PIXEL:
|
|
||||||
result = (Rect){1, 1, -2, -2};
|
|
||||||
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
|
||||||
result.x -= 1;
|
|
||||||
result.width += 1;
|
|
||||||
}
|
|
||||||
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
|
||||||
result.width += 1;
|
|
||||||
}
|
|
||||||
if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE) {
|
|
||||||
result.y -= 1;
|
|
||||||
result.height += 1;
|
|
||||||
}
|
|
||||||
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
|
||||||
result.height += 1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
|
|
||||||
case BS_NONE:
|
|
||||||
return (Rect){0, 0, 0, 0};
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1046,10 +1155,11 @@ int con_border_style(Con *con) {
|
||||||
* floating window.
|
* floating window.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void con_set_border_style(Con *con, int border_style) {
|
void con_set_border_style(Con *con, int border_style, int border_width) {
|
||||||
/* Handle the simple case: non-floating containerns */
|
/* Handle the simple case: non-floating containerns */
|
||||||
if (!con_is_floating(con)) {
|
if (!con_is_floating(con)) {
|
||||||
con->border_style = border_style;
|
con->border_style = border_style;
|
||||||
|
con->current_border_width = border_width;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,6 +1178,7 @@ void con_set_border_style(Con *con, int border_style) {
|
||||||
|
|
||||||
/* Change the border style, get new border/decoration values. */
|
/* Change the border style, get new border/decoration values. */
|
||||||
con->border_style = border_style;
|
con->border_style = border_style;
|
||||||
|
con->current_border_width = border_width;
|
||||||
bsr = con_border_style_rect(con);
|
bsr = con_border_style_rect(con);
|
||||||
int deco_height =
|
int deco_height =
|
||||||
(con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
|
(con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
|
||||||
|
@ -1125,7 +1236,6 @@ void con_set_layout(Con *con, int layout) {
|
||||||
* split. */
|
* split. */
|
||||||
new->layout = layout;
|
new->layout = layout;
|
||||||
new->last_split_layout = con->last_split_layout;
|
new->last_split_layout = con->last_split_layout;
|
||||||
new->split = true;
|
|
||||||
|
|
||||||
Con *old_focused = TAILQ_FIRST(&(con->focus_head));
|
Con *old_focused = TAILQ_FIRST(&(con->focus_head));
|
||||||
if (old_focused == TAILQ_END(&(con->focus_head)))
|
if (old_focused == TAILQ_END(&(con->focus_head)))
|
||||||
|
@ -1149,6 +1259,7 @@ void con_set_layout(Con *con, int layout) {
|
||||||
|
|
||||||
tree_flatten(croot);
|
tree_flatten(croot);
|
||||||
}
|
}
|
||||||
|
con_force_split_parents_redraw(con);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,6 +1280,7 @@ void con_set_layout(Con *con, int layout) {
|
||||||
} else {
|
} else {
|
||||||
con->layout = layout;
|
con->layout = layout;
|
||||||
}
|
}
|
||||||
|
con_force_split_parents_redraw(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1248,6 +1360,8 @@ static void con_on_remove_child(Con *con) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
con_force_split_parents_redraw(con);
|
||||||
|
|
||||||
/* TODO: check if this container would swallow any other client and
|
/* TODO: check if this container would swallow any other client and
|
||||||
* don’t close it automatically. */
|
* don’t close it automatically. */
|
||||||
int children = con_num_children(con);
|
int children = con_num_children(con);
|
||||||
|
@ -1294,7 +1408,7 @@ Rect con_minimum_size(Con *con) {
|
||||||
/* For horizontal/vertical split containers we sum up the width (h-split)
|
/* For horizontal/vertical split containers we sum up the width (h-split)
|
||||||
* or height (v-split) and use the maximum of the height (h-split) or width
|
* or height (v-split) and use the maximum of the height (h-split) or width
|
||||||
* (v-split) as minimum size. */
|
* (v-split) as minimum size. */
|
||||||
if (con->split) {
|
if (con_is_split(con)) {
|
||||||
uint32_t width = 0, height = 0;
|
uint32_t width = 0, height = 0;
|
||||||
Con *child;
|
Con *child;
|
||||||
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
|
@ -1312,7 +1426,7 @@ Rect con_minimum_size(Con *con) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
|
ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
|
||||||
con->type, con->layout, con->split);
|
con->type, con->layout, con_is_split(con));
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1378,3 +1492,104 @@ bool con_fullscreen_permits_focusing(Con *con) {
|
||||||
/* Focusing con would hide it behind a fullscreen window, disallow it. */
|
/* Focusing con would hide it behind a fullscreen window, disallow it. */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Checks if the given container has an urgent child.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool con_has_urgent_child(Con *con) {
|
||||||
|
Con *child;
|
||||||
|
|
||||||
|
if (con_is_leaf(con))
|
||||||
|
return con->urgent;
|
||||||
|
|
||||||
|
/* We are not interested in floating windows since they can only be
|
||||||
|
* attached to a workspace → nodes_head instead of focus_head */
|
||||||
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
|
if (con_has_urgent_child(child))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make all parent containers urgent if con is urgent or clear the urgent flag
|
||||||
|
* of all parent containers if there are no more urgent children left.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void con_update_parents_urgency(Con *con) {
|
||||||
|
Con *parent = con->parent;
|
||||||
|
|
||||||
|
bool new_urgency_value = con->urgent;
|
||||||
|
while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) {
|
||||||
|
if (new_urgency_value) {
|
||||||
|
parent->urgent = true;
|
||||||
|
} else {
|
||||||
|
/* We can only reset the urgency when the parent
|
||||||
|
* has no other urgent children */
|
||||||
|
if (!con_has_urgent_child(parent))
|
||||||
|
parent->urgent = false;
|
||||||
|
}
|
||||||
|
parent = parent->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a string representing the subtree under con.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *con_get_tree_representation(Con *con) {
|
||||||
|
/* this code works as follows:
|
||||||
|
* 1) create a string with the layout type (D/V/H/T/S) and an opening bracket
|
||||||
|
* 2) append the tree representation of the children to the string
|
||||||
|
* 3) add closing bracket
|
||||||
|
*
|
||||||
|
* The recursion ends when we hit a leaf, in which case we return the
|
||||||
|
* class_instance of the contained window.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* end of recursion */
|
||||||
|
if (con_is_leaf(con)) {
|
||||||
|
if (!con->window)
|
||||||
|
return sstrdup("nowin");
|
||||||
|
|
||||||
|
if (!con->window->class_instance)
|
||||||
|
return sstrdup("noinstance");
|
||||||
|
|
||||||
|
return sstrdup(con->window->class_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *buf;
|
||||||
|
/* 1) add the Layout type to buf */
|
||||||
|
if (con->layout == L_DEFAULT)
|
||||||
|
buf = sstrdup("D[");
|
||||||
|
else if (con->layout == L_SPLITV)
|
||||||
|
buf = sstrdup("V[");
|
||||||
|
else if (con->layout == L_SPLITH)
|
||||||
|
buf = sstrdup("H[");
|
||||||
|
else if (con->layout == L_TABBED)
|
||||||
|
buf = sstrdup("T[");
|
||||||
|
else if (con->layout == L_STACKED)
|
||||||
|
buf = sstrdup("S[");
|
||||||
|
|
||||||
|
/* 2) append representation of children */
|
||||||
|
Con *child;
|
||||||
|
TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
|
||||||
|
char *child_txt = con_get_tree_representation(child);
|
||||||
|
|
||||||
|
char *tmp_buf;
|
||||||
|
sasprintf(&tmp_buf, "%s%s%s", buf,
|
||||||
|
(TAILQ_FIRST(&(con->nodes_head)) == child ? "" : " "), child_txt);
|
||||||
|
free(buf);
|
||||||
|
buf = tmp_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3) close the brackets */
|
||||||
|
char *complete_buf;
|
||||||
|
sasprintf(&complete_buf, "%s]", buf);
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return complete_buf;
|
||||||
|
}
|
||||||
|
|
13
src/config.c
13
src/config.c
|
@ -46,6 +46,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
|
||||||
}
|
}
|
||||||
GRAB_KEY(mods);
|
GRAB_KEY(mods);
|
||||||
GRAB_KEY(mods | xcb_numlock_mask);
|
GRAB_KEY(mods | xcb_numlock_mask);
|
||||||
|
GRAB_KEY(mods | XCB_MOD_MASK_LOCK);
|
||||||
GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,6 +195,13 @@ void switch_mode(const char *new_mode) {
|
||||||
bindings = mode->bindings;
|
bindings = mode->bindings;
|
||||||
translate_keysyms();
|
translate_keysyms();
|
||||||
grab_all_keys(conn, false);
|
grab_all_keys(conn, false);
|
||||||
|
|
||||||
|
char *event_msg;
|
||||||
|
sasprintf(&event_msg, "{\"change\":\"%s\"}", mode->name);
|
||||||
|
|
||||||
|
ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg);
|
||||||
|
FREE(event_msg);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,9 +418,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
||||||
|
|
||||||
config.default_border = BS_NORMAL;
|
config.default_border = BS_NORMAL;
|
||||||
config.default_floating_border = BS_NORMAL;
|
config.default_floating_border = BS_NORMAL;
|
||||||
|
config.default_border_width = 2;
|
||||||
/* Set default_orientation to NO_ORIENTATION for auto orientation. */
|
/* Set default_orientation to NO_ORIENTATION for auto orientation. */
|
||||||
config.default_orientation = NO_ORIENTATION;
|
config.default_orientation = NO_ORIENTATION;
|
||||||
|
|
||||||
|
/* Set default urgency reset delay to 500ms */
|
||||||
|
if (config.workspace_urgency_timer == 0)
|
||||||
|
config.workspace_urgency_timer = 0.5;
|
||||||
|
|
||||||
parse_configuration(override_configpath);
|
parse_configuration(override_configpath);
|
||||||
|
|
||||||
if (reload) {
|
if (reload) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue