Merge branch 'next'
This commit is contained in:
commit
2bf7793d4d
|
@ -14,6 +14,7 @@ testcases/_Inline
|
|||
testcases/inc
|
||||
testcases/META.yml
|
||||
test.commands_parser
|
||||
test.config_parser
|
||||
*.output
|
||||
*.tab.*
|
||||
*.yy.c
|
||||
|
|
2
Makefile
2
Makefile
|
@ -30,7 +30,7 @@ dist: distclean
|
|||
[ ! -d i3-${VERSION} ] || rm -rf i3-${VERSION}
|
||||
[ ! -e i3-${VERSION}.tar.bz2 ] || rm i3-${VERSION}.tar.bz2
|
||||
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}
|
||||
# Only copy toplevel documentation (important stuff)
|
||||
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
|
||||
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
|
||||
it by "layout toggle split", which will work as "layout default" did
|
||||
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
|
||||
V_ASCIIDOC = echo ASCIIDOC $@;
|
||||
V_POD2HTML = echo POD2HTML $@;
|
||||
V_POD2MAN = echo POD2MAN $@;
|
||||
V_A2X = echo A2X $@;
|
||||
endif
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
#!/usr/bin/env perl
|
||||
# vim:ts=4:sw=4:expandtab
|
||||
# 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 warnings;
|
||||
|
@ -9,7 +15,7 @@ use AnyEvent::I3;
|
|||
use File::Temp;
|
||||
use v5.10;
|
||||
|
||||
my $i3 = i3("/tmp/nestedcons");
|
||||
my $i3 = i3();
|
||||
|
||||
my $tree = $i3->get_tree->recv;
|
||||
|
||||
|
@ -26,7 +32,13 @@ sub dump_node {
|
|||
my $w = (defined($n->{window}) ? $n->{window} : "N");
|
||||
my $na = $n->{name};
|
||||
$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(";
|
||||
|
||||
|
@ -36,10 +48,29 @@ sub dump_node {
|
|||
dump_node($_, $n) for @{$n->{nodes}};
|
||||
}
|
||||
|
||||
dump_node($tree);
|
||||
say $tmp "draw(n" . $tree->{id} . ", (0, 0));";
|
||||
sub find_node_with_name {
|
||||
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);
|
||||
my $rep = "$tmp";
|
||||
$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
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
docs/debugging.html
|
||||
docs/debugging-release-version.html
|
||||
docs/hacking-howto.html
|
||||
docs/i3bar-protocol.html
|
||||
docs/userguide.html
|
||||
|
|
|
@ -8,4 +8,5 @@ man/i3-migrate-config-to-v4.1
|
|||
man/i3-sensible-pager.1
|
||||
man/i3-sensible-editor.1
|
||||
man/i3-sensible-terminal.1
|
||||
man/i3-dmenu-desktop.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
|
||||
===================================================================
|
||||
--- 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.1.1/i3-sensible-terminal 2011-12-28 23:52:00.826775027 +0100
|
||||
--- i3-4.3.orig/i3-sensible-terminal 2012-09-19 18:08:09.000000000 +0200
|
||||
+++ i3-4.3/i3-sensible-terminal 2012-09-19 18:32:06.393883488 +0200
|
||||
@@ -4,11 +4,7 @@
|
||||
#
|
||||
# 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
|
||||
-# distribution-specific mechanism to find the preferred terminal emulator. On
|
||||
-# 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 x-terminal-emulator 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 xfce4-terminal; do
|
||||
if which $terminal > /dev/null 2>&1; then
|
||||
exec $terminal "$@"
|
||||
fi
|
||||
|
|
|
@ -38,7 +38,7 @@ override_dh_auto_build:
|
|||
$(MAKE) -C docs
|
||||
|
||||
override_dh_installchangelogs:
|
||||
dh_installchangelogs RELEASE-NOTES-4.2
|
||||
dh_installchangelogs RELEASE-NOTES-4.4
|
||||
|
||||
override_dh_install:
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
Debugging i3: How To
|
||||
====================
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
February 2012
|
||||
December 2012
|
||||
|
||||
This document describes how to debug i3 suitably for sending us useful bug
|
||||
reports, even if you have no clue of C programming.
|
||||
This document describes how to debug i3 to send us useful bug
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -21,25 +21,41 @@ i3 version 4.1.2-248-g51728ba (2012-02-12, branch "next")
|
|||
|
||||
Your version can look like this:
|
||||
|
||||
4.1.2::
|
||||
You are using a release version. Please
|
||||
upgrade to a development version first, or read
|
||||
link:debugging-release-version.html[Debugging i3: How To (release version)].
|
||||
4.1.2 (release version)::
|
||||
You are using a release version. 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 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
|
||||
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
|
||||
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
|
||||
logging explicitly.
|
||||
2. Core dumps are enabled by default.
|
||||
3. If you are using a version from the Debian/Ubuntu autobuilder, it is
|
||||
compiled without optimization. Debug symbols are available in the i3-wm-dbg
|
||||
package. When compiling i3 yourself, debug mode is the default.
|
||||
== Enabling logging
|
||||
|
||||
If you are using a development version (see previous section), you don’t need
|
||||
to do anything -- skip to section 3.
|
||||
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
--------------------------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
2. Generate a backtrace (see below)
|
||||
3. Switch back to the crash dialog (Ctrl-Alt-F7)
|
||||
4. Restart i3 in-place (you will keep your session), continue working
|
||||
|
||||
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
|
||||
-----------------
|
||||
After pressing "b" in the crash dialog, you will get a file called
|
||||
+/tmp/i3-backtrace.%d.%d.txt+ where the first +%d+ is replaced by i3’s process
|
||||
id (PID) and the second one is incremented each time you generate a backtrace,
|
||||
starting at 0.
|
||||
|
||||
== 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
|
||||
|
||||
ASCIIDOC_NOTOC_TARGETS = \
|
||||
docs/debugging.html \
|
||||
docs/debugging-release-version.html
|
||||
docs/debugging.html
|
||||
|
||||
ASCIIDOC_TOC_TARGETS = \
|
||||
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
|
||||
consists of a single JSON hash:
|
||||
|
||||
*Example*:
|
||||
----------------
|
||||
*Minimal example*:
|
||||
------------------------------
|
||||
{ "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}+,
|
||||
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
|
||||
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
|
||||
|
||||
full_text::
|
||||
|
@ -116,6 +134,16 @@ color::
|
|||
when it is associated.
|
||||
Colors are specified in hex (like in HTML), starting with a leading
|
||||
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::
|
||||
Every block should have a unique +name+ (string) entry so that it can
|
||||
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)",
|
||||
"short_text": "10.0.0.1",
|
||||
"color": "#00ff00",
|
||||
"min_width": 300,
|
||||
"align": "right",
|
||||
"urgent": false,
|
||||
"name": "ethernet",
|
||||
"instance": "eth0"
|
||||
|
|
53
docs/ipc
53
docs/ipc
|
@ -1,7 +1,7 @@
|
|||
IPC interface (interprocess communication)
|
||||
==========================================
|
||||
Michael Stapelberg <michael@i3wm.org>
|
||||
August 2012
|
||||
October 2012
|
||||
|
||||
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
|
||||
|
@ -19,6 +19,13 @@ calling +i3 --get-socketpath+.
|
|||
All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
|
||||
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
|
||||
|
||||
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:
|
||||
------------------------------------------------------------------------------
|
||||
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:
|
||||
|
@ -274,6 +281,8 @@ name (string)::
|
|||
border (string)::
|
||||
Can be either "normal", "none" or "1pixel", dependending on the
|
||||
container’s border style.
|
||||
current_border_width (integer)::
|
||||
Number of pixels of the border width.
|
||||
layout (string)::
|
||||
Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
|
||||
"output".
|
||||
|
@ -610,6 +619,8 @@ workspace (0)::
|
|||
output (1)::
|
||||
Sent when RandR issues a change notification (of either screens,
|
||||
outputs, CRTCs or output properties).
|
||||
mode (2)::
|
||||
Sent whenever i3 changes its binding mode.
|
||||
|
||||
*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",
|
||||
"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:*
|
||||
---------------------
|
||||
{ "change": "focus" }
|
||||
{
|
||||
"change": "focus",
|
||||
"current": {
|
||||
"id": 28489712,
|
||||
"type":4,
|
||||
...
|
||||
}
|
||||
"old": {
|
||||
"id": 28489715,
|
||||
"type": 4,
|
||||
...
|
||||
}
|
||||
}
|
||||
---------------------
|
||||
|
||||
=== output event
|
||||
|
@ -651,7 +682,21 @@ This event consists of a single serialized map containing a property
|
|||
{ "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
|
||||
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.
|
||||
|
||||
=== 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
|
||||
|
||||
==== 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
|
||||
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
|
||||
are your homerow.
|
||||
|
||||
== Using i3
|
||||
|
||||
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)
|
||||
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)
|
||||
being a popular alternative.
|
||||
|
||||
=== Opening terminals and moving around
|
||||
|
||||
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
|
||||
pressing mod+Enter, a new terminal will be opened. It will fill the whole
|
||||
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
|
||||
space available on your screen.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
terminals, use +mod+K+ or +mod+L+. Of course, you can also use the arrow keys.
|
||||
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
|
||||
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
|
||||
specific direction (horizontal by default). Every window can be split
|
||||
|
@ -61,8 +61,8 @@ windows.
|
|||
|
||||
TODO: picture of the tree
|
||||
|
||||
To split a window vertically, press +mod+v+ before you create the new window.
|
||||
To split it horizontally, press +mod+h+.
|
||||
To split a window vertically, press +$mod+v+ before you create the new window.
|
||||
To split it horizontally, press +$mod+h+.
|
||||
|
||||
=== Changing the container layout
|
||||
|
||||
|
@ -80,15 +80,15 @@ tabbed::
|
|||
The same principle as +stacking+, but the list of windows at the top is only
|
||||
a single line which is vertically split.
|
||||
|
||||
To switch modes, press +mod+e+ for splith/splitv (it toggles), +mod+s+ for
|
||||
stacking and +mod+w+ for tabbed.
|
||||
To switch modes, press +$mod+e+ for splith/splitv (it toggles), +$mod+s+ for
|
||||
stacking and +$mod+w+ for tabbed.
|
||||
|
||||
image:modes.png[Container modes]
|
||||
|
||||
=== Toggling fullscreen mode for a window
|
||||
|
||||
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
|
||||
available outputs (the command is +fullscreen global+).
|
||||
|
@ -96,7 +96,7 @@ available outputs (the command is +fullscreen global+).
|
|||
=== Opening other applications
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -118,7 +118,7 @@ depends on the application.
|
|||
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
Similarly to switching workspaces, the target workspace will be created if
|
||||
it does not yet exist.
|
||||
|
@ -148,11 +148,11 @@ columns/rows with your keyboard.
|
|||
=== Restarting i3 inplace
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
around. By grabbing the borders and moving them you can resize the window. You
|
||||
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
|
||||
for splith and tabbed). So, in our example with the workspace, the default
|
||||
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:
|
||||
|
||||
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
|
||||
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.
|
||||
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
|
||||
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
|
||||
terminal and it will open below the current one:
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -316,13 +316,15 @@ and fall back to a working font.
|
|||
*Syntax*:
|
||||
------------------------------
|
||||
font <X core font description>
|
||||
font xft:<a FreeType font description>
|
||||
font pango:[family list] [style options] [size]
|
||||
------------------------------
|
||||
|
||||
*Examples*:
|
||||
--------------------------------------------------------------
|
||||
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]]
|
||||
|
@ -362,10 +364,10 @@ bindcode [--release] [Modifiers+]keycode command
|
|||
*Examples*:
|
||||
--------------------------------
|
||||
# Fullscreen
|
||||
bindsym mod+f fullscreen
|
||||
bindsym $mod+f fullscreen
|
||||
|
||||
# Restart
|
||||
bindsym mod+Shift+r restart
|
||||
bindsym $mod+Shift+r restart
|
||||
|
||||
# Notebook-specific hotkeys
|
||||
bindcode 214 exec --no-startup-id /home/michael/toggle_beamer.sh
|
||||
|
@ -479,11 +481,13 @@ workspace_layout tabbed
|
|||
=== Border style for new windows
|
||||
|
||||
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*:
|
||||
---------------------------------------------
|
||||
new_window <normal|1pixel|none>
|
||||
new_window <normal|1pixel|none|pixel>
|
||||
new_float <normal|1pixel|none|pixel>
|
||||
---------------------------------------------
|
||||
|
||||
*Example*:
|
||||
|
@ -491,6 +495,19 @@ new_window <normal|1pixel|none>
|
|||
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
|
||||
|
||||
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
|
||||
(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).
|
||||
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
|
||||
you go out of fullscreen).
|
||||
2. Leave fullscreen mode. This is the default.
|
||||
3. Leave fullscreen mode.
|
||||
|
||||
*Syntax*:
|
||||
-------------------------------------------------
|
||||
popup_during_fullscreen <ignore|leave_fullscreen>
|
||||
popup_during_fullscreen <smart|ignore|leave_fullscreen>
|
||||
-------------------------------------------------
|
||||
|
||||
*Example*:
|
||||
------------------------------
|
||||
popup_during_fullscreen ignore
|
||||
popup_during_fullscreen smart
|
||||
------------------------------
|
||||
|
||||
=== 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
|
||||
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*:
|
||||
--------------------------------------
|
||||
|
@ -874,6 +893,30 @@ workspace_auto_back_and_forth <yes|no>
|
|||
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
|
||||
|
||||
The bar at the bottom of your monitor is drawn by a separate process called
|
||||
|
@ -1079,7 +1122,7 @@ font <font>
|
|||
--------------------------------------------------------------
|
||||
bar {
|
||||
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*:
|
||||
--------------------------------------------------------
|
||||
bindsym mod+x move container to workspace 3; workspace 3
|
||||
bindsym $mod+x move container to workspace 3; workspace 3
|
||||
--------------------------------------------------------
|
||||
|
||||
[[command_criteria]]
|
||||
|
@ -1182,10 +1225,10 @@ which have the class Firefox, use:
|
|||
|
||||
*Example*:
|
||||
------------------------------------
|
||||
bindsym mod+x [class="Firefox"] kill
|
||||
bindsym $mod+x [class="Firefox"] kill
|
||||
|
||||
# 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:
|
||||
|
@ -1231,10 +1274,10 @@ exec [--no-startup-id] command
|
|||
*Example*:
|
||||
------------------------------
|
||||
# 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
|
||||
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
|
||||
|
@ -1265,8 +1308,8 @@ split <vertical|horizontal>
|
|||
|
||||
*Example*:
|
||||
------------------------------
|
||||
bindsym mod+v split vertical
|
||||
bindsym mod+h split horizontal
|
||||
bindsym $mod+v split vertical
|
||||
bindsym $mod+h split horizontal
|
||||
------------------------------
|
||||
|
||||
=== Manipulating layout
|
||||
|
@ -1287,21 +1330,21 @@ layout toggle [split|all]
|
|||
|
||||
*Examples*:
|
||||
--------------
|
||||
bindsym mod+s layout stacking
|
||||
bindsym mod+l layout toggle split
|
||||
bindsym mod+w layout tabbed
|
||||
bindsym $mod+s layout stacking
|
||||
bindsym $mod+l layout toggle split
|
||||
bindsym $mod+w layout tabbed
|
||||
|
||||
# Toggle between stacking/tabbed/split:
|
||||
bindsym mod+x layout toggle
|
||||
bindsym $mod+x layout toggle
|
||||
|
||||
# Toggle between stacking/tabbed/splith/splitv:
|
||||
bindsym mod+x layout toggle all
|
||||
bindsym $mod+x layout toggle all
|
||||
|
||||
# Toggle fullscreen
|
||||
bindsym mod+f fullscreen
|
||||
bindsym $mod+f fullscreen
|
||||
|
||||
# Toggle floating/tiling
|
||||
bindsym mod+t floating toggle
|
||||
bindsym $mod+t floating toggle
|
||||
--------------
|
||||
|
||||
=== Focusing/Moving containers
|
||||
|
@ -1343,36 +1386,36 @@ relevant for floating containers. The default amount is 10 pixels.
|
|||
*Examples*:
|
||||
----------------------
|
||||
# Focus container on the left, bottom, top, right:
|
||||
bindsym mod+j focus left
|
||||
bindsym mod+k focus down
|
||||
bindsym mod+l focus up
|
||||
bindsym mod+semicolon focus right
|
||||
bindsym $mod+j focus left
|
||||
bindsym $mod+k focus down
|
||||
bindsym $mod+l focus up
|
||||
bindsym $mod+semicolon focus right
|
||||
|
||||
# Focus parent container
|
||||
bindsym mod+u focus parent
|
||||
bindsym $mod+u focus parent
|
||||
|
||||
# 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
|
||||
bindsym mod+x focus output right
|
||||
bindsym $mod+x focus output right
|
||||
|
||||
# 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:
|
||||
bindsym mod+j move left
|
||||
bindsym mod+k move down
|
||||
bindsym mod+l move up
|
||||
bindsym mod+semicolon move right
|
||||
bindsym $mod+j move left
|
||||
bindsym $mod+k move down
|
||||
bindsym $mod+l move up
|
||||
bindsym $mod+semicolon move right
|
||||
|
||||
# Move container, but make floating containers
|
||||
# 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
|
||||
# of all outputs
|
||||
bindsym mod+c move absolute position center
|
||||
bindsym $mod+c move absolute position center
|
||||
----------------------
|
||||
|
||||
=== Changing (named) workspaces/moving to workspaces
|
||||
|
@ -1395,38 +1438,40 @@ RandR output.
|
|||
|
||||
[[back_and_forth]]
|
||||
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*:
|
||||
-----------------------------------
|
||||
workspace <next|prev|next_on_output|prev_on_output>
|
||||
workspace back_and_forth
|
||||
workspace <name>
|
||||
workspace number <number>
|
||||
workspace number <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>
|
||||
-----------------------------------
|
||||
|
||||
*Examples*:
|
||||
-------------------------
|
||||
bindsym mod+1 workspace 1
|
||||
bindsym mod+2 workspace 2
|
||||
bindsym $mod+1 workspace 1
|
||||
bindsym $mod+2 workspace 2
|
||||
...
|
||||
|
||||
bindsym mod+Shift+1 move container to workspace 1
|
||||
bindsym mod+Shift+2 move container to workspace 2
|
||||
bindsym $mod+Shift+1 move container to workspace 1
|
||||
bindsym $mod+Shift+2 move container to workspace 2
|
||||
...
|
||||
|
||||
# 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
|
||||
bindsym mod+x move workspace to output right
|
||||
bindsym $mod+x move workspace to output right
|
||||
|
||||
# move firefox to current workspace
|
||||
bindsym mod+F1 [class="Firefox"] move workspace current
|
||||
bindsym $mod+F1 [class="Firefox"] move workspace current
|
||||
-------------------------
|
||||
|
||||
==== Named workspaces
|
||||
|
@ -1436,7 +1481,7 @@ workspace command, you can use an arbitrary name:
|
|||
|
||||
*Example*:
|
||||
-------------------------
|
||||
bindsym mod+1 workspace mail
|
||||
bindsym $mod+1 workspace mail
|
||||
...
|
||||
-------------------------
|
||||
|
||||
|
@ -1445,8 +1490,8 @@ number, like this:
|
|||
|
||||
*Example*:
|
||||
-------------------------
|
||||
bindsym mod+1 workspace 1: mail
|
||||
bindsym mod+2 workspace 2: www
|
||||
bindsym $mod+1 workspace 1: mail
|
||||
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+
|
||||
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
|
||||
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
|
||||
|
||||
You can rename workspaces. This might be useful to start with the default
|
||||
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*:
|
||||
----------------------------------------------------
|
||||
rename workspace <old_name> to <new_name>
|
||||
rename workspace to <new_name>
|
||||
----------------------------------------------------
|
||||
|
||||
*Examples*:
|
||||
------------------------------------------------
|
||||
--------------------------------------------------------------------------
|
||||
i3-msg 'rename workspace 5 to 6'
|
||||
i3-msg 'rename workspace 1 to "1: 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
|
||||
|
||||
|
@ -1494,10 +1550,10 @@ move workspace to output <<left|right|down|up>|<output>>
|
|||
--------------------------------------------------------
|
||||
# Move the current workspace to the next output
|
||||
# (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.
|
||||
bindsym mod+x move container to output VGA1
|
||||
bindsym $mod+x move container to output VGA1
|
||||
--------------------------------------------------------
|
||||
|
||||
[[resizingconfig]]
|
||||
|
@ -1548,7 +1604,7 @@ mode "resize" {
|
|||
}
|
||||
|
||||
# Enter resize mode
|
||||
bindsym mod+r mode "resize"
|
||||
bindsym $mod+r mode "resize"
|
||||
----------------------------------------------------------------------
|
||||
|
||||
=== Jumping to specific windows
|
||||
|
@ -1569,7 +1625,7 @@ with criteria for that.
|
|||
*Examples*:
|
||||
------------------------------------------------
|
||||
# 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)
|
||||
|
@ -1605,10 +1661,10 @@ TODO: make i3-input replace %s
|
|||
*Examples*:
|
||||
---------------------------------------
|
||||
# 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
|
||||
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
|
||||
|
@ -1625,9 +1681,9 @@ There is also +border toggle+ which will toggle the different border styles.
|
|||
|
||||
*Examples*:
|
||||
----------------------------
|
||||
bindsym mod+t border normal
|
||||
bindsym mod+y border 1pixel
|
||||
bindsym mod+u border none
|
||||
bindsym $mod+t border normal
|
||||
bindsym $mod+y border 1pixel
|
||||
bindsym $mod+u border none
|
||||
----------------------------
|
||||
|
||||
[[stack-limit]]
|
||||
|
@ -1672,9 +1728,9 @@ however you don’t need to (simply killing your X session is fine as well).
|
|||
|
||||
*Examples*:
|
||||
----------------------------
|
||||
bindsym mod+Shift+r restart
|
||||
bindsym mod+Shift+w reload
|
||||
bindsym mod+Shift+e exit
|
||||
bindsym $mod+Shift+r restart
|
||||
bindsym $mod+Shift+w reload
|
||||
bindsym $mod+Shift+e exit
|
||||
----------------------------
|
||||
|
||||
=== 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
|
||||
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
|
||||
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
|
||||
editor always at hand. However, you can also use this for other permanently
|
||||
|
@ -1702,10 +1760,10 @@ scratchpad show
|
|||
*Examples*:
|
||||
------------------------------------------------
|
||||
# Make the currently focused window a scratchpad
|
||||
bindsym mod+Shift+minus move scratchpad
|
||||
bindsym $mod+Shift+minus move scratchpad
|
||||
|
||||
# Show the first scratchpad window
|
||||
bindsym mod+minus scratchpad show
|
||||
bindsym $mod+minus scratchpad show
|
||||
|
||||
# Show the sup-mail scratchpad window, if any.
|
||||
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
|
||||
track of which window you put where. Thus, you can use vim-like marks to
|
||||
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
|
||||
|
||||
|
|
|
@ -12,8 +12,18 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
use Data::Dumper;
|
||||
use Getopt::Long;
|
||||
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
|
||||
sub slurp {
|
||||
open my $fh, '<', shift;
|
||||
|
@ -24,8 +34,6 @@ sub slurp {
|
|||
# Stores the different states.
|
||||
my %states;
|
||||
|
||||
# XXX: don’t hardcode input and output
|
||||
my $input = '../parser-specs/commands.spec';
|
||||
my @raw_lines = split("\n", slurp($input));
|
||||
my @lines;
|
||||
|
||||
|
@ -101,24 +109,30 @@ for my $line (@lines) {
|
|||
# Second step: Generate the enum values for all states.
|
||||
|
||||
# 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
|
||||
# seems to eat it.
|
||||
my %statenum;
|
||||
say $enumfh 'typedef enum {';
|
||||
my $cnt = 0;
|
||||
for my $state (@keys, '__CALL') {
|
||||
say $enumfh " $state = $cnt,";
|
||||
$statenum{$state} = $cnt;
|
||||
$cnt++;
|
||||
}
|
||||
say $enumfh '} cmdp_state;';
|
||||
close($enumfh);
|
||||
|
||||
# Third step: Generate the call function.
|
||||
open(my $callfh, '>', 'GENERATED_call.h');
|
||||
say $callfh 'static void GENERATED_call(const int call_identifier, struct CommandResult *result) {';
|
||||
open(my $callfh, '>', "GENERATED_${prefix}_call.h");
|
||||
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) {';
|
||||
my $call_id = 0;
|
||||
for my $state (@keys) {
|
||||
|
@ -132,13 +146,23 @@ for my $state (@keys) {
|
|||
$next_state ||= 'INITIAL';
|
||||
my $fmt = $cmd;
|
||||
# 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;
|
||||
# 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_]+)/%ld/g;
|
||||
$fmt =~ s/"([a-z0-9_]+)"/%s/g;
|
||||
$fmt =~ s/(?:-?|\b)[0-9]+\b/%d/g;
|
||||
|
||||
say $callfh " case $call_id:";
|
||||
say $callfh " result->next_state = $next_state;";
|
||||
say $callfh '#ifndef TEST_PARSER';
|
||||
my $real_cmd = $cmd;
|
||||
if ($real_cmd =~ /\(\)/) {
|
||||
|
@ -152,9 +176,14 @@ for my $state (@keys) {
|
|||
$cmd =~ s/[^(]+\(//;
|
||||
$cmd =~ s/\)$//;
|
||||
$cmd = ", $cmd" if length($cmd) > 0;
|
||||
$cmd =~ s/, NULL//g;
|
||||
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 " state = $next_state;";
|
||||
say $callfh " break;";
|
||||
$token->{next_state} = "call $call_id";
|
||||
$call_id++;
|
||||
|
@ -162,17 +191,18 @@ for my $state (@keys) {
|
|||
}
|
||||
say $callfh ' default:';
|
||||
say $callfh ' printf("BUG in the parser. state = %d\n", call_identifier);';
|
||||
say $callfh ' assert(false);';
|
||||
say $callfh ' }';
|
||||
say $callfh '}';
|
||||
close($callfh);
|
||||
|
||||
# Fourth step: Generate the token datastructures.
|
||||
|
||||
open(my $tokfh, '>', 'GENERATED_tokens.h');
|
||||
open(my $tokfh, '>', "GENERATED_${prefix}_tokens.h");
|
||||
|
||||
for my $state (@keys) {
|
||||
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) {
|
||||
my $call_identifier = 0;
|
||||
my $token_name = $token->{token};
|
||||
|
@ -192,7 +222,7 @@ for my $state (@keys) {
|
|||
say $tokfh '};';
|
||||
}
|
||||
|
||||
say $tokfh 'cmdp_token_ptr tokens[' . scalar @keys . '] = {';
|
||||
say $tokfh 'static cmdp_token_ptr tokens[' . scalar @keys . '] = {';
|
||||
for my $state (@keys) {
|
||||
my $tokens = $states{$state};
|
||||
say $tokfh ' { tokens_' . $state . ', ' . scalar @$tokens . ' },';
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#endif
|
||||
|
||||
/* 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
|
||||
#elif defined(__FreeBSD__)
|
||||
/* Defining this macro before including stdio.h is necessary in order to have
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _XCB_H
|
||||
#define _XCB_H
|
||||
#ifndef I3_XCB_H
|
||||
#define I3_XCB_H
|
||||
|
||||
/* from X11/keysymdef.h */
|
||||
#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
|
||||
#define _I3_INPUT
|
||||
#ifndef I3_INPUT
|
||||
#define I3_INPUT
|
||||
|
||||
#include <err.h>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _I3_NAGBAR
|
||||
#define _I3_NAGBAR
|
||||
#ifndef I3_NAGBAR
|
||||
#define I3_NAGBAR
|
||||
|
||||
#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
|
||||
# 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
|
||||
# chose an xft font, such as:
|
||||
# font xft:DejaVu Sans Mono 10
|
||||
# chose a FreeType font, such as:
|
||||
# font pango:DejaVu Sans Mono 10
|
||||
|
||||
# use Mouse+Mod1 to drag floating windows to their wanted position
|
||||
floating_modifier Mod1
|
||||
|
@ -29,6 +29,10 @@ bindsym Mod1+Shift+q kill
|
|||
|
||||
# start dmenu (a program launcher)
|
||||
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
|
||||
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
|
||||
# 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
|
||||
# chose an xft font, such as:
|
||||
# font xft:DejaVu Sans Mono 10
|
||||
# chose a FreeType font, such as:
|
||||
# font pango:DejaVu Sans Mono 10
|
||||
|
||||
# Use Mouse+$mod to drag floating windows to their wanted position
|
||||
floating_modifier $mod
|
||||
|
@ -30,6 +30,10 @@ bindcode $mod+Shift+24 kill
|
|||
|
||||
# start dmenu (a program launcher)
|
||||
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
|
||||
bindcode $mod+44 focus left
|
||||
|
@ -79,7 +83,7 @@ bindcode $mod+65 focus mode_toggle
|
|||
bindcode $mod+38 focus parent
|
||||
|
||||
# focus the child container
|
||||
#bindcode $mod+d focus child
|
||||
#bindsym $mod+d focus child
|
||||
|
||||
# switch to workspace
|
||||
bindcode $mod+10 workspace 1
|
||||
|
|
|
@ -10,8 +10,31 @@
|
|||
#ifndef CHILD_H_
|
||||
#define CHILD_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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.
|
||||
* 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;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
ALIGN_RIGHT
|
||||
} blockalign_t;
|
||||
|
||||
/* This data structure represents one JSON dictionary, multiple of these make
|
||||
* up one status line. */
|
||||
struct status_block {
|
||||
i3String *full_text;
|
||||
|
||||
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(). */
|
||||
uint32_t width;
|
||||
uint32_t x_offset;
|
||||
uint32_t x_append;
|
||||
|
||||
TAILQ_ENTRY(status_block) blocks;
|
||||
};
|
||||
|
@ -48,10 +60,11 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head;
|
|||
#include "outputs.h"
|
||||
#include "util.h"
|
||||
#include "workspaces.h"
|
||||
#include "mode.h"
|
||||
#include "trayclients.h"
|
||||
#include "xcb.h"
|
||||
#include "config.h"
|
||||
#include "libi3.h"
|
||||
#include "determine_json_version.h"
|
||||
#include "parse_json_header.h"
|
||||
|
||||
#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
|
||||
#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 */
|
||||
#define FREE(p) do { \
|
||||
if (p != NULL) { \
|
||||
|
|
|
@ -110,7 +110,7 @@ void reconfig_windows(void);
|
|||
* 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
|
||||
|
@ -118,4 +118,10 @@ void draw_bars(void);
|
|||
*/
|
||||
void redraw_bars(void);
|
||||
|
||||
/*
|
||||
* Set the current binding mode
|
||||
*
|
||||
*/
|
||||
void set_current_mode(struct mode *mode);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,19 +25,20 @@
|
|||
#include "common.h"
|
||||
|
||||
/* Global variables for child_*() */
|
||||
pid_t child_pid;
|
||||
i3bar_child child = { 0 };
|
||||
|
||||
/* stdin- and sigchild-watchers */
|
||||
ev_io *stdin_io;
|
||||
ev_child *child_sig;
|
||||
|
||||
/* JSON parser for stdin */
|
||||
bool first_line = true;
|
||||
bool plaintext = false;
|
||||
yajl_callbacks callbacks;
|
||||
yajl_handle parser;
|
||||
|
||||
typedef struct parser_ctx {
|
||||
/* True if one of the parsed blocks was urgent */
|
||||
bool has_urgent;
|
||||
|
||||
/* A copy of the last JSON map key. */
|
||||
char *last_map_key;
|
||||
|
||||
|
@ -69,6 +70,8 @@ void cleanup(void) {
|
|||
ev_child_stop(main_loop, 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;
|
||||
}
|
||||
|
||||
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
|
||||
static int stdin_string(void *context, const unsigned char *val, size_t len) {
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -132,6 +164,8 @@ static int stdin_end_map(void *context) {
|
|||
* i3bar doesn’t crash and the user gets an annoying message. */
|
||||
if (!new_block->full_text)
|
||||
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);
|
||||
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
|
||||
* in statusline
|
||||
* Helper function to read stdin
|
||||
*
|
||||
*/
|
||||
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 n = 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 */
|
||||
ELOG("stdin: received EOF\n");
|
||||
cleanup();
|
||||
draw_bars();
|
||||
return;
|
||||
draw_bars(false);
|
||||
*ret_buffer_len = -1;
|
||||
return NULL;
|
||||
}
|
||||
rec += n;
|
||||
|
||||
|
@ -185,51 +219,94 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
|
|||
}
|
||||
if (*buffer == '\0') {
|
||||
FREE(buffer);
|
||||
return;
|
||||
rec = -1;
|
||||
}
|
||||
*ret_buffer_len = rec;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char *json_input = buffer;
|
||||
if (first_line) {
|
||||
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. */
|
||||
plaintext = (determine_json_version(buffer, buffer_len, &consumed) == -1);
|
||||
if (plaintext) {
|
||||
/* 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);
|
||||
} else {
|
||||
json_input += consumed;
|
||||
rec -= consumed;
|
||||
}
|
||||
first_line = false;
|
||||
}
|
||||
if (!plaintext) {
|
||||
yajl_status status = yajl_parse(parser, json_input, rec);
|
||||
static void read_flat_input(char *buffer, int length) {
|
||||
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
||||
/* 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[length-1] == '\n' || buffer[length-1] == '\r')
|
||||
buffer[length-1] = '\0';
|
||||
else buffer[length] = '\0';
|
||||
first->full_text = i3string_from_utf8(buffer);
|
||||
}
|
||||
|
||||
static bool read_json_input(unsigned char *input, int length) {
|
||||
yajl_status status = yajl_parse(parser, input, length);
|
||||
bool has_urgent = false;
|
||||
#if YAJL_MAJOR >= 2
|
||||
if (status != yajl_status_ok) {
|
||||
if (status != yajl_status_ok) {
|
||||
#else
|
||||
if (status != yajl_status_ok && status != yajl_status_insufficient_data) {
|
||||
if (status != yajl_status_ok && status != yajl_status_insufficient_data) {
|
||||
#endif
|
||||
fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
|
||||
status, rec, json_input);
|
||||
}
|
||||
fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n",
|
||||
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 {
|
||||
struct status_block *first = TAILQ_FIRST(&statusline_head);
|
||||
/* 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);
|
||||
read_flat_input((char*)buffer, rec);
|
||||
}
|
||||
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) {
|
||||
ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
|
||||
child_pid,
|
||||
child.pid,
|
||||
watcher->rstatus);
|
||||
cleanup();
|
||||
}
|
||||
|
@ -255,7 +332,9 @@ void start_child(char *command) {
|
|||
/* Allocate a yajl parser which will be used to parse stdin. */
|
||||
memset(&callbacks, '\0', sizeof(yajl_callbacks));
|
||||
callbacks.yajl_map_key = stdin_map_key;
|
||||
callbacks.yajl_boolean = stdin_boolean;
|
||||
callbacks.yajl_string = stdin_string;
|
||||
callbacks.yajl_integer = stdin_integer;
|
||||
callbacks.yajl_start_array = stdin_start_array;
|
||||
callbacks.yajl_end_array = stdin_end_array;
|
||||
callbacks.yajl_start_map = stdin_start_map;
|
||||
|
@ -268,14 +347,13 @@ void start_child(char *command) {
|
|||
parser = yajl_alloc(&callbacks, NULL, &parser_context);
|
||||
#endif
|
||||
|
||||
child_pid = 0;
|
||||
if (command != NULL) {
|
||||
int fd[2];
|
||||
if (pipe(fd) == -1)
|
||||
err(EXIT_FAILURE, "pipe(fd)");
|
||||
|
||||
child_pid = fork();
|
||||
switch (child_pid) {
|
||||
child.pid = fork();
|
||||
switch (child.pid) {
|
||||
case -1:
|
||||
ELOG("Couldn't fork(): %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -298,12 +376,6 @@ void start_child(char *command) {
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -312,12 +384,12 @@ void start_child(char *command) {
|
|||
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
|
||||
|
||||
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);
|
||||
|
||||
/* We must cleanup, if the child unexpectedly terminates */
|
||||
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);
|
||||
|
||||
atexit(kill_child_at_exit);
|
||||
|
@ -328,9 +400,10 @@ void start_child(char *command) {
|
|||
*
|
||||
*/
|
||||
void kill_child_at_exit(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGCONT);
|
||||
kill(child_pid, SIGTERM);
|
||||
if (child.pid > 0) {
|
||||
if (child.cont_signal > 0 && child.stopped)
|
||||
kill(child.pid, child.cont_signal);
|
||||
kill(child.pid, SIGTERM);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,12 +413,12 @@ void kill_child_at_exit(void) {
|
|||
*
|
||||
*/
|
||||
void kill_child(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGCONT);
|
||||
kill(child_pid, SIGTERM);
|
||||
if (child.pid > 0) {
|
||||
if (child.cont_signal > 0 && child.stopped)
|
||||
kill(child.pid, child.cont_signal);
|
||||
kill(child.pid, SIGTERM);
|
||||
int status;
|
||||
waitpid(child_pid, &status, 0);
|
||||
child_pid = 0;
|
||||
waitpid(child.pid, &status, 0);
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
@ -355,8 +428,9 @@ void kill_child(void) {
|
|||
*
|
||||
*/
|
||||
void stop_child(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGSTOP);
|
||||
if (child.stop_signal > 0 && !child.stopped) {
|
||||
child.stopped = true;
|
||||
kill(child.pid, child.stop_signal);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -365,7 +439,8 @@ void stop_child(void) {
|
|||
*
|
||||
*/
|
||||
void cont_child(void) {
|
||||
if (child_pid != 0) {
|
||||
kill(child_pid, SIGCONT);
|
||||
if (child.cont_signal > 0 && child.stopped) {
|
||||
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) {
|
||||
DLOG("Got Workspace-Data!\n");
|
||||
parse_workspaces_json(reply);
|
||||
draw_bars();
|
||||
draw_bars(false);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -71,7 +71,7 @@ void got_output_reply(char *reply) {
|
|||
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 */
|
||||
handler_t event_handlers[] = {
|
||||
&got_workspace_event,
|
||||
&got_output_event
|
||||
&got_output_event,
|
||||
&got_mode_event
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -297,8 +309,8 @@ void destroy_connection(void) {
|
|||
*/
|
||||
void subscribe_events(void) {
|
||||
if (config.disable_ws) {
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\" ]");
|
||||
i3_send_msg(I3_IPC_MESSAGE_TYPE_SUBSCRIBE, "[ \"output\", \"mode\" ]");
|
||||
} 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);
|
||||
}
|
129
i3bar/src/xcb.c
129
i3bar/src/xcb.c
|
@ -74,6 +74,9 @@ ev_check *xcb_chk;
|
|||
ev_io *xcb_io;
|
||||
ev_io *xkb_io;
|
||||
|
||||
/* The name of current binding mode */
|
||||
static mode binding;
|
||||
|
||||
/* The parsed colors */
|
||||
struct xcb_colors_t {
|
||||
uint32_t bar_fg;
|
||||
|
@ -120,10 +123,31 @@ void refresh_statusline(void) {
|
|||
continue;
|
||||
|
||||
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 (TAILQ_NEXT(block, blocks) != NULL)
|
||||
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
|
||||
|
@ -144,8 +168,8 @@ void refresh_statusline(void) {
|
|||
|
||||
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
|
||||
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg);
|
||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x, 0, block->width);
|
||||
x += block->width;
|
||||
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 0, block->width);
|
||||
x += block->width + block->x_offset + block->x_append;
|
||||
|
||||
if (TAILQ_NEXT(block, blocks) != NULL) {
|
||||
/* This is not the last block, draw a separator. */
|
||||
|
@ -302,16 +326,24 @@ void handle_button(xcb_button_press_event_t *event) {
|
|||
}
|
||||
break;
|
||||
case 4:
|
||||
/* Mouse wheel down. We select the next ws */
|
||||
if (cur_ws != TAILQ_FIRST(walk->workspaces)) {
|
||||
cur_ws = TAILQ_PREV(cur_ws, ws_head, tailq);
|
||||
}
|
||||
/* Mouse wheel up. We select the previous ws, if any.
|
||||
* 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);
|
||||
break;
|
||||
case 5:
|
||||
/* Mouse wheel up. We select the previos ws */
|
||||
if (cur_ws != TAILQ_LAST(walk->workspaces, ws_head)) {
|
||||
cur_ws = TAILQ_NEXT(cur_ws, tailq);
|
||||
}
|
||||
/* Mouse wheel down. We select the next ws, if any.
|
||||
* 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);
|
||||
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
|
||||
* position */
|
||||
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 */
|
||||
configure_trayclients();
|
||||
draw_bars();
|
||||
draw_bars(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -624,13 +656,13 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
|
|||
xcb_unmap_window(xcb_connection, trayclient->win);
|
||||
trayclient->mapped = map_it;
|
||||
configure_trayclients();
|
||||
draw_bars();
|
||||
draw_bars(false);
|
||||
} else if (!trayclient->mapped && map_it) {
|
||||
/* need to map the window */
|
||||
xcb_map_window(xcb_connection, trayclient->win);
|
||||
trayclient->mapped = map_it;
|
||||
configure_trayclients();
|
||||
draw_bars();
|
||||
draw_bars(false);
|
||||
}
|
||||
free(xembedr);
|
||||
}
|
||||
|
@ -1398,12 +1430,15 @@ void reconfig_windows(void) {
|
|||
* Render the bars, with buttons and statusline
|
||||
*
|
||||
*/
|
||||
void draw_bars(void) {
|
||||
void draw_bars(bool unhide) {
|
||||
DLOG("Drawing Bars...\n");
|
||||
int i = 0;
|
||||
|
||||
refresh_statusline();
|
||||
|
||||
static char *last_urgent_ws = NULL;
|
||||
bool walks_away = true;
|
||||
|
||||
i3_output *outputs_walk;
|
||||
SLIST_FOREACH(outputs_walk, outputs, slist) {
|
||||
if (!outputs_walk->active) {
|
||||
|
@ -1460,8 +1495,6 @@ void draw_bars(void) {
|
|||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
bg_color = colors.urgent_ws_bg;
|
||||
border_color = colors.urgent_ws_border;
|
||||
has_urgent = true;
|
||||
unhide = true;
|
||||
if (!ws_walk->focused) {
|
||||
FREE(last_urgent_ws);
|
||||
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 vals_border[] = { border_color, border_color };
|
||||
|
@ -1520,16 +1551,56 @@ void draw_bars(void) {
|
|||
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);
|
||||
i += 10 + ws_walk->name_width + 1;
|
||||
|
||||
}
|
||||
|
||||
if (!has_urgent && !mod_pressed && walks_away) {
|
||||
FREE(last_urgent_ws);
|
||||
hide_bars();
|
||||
if (binding.name) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1554,3 +1625,13 @@ void redraw_bars(void) {
|
|||
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.
|
||||
*
|
||||
*/
|
||||
#ifndef _ALL_H
|
||||
#define _ALL_H
|
||||
#ifndef I3_ALL_H
|
||||
#define I3_ALL_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -79,6 +79,8 @@
|
|||
#include "scratchpad.h"
|
||||
#include "commands.h"
|
||||
#include "commands_parser.h"
|
||||
#include "config_directives.h"
|
||||
#include "config_parser.h"
|
||||
#include "fake_outputs.h"
|
||||
#include "display_version.h"
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* assignments.c: Assignments for specific windows (for_window).
|
||||
*
|
||||
*/
|
||||
#ifndef _ASSIGNMENTS_H
|
||||
#define _ASSIGNMENTS_H
|
||||
#ifndef I3_ASSIGNMENTS_H
|
||||
#define I3_ASSIGNMENTS_H
|
||||
|
||||
/**
|
||||
* Checks the list of assignments for the given window and runs all matching
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* click.c: Button press (mouse click) events.
|
||||
*
|
||||
*/
|
||||
#ifndef _CLICK_H
|
||||
#define _CLICK_H
|
||||
#ifndef I3_CLICK_H
|
||||
#define I3_CLICK_H
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*
|
||||
*/
|
||||
#ifndef _CMDPARSE_H
|
||||
#define _CMDPARSE_H
|
||||
#ifndef I3_CMDPARSE_H
|
||||
#define I3_CMDPARSE_H
|
||||
|
||||
char *parse_cmd(const char *new);
|
||||
|
||||
|
|
|
@ -7,26 +7,14 @@
|
|||
* commands.c: all command functions (see commands_parser.c)
|
||||
*
|
||||
*/
|
||||
#ifndef _COMMANDS_H
|
||||
#define _COMMANDS_H
|
||||
#ifndef I3_COMMANDS_H
|
||||
#define I3_COMMANDS_H
|
||||
|
||||
#include "commands_parser.h"
|
||||
|
||||
/** The beginning of the prototype for every cmd_ function. */
|
||||
#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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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>'.
|
||||
*
|
||||
|
@ -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'.
|
||||
*
|
||||
*/
|
||||
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>'.
|
||||
|
@ -152,7 +146,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name);
|
|||
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);
|
||||
|
@ -212,25 +206,25 @@ void cmd_layout(I3_CMD, char *layout_str);
|
|||
void cmd_layout_toggle(I3_CMD, char *toggle_mode);
|
||||
|
||||
/**
|
||||
* Implementaiton of 'exit'.
|
||||
* Implementation of 'exit'.
|
||||
*
|
||||
*/
|
||||
void cmd_exit(I3_CMD);
|
||||
|
||||
/**
|
||||
* Implementaiton of 'reload'.
|
||||
* Implementation of 'reload'.
|
||||
*
|
||||
*/
|
||||
void cmd_reload(I3_CMD);
|
||||
|
||||
/**
|
||||
* Implementaiton of 'restart'.
|
||||
* Implementation of 'restart'.
|
||||
*
|
||||
*/
|
||||
void cmd_restart(I3_CMD);
|
||||
|
||||
/**
|
||||
* Implementaiton of 'open'.
|
||||
* Implementation of 'open'.
|
||||
*
|
||||
*/
|
||||
void cmd_open(I3_CMD);
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* commands.c: all command functions (see commands_parser.c)
|
||||
*
|
||||
*/
|
||||
#ifndef _COMMANDS_PARSER_H
|
||||
#define _COMMANDS_PARSER_H
|
||||
#ifndef I3_COMMANDS_PARSER_H
|
||||
#define I3_COMMANDS_PARSER_H
|
||||
|
||||
#include <yajl/yajl_gen.h>
|
||||
|
||||
|
@ -27,6 +27,11 @@ struct CommandResult {
|
|||
|
||||
/* Whether the command requires calling 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);
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
* …).
|
||||
*
|
||||
*/
|
||||
#ifndef _CON_H
|
||||
#define _CON_H
|
||||
#ifndef I3_CON_H
|
||||
#define I3_CON_H
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Returns true if the container is internal, such as __i3_scratch
|
||||
*
|
||||
*/
|
||||
bool con_is_internal(Con *con);
|
||||
|
||||
/**
|
||||
* Returns true if the node is floating.
|
||||
*
|
||||
|
@ -244,7 +262,7 @@ int con_border_style(Con *con);
|
|||
* 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
|
||||
|
@ -293,4 +311,23 @@ Rect con_minimum_size(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
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
* mode).
|
||||
*
|
||||
*/
|
||||
#ifndef _CONFIG_H
|
||||
#define _CONFIG_H
|
||||
#ifndef I3_CONFIG_H
|
||||
#define I3_CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "queue.h"
|
||||
|
@ -24,6 +24,8 @@ extern char *current_configpath;
|
|||
extern Config config;
|
||||
extern SLIST_HEAD(modes_head, Mode) modes;
|
||||
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
|
||||
|
@ -98,6 +100,7 @@ struct Config {
|
|||
int default_layout;
|
||||
int container_stack_limit;
|
||||
int container_stack_limit_value;
|
||||
int default_border_width;
|
||||
|
||||
/** Default orientation for new containers */
|
||||
int default_orientation;
|
||||
|
@ -149,6 +152,13 @@ struct Config {
|
|||
* between two workspaces. */
|
||||
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. */
|
||||
border_style_t default_border;
|
||||
|
||||
|
@ -181,8 +191,15 @@ struct Config {
|
|||
|
||||
/** What should happen when a new popup is opened during fullscreen mode */
|
||||
enum {
|
||||
PDF_LEAVE_FULLSCREEN = 0,
|
||||
PDF_IGNORE = 1
|
||||
/* display (and focus) the popup when it belongs to the fullscreen
|
||||
* 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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
*/
|
||||
#ifndef _DATA_H
|
||||
#define _DATA_H
|
||||
#ifndef I3_DATA_H
|
||||
#define I3_DATA_H
|
||||
|
||||
#define SN_API_NOT_YET_FROZEN 1
|
||||
#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 { 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
|
||||
* only this specific window or the whole X11 client */
|
||||
|
@ -441,8 +441,6 @@ struct Assignment {
|
|||
*/
|
||||
struct Con {
|
||||
bool mapped;
|
||||
/** whether this is a split container or not */
|
||||
bool split;
|
||||
enum {
|
||||
CT_ROOT = 0,
|
||||
CT_OUTPUT = 1,
|
||||
|
@ -486,6 +484,7 @@ struct Con {
|
|||
|
||||
/* the x11 border pixel attribute */
|
||||
int border_width;
|
||||
int current_border_width;
|
||||
|
||||
/* minimum increment size specified for the window (in pixels) */
|
||||
int width_increment;
|
||||
|
@ -497,6 +496,9 @@ struct Con {
|
|||
* inside this container (if any) sets the urgency hint, for example. */
|
||||
bool urgent;
|
||||
|
||||
/* timer used for disabling urgency */
|
||||
struct ev_timer *urgency_timer;
|
||||
|
||||
/* ids/pixmap/graphics context for the frame window */
|
||||
xcb_window_t frame;
|
||||
xcb_pixmap_t pixmap;
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* events. This code is from xcb-util.
|
||||
*
|
||||
*/
|
||||
#ifndef _DEBUG_H
|
||||
#define _DEBUG_H
|
||||
#ifndef I3_DEBUG_H
|
||||
#define I3_DEBUG_H
|
||||
|
||||
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
|
||||
* i3 --moreversion.
|
||||
*/
|
||||
#ifndef _DISPLAY_VERSION_H
|
||||
#define _DISPLAY_VERSION_H
|
||||
#ifndef I3_DISPLAY_VERSION_H
|
||||
#define I3_DISPLAY_VERSION_H
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef _EWMH_C
|
||||
#define _EWMH_C
|
||||
#ifndef I3_EWMH_C
|
||||
#define I3_EWMH_C
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef _FAKE_OUTPUTS_H
|
||||
#define _FAKE_OUTPUTS_H
|
||||
#ifndef I3_FAKE_OUTPUTS_H
|
||||
#define I3_FAKE_OUTPUTS_H
|
||||
|
||||
/**
|
||||
* Creates outputs according to the given specification.
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* floating.c: Floating windows.
|
||||
*
|
||||
*/
|
||||
#ifndef _FLOATING_H
|
||||
#define _FLOATING_H
|
||||
#ifndef I3_FLOATING_H
|
||||
#define I3_FLOATING_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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
/**
|
||||
* 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,
|
||||
xcb_window_t confine_to, border_t border, callback_t callback,
|
||||
const void *extra);
|
||||
xcb_window_t confine_to, border_t border, int cursor,
|
||||
callback_t callback, const void *extra);
|
||||
|
||||
/**
|
||||
* Repositions the CT_FLOATING_CON to have the coordinates specified by
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* …).
|
||||
*
|
||||
*/
|
||||
#ifndef _HANDLERS_H
|
||||
#define _HANDLERS_H
|
||||
#ifndef I3_HANDLERS_H
|
||||
#define I3_HANDLERS_H
|
||||
|
||||
#include <xcb/randr.h>
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* i3.h: global variables that are used all over i3.
|
||||
*
|
||||
*/
|
||||
#ifndef _I3_H
|
||||
#define _I3_H
|
||||
#ifndef I3_I3_H
|
||||
#define I3_I3_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* for the IPC interface to i3 (see docs/ipc for more information).
|
||||
*
|
||||
*/
|
||||
#ifndef _I3_IPC_H
|
||||
#define _I3_IPC_H
|
||||
#ifndef I3_I3_IPC_H
|
||||
#define I3_I3_IPC_H
|
||||
|
||||
/*
|
||||
* Messages from clients to i3
|
||||
|
@ -84,4 +84,7 @@
|
|||
/* The output event will be triggered upon changes in the output list */
|
||||
#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
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
|
||||
*
|
||||
*/
|
||||
#ifndef _IPC_H
|
||||
#define _IPC_H
|
||||
#ifndef I3_IPC_H
|
||||
#define I3_IPC_H
|
||||
|
||||
#include <ev.h>
|
||||
#include <stdbool.h>
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* key_press.c: key press handler
|
||||
*
|
||||
*/
|
||||
#ifndef _KEY_PRESS_H
|
||||
#define _KEY_PRESS_H
|
||||
#ifndef I3_KEY_PRESS_H
|
||||
#define I3_KEY_PRESS_H
|
||||
|
||||
/**
|
||||
* 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, …
|
||||
*
|
||||
*/
|
||||
#ifndef _LIBI3_H
|
||||
#define _LIBI3_H
|
||||
#ifndef I3_LIBI3_H
|
||||
#define I3_LIBI3_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* restart.
|
||||
*
|
||||
*/
|
||||
#ifndef _LOAD_LAYOUT_H
|
||||
#define _LOAD_LAYOUT_H
|
||||
#ifndef I3_LOAD_LAYOUT_H
|
||||
#define I3_LOAD_LAYOUT_H
|
||||
|
||||
void tree_append_json(const char *filename);
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* log.c: Logging functions.
|
||||
*
|
||||
*/
|
||||
#ifndef _LOG_H
|
||||
#define _LOG_H
|
||||
#ifndef I3_LOG_H
|
||||
#define I3_LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* manage.c: Initially managing new windows (or existing ones on restart).
|
||||
*
|
||||
*/
|
||||
#ifndef _MANAGE_H
|
||||
#define _MANAGE_H
|
||||
#ifndef I3_MANAGE_H
|
||||
#define I3_MANAGE_H
|
||||
|
||||
#include "data.h"
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
* match_matches_window() to find the windows affected by this command.
|
||||
*
|
||||
*/
|
||||
#ifndef _MATCH_H
|
||||
#define _MATCH_H
|
||||
#ifndef I3_MATCH_H
|
||||
#define I3_MATCH_H
|
||||
|
||||
/*
|
||||
* Initializes the Match data structure. This function is necessary because the
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* move.c: Moving containers into some direction.
|
||||
*
|
||||
*/
|
||||
#ifndef _MOVE_H
|
||||
#define _MOVE_H
|
||||
#ifndef I3_MOVE_H
|
||||
#define I3_MOVE_H
|
||||
|
||||
/**
|
||||
* Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* output.c: Output (monitor) related functions.
|
||||
*
|
||||
*/
|
||||
#ifndef _OUTPUT_H
|
||||
#define _OUTPUT_H
|
||||
#ifndef I3_OUTPUT_H
|
||||
#define I3_OUTPUT_H
|
||||
|
||||
/**
|
||||
* Returns the output container below the given output container.
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
* (take your time to read it completely, it answers all questions).
|
||||
*
|
||||
*/
|
||||
#ifndef _RANDR_H
|
||||
#define _RANDR_H
|
||||
#ifndef I3_RANDR_H
|
||||
#define I3_RANDR_H
|
||||
|
||||
#include "data.h"
|
||||
#include <xcb/randr.h>
|
||||
|
@ -18,6 +18,11 @@
|
|||
TAILQ_HEAD(outputs_head, xoutput);
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* regex.c: Interface to libPCRE (perl compatible regular expressions).
|
||||
*
|
||||
*/
|
||||
#ifndef _REGEX_H
|
||||
#define _REGEX_H
|
||||
#ifndef I3_REGEX_H
|
||||
#define I3_REGEX_H
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef _RENDER_H
|
||||
#define _RENDER_H
|
||||
#ifndef I3_RENDER_H
|
||||
#define I3_RENDER_H
|
||||
|
||||
/**
|
||||
* "Renders" the given container (and its children), meaning that all rects are
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* resize.c: Interactive resizing.
|
||||
*
|
||||
*/
|
||||
#ifndef _RESIZE_H
|
||||
#define _RESIZE_H
|
||||
#ifndef I3_RESIZE_H
|
||||
#define I3_RESIZE_H
|
||||
|
||||
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)
|
||||
*
|
||||
*/
|
||||
#ifndef _SCRATCHPAD_H
|
||||
#define _SCRATCHPAD_H
|
||||
#ifndef I3_SCRATCHPAD_H
|
||||
#define I3_SCRATCHPAD_H
|
||||
|
||||
/**
|
||||
* Moves the specified window to the __i3_scratch workspace, making it floating
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* default (ringbuffer for storing the debug log).
|
||||
*
|
||||
*/
|
||||
#ifndef _I3_SHMLOG_H
|
||||
#define _I3_SHMLOG_H
|
||||
#ifndef I3_I3_SHMLOG_H
|
||||
#define I3_I3_SHMLOG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
* to restart inplace).
|
||||
*
|
||||
*/
|
||||
#ifndef _SIGHANDLER_H
|
||||
#define _SIGHANDLER_H
|
||||
#ifndef I3_SIGHANDLER_H
|
||||
#define I3_SIGHANDLER_H
|
||||
|
||||
/**
|
||||
* Setup signal handlers to safely handle SIGSEGV and SIGFPE
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
* the appropriate workspace.
|
||||
*
|
||||
*/
|
||||
#ifndef _STARTUP_H
|
||||
#define _STARTUP_H
|
||||
#ifndef I3_STARTUP_H
|
||||
#define I3_STARTUP_H
|
||||
|
||||
#define SN_API_NOT_YET_FROZEN 1
|
||||
#include <libsn/sn-monitor.h>
|
||||
|
@ -31,12 +31,27 @@
|
|||
*/
|
||||
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
|
||||
*
|
||||
*/
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef _TREE_H
|
||||
#define _TREE_H
|
||||
#ifndef I3_TREE_H
|
||||
#define I3_TREE_H
|
||||
|
||||
extern Con *croot;
|
||||
/* TODO: i am not sure yet how much access to the focused container should
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* also libi3).
|
||||
*
|
||||
*/
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
#ifndef I3_UTIL_H
|
||||
#define I3_UTIL_H
|
||||
|
||||
#include <err.h>
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* window.c: Updates window attributes (X11 hints/properties).
|
||||
*
|
||||
*/
|
||||
#ifndef _WINDOW_H
|
||||
#define _WINDOW_H
|
||||
#ifndef I3_WINDOW_H
|
||||
#define I3_WINDOW_H
|
||||
|
||||
/**
|
||||
* Updates the WM_CLASS (consisting of the class and instance) for the
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* workspaces.
|
||||
*
|
||||
*/
|
||||
#ifndef _WORKSPACE_H
|
||||
#define _WORKSPACE_H
|
||||
#ifndef I3_WORKSPACE_H
|
||||
#define I3_WORKSPACE_H
|
||||
|
||||
#include "data.h"
|
||||
#include "tree.h"
|
||||
|
@ -95,6 +95,12 @@ Con* workspace_prev_on_output(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
|
||||
/**
|
||||
|
@ -168,4 +174,11 @@ void ws_force_orientation(Con *ws, orientation_t orientation);
|
|||
*/
|
||||
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
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* render.c). Basically a big state machine.
|
||||
*
|
||||
*/
|
||||
#ifndef _X_H
|
||||
#define _X_H
|
||||
#ifndef I3_X_H
|
||||
#define I3_X_H
|
||||
|
||||
/** Stores the X11 window ID of the currently focused window */
|
||||
extern xcb_window_t focused_id;
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* xcb.c: Helper functions for easier usage of XCB
|
||||
*
|
||||
*/
|
||||
#ifndef _XCB_H
|
||||
#define _XCB_H
|
||||
#ifndef I3_XCB_H
|
||||
#define I3_XCB_H
|
||||
|
||||
#include "data.h"
|
||||
#include "xcursor.h"
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
* older versions.
|
||||
*
|
||||
*/
|
||||
#ifndef _XCB_COMPAT_H
|
||||
#define _XCB_COMPAT_H
|
||||
#ifndef I3_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 xcb_get_wm_protocols
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* xcursor.c: libXcursor support for themed cursors.
|
||||
*
|
||||
*/
|
||||
#ifndef _XCURSOR_CURSOR_H
|
||||
#define _XCURSOR_CURSOR_H
|
||||
#ifndef I3_XCURSOR_CURSOR_H
|
||||
#define I3_XCURSOR_CURSOR_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
|
@ -16,7 +16,12 @@ enum xcursor_cursor_t {
|
|||
XCURSOR_CURSOR_POINTER = 0,
|
||||
XCURSOR_CURSOR_RESIZE_HORIZONTAL,
|
||||
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_MOVE,
|
||||
XCURSOR_CURSOR_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
* driver which does not support RandR in 2011 *sigh*.
|
||||
*
|
||||
*/
|
||||
#ifndef _XINERAMA_H
|
||||
#define _XINERAMA_H
|
||||
#ifndef I3_XINERAMA_H
|
||||
#define I3_XINERAMA_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
|
||||
/* 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:");
|
||||
if (load_pango_font(&font, pattern))
|
||||
return font;
|
||||
|
|
|
@ -7,7 +7,7 @@ template::[header-declarations]
|
|||
<refentrytitle>{mantitle}</refentrytitle>
|
||||
<manvolnum>{manvolnum}</manvolnum>
|
||||
<refmiscinfo class="source">i3</refmiscinfo>
|
||||
<refmiscinfo class="version">4.3</refmiscinfo>
|
||||
<refmiscinfo class="version">4.4</refmiscinfo>
|
||||
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
|
||||
</refmeta>
|
||||
<refnamediv>
|
||||
|
|
|
@ -24,7 +24,8 @@ workspaces.
|
|||
|
||||
get_outputs::
|
||||
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::
|
||||
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
|
||||
|
||||
A2X = a2x
|
||||
POD2MAN = pod2man
|
||||
|
||||
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/i3bar.1 \
|
||||
man/i3-msg.1 \
|
||||
|
@ -17,14 +19,21 @@ MANS_1 = \
|
|||
man/i3-sensible-terminal.1 \
|
||||
man/i3-dump-log.1
|
||||
|
||||
MANS_POD = \
|
||||
man/i3-dmenu-desktop.1
|
||||
|
||||
MANS = \
|
||||
$(MANS_1)
|
||||
$(MANS_ASCIIDOC) \
|
||||
$(MANS_POD)
|
||||
|
||||
mans: $(MANS)
|
||||
|
||||
$(MANS_1): %.1: %.man man/asciidoc.conf
|
||||
$(MANS_ASCIIDOC): %.1: %.man man/asciidoc.conf
|
||||
$(A2X_MAN_CALL)
|
||||
|
||||
$(MANS_POD): %.1: i3-dmenu-desktop
|
||||
$(POD2MAN_CALL)
|
||||
|
||||
clean-mans:
|
||||
for file in $(notdir $(MANS)); \
|
||||
do \
|
||||
|
|
|
@ -61,10 +61,20 @@ state EXEC:
|
|||
command = string
|
||||
-> call cmd_exec($nosn, $command)
|
||||
|
||||
# border normal|none|1pixel|toggle
|
||||
# border normal|none|1pixel|toggle|1pixel
|
||||
state BORDER:
|
||||
border_style = 'normal', 'none', '1pixel', 'toggle'
|
||||
-> call cmd_border($border_style)
|
||||
border_style = 'normal', 'pixel'
|
||||
-> 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 toggle [split|all]
|
||||
|
@ -185,17 +195,30 @@ state RESIZE_TILING_OR:
|
|||
-> call cmd_resize($way, $direction, $resize_px, $resize_ppt)
|
||||
|
||||
# rename workspace <name> to <name>
|
||||
# rename workspace to <name>
|
||||
state RENAME:
|
||||
'workspace'
|
||||
-> RENAME_WORKSPACE
|
||||
|
||||
state RENAME_WORKSPACE:
|
||||
old_name = 'to'
|
||||
-> RENAME_WORKSPACE_LIKELY_TO
|
||||
old_name = word
|
||||
-> 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:
|
||||
'to'
|
||||
->
|
||||
-> RENAME_WORKSPACE_NEW_NAME
|
||||
|
||||
state RENAME_WORKSPACE_NEW_NAME:
|
||||
end
|
||||
-> call cmd_rename_workspace(NULL, "to")
|
||||
new_name = string
|
||||
-> call cmd_rename_workspace($old_name, $new_name)
|
||||
|
||||
|
@ -243,6 +266,8 @@ state MOVE_WORKSPACE:
|
|||
-> MOVE_WORKSPACE_TO_OUTPUT
|
||||
workspace = 'next', 'prev', 'next_on_output', 'prev_on_output', 'current'
|
||||
-> call cmd_move_con_to_workspace($workspace)
|
||||
'back_and_forth'
|
||||
-> call cmd_move_con_to_workspace_back_and_forth()
|
||||
'number'
|
||||
-> MOVE_WORKSPACE_NUMBER
|
||||
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
|
||||
|
||||
syntax region i3specLiteral start=/'/ end=/'/
|
||||
syntax keyword i3specToken string word end
|
||||
syntax keyword i3specToken string word number end
|
||||
highlight link i3specLiteral String
|
||||
highlight link i3specToken String
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ EOL (\r?\n)
|
|||
%s OUTPUT_COND
|
||||
%s FOR_WINDOW_COND
|
||||
%s EAT_WHITESPACE
|
||||
%s BORDER_WIDTH
|
||||
|
||||
%x BUFFER_LINE
|
||||
%x BAR
|
||||
|
@ -171,6 +172,7 @@ EOL (\r?\n)
|
|||
}
|
||||
<ASSIGN_TARGET_COND>[ \t]*→[ \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>. { 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; }
|
||||
|
@ -200,9 +202,10 @@ auto { return TOK_AUTO; }
|
|||
workspace_layout { return TOK_WORKSPACE_LAYOUT; }
|
||||
new_window { return TOKNEWWINDOW; }
|
||||
new_float { return TOKNEWFLOAT; }
|
||||
normal { return TOK_NORMAL; }
|
||||
normal { yy_push_state(BORDER_WIDTH); return TOK_NORMAL; }
|
||||
none { return TOK_NONE; }
|
||||
1pixel { return TOK_1PIXEL; }
|
||||
pixel { yy_push_state(BORDER_WIDTH); return TOK_PIXEL; }
|
||||
hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; }
|
||||
both { return TOK_BOTH; }
|
||||
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; }
|
||||
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; }
|
||||
popup_during_fullscreen { return TOK_POPUP_DURING_FULLSCREEN; }
|
||||
ignore { return TOK_IGNORE; }
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "all.h"
|
||||
|
||||
bool force_old_config_parser = false;
|
||||
|
||||
static pid_t configerror_pid = -1;
|
||||
|
||||
static Match current_match;
|
||||
|
@ -108,6 +110,7 @@ static int detect_version(char *buf) {
|
|||
strncasecmp(bind, "focus down", strlen("focus down")) == 0 ||
|
||||
strncasecmp(bind, "border normal", strlen("border normal")) == 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, "--no-startup-id", strlen("--no-startup-id")) == 0 ||
|
||||
strncasecmp(bind, "bar", strlen("bar")) == 0) {
|
||||
|
@ -624,15 +627,20 @@ void parse_file(const char *f) {
|
|||
}
|
||||
}
|
||||
|
||||
/* now lex/parse it */
|
||||
yy_scan_string(new);
|
||||
|
||||
context = scalloc(sizeof(struct context));
|
||||
context->filename = f;
|
||||
|
||||
if (yyparse() != 0) {
|
||||
fprintf(stderr, "Could not parse configfile\n");
|
||||
exit(1);
|
||||
if (force_old_config_parser) {
|
||||
/* now lex/parse it */
|
||||
yy_scan_string(new);
|
||||
if (yyparse() != 0) {
|
||||
fprintf(stderr, "Could not parse configfile\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
struct ConfigResult *config_output = parse_config(new, context);
|
||||
yajl_gen_free(config_output->json_gen);
|
||||
}
|
||||
|
||||
check_for_duplicate_bindings(context);
|
||||
|
@ -668,7 +676,8 @@ void parse_file(const char *f) {
|
|||
start_configerror_nagbar(f);
|
||||
}
|
||||
|
||||
yylex_destroy();
|
||||
if (force_old_config_parser)
|
||||
yylex_destroy();
|
||||
FREE(context->line_copy);
|
||||
free(context);
|
||||
FREE(font_pattern);
|
||||
|
@ -728,6 +737,7 @@ void parse_file(const char *f) {
|
|||
%token <color> TOKCOLOR
|
||||
%token TOKARROW "→"
|
||||
%token TOKMODE "mode"
|
||||
%token TOK_TIME_MS "ms"
|
||||
%token TOK_BAR "bar"
|
||||
%token TOK_ORIENTATION "default_orientation"
|
||||
%token TOK_HORIZ "horizontal"
|
||||
|
@ -738,6 +748,7 @@ void parse_file(const char *f) {
|
|||
%token TOKNEWFLOAT "new_float"
|
||||
%token TOK_NORMAL "normal"
|
||||
%token TOK_NONE "none"
|
||||
%token TOK_PIXEL "pixel"
|
||||
%token TOK_1PIXEL "1pixel"
|
||||
%token TOK_HIDE_EDGE_BORDERS "hide_edge_borders"
|
||||
%token TOK_BOTH "both"
|
||||
|
@ -746,6 +757,7 @@ void parse_file(const char *f) {
|
|||
%token TOK_FORCE_XINERAMA "force_xinerama"
|
||||
%token TOK_FAKE_OUTPUTS "fake_outputs"
|
||||
%token TOK_WORKSPACE_AUTO_BAF "workspace_auto_back_and_forth"
|
||||
%token TOK_WORKSPACE_URGENCY_TIMER "force_display_urgency_hint"
|
||||
%token TOKWORKSPACEBAR "workspace_bar"
|
||||
%token TOK_DEFAULT "default"
|
||||
%token TOK_STACKING "stacking"
|
||||
|
@ -816,9 +828,11 @@ void parse_file(const char *f) {
|
|||
%type <number> bar_mode_mode
|
||||
%type <number> bar_modifier_modifier
|
||||
%type <number> optional_no_startup_id
|
||||
%type <number> optional_border_width
|
||||
%type <number> optional_release
|
||||
%type <string> command
|
||||
%type <string> word_or_number
|
||||
%type <string> duration
|
||||
%type <string> qstring_or_number
|
||||
%type <string> optional_workspace_name
|
||||
%type <string> workspace_name
|
||||
|
@ -848,6 +862,7 @@ line:
|
|||
| force_focus_wrapping
|
||||
| force_xinerama
|
||||
| fake_outputs
|
||||
| force_display_urgency_hint
|
||||
| workspace_back_and_forth
|
||||
| workspace_bar
|
||||
| workspace
|
||||
|
@ -1052,6 +1067,11 @@ word_or_number:
|
|||
}
|
||||
;
|
||||
|
||||
duration:
|
||||
NUMBER { sasprintf(&$$, "%d", $1); }
|
||||
| NUMBER TOK_TIME_MS { sasprintf(&$$, "%d", $1); }
|
||||
;
|
||||
|
||||
mode:
|
||||
TOKMODE QUOTEDSTRING '{' modelines '}'
|
||||
{
|
||||
|
@ -1471,9 +1491,27 @@ new_float:
|
|||
;
|
||||
|
||||
border_style:
|
||||
TOK_NORMAL { $$ = BS_NORMAL; }
|
||||
| TOK_NONE { $$ = BS_NONE; }
|
||||
| TOK_1PIXEL { $$ = BS_1PIXEL; }
|
||||
TOK_NORMAL optional_border_width
|
||||
{
|
||||
/* 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:
|
||||
|
@ -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:
|
||||
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:
|
||||
/* empty */ { $$ = false; }
|
||||
| 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("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 */
|
||||
if (con->parent->type == CT_DOCKAREA)
|
||||
goto done;
|
||||
|
@ -207,21 +223,13 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* 2: focus this con. 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 != focused_workspace)
|
||||
workspace_show(ws);
|
||||
focused_id = XCB_NONE;
|
||||
/* 2: focus this con. */
|
||||
con_focus(con);
|
||||
|
||||
/* 3: For floating containers, we also want to raise them on click.
|
||||
* We will skip handling events on floating cons in fullscreen mode */
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
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");
|
||||
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
|
||||
xcb_flush(conn);
|
||||
|
|
259
src/commands.c
259
src/commands.c
|
@ -38,7 +38,6 @@
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
static owindows_head owindows;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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)
|
||||
output = get_output_most(D_RIGHT, current_output);
|
||||
} 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)
|
||||
output = get_output_most(D_LEFT, current_output);
|
||||
} 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)
|
||||
output = get_output_most(D_DOWN, current_output);
|
||||
} 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)
|
||||
output = get_output_most(D_UP, current_output);
|
||||
} 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 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
|
||||
// messages on parser errors.
|
||||
#if 0
|
||||
|
@ -199,6 +221,20 @@ void cmd_MIGRATION_start_nagbar(void) {
|
|||
* 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
|
||||
* 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 wasn't specified and we don't have any window focused. */
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -400,6 +437,38 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
|
|||
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>'.
|
||||
*
|
||||
|
@ -421,9 +490,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
|||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
|
||||
ELOG("No window to move, you have focused a workspace.\n");
|
||||
else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
|
||||
!con_has_children(focused)) {
|
||||
ysuccess(false);
|
||||
return;
|
||||
}
|
||||
|
@ -432,6 +500,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
|
|||
/* get the workspace */
|
||||
Con *ws = workspace_get(name, NULL);
|
||||
|
||||
ws = maybe_auto_back_and_forth_workspace(ws);
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
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) {
|
||||
|
@ -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 wasn't specified and we don't have any window focused. */
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -469,8 +540,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
|||
if (parsed_num == LONG_MIN ||
|
||||
parsed_num == LONG_MAX ||
|
||||
parsed_num < 0 ||
|
||||
*endptr != '\0') {
|
||||
LOG("Could not parse \"%s\" as a number.\n", which);
|
||||
endptr == which) {
|
||||
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||
y(map_open);
|
||||
ystr("success");
|
||||
y(bool, false);
|
||||
|
@ -489,6 +560,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
|
|||
workspace = workspace_get(which, NULL);
|
||||
}
|
||||
|
||||
workspace = maybe_auto_back_and_forth_workspace(workspace);
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
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) {
|
||||
LOG("floating resize\n");
|
||||
Rect old_rect = floating_con->rect;
|
||||
|
||||
if (strcmp(direction, "up") == 0) {
|
||||
floating_con->rect.y -= px;
|
||||
floating_con->rect.height += px;
|
||||
} else if (strcmp(direction, "down") == 0 || strcmp(direction, "height") == 0) {
|
||||
floating_con->rect.height += px;
|
||||
} else if (strcmp(direction, "left") == 0) {
|
||||
floating_con->rect.x -= px;
|
||||
floating_con->rect.width += px;
|
||||
} else {
|
||||
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");
|
||||
/* get the appropriate current container (skip stacked/tabbed cons) */
|
||||
Con *current = focused;
|
||||
Con *other = NULL;
|
||||
double percentage = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
/* get the appropriate current container (skip stacked/tabbed cons) */
|
||||
Con *current = focused;
|
||||
while (current->parent->layout == L_STACKED ||
|
||||
current->parent->layout == L_TABBED)
|
||||
current = current->parent;
|
||||
|
@ -697,17 +781,22 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
|
|||
ppt *= -1;
|
||||
}
|
||||
|
||||
Con *floating_con;
|
||||
if ((floating_con = con_inside_floating(focused))) {
|
||||
cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
|
||||
} else {
|
||||
if (strcmp(direction, "width") == 0 ||
|
||||
strcmp(direction, "height") == 0) {
|
||||
if (!cmd_resize_tiling_width_height(current_match, cmd_output, way, direction, ppt))
|
||||
return;
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
||||
owindow *current;
|
||||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
Con *floating_con;
|
||||
if ((floating_con = con_inside_floating(current->con))) {
|
||||
cmd_resize_floating(current_match, cmd_output, way, direction, floating_con, px);
|
||||
} else {
|
||||
if (!cmd_resize_tiling_direction(current_match, cmd_output, way, direction, ppt))
|
||||
return;
|
||||
if (strcmp(direction, "width") == 0 ||
|
||||
strcmp(direction, "height") == 0) {
|
||||
if (!cmd_resize_tiling_width_height(current_match, cmd_output, current->con, way, direction, ppt))
|
||||
return;
|
||||
} else {
|
||||
if (!cmd_resize_tiling_direction(current_match, cmd_output, current->con, way, direction, ppt))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
DLOG("border style should be changed to %s\n", border_style_str);
|
||||
void cmd_border(I3_CMD, char *border_style_str, char *border_width ) {
|
||||
DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width);
|
||||
owindow *current;
|
||||
|
||||
HANDLE_EMPTY_MATCH;
|
||||
|
@ -729,23 +818,39 @@ void cmd_border(I3_CMD, char *border_style_str) {
|
|||
TAILQ_FOREACH(current, &owindows, owindows) {
|
||||
DLOG("matching: %p / %s\n", current->con, current->con->name);
|
||||
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) {
|
||||
border_style++;
|
||||
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 {
|
||||
if (strcmp(border_style_str, "normal") == 0)
|
||||
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;
|
||||
else if (strcmp(border_style_str, "1pixel") == 0)
|
||||
border_style = BS_1PIXEL;
|
||||
else {
|
||||
ELOG("BUG: called with border_style=%s\n", border_style_str);
|
||||
ysuccess(false);
|
||||
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;
|
||||
|
@ -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) {
|
||||
|
@ -818,8 +923,8 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
|||
if (parsed_num == LONG_MIN ||
|
||||
parsed_num == LONG_MAX ||
|
||||
parsed_num < 0 ||
|
||||
*endptr != '\0') {
|
||||
LOG("Could not parse \"%s\" as a number.\n", which);
|
||||
endptr == which) {
|
||||
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
|
||||
y(map_open);
|
||||
ystr("success");
|
||||
y(bool, false);
|
||||
|
@ -838,8 +943,6 @@ void cmd_workspace_number(I3_CMD, char *which) {
|
|||
if (!workspace) {
|
||||
LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
|
||||
ysuccess(true);
|
||||
/* terminate the which string after the endposition of the number */
|
||||
*endptr = '\0';
|
||||
workspace_show_by_name(which);
|
||||
cmd_output->needs_tree_render = true;
|
||||
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
|
||||
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)
|
||||
output = get_output_next(D_DOWN, current_output);
|
||||
output = get_output_next(D_DOWN, current_output, CLOSEST_OUTPUT);
|
||||
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)
|
||||
output = get_output_next(D_RIGHT, current_output);
|
||||
output = get_output_next(D_RIGHT, current_output, CLOSEST_OUTPUT);
|
||||
else
|
||||
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);
|
||||
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);
|
||||
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) {
|
||||
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 */
|
||||
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
|
||||
}
|
||||
DLOG("Detaching\n");
|
||||
|
||||
/* 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_detach(ws);
|
||||
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. */
|
||||
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;
|
||||
|
@ -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) {
|
||||
|
@ -1187,14 +1310,6 @@ void cmd_exec(I3_CMD, char *nosn, char *command) {
|
|||
*
|
||||
*/
|
||||
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);
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
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) {
|
||||
|
@ -1490,7 +1597,7 @@ void cmd_exit(I3_CMD) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Implementaiton of 'reload'.
|
||||
* Implementation of 'reload'.
|
||||
*
|
||||
*/
|
||||
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) {
|
||||
|
@ -1519,7 +1626,7 @@ void cmd_restart(I3_CMD) {
|
|||
}
|
||||
|
||||
/*
|
||||
* Implementaiton of 'open'.
|
||||
* Implementation of 'open'.
|
||||
*
|
||||
*/
|
||||
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) {
|
||||
LOG("Renaming workspace \"%s\" to \"%s\"\n", old_name, new_name);
|
||||
if (old_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;
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
||||
GREP_FIRST(workspace, output_get_content(output),
|
||||
!strcasecmp(child->name, old_name));
|
||||
if (old_name) {
|
||||
TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
|
||||
GREP_FIRST(workspace, output_get_content(output),
|
||||
!strcasecmp(child->name, old_name));
|
||||
} else {
|
||||
workspace = con_get_workspace(focused);
|
||||
}
|
||||
|
||||
if (!workspace) {
|
||||
// TODO: we should include the old workspace name here and use yajl for
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
* input parser-specs/commands.spec.
|
||||
******************************************************************************/
|
||||
|
||||
#include "GENERATED_enums.h"
|
||||
#include "GENERATED_command_enums.h"
|
||||
|
||||
typedef struct token {
|
||||
char *name;
|
||||
|
@ -63,7 +63,7 @@ typedef struct tokenptr {
|
|||
int n;
|
||||
} cmdp_token_ptr;
|
||||
|
||||
#include "GENERATED_tokens.h"
|
||||
#include "GENERATED_command_tokens.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* 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 command_output;
|
||||
|
||||
#include "GENERATED_call.h"
|
||||
#include "GENERATED_command_call.h"
|
||||
|
||||
|
||||
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.needs_tree_render = false;
|
||||
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
|
||||
* whole parser result request a tree_render(). */
|
||||
if (subcommand_output.needs_tree_render)
|
||||
|
|
335
src/con.c
335
src/con.c
|
@ -28,6 +28,20 @@ char *colors[] = {
|
|||
|
||||
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).
|
||||
* 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->window = window;
|
||||
new->border_style = config.default_border;
|
||||
new->current_border_width = -1;
|
||||
static int cnt = 0;
|
||||
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
|
||||
* to focus them. */
|
||||
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) {
|
||||
con_force_split_parents_redraw(con);
|
||||
if (con->type == CT_FLOATING_CON) {
|
||||
TAILQ_REMOVE(&(con->parent->floating_head), con, floating_windows);
|
||||
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
||||
|
@ -195,8 +212,14 @@ void con_focus(Con *con) {
|
|||
con_focus(con->parent);
|
||||
|
||||
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_update_parents_urgency(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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* 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)
|
||||
return false;
|
||||
|
||||
if (con->split) {
|
||||
if (con_is_split(con)) {
|
||||
DLOG("container %p does not accept windows, it is a split container.\n", con);
|
||||
return false;
|
||||
}
|
||||
|
@ -337,6 +386,14 @@ Con *con_get_fullscreen_con(Con *con, int fullscreen_mode) {
|
|||
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.
|
||||
*
|
||||
|
@ -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) {
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
DLOG("Moving workspaces is not yet implemented.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent moving if this would violate the fullscreen focus restrictions. */
|
||||
if (!con_fullscreen_permits_focusing(workspace)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
* of this function. */
|
||||
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);
|
||||
/* 5: re-attach the con to the parent of this focused container */
|
||||
Con *parent = con->parent;
|
||||
|
@ -671,14 +750,16 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||
* invisible.
|
||||
* 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. */
|
||||
if ((workspace->name[0] != '_' || workspace->name[1] != '_') &&
|
||||
con_get_fullscreen_con(workspace, CF_OUTPUT) == NULL) {
|
||||
/* We need to save focus on workspace level and restore it afterwards.
|
||||
* Otherwise, we might focus a different workspace without actually
|
||||
* switching workspaces. */
|
||||
if (!con_is_internal(workspace) && !fullscreen) {
|
||||
/* We need to save the focused workspace on the output in case the
|
||||
* new workspace is hidden and it's necessary to immediately switch
|
||||
* back to the originally-focused workspace. */
|
||||
Con *old_focus = TAILQ_FIRST(&(output_get_content(dest_output)->focus_head));
|
||||
con_focus(con_descend_focused(con));
|
||||
con_focus(old_focus);
|
||||
|
||||
/* Restore focus if the output's focused workspace has changed. */
|
||||
if (con_get_workspace(focused) != old_focus)
|
||||
con_focus(old_focus);
|
||||
}
|
||||
|
||||
/* 8: when moving to a visible workspace on a different output, we keep the
|
||||
|
@ -686,8 +767,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
|
|||
* don’t want to focus invisible workspaces */
|
||||
if (source_output != dest_output &&
|
||||
workspace_is_visible(workspace) &&
|
||||
workspace->name[0] != '_' &&
|
||||
workspace->name[1] != '_') {
|
||||
!con_is_internal(workspace)) {
|
||||
DLOG("Moved to a different output, focusing target\n");
|
||||
} else {
|
||||
/* 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));
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
@ -893,6 +1005,7 @@ Con *con_descend_tiling_focused(Con *con) {
|
|||
*/
|
||||
Con *con_descend_direction(Con *con, direction_t direction) {
|
||||
Con *most = NULL;
|
||||
Con *current;
|
||||
int orientation = con_orientation(con);
|
||||
DLOG("con_descend_direction(%p, orientation %d, direction %d)\n", con, orientation, direction);
|
||||
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,
|
||||
* we recurse to chose the left/right con or at least the last
|
||||
* 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 {
|
||||
/* If the con has no orientation set, it’s not a split container
|
||||
* 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,
|
||||
* we recurse to chose the top/bottom con or at least the last
|
||||
* 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 {
|
||||
/* If the con has no orientation set, it’s not a split container
|
||||
* 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) {
|
||||
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;
|
||||
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. */
|
||||
int border_style = con_border_style(con);
|
||||
if (border_style == BS_NONE)
|
||||
return (Rect){ 0, 0, 0, 0 };
|
||||
borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
|
||||
switch (border_style) {
|
||||
case BS_NORMAL:
|
||||
result = (Rect){2, 0, -(2 * 2), -2};
|
||||
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
||||
result.x -= 2;
|
||||
result.width += 2;
|
||||
}
|
||||
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
||||
result.width += 2;
|
||||
}
|
||||
/* With normal borders we never hide the upper border */
|
||||
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
||||
result.height += 2;
|
||||
}
|
||||
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);
|
||||
if (border_style == BS_NORMAL) {
|
||||
result = (Rect){border_width, 0 , -(2 * border_width), -(border_width)};
|
||||
} else {
|
||||
result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
|
||||
}
|
||||
if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) {
|
||||
result.x -= border_width;
|
||||
result.width += border_width;
|
||||
}
|
||||
if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) {
|
||||
result.width += border_width;
|
||||
}
|
||||
if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE && (border_style != BS_NORMAL)) {
|
||||
result.y -= border_width;
|
||||
result.height += border_width;
|
||||
}
|
||||
if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) {
|
||||
result.height += border_width;
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1046,10 +1155,11 @@ int con_border_style(Con *con) {
|
|||
* 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 */
|
||||
if (!con_is_floating(con)) {
|
||||
con->border_style = border_style;
|
||||
con->current_border_width = border_width;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1068,6 +1178,7 @@ void con_set_border_style(Con *con, int border_style) {
|
|||
|
||||
/* Change the border style, get new border/decoration values. */
|
||||
con->border_style = border_style;
|
||||
con->current_border_width = border_width;
|
||||
bsr = con_border_style_rect(con);
|
||||
int deco_height =
|
||||
(con->border_style == BS_NORMAL ? config.font.height + 5 : 0);
|
||||
|
@ -1125,7 +1236,6 @@ void con_set_layout(Con *con, int layout) {
|
|||
* split. */
|
||||
new->layout = layout;
|
||||
new->last_split_layout = con->last_split_layout;
|
||||
new->split = true;
|
||||
|
||||
Con *old_focused = TAILQ_FIRST(&(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);
|
||||
}
|
||||
con_force_split_parents_redraw(con);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1169,6 +1280,7 @@ void con_set_layout(Con *con, int layout) {
|
|||
} else {
|
||||
con->layout = layout;
|
||||
}
|
||||
con_force_split_parents_redraw(con);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1248,6 +1360,8 @@ static void con_on_remove_child(Con *con) {
|
|||
return;
|
||||
}
|
||||
|
||||
con_force_split_parents_redraw(con);
|
||||
|
||||
/* TODO: check if this container would swallow any other client and
|
||||
* don’t close it automatically. */
|
||||
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)
|
||||
* or height (v-split) and use the maximum of the height (h-split) or width
|
||||
* (v-split) as minimum size. */
|
||||
if (con->split) {
|
||||
if (con_is_split(con)) {
|
||||
uint32_t width = 0, height = 0;
|
||||
Con *child;
|
||||
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",
|
||||
con->type, con->layout, con->split);
|
||||
con->type, con->layout, con_is_split(con));
|
||||
assert(false);
|
||||
}
|
||||
|
||||
|
@ -1378,3 +1492,104 @@ bool con_fullscreen_permits_focusing(Con *con) {
|
|||
/* Focusing con would hide it behind a fullscreen window, disallow it. */
|
||||
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 | xcb_numlock_mask);
|
||||
GRAB_KEY(mods | 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;
|
||||
translate_keysyms();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -410,9 +418,14 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
|
|||
|
||||
config.default_border = BS_NORMAL;
|
||||
config.default_floating_border = BS_NORMAL;
|
||||
config.default_border_width = 2;
|
||||
/* Set default_orientation to NO_ORIENTATION for auto 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);
|
||||
|
||||
if (reload) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue