IndentWidth: 4
PointerBindsToType: false
ColumnLimit: 0
-ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE ]
SpaceBeforeParens: ControlStatements
"min" means minimum required version
"lkgv" means last known good version
-┌─────────────┬────────┬────────┬────────────────────────────────────────┐
-│ dependency │ min. │ lkgv │ URL │
-├─────────────┼────────┼────────┼────────────────────────────────────────┤
-│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
-│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │
-│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │
-│ util-cursor³│ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │
-│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │
-│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │
-│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │
-│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
-│ Pod::Simple²│ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/
-│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
-│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │
-│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
-│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
-│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
-│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
-└─────────────┴────────┴────────┴────────────────────────────────────────┘
+┌──────────────┬────────┬────────┬────────────────────────────────────────┐
+│ dependency │ min. │ lkgv │ URL │
+├──────────────┼────────┼────────┼────────────────────────────────────────┤
+│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │
+│ libxcb │ 1.1.93 │ 1.10 │ http://xcb.freedesktop.org/dist/ │
+│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │
+│ xkbcommon │ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │
+│ xkbcommon-x11│ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │
+│ util-cursor³ │ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │
+│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │
+│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │
+│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │
+│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │
+│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/
+│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │
+│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │
+│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │
+│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification
+│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │
+│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │
+└──────────────┴────────┴────────┴────────────────────────────────────────┘
¹ libsn = libstartup-notification
² Pod::Simple is a Perl module required for converting the testsuite
documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple
i3-migrate-config-to-v4 and i3-dmenu-desktop are implemented in Perl, but have
no dependencies besides Perl 5.10.
- i3-save-tree is also implemented in Perl and needs AnyEvent::I3 and JSON::XS.
- While i3-save-tree is not required for running i3 itself, it is strongly
- recommended to provide it in distribution packages.
+ i3-save-tree is also implemented in Perl and needs AnyEvent::I3 (>= 0.12) and
+ JSON::XS. While i3-save-tree is not required for running i3 itself, it is
+ strongly recommended to provide it in distribution packages.
XCB_CFLAGS += $(call cflags_for_lib, xcb-util)
XCB_LIBS += $(call ldflags_for_lib, xcb-util)
endif
+XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb)
# XCB keyboard stuff
XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms)
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama)
XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr)
-# Xlib
-X11_CFLAGS := $(call cflags_for_lib, x11)
-X11_LIBS := $(call ldflags_for_lib, x11,X11)
+XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon)
+XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon)
+XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11)
+XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11)
# Xcursor
XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor)
-i3-wm (4.7.3-1) unstable; urgency=low
+i3-wm (4.8.1-1) unstable; urgency=medium
* NOT YET RELEASED
- -- Michael Stapelberg <stapelberg@debian.org> Thu, 23 Jan 2014 23:11:48 +0100
+ -- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Jun 2014 19:37:32 +0200
+
+i3-wm (4.8-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Bump standards-version to 3.9.5 (no changes necessary)
+
+ -- Michael Stapelberg <stapelberg@debian.org> Sun, 15 Jun 2014 19:15:29 +0200
i3-wm (4.7.2-1) unstable; urgency=low
libxcb-randr0-dev,
libxcb-icccm4-dev,
libxcb-cursor-dev,
+ libxcb-xkb-dev,
+ libxkbcommon-dev (>= 0.4.0),
+ libxkbcommon-x11-dev (>= 0.4.0),
asciidoc (>= 8.4.4),
xmlto,
docbook-xml,
libcairo2-dev,
libpango1.0-dev,
libpod-simple-perl
-Standards-Version: 3.9.4
+Standards-Version: 3.9.5
Homepage: http://i3wm.org/
Package: i3
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
Provides: x-window-manager
Suggests: rxvt-unicode | x-terminal-emulator
-Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl, libjson-xs-perl
+Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl
Description: improved dynamic tiling window manager
Key features of i3 are good documentation, reasonable defaults (changeable in
a simple configuration file) and good multi-monitor support. The user
man/i3-sensible-editor.1
man/i3-sensible-terminal.1
man/i3-dmenu-desktop.1
+man/i3-save-tree.1
man/i3bar.1
+++ /dev/null
-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
-+++ i3-4.1.1/man/i3-sensible-terminal.man 2011-12-28 23:57:06.725802633 +0100
-@@ -22,6 +22,7 @@
- It tries to start one of the following (in that order):
-
- * $TERMINAL (this is a non-standard variable)
-+* x-terminal-emulator
- * urxvt
- * rxvt
- * terminator
+++ /dev/null
-use-x-terminal-emulator.patch
-manpage-x-terminal-emulator.patch
+++ /dev/null
-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.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
- # emulators.
--#
--# 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 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
http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
or, for more documentation, see http://git-scm.com/documentation
+You can view the git repository online at http://code.i3wm.org.
+
Please talk to us before working on new features to see whether they will be
accepted. There are a few things which we don’t want to see in i3, e.g. a
command which will focus windows in an alt+tab like way.
when you want to set a sensible minimum width regardless of which font you
are using, and at what particular size.
align::
- Align text on the +center+ (default), +right+ or +left+ of the block, when
+ Align text on the +center+, +right+ or +left+ (default) of the block, when
the minimum width of the latter, specified by the +min_width+ key, is not
reached.
name and instance::
IPC interface (interprocess communication)
==========================================
Michael Stapelberg <michael@i3wm.org>
-February 2014
+October 2014
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
num (integer)::
The logical number of the workspace. Corresponds to the command
- to switch to this workspace.
+ to switch to this workspace. For named workspaces, this will be -1.
name (string)::
The name of this workspace (by default num+1), as changed by the
user. Encoded in UTF-8.
So, when using the +default+ layout, you will have a 2 pixel border on
each side, making the window_rect +{ "x": 2, "y": 0, "width": 632,
"height": 366 }+ (for example).
+deco_rect (map)::
+ The coordinates of the *window decoration* inside its container. These
+ coordinates are relative to the container and do not include the actual
+ client window.
geometry (map)::
The original geometry the window specified when i3 mapped it. Used when
switching a window to floating mode, for example.
*Example:*
---------------------------------
type: SUBSCRIBE
-payload: [ "workspace", "focus" ]
+payload: [ "workspace", "output" ]
---------------------------------
barconfig_update (4)::
Sent when the hidden_state or mode field in the barconfig of any bar
instance was updated and when the config is reloaded.
+binding (5)::
+ Sent when a configured command binding is triggered with the keyboard or
+ mouse
*Example:*
--------------------------------------------------------------------
This event consists of a single serialized map containing a property
+change (string)+ which indicates the type of the change ("focus", "init",
-"empty", "urgent").
+"empty", "urgent"). A +current (object)+ property will be present with the
+affected workspace whenever the type of event affects a workspace (otherwise,
+it will be +null).
-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.
+When the change is "focus", an +old (object)+ property will be present with the
+previous workspace. 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 (string)+ which indicates the type of the change
* +new+ - the window has become managed by i3
+* +close+ - the window has closed
* +focus+ - the window has received input focus
* +title+ - the window's title has changed
* +fullscreen_mode+ - the window has entered or exited fullscreen mode
+* +move+ - the window has changed its position in the tree
+* +floating+ - the window has transitioned to or from floating
+* +urgent+ - the window has become urgent or lost its urgent status
Additionally a +container (object)+ field will be present, which consists
of the window's parent container. Be aware that for the "new" event, the
barconfig of the specified bar_id that were updated in i3. This event is the
same as a +GET_BAR_CONFIG+ reply for the bar with the given id.
+=== binding event
+
+This event consists of a single serialized map reporting on the details of a
+binding that ran a command because of user input. The +change (sring)+ field
+indicates what sort of binding event was triggered (right now it will always be
++"run"+ but may be expanded in the future).
+
+The +binding (object)+ field contains details about the binding that was run:
+
+command (string)::
+ The i3 command that is configured to run for this binding.
+mods (array of strings)::
+ The modifier keys that were configured with this binding.
+input_code (integer)::
+ If the binding was configured with +bindcode+, this will be the key code
+ that was given for the binding. If the binding is a mouse binding, it will be
+ the number of the mouse button that was pressed. Otherwise it will be 0.
+symbol (string or null)::
+ If this is a keyboard binding that was configured with +bindsym+, this
+ field will contain the given symbol. Otherwise it will be +null+.
+input_type (string)::
+ This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was
+ a keyboard or a mouse binding.
+
+*Example:*
+---------------------------
+{
+ "change": "run",
+ "binding": {
+ "command": "nop",
+ "mods": [
+ "shift",
+ "ctrl"
+ ],
+ "input_code": 0,
+ "symbol": "t",
+ "input_type": "keyboard"
+ }
+}
+---------------------------
+
== See also (existing libraries)
[[libraries]]
know):
C::
- i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
-
- https://github.com/acrisci/i3ipc-glib
+ * i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
+ * https://github.com/acrisci/i3ipc-glib
Go::
* https://github.com/proxypoke/i3ipc
JavaScript::
* https://github.com/acrisci/i3ipc-gjs
Lua::
- * https:/github.com/acrisci/i3ipc-lua
+ * https://github.com/acrisci/i3ipc-lua
Perl::
* https://metacpan.org/module/AnyEvent::I3
Python::
* https://github.com/whitelynx/i3ipc (not maintained)
* https://github.com/ziberna/i3-py (not maintained)
Ruby::
- http://github.com/badboy/i3-ipc
+ * http://github.com/badboy/i3-ipc
used to install the testsuite. Many users prefer to use the more modern
+cpanminus+ instead, though (because it asks no questions and just works):
+The tests additionally require +Xephyr(1)+ to run a nested X server. Install
++xserver-xephyr+ on Debian or +xorg-xserver-xephyr+ on Arch Linux.
+
.Installing testsuite dependencies using cpanminus (preferred)
--------------------------------------------------------------------------------
$ cd ~/i3/testcases
with an appropriate configuration file and creates a folder for each run
containing the appropriate i3 logfile for each testcase. The latest folder can
always be found under the symlink +latest/+. Unless told differently, it will
-run the tests on a separate X server instance (using the Xdummy script).
+run the tests on a separate X server instance (using Xephyr).
.Example invocation of complete-run.pl+
---------------------------------------
If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:
---------------------------------------------------
-$ ./complete-run.pl --parallel=1 --keep-xdummy-output
+$ ./complete-run.pl --parallel=1 --keep-xserver-output
---------------------------------------------------
-One common cause of failures is not having the X dummy server module
-installed. Under Debian and Ubuntu this is the package
-+xserver-xorg-video-dummy+.
+This will show the output of Xephyr, which is the X server implementation we
+use for testing.
==== IPC interface
=== Filesystem structure
In the git root of i3, the testcases live in the folder +testcases+. This
-folder contains the +complete-run.pl+ and +Xdummy+ scripts and a base
-configuration file which will be used for the tests. The different testcases
-(their file extension is .t, not .pl) themselves can be found in the
-conventionally named subfolder +t+:
+folder contains the +complete-run.pl+ and a base configuration file which will
+be used for the tests. The different testcases (their file extension is .t, not
+.pl) themselves can be found in the conventionally named subfolder +t+:
.Filesystem structure
--------------------------------------------
│ │ ├── omitted for brevity
│ │ ├── ...
│ │ └── 74-regress-focus-toggle.t
-│ └── Xdummy
--------------------------------------------
== Anatomy of a testcase
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+).
+available outputs (the command is +fullscreen toggle global+).
=== Opening other applications
=== Exiting i3
To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
+By default, a dialog will ask you to confirm if you really want to quit.
=== Floating
*Examples*:
--------------------------------
# Fullscreen
-bindsym $mod+f fullscreen
+bindsym $mod+f fullscreen toggle
# Restart
bindsym $mod+Shift+r restart
bindings. For example, when typing, capslock+1 or capslock+2 for switching
workspaces is totally convenient. Try it :-).
+[[mousebindings]]
+
+=== Mouse bindings
+
+A mouse binding makes i3 execute a command upon pressing a specific mouse
+button in the scope of the clicked container (see <<command_criteria>>). You
+can configure mouse bindings in a similar way to key bindings.
+
+*Syntax*:
+----------------------------------
+bindsym [--whole-window] [Modifiers+]button[n] command
+----------------------------------
+
+By default, the binding will only run when you click on the titlebar of the
+window. If the +--whole-window+ flag is given, it will run when any part of the
+window is clicked.
+
+*Examples*:
+--------------------------------
+# The middle button over a titlebar kills the window
+bindsym button2 kill
+
+# The middle button and a modifer over any part of the window kills the window
+bindsym --whole-window $mod+button2 kill
+
+# The right button toggles floating
+bindsym button3 floating toggle
+bindsym $mod+button3 floating toggle
+
+# The side buttons move the window around
+bindsym button9 move left
+bindsym button8 move right
+--------------------------------
+
[[floating_modifier]]
=== The floating modifier
Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+).
+=== Mouse button commands
+
+Specifies a command to run when a button was pressed on i3bar to override the
+default behavior. Currently only the mouse wheel buttons are supported. This is
+useful for disabling the scroll wheel action or running scripts that implement
+custom behavior for these buttons.
+
+*Syntax*:
+---------------------
+wheel_up_cmd <command>
+wheel_down_cmd <command>
+---------------------
+
+*Example*:
+---------------------
+bar {
+ wheel_up_cmd nop
+ wheel_down_cmd exec ~/.i3/scripts/custom_wheel_down
+}
+---------------------
+
=== Bar ID
Specifies the bar ID for the configured bar instance. If this option is missing,
or +layout splith+ to change the current container layout to splith/splitv,
stacking, tabbed layout, splitv or splith, respectively.
-To make the current window (!) fullscreen, use +fullscreen+, to make
-it floating (or tiling again) use +floating enable+ respectively +floating disable+
-(or +floating toggle+):
+To make the current window (!) fullscreen, use +fullscreen enable+ (or
++fullscreen enable global+ for the global mode), to leave either fullscreen
+mode use +fullscreen disable+, and to toggle between these two states use
++fullscreen toggle+ (or +fullscreen toggle global+).
+
+Likewise, to make the current window floating (or tiling again) use +floating
+enable+ respectively +floating disable+ (or +floating toggle+):
*Syntax*:
--------------
bindsym $mod+x layout toggle all
# Toggle fullscreen
-bindsym $mod+f fullscreen
+bindsym $mod+f fullscreen toggle
# Toggle floating/tiling
bindsym $mod+t floating toggle
i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c)
i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h)
-i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS)
-i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS)
+i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS)
+i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS)
i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o)
#include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-x11.h>
+
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
static xcb_gcontext_t pixmap_gc;
static xcb_key_symbols_t *symbols;
xcb_window_t root;
-Display *dpy;
+static struct xkb_keymap *xkb_keymap;
+static uint8_t xkb_base_event;
+static uint8_t xkb_base_error;
static void finish();
* This reduces a lot of confusion for users who switch keyboard
* layouts from qwerty to qwertz or other slight variations of
* qwerty (yes, that happens quite often). */
- KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
- if (!keysym_used_on_other_key(sym, keycode))
+ const xkb_keysym_t *syms;
+ int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms);
+ if (num == 0)
+ errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
+ if (!keysym_used_on_other_key(syms[0], keycode))
level = 0;
}
- KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, level);
- char *str = XKeysymToString(sym);
+
+ const xkb_keysym_t *syms;
+ int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms);
+ if (num == 0)
+ errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode);
+ if (num > 1)
+ printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num);
+
+ char str[4096];
+ if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1)
+ errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]);
const char *release = get_string("release");
char *res;
char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
static void finish() {
printf("creating \"%s\"...\n", config_path);
- if (!(dpy = XOpenDisplay(NULL)))
- errx(1, "Could not connect to X11");
+ struct xkb_context *xkb_context;
+
+ if ((xkb_context = xkb_context_new(0)) == NULL)
+ errx(1, "could not create xkbcommon context");
+
+ int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn);
+ if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL)
+ errx(1, "xkb_x11_keymap_new_from_device failed");
FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
if (kc_config == NULL)
xcb_connection_has_error(conn))
errx(1, "Cannot open display\n");
+ if (xkb_x11_setup_xkb_extension(conn,
+ XKB_X11_MIN_MAJOR_XKB_VERSION,
+ XKB_X11_MIN_MINOR_XKB_VERSION,
+ 0,
+ NULL,
+ NULL,
+ &xkb_base_event,
+ &xkb_base_error) != 1)
+ errx(EXIT_FAILURE, "Could not setup XKB extension.");
+
if (socket_path == NULL)
socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen);
#pragma once
/* from X11/keysymdef.h */
-#define XCB_NUM_LOCK 0xff7f
+#define XCB_NUM_LOCK 0xff7f
-#define xmacro(atom) xcb_atom_t A_ ## atom;
+#define xmacro(atom) xcb_atom_t A_##atom;
#include "atoms.xmacro"
#undef xmacro
#include <err.h>
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
-#define FREE(pointer) do { \
+#define FREE(pointer) \
+ do { \
if (pointer != NULL) { \
- free(pointer); \
- pointer = NULL; \
- } \
-} \
-while (0)
+ free(pointer); \
+ pointer = NULL; \
+ } \
+ } while (0)
extern xcb_window_t root;
#include <err.h>
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
-#define FREE(pointer) do { \
+#define FREE(pointer) \
+ do { \
if (pointer != NULL) { \
- free(pointer); \
- pointer = NULL; \
- } \
-} \
-while (0)
+ free(pointer); \
+ pointer = NULL; \
+ } \
+ } while (0)
-#define xmacro(atom) xcb_atom_t A_ ## atom;
+#define xmacro(atom) xcb_atom_t A_##atom;
#include "atoms.xmacro"
#undef xmacro
name
geometry
window_properties
+ mark
);
sub strip_containers {
# Hopefully one of these is installed (no flamewars about preference please!):
for editor in $VISUAL $EDITOR nano vim vi emacs pico qe mg jed gedit mc-edit; do
- if which $editor > /dev/null 2>&1; then
+ if command -v $editor > /dev/null 2>&1; then
exec $editor "$@"
fi
done
# We don't use 'more' because it will exit if the file is too short.
# Worst case scenario we'll open the file in your editor.
for pager in $PAGER less most w3m i3-sensible-editor; do
- if which $pager > /dev/null 2>&1; then
+ if command -v $pager > /dev/null 2>&1; then
exec $pager "$@"
fi
done
# This script tries to exec a terminal emulator by trying some known terminal
# emulators.
#
-# 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 xfce4-terminal; do
- if which $terminal > /dev/null 2>&1; then
+# We welcome patches that add distribution-specific mechanisms to find the
+# preferred terminal emulator. On Debian, there is the x-terminal-emulator
+# symlink for example.
+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal; do
+ if command -v $terminal > /dev/null 2>&1; then
exec $terminal "$@"
fi
done
bindsym Mod1+v split v
# enter fullscreen mode for the focused container
-bindsym Mod1+f fullscreen
+bindsym Mod1+f fullscreen toggle
# change container layout (stacked, tabbed, toggle split)
bindsym Mod1+s layout stacking
bindcode $mod+55 split v
# enter fullscreen mode for the focused container
-bindcode $mod+41 fullscreen
+bindcode $mod+41 fullscreen toggle
# change container layout (stacked, tabbed, toggle split)
bindcode $mod+39 layout stacking
TryExec=i3
Type=Application
X-LightDM-DesktopName=i3
+DesktopNames=i3
i3bar_SOURCES := $(wildcard i3bar/src/*.c)
i3bar_HEADERS := $(wildcard i3bar/include/*.h)
-i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
-i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS)
+i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS)
+i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS)
i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o)
typedef struct rect_t rect;
-struct ev_loop* main_loop;
-char *statusline;
-char *statusline_buffer;
+struct ev_loop *main_loop;
+char *statusline;
+char *statusline_buffer;
struct rect_t {
int x;
};
typedef enum {
+ /* First value to make it the default. */
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
} position_t;
/* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
-typedef enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } bar_display_mode_t;
+typedef enum { M_DOCK = 0,
+ M_HIDE = 1,
+ M_INVISIBLE = 2 } bar_display_mode_t;
typedef struct config_t {
- int modifier;
- position_t position;
- int verbose;
+ int modifier;
+ char *wheel_up_cmd;
+ char *wheel_down_cmd;
+ position_t position;
+ int verbose;
struct xcb_color_strings_t colors;
- bool disable_binding_mode_indicator;
- bool disable_ws;
- bool strip_ws_numbers;
- char *bar_id;
- char *command;
- char *fontname;
- char *tray_output;
- int num_outputs;
- char **outputs;
+ bool disable_binding_mode_indicator;
+ bool disable_ws;
+ bool strip_ws_numbers;
+ char *bar_id;
+ char *command;
+ char *fontname;
+ char *tray_output;
+ int num_outputs;
+ char **outputs;
bar_display_mode_t hide_on_modifier;
/* The current hidden_state of the bar, which indicates whether it is hidden or shown */
- enum { S_HIDE = 0, S_SHOW = 1 } hidden_state;
+ enum { S_HIDE = 0,
+ S_SHOW = 1 } hidden_state;
} config_t;
config_t config;
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
*
*/
-int i3_send_msg(uint32_t type, const char* payload);
+int i3_send_msg(uint32_t type, const char *payload);
/*
* Subscribe to all the i3-events, we need
typedef struct i3_output i3_output;
SLIST_HEAD(outputs_head, i3_output);
-struct outputs_head *outputs;
+struct outputs_head* outputs;
/*
* Start parsing the received json-string
i3_output* get_output_by_name(char* name);
struct i3_output {
- char* name; /* Name of the output */
- bool active; /* If the output is active */
- bool primary; /* If it is the primary output */
- int ws; /* The number of the currently visible ws */
- rect rect; /* The rect (relative to the root-win) */
-
- xcb_window_t bar; /* The id of the bar of the output */
- xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */
- xcb_gcontext_t bargc; /* The graphical context of the bar */
-
- struct ws_head *workspaces; /* The workspaces on this output */
- struct tc_head *trayclients; /* The tray clients on this output */
+ char* name; /* Name of the output */
+ bool active; /* If the output is active */
+ bool primary; /* If it is the primary output */
+ bool visible; /* If the bar is visible on this output */
+ int ws; /* The number of the currently visible ws */
+ rect rect; /* The rect (relative to the root-win) */
+
+ xcb_window_t bar; /* The id of the bar of the output */
+ xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */
+ xcb_gcontext_t bargc; /* The graphical context of the bar */
+
+ struct ws_head* workspaces; /* The workspaces on this output */
+ struct tc_head* trayclients; /* The tray clients on this output */
SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */
};
TAILQ_HEAD(tc_head, trayclient);
struct trayclient {
- xcb_window_t win; /* The window ID of the tray client */
- bool mapped; /* Whether this window is mapped */
- int xe_version; /* The XEMBED version supported by the client */
+ xcb_window_t win; /* The window ID of the tray client */
+ bool mapped; /* Whether this window is mapped */
+ int xe_version; /* The XEMBED version supported by the client */
- TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */
+ TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */
};
/* Get the maximum/minimum of x and y */
#undef MAX
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
#undef MIN
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0)
/* Securely free p */
-#define FREE(p) do { \
- if (p != NULL) { \
- free(p); \
- p = NULL; \
- } \
-} while (0)
+#define FREE(p) \
+ do { \
+ if (p != NULL) { \
+ free(p); \
+ p = NULL; \
+ } \
+ } while (0)
/* Securely fee single-linked list */
-#define FREE_SLIST(l, type) do { \
- type *walk = SLIST_FIRST(l); \
- while (!SLIST_EMPTY(l)) { \
- SLIST_REMOVE_HEAD(l, slist); \
- FREE(walk); \
- walk = SLIST_FIRST(l); \
- } \
-} while (0)
+#define FREE_SLIST(l, type) \
+ do { \
+ type *walk = SLIST_FIRST(l); \
+ while (!SLIST_EMPTY(l)) { \
+ SLIST_REMOVE_HEAD(l, slist); \
+ FREE(walk); \
+ walk = SLIST_FIRST(l); \
+ } \
+ } while (0)
/* Securely fee tail-queues */
-#define FREE_TAILQ(l, type) do { \
- type *walk = TAILQ_FIRST(l); \
- while (!TAILQ_EMPTY(l)) { \
- TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
- FREE(walk); \
- walk = TAILQ_FIRST(l); \
- } \
-} while (0)
+#define FREE_TAILQ(l, type) \
+ do { \
+ type *walk = TAILQ_FIRST(l); \
+ while (!TAILQ_EMPTY(l)) { \
+ TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \
+ FREE(walk); \
+ walk = TAILQ_FIRST(l); \
+ } \
+ } while (0)
#if defined(DLOG)
#undef DLOG
#endif
/* Use cool logging-macros */
-#define DLOG(fmt, ...) do { \
- if (config.verbose) { \
- printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
- } \
-} while(0)
+#define DLOG(fmt, ...) \
+ do { \
+ if (config.verbose) { \
+ printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
+ } \
+ } while (0)
/* We will include libi3.h which define its own version of ELOG.
* We want *our* version, so we undef the libi3 one. */
#if defined(ELOG)
#undef ELOG
#endif
-#define ELOG(fmt, ...) do { \
- fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
-} while(0)
+#define ELOG(fmt, ...) \
+ do { \
+ fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
+ } while (0)
void free_workspaces(void);
struct i3_ws {
- int num; /* The internal number of the ws */
- char *canonical_name; /* The true name of the ws according to the ipc */
- i3String *name; /* The name of the ws that is displayed on the bar */
- int name_width; /* The rendered width of the name */
- bool visible; /* If the ws is currently visible on an output */
- bool focused; /* If the ws is currently focused */
- bool urgent; /* If the urgent-hint of the ws is set */
- rect rect; /* The rect of the ws (not used (yet)) */
- struct i3_output *output; /* The current output of the ws */
-
- TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */
+ int num; /* The internal number of the ws */
+ char *canonical_name; /* The true name of the ws according to the ipc */
+ i3String *name; /* The name of the ws that is displayed on the bar */
+ int name_width; /* The rendered width of the name */
+ bool visible; /* If the ws is currently visible on an output */
+ bool focused; /* If the ws is currently focused */
+ bool urgent; /* If the urgent-hint of the ws is set */
+ rect rect; /* The rect of the ws (not used (yet)) */
+ struct i3_output *output; /* The current output of the ws */
+
+ TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */
};
#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
-#define SYSTEM_TRAY_REQUEST_DOCK 0
-#define SYSTEM_TRAY_BEGIN_MESSAGE 1
-#define SYSTEM_TRAY_CANCEL_MESSAGE 2
-#define XEMBED_MAPPED (1 << 0)
-#define XEMBED_EMBEDDED_NOTIFY 0
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_EMBEDDED_NOTIFY 0
struct xcb_color_strings_t {
char *bar_fg;
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;
+ if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
+ ctx->block.align = ALIGN_CENTER;
} else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) {
ctx->block.align = ALIGN_RIGHT;
} else {
- ctx->block.align = ALIGN_CENTER;
+ ctx->block.align = ALIGN_LEFT;
}
} else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
i3String *text = i3string_from_utf8_with_length((const char *)val, len);
/* Ensure we have a full_text set, so that when it is missing (or null),
* 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)");
+ new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
if (new_block->urgent)
ctx->has_urgent = true;
TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
static int stdin_end_array(void *context) {
DLOG("dumping statusline:\n");
struct status_block *current;
- TAILQ_FOREACH (current, &statusline_head, blocks) {
+ TAILQ_FOREACH(current, &statusline_head, blocks) {
DLOG("full_text = %s\n", i3string_as_utf8(current->full_text));
DLOG("color = %s\n", current->color);
}
return 1;
}
+ if (!strcmp(cur_key, "wheel_up_cmd")) {
+ DLOG("wheel_up_cmd = %.*s\n", len, val);
+ FREE(config.wheel_up_cmd);
+ sasprintf(&config.wheel_up_cmd, "%.*s", len, val);
+ return 1;
+ }
+
+ if (!strcmp(cur_key, "wheel_down_cmd")) {
+ DLOG("wheel_down_cmd = %.*s\n", len, val);
+ FREE(config.wheel_down_cmd);
+ sasprintf(&config.wheel_down_cmd, "%.*s", len, val);
+ return 1;
+ }
+
if (!strcmp(cur_key, "position")) {
DLOG("position = %.*s\n", len, val);
config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
*
*/
void free_colors(struct xcb_color_strings_t *colors) {
-#define FREE_COLOR(x) \
- do { \
- FREE(colors->x); \
+#define FREE_COLOR(x) \
+ do { \
+ FREE(colors->x); \
} while (0)
FREE_COLOR(bar_fg);
FREE_COLOR(bar_bg);
reconfig_windows(false);
i3_output *o_walk;
- SLIST_FOREACH (o_walk, outputs, slist) {
+ SLIST_FOREACH(o_walk, outputs, slist) {
kick_tray_clients(o_walk);
}
if (name == NULL) {
return NULL;
}
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!strcmp(walk->name, name)) {
break;
}
}
i3_ws *ws_walk;
- SLIST_FOREACH (outputs_walk, outputs, slist) {
+ SLIST_FOREACH(outputs_walk, outputs, slist) {
if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) {
- TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) {
+ TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
I3STRING_FREE(ws_walk->name);
FREE(ws_walk->canonical_name);
}
*
*/
#include <xcb/xcb.h>
+#include <xcb/xkb.h>
#include <xcb/xproto.h>
#include <xcb/xcb_aux.h>
int bar_height;
/* These are only relevant for XKB, which we only need for grabbing modifiers */
-Display *xkb_dpy;
-int xkb_event_base;
+int xkb_base;
int mod_pressed = 0;
/* Because the statusline is the same on all outputs, we have
statusline_width = 0;
/* Predict the text width of all blocks (in pixels). */
- TAILQ_FOREACH (block, &statusline_head, blocks) {
+ TAILQ_FOREACH(block, &statusline_head, blocks) {
if (i3string_get_num_bytes(block->full_text) == 0)
continue;
/* If this is not the last block, add some pixels for a separator. */
if (TAILQ_NEXT(block, blocks) != NULL)
- block->width += block->sep_block_width;
+ statusline_width += block->sep_block_width;
statusline_width += block->width + block->x_offset + block->x_append;
}
realloc_sl_buffer();
/* Clear the statusline pixmap. */
- xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, font.height + logical_px(5)};
+ xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height};
xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
/* Draw the text of each block. */
uint32_t x = 0;
- TAILQ_FOREACH (block, &statusline_head, blocks) {
+ TAILQ_FOREACH(block, &statusline_head, blocks) {
if (i3string_get_num_bytes(block->full_text) == 0)
continue;
+ uint32_t fg_color;
- 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 + block->x_offset, 1, block->width);
- x += block->width + block->x_offset + block->x_append;
+ /* If this block is urgent, draw it with the defined color and border. */
+ if (block->urgent) {
+ fg_color = colors.urgent_ws_fg;
+
+ uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
+
+ /* Draw the background */
+ uint32_t bg_color = colors.urgent_ws_bg;
+ uint32_t bg_values[] = { bg_color, bg_color };
+ xcb_change_gc(xcb_connection, statusline_ctx, mask, bg_values);
+
+ /* The urgent background “overshoots” by 2 px so that the text that
+ * is printed onto it will not be look so cut off. */
+ xcb_rectangle_t bg_rect = { x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2) };
+ xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect);
+ } else {
+ fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
+ }
+
+ set_font_colors(statusline_ctx, fg_color, colors.bar_bg);
+ draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width);
+ x += block->width + block->sep_block_width + block->x_offset + block->x_append;
if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) {
/* This is not the last block, draw a separator. */
xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
statusline_ctx, 2,
- (xcb_point_t[]) {{x - sep_offset, 2},
- {x - sep_offset, font.height - 2}});
+ (xcb_point_t[]) { { x - sep_offset, logical_px(4) },
+ { x - sep_offset, bar_height - logical_px(4) } });
}
}
}
}
i3_output *walk;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active) {
continue;
}
cont_child();
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (walk->bar == XCB_NONE) {
continue;
}
/* Determine, which bar was clicked */
i3_output *walk;
xcb_window_t bar = event->event;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (walk->bar == bar) {
break;
}
}
/* TODO: Move this to extern get_ws_for_output() */
- TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
+ TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
if (cur_ws->visible) {
break;
}
/* First calculate width of tray area */
trayclient *trayclient;
int tray_width = 0;
- TAILQ_FOREACH_REVERSE (trayclient, walk->trayclients, tc_head, tailq) {
+ TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
tray_width += (font.height + logical_px(2));
if (x >= 0) {
struct status_block *block;
- TAILQ_FOREACH (block, &statusline_head, blocks) {
+ TAILQ_FOREACH(block, &statusline_head, blocks) {
last_block_x = block_x;
block_x += block->width + block->x_offset + block->x_append;
* 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 `wheel_up_cmd [COMMAND]` was specified, it should override
+ * the default behavior */
+ if (config.wheel_up_cmd) {
+ i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_up_cmd);
+ return;
+ }
+
if (cur_ws == TAILQ_FIRST(walk->workspaces))
return;
* 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 `wheel_down_cmd [COMMAND]` was specified, it should override
+ * the default behavior */
+ if (config.wheel_down_cmd) {
+ i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_down_cmd);
+ return;
+ }
+
if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head))
return;
break;
case 1:
/* Check if this event regards a workspace button */
- TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
+ TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
DLOG("x = %d\n", x);
if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
break;
/* Otherwise, focus our currently visible workspace if it is not
* already focused */
if (cur_ws == NULL) {
- TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
+ TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
if (cur_ws->visible && !cur_ws->focused)
break;
}
free(buffer);
}
+/*
+ * Handle visibility notifications: when none of the bars are visible, e.g.
+ * if windows are in full-screen on each output, suspend the child process.
+ *
+ */
+static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
+ bool visible = (event->state != XCB_VISIBILITY_FULLY_OBSCURED);
+ int num_visible = 0;
+ i3_output *output;
+
+ SLIST_FOREACH (output, outputs, slist) {
+ if (!output->active) {
+ continue;
+ }
+ if (output->bar == event->window) {
+ if (output->visible == visible) {
+ return;
+ }
+ output->visible = visible;
+ }
+ num_visible += output->visible;
+ }
+
+ if (num_visible == 0) {
+ stop_child();
+ } else if (num_visible == visible) {
+ /* Wake the child only when transitioning from 0 to 1 visible bar.
+ * We cannot transition from 0 to 2 or more visible bars at once since
+ * visibility events are delivered to each window separately */
+ cont_child();
+ }
+}
+
/*
* Adjusts the size of the tray window and alignment of the tray clients by
* configuring their respective x coordinates. To be called when mapping or
static void configure_trayclients(void) {
trayclient *trayclient;
i3_output *output;
- SLIST_FOREACH (output, outputs, slist) {
+ SLIST_FOREACH(output, outputs, slist) {
if (!output->active)
continue;
int clients = 0;
- TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
+ TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
clients++;
DLOG("X window %08x requested docking\n", client);
i3_output *walk, *output = NULL;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
if (config.tray_output) {
if (output == NULL &&
config.tray_output &&
strcasecmp("primary", config.tray_output) == 0) {
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
DLOG("DestroyNotify for window = %08x, event = %08x\n", event->window, event->event);
i3_output *walk;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("checking output %s\n", walk->name);
trayclient *trayclient;
- TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
+ TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
if (trayclient->win != event->window)
continue;
DLOG("MapNotify for window = %08x, event = %08x\n", event->window, event->event);
i3_output *walk;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("checking output %s\n", walk->name);
trayclient *trayclient;
- TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
+ TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
if (trayclient->win != event->window)
continue;
DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event);
i3_output *walk;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("checking output %s\n", walk->name);
trayclient *trayclient;
- TAILQ_FOREACH (trayclient, walk->trayclients, tailq) {
+ TAILQ_FOREACH(trayclient, walk->trayclients, tailq) {
if (trayclient->win != event->window)
continue;
DLOG("xembed_info updated\n");
trayclient *trayclient = NULL, *walk;
i3_output *o_walk;
- SLIST_FOREACH (o_walk, outputs, slist) {
+ SLIST_FOREACH(o_walk, outputs, slist) {
if (!o_walk->active)
continue;
- TAILQ_FOREACH (walk, o_walk->trayclients, tailq) {
+ TAILQ_FOREACH(walk, o_walk->trayclients, tailq) {
if (walk->win != event->window)
continue;
trayclient = walk;
trayclient *trayclient;
i3_output *output;
- SLIST_FOREACH (output, outputs, slist) {
+ SLIST_FOREACH(output, outputs, slist) {
if (!output->active)
continue;
int clients = 0;
- TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) {
+ TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
clients++;
}
while ((event = xcb_poll_for_event(xcb_connection)) != NULL) {
- switch (event->response_type & ~0x80) {
+ int type = (event->response_type & ~0x80);
+
+ if (type == xkb_base && xkb_base > -1) {
+ DLOG("received an xkb event\n");
+
+ xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
+ if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
+ int modstate = state->mods & config.modifier;
+
+#define DLOGMOD(modmask, status) \
+ do { \
+ switch (modmask) { \
+ case ShiftMask: \
+ DLOG("ShiftMask got " #status "!\n"); \
+ break; \
+ case ControlMask: \
+ DLOG("ControlMask got " #status "!\n"); \
+ break; \
+ case Mod1Mask: \
+ DLOG("Mod1Mask got " #status "!\n"); \
+ break; \
+ case Mod2Mask: \
+ DLOG("Mod2Mask got " #status "!\n"); \
+ break; \
+ case Mod3Mask: \
+ DLOG("Mod3Mask got " #status "!\n"); \
+ break; \
+ case Mod4Mask: \
+ DLOG("Mod4Mask got " #status "!\n"); \
+ break; \
+ case Mod5Mask: \
+ DLOG("Mod5Mask got " #status "!\n"); \
+ break; \
+ } \
+ } while (0)
+
+ if (modstate != mod_pressed) {
+ if (modstate == 0) {
+ DLOGMOD(config.modifier, released);
+ if (!activated_mode)
+ hide_bars();
+ } else {
+ DLOGMOD(config.modifier, pressed);
+ activated_mode = false;
+ unhide_bars();
+ }
+ mod_pressed = modstate;
+ }
+#undef DLOGMOD
+ }
+
+ free(event);
+ continue;
+ }
+
+ switch (type) {
+ case XCB_VISIBILITY_NOTIFY:
+ /* Visibility change: a bar is [un]obscured by other window */
+ handle_visibility_notify((xcb_visibility_notify_event_t *)event);
+ break;
case XCB_EXPOSE:
/* Expose-events happen, when the window needs to be redrawn */
redraw_bars();
void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
}
-/*
- * We need to bind to the modifier per XKB. Sadly, XCB does not implement this
- *
- */
-void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
- XkbEvent ev;
- int modstate = 0;
-
- DLOG("Got XKB-Event!\n");
-
- while (XPending(xkb_dpy)) {
- XNextEvent(xkb_dpy, (XEvent *)&ev);
-
- if (ev.type != xkb_event_base) {
- ELOG("No Xkb-Event!\n");
- continue;
- }
-
- if (ev.any.xkb_type != XkbStateNotify) {
- ELOG("No State Notify!\n");
- continue;
- }
-
- unsigned int mods = ev.state.mods;
- modstate = mods & config.modifier;
- }
-
-#define DLOGMOD(modmask, status) \
- do { \
- switch (modmask) { \
- case ShiftMask: \
- DLOG("ShiftMask got " #status "!\n"); \
- break; \
- case ControlMask: \
- DLOG("ControlMask got " #status "!\n"); \
- break; \
- case Mod1Mask: \
- DLOG("Mod1Mask got " #status "!\n"); \
- break; \
- case Mod2Mask: \
- DLOG("Mod2Mask got " #status "!\n"); \
- break; \
- case Mod3Mask: \
- DLOG("Mod3Mask got " #status "!\n"); \
- break; \
- case Mod4Mask: \
- DLOG("Mod4Mask got " #status "!\n"); \
- break; \
- case Mod5Mask: \
- DLOG("Mod5Mask got " #status "!\n"); \
- break; \
- } \
- } while (0)
-
- if (modstate != mod_pressed) {
- if (modstate == 0) {
- DLOGMOD(config.modifier, released);
- if (!activated_mode)
- hide_bars();
- } else {
- DLOGMOD(config.modifier, pressed);
- activated_mode = false;
- unhide_bars();
- }
- mod_pressed = modstate;
- }
-
-#undef DLOGMOD
-}
-
/*
* Early initialization of the connection to X11: Everything which does not
* depend on 'config'.
*
*/
void register_xkb_keyevents() {
- if (xkb_dpy == NULL) {
- int xkb_major, xkb_minor, xkb_errbase, xkb_err;
- xkb_major = XkbMajorVersion;
- xkb_minor = XkbMinorVersion;
-
- xkb_dpy = XkbOpenDisplay(NULL,
- &xkb_event_base,
- &xkb_errbase,
- &xkb_major,
- &xkb_minor,
- &xkb_err);
-
- if (xkb_dpy == NULL) {
- ELOG("No XKB!\n");
- exit(EXIT_FAILURE);
- }
-
- if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) {
- ELOG("Could not set FD_CLOEXEC on xkbdpy: %s\n", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- int i1;
- if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) {
- ELOG("XKB not supported by X-server!\n");
- exit(EXIT_FAILURE);
- }
-
- if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) {
- ELOG("Could not grab Key!\n");
- exit(EXIT_FAILURE);
- }
-
- xkb_io = smalloc(sizeof(ev_io));
- ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ);
- ev_io_start(main_loop, xkb_io);
- XFlush(xkb_dpy);
+ const xcb_query_extension_reply_t *extreply;
+ extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
+ if (!extreply->present) {
+ ELOG("xkb is not present on this server\n");
+ exit(EXIT_FAILURE);
}
+ DLOG("initializing xcb-xkb\n");
+ xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
+ xcb_xkb_select_events(conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+ 0,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
+ 0xff,
+ 0xff,
+ NULL);
+ xkb_base = extreply->first_event;
}
/*
*
*/
void deregister_xkb_keyevents() {
- if (xkb_dpy != NULL) {
- ev_io_stop(main_loop, xkb_io);
- XCloseDisplay(xkb_dpy);
- close(xkb_io->fd);
- FREE(xkb_io);
- xkb_dpy = NULL;
- }
+ xcb_xkb_select_events(conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ 0,
+ 0,
+ 0,
+ 0xff,
+ 0xff,
+ NULL);
}
/*
void clean_xcb(void) {
i3_output *o_walk;
free_workspaces();
- SLIST_FOREACH (o_walk, outputs, slist) {
+ SLIST_FOREACH(o_walk, outputs, slist) {
destroy_window(o_walk);
FREE(o_walk->trayclients);
FREE(o_walk->workspaces);
static bool tray_configured = false;
i3_output *walk;
- SLIST_FOREACH (walk, outputs, slist) {
+ SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active) {
/* If an output is not active, we destroy its bar */
/* FIXME: Maybe we rather want to unmap? */
* */
values[2] = XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT;
+ if (config.hide_on_modifier == M_DOCK) {
+ /* If the bar is normally visible, catch visibility change events to suspend
+ * the status process when the bar is obscured by full-screened windows. */
+ values[2] |= XCB_EVENT_MASK_VISIBILITY_CHANGE;
+ walk->visible = true;
+ }
if (!config.disable_ws) {
values[2] |= XCB_EVENT_MASK_BUTTON_PRESS;
}
* VGA-1 but output == [HDMI-1]).
*/
i3_output *output;
- SLIST_FOREACH (output, outputs, slist) {
+ SLIST_FOREACH(output, outputs, slist) {
if (strcasecmp(output->name, tray_output) == 0 ||
(strcasecmp(tray_output, "primary") == 0 && output->primary)) {
init_tray();
refresh_statusline();
i3_output *outputs_walk;
- SLIST_FOREACH (outputs_walk, outputs, slist) {
+ SLIST_FOREACH(outputs_walk, outputs, slist) {
if (!outputs_walk->active) {
DLOG("Output %s inactive, skipping...\n", outputs_walk->name);
continue;
* position */
trayclient *trayclient;
int traypx = 0;
- TAILQ_FOREACH (trayclient, outputs_walk->trayclients, tailq) {
+ TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) {
if (!trayclient->mapped)
continue;
/* We assume the tray icons are quadratic (we use the font
* *height* as *width* of the icons) because we configured them
* like this. */
- traypx += font.height + 2;
+ traypx += font.height + logical_px(2);
}
/* Add 2px of padding if there are any tray icons */
if (traypx > 0)
- traypx += 2;
+ traypx += logical_px(2);
xcb_copy_area(xcb_connection,
statusline_pm,
outputs_walk->buffer,
outputs_walk->bargc,
- MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0,
- MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3,
- MIN(outputs_walk->rect.w - traypx - 4, (int)statusline_width), font.height + 2);
+ MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + logical_px(4))), 0,
+ MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0,
+ MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height);
}
if (!config.disable_ws) {
i3_ws *ws_walk;
- TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) {
+ TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
uint32_t fg_color = colors.inactive_ws_fg;
*/
void redraw_bars(void) {
i3_output *outputs_walk;
- SLIST_FOREACH (outputs_walk, outputs, slist) {
+ SLIST_FOREACH(outputs_walk, outputs, slist) {
if (!outputs_walk->active) {
continue;
}
xmacro(_NET_CLIENT_LIST)
xmacro(_NET_CLIENT_LIST_STACKING)
xmacro(_NET_CURRENT_DESKTOP)
+xmacro(_NET_NUMBER_OF_DESKTOPS)
+xmacro(_NET_DESKTOP_NAMES)
+xmacro(_NET_DESKTOP_VIEWPORT)
xmacro(_NET_ACTIVE_WINDOW)
+xmacro(_NET_CLOSE_WINDOW)
xmacro(_NET_STARTUP_ID)
xmacro(_NET_WORKAREA)
xmacro(WM_PROTOCOLS)
xmacro(_NET_REQUEST_FRAME_EXTENTS)
xmacro(_NET_FRAME_EXTENTS)
xmacro(_MOTIF_WM_HINTS)
+xmacro(WM_CHANGE_STATE)
*
*/
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
- const char *release, const char *command, const char *mode);
+ const char *release, const char *whole_window, const char *command, const char *mode);
/**
* Grab the bound keys (tell X to send us keypress events for those keycodes)
void check_for_duplicate_bindings(struct context *context);
/**
- * Runs the given binding and handles parse errors. Returns a CommandResult for
- * running the binding's command. Caller should render tree if
- * needs_tree_render is true. Free with command_result_free().
+ * Frees the binding. If bind is null, it simply returns.
+ */
+void binding_free(Binding *bind);
+
+/**
+ * Runs the given binding and handles parse errors. If con is passed, it will
+ * execute the command binding with that container selected by criteria.
+ * Returns a CommandResult for running the binding's command. Caller should
+ * render tree if needs_tree_render is true. Free with command_result_free().
*
*/
-CommandResult *run_binding(Binding *bind);
+CommandResult *run_binding(Binding *bind, Con *con);
void cmd_focus(I3_CMD);
/**
- * Implementation of 'fullscreen [global]'.
+ * Implementation of 'fullscreen [enable|disable|toggle] [global]'.
*
*/
-void cmd_fullscreen(I3_CMD, char *fullscreen_mode);
+void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode);
/**
* Implementation of 'move <direction> [<pixels> [px]]'.
bool needs_tree_render;
};
+/**
+ * Parses a string (or word, if as_word is true). Extracted out of
+ * parse_command so that it can be used in src/workspace.c for interpreting
+ * workspace commands.
+ *
+ */
+char *parse_string(const char **walk, bool as_word);
+
/**
* Parses and executes the given command. If a caller-allocated yajl_gen is
* passed, a json reply will be generated in the format specified by the ipc
*/
Con *con_new_skeleton(Con *parent, i3Window *window);
-
/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
*
*/
*/
void con_toggle_fullscreen(Con *con, int fullscreen_mode);
+/**
+ * Enables fullscreen mode for the given container, if necessary.
+ *
+ */
+void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode);
+
+/**
+ * Disables fullscreen mode for the given container, if necessary.
+ *
+ */
+void con_disable_fullscreen(Con *con);
+
/**
* Moves the given container to the currently focused container on the given
* workspace.
char *socket_path;
/** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */
- enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } mode;
+ enum { M_DOCK = 0,
+ M_HIDE = 1,
+ M_INVISIBLE = 2 } mode;
/* The current hidden_state of the bar, which indicates whether it is hidden or shown */
- enum { S_HIDE = 0, S_SHOW = 1 } hidden_state;
+ enum { S_HIDE = 0,
+ S_SHOW = 1 } hidden_state;
/** Bar modifier (to show bar when in hide mode). */
enum {
M_MOD5 = 7
} modifier;
+ /** Command that should be run when mouse wheel up button is pressed over
+ * i3bar to override the default behavior. */
+ char *wheel_up_cmd;
+
+ /** Command that should be run when mouse wheel down button is pressed over
+ * i3bar to override the default behavior. */
+ char *wheel_down_cmd;
+
/** Bar position (bottom by default). */
- enum { P_BOTTOM = 0, P_TOP = 1 } position;
+ enum { P_BOTTOM = 0,
+ P_TOP = 1 } position;
/** Command that should be run to execute i3bar, give a full path if i3bar is not
* in your $PATH.
TAILQ_ENTRY(Barconfig) configs;
};
+/**
+ * Finds the configuration file to use (either the one specified by
+ * override_configpath), the user’s one or the system default) and calls
+ * parse_file().
+ *
+ * If you specify override_configpath, only this path is used to look for a
+ * configuration file.
+ *
+ * If use_nagbar is false, don't try to start i3-nagbar but log the errors to
+ * stdout/stderr instead.
+ *
+ */
+bool parse_configuration(const char *override_configpath, bool use_nagbar);
+
/**
* Reads the configuration from ~/.i3/config or /etc/i3/config if not found.
*
* 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__ )
+ 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
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(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, 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(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, 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_wheel_up_cmd, const char *command);
+CFGFUN(bar_wheel_down_cmd, const char *command);
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);
/**
* Parses the given file by first replacing the variables, then calling
- * parse_config and possibly launching i3-nagbar.
+ * parse_config and launching i3-nagbar if use_nagbar is true.
+ *
+ * The return value is a boolean indicating whether there were errors during
+ * parsing.
*
*/
-void parse_file(const char *f);
+bool parse_file(const char *f, bool use_nagbar);
typedef struct Assignment Assignment;
typedef struct Window i3Window;
-
/******************************************************************************
* Helper types
*****************************************************************************/
-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_PIXEL = 2 } border_style_t;
+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_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 */
-typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t;
+typedef enum { DONT_KILL_WINDOW = 0,
+ KILL_WINDOW = 1,
+ KILL_CLIENT = 2 } kill_window_t;
/** describes if the window is adjacent to the output (physical screen) edges. */
typedef enum { ADJ_NONE = 0,
ADJ_LEFT_SCREEN_EDGE = (1 << 0),
ADJ_RIGHT_SCREEN_EDGE = (1 << 1),
ADJ_UPPER_SCREEN_EDGE = (1 << 2),
- ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t;
+ ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
enum {
BIND_NONE = 0,
- BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */
- BIND_CONTROL = XCB_MOD_MASK_CONTROL, /* (1 << 2) */
- BIND_MOD1 = XCB_MOD_MASK_1, /* (1 << 3) */
- BIND_MOD2 = XCB_MOD_MASK_2, /* (1 << 4) */
- BIND_MOD3 = XCB_MOD_MASK_3, /* (1 << 5) */
- BIND_MOD4 = XCB_MOD_MASK_4, /* (1 << 6) */
- BIND_MOD5 = XCB_MOD_MASK_5, /* (1 << 7) */
+ BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */
+ BIND_CONTROL = XCB_MOD_MASK_CONTROL, /* (1 << 2) */
+ BIND_MOD1 = XCB_MOD_MASK_1, /* (1 << 3) */
+ BIND_MOD2 = XCB_MOD_MASK_2, /* (1 << 4) */
+ BIND_MOD3 = XCB_MOD_MASK_3, /* (1 << 5) */
+ BIND_MOD4 = XCB_MOD_MASK_4, /* (1 << 6) */
+ BIND_MOD5 = XCB_MOD_MASK_5, /* (1 << 7) */
BIND_MODE_SWITCH = (1 << 8)
};
B_UPON_KEYRELEASE_IGNORE_MODS = 2,
} release;
+ /** If this is true for a mouse binding, the binding should be executed
+ * when the button is pressed over any part of the window, not just the
+ * title bar (default). */
+ bool whole_window;
+
uint32_t number_keycodes;
/** Keycode to bind */
* This is an array of number_keycodes size. */
xcb_keycode_t *translated_to;
-
/** Command, like in command mode */
char *command;
bool doesnt_accept_focus;
/** Whether the window says it is a dock window */
- enum { W_NODOCK = 0, W_DOCK_TOP = 1, W_DOCK_BOTTOM = 2 } dock;
+ enum { W_NODOCK = 0,
+ W_DOCK_TOP = 1,
+ W_DOCK_BOTTOM = 2 } dock;
/** When this window was marked urgent. 0 means not urgent */
struct timeval urgent;
M_DOCK_BOTTOM = 3
} dock;
xcb_window_t id;
- enum { M_ANY = 0, M_TILING, M_FLOATING } floating;
+ enum { M_ANY = 0,
+ M_TILING,
+ M_FLOATING } floating;
Con *con_id;
/* Where the window looking for a match should be inserted:
* (dockareas)
*
*/
- enum { M_HERE = 0, M_ASSIGN_WS, M_BELOW } insert_where;
+ enum { M_HERE = 0,
+ M_ASSIGN_WS,
+ M_BELOW } insert_where;
TAILQ_ENTRY(Match) matches;
*
*/
enum {
- A_ANY = 0,
- A_COMMAND = (1 << 0),
+ A_ANY = 0,
+ A_COMMAND = (1 << 0),
A_TO_WORKSPACE = (1 << 1),
- A_TO_OUTPUT = (1 << 2)
+ A_TO_OUTPUT = (1 << 2)
} type;
/** the criteria to check if a window matches */
};
/** Fullscreen modes. Used by Con.fullscreen_mode. */
-typedef enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode_t;
+typedef enum { CF_NONE = 0,
+ CF_OUTPUT = 1,
+ CF_GLOBAL = 2 } fullscreen_mode_t;
/**
* A 'Con' represents everything from the X11 root window down to a single X11 window.
TAILQ_ENTRY(Con) floating_windows;
/** callbacks */
- void(*on_remove_child)(Con *);
+ void (*on_remove_child)(Con *);
enum {
/* Not a scratchpad window. */
*/
void ewmh_update_current_desktop(void);
+/**
+ * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
+ * noninternal workspaces.
+ */
+void ewmh_update_number_of_desktops(void);
+
+/**
+ * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
+ * list of NULL-terminated strings in UTF-8 encoding"
+ */
+void ewmh_update_desktop_names(void);
+
+/**
+ * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
+ * define the top left corner of each desktop's viewport.
+ */
+void ewmh_update_desktop_viewport(void);
+
/**
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
*
#include "tree.h"
/** Callback for dragging */
-typedef void(*callback_t)(Con*, Rect*, uint32_t, uint32_t, const void*);
+typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *);
/** Macro to create a callback function for dragging */
-#define DRAGGING_CB(name) \
- static void name(Con *con, Rect *old_rect, uint32_t new_x, \
- uint32_t new_y, const void *extra)
+#define DRAGGING_CB(name) \
+ static void name(Con *con, Rect *old_rect, uint32_t new_x, \
+ uint32_t new_y, const void *extra)
/** On which border was the dragging initiated? */
-typedef enum { BORDER_LEFT = (1 << 0),
- BORDER_RIGHT = (1 << 1),
- BORDER_TOP = (1 << 2),
- BORDER_BOTTOM = (1 << 3)} border_t;
+typedef enum { BORDER_LEFT = (1 << 0),
+ BORDER_RIGHT = (1 << 1),
+ BORDER_TOP = (1 << 2),
+ BORDER_BOTTOM = (1 << 3) } border_t;
/**
* Enables floating mode for the given container by detaching it from its
*
*/
drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event,
- xcb_window_t confine_to, border_t border, int cursor,
- 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
#include <xcb/randr.h>
extern int randr_base;
+extern int xkb_base;
/**
* Adds the given sequence to the list of events which are ignored.
#include <sys/resource.h>
#include <xcb/xcb_keysyms.h>
+#include <xcb/xkb.h>
#include <X11/XKBlib.h>
extern char **start_argv;
extern Display *xlibdpy, *xkbdpy;
extern int xkb_current_group;
-extern TAILQ_HEAD(bindings_head, Binding) *bindings;
+extern TAILQ_HEAD(bindings_head, Binding) * bindings;
extern TAILQ_HEAD(autostarts_head, Autostart) autostarts;
extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always;
extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments;
char magic[6];
uint32_t size;
uint32_t type;
-} __attribute__ ((packed)) i3_ipc_header_t;
+} __attribute__((packed)) i3_ipc_header_t;
/*
* Messages from clients to i3
*/
/** Never change this, only on major IPC breakage (don’t do that) */
-#define I3_IPC_MAGIC "i3-ipc"
+#define I3_IPC_MAGIC "i3-ipc"
/** The payload of the message will be interpreted as a command */
-#define I3_IPC_MESSAGE_TYPE_COMMAND 0
+#define I3_IPC_MESSAGE_TYPE_COMMAND 0
/** Requests the current workspaces from i3 */
-#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1
+#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1
/** Subscribe to the specified events */
-#define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2
+#define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2
/** Requests the current outputs from i3 */
-#define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3
+#define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3
/** Requests the tree layout from i3 */
-#define I3_IPC_MESSAGE_TYPE_GET_TREE 4
+#define I3_IPC_MESSAGE_TYPE_GET_TREE 4
/** Request the current defined marks from i3 */
-#define I3_IPC_MESSAGE_TYPE_GET_MARKS 5
+#define I3_IPC_MESSAGE_TYPE_GET_MARKS 5
/** Request the configuration for a specific 'bar' */
-#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
+#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6
/** Request the i3 version */
-#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7
+#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7
/*
* Messages from i3 to clients
*/
/** Command reply type */
-#define I3_IPC_REPLY_TYPE_COMMAND 0
+#define I3_IPC_REPLY_TYPE_COMMAND 0
/** Workspaces reply type */
-#define I3_IPC_REPLY_TYPE_WORKSPACES 1
+#define I3_IPC_REPLY_TYPE_WORKSPACES 1
/** Subscription reply type */
-#define I3_IPC_REPLY_TYPE_SUBSCRIBE 2
+#define I3_IPC_REPLY_TYPE_SUBSCRIBE 2
/** Outputs reply type */
-#define I3_IPC_REPLY_TYPE_OUTPUTS 3
+#define I3_IPC_REPLY_TYPE_OUTPUTS 3
/** Tree reply type */
-#define I3_IPC_REPLY_TYPE_TREE 4
+#define I3_IPC_REPLY_TYPE_TREE 4
/** Marks reply type */
-#define I3_IPC_REPLY_TYPE_MARKS 5
+#define I3_IPC_REPLY_TYPE_MARKS 5
/** Bar config reply type */
-#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
+#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
/** i3 version reply type */
-#define I3_IPC_REPLY_TYPE_VERSION 7
+#define I3_IPC_REPLY_TYPE_VERSION 7
/*
* Events from i3 to clients. Events have the first bit set high.
*
*/
-#define I3_IPC_EVENT_MASK (1 << 31)
+#define I3_IPC_EVENT_MASK (1 << 31)
/* The workspace event will be triggered upon changes in the workspace list */
-#define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0)
+#define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0)
/* The output event will be triggered upon changes in the output list */
-#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1)
+#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1)
/* The output event will be triggered upon mode changes */
-#define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2)
+#define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2)
/* The window event will be triggered upon window changes */
-#define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3)
+#define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3)
/** Bar config update will be triggered to update the bar config */
-#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
+#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4)
+
+/** The binding event will be triggered when bindings run */
+#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
extern char *current_socketpath;
typedef struct ipc_client {
- int fd;
+ int fd;
- /* The events which this client wants to receive */
- int num_events;
- char **events;
+ /* The events which this client wants to receive */
+ int num_events;
+ char **events;
- TAILQ_ENTRY(ipc_client) clients;
+ TAILQ_ENTRY(ipc_client) clients;
} ipc_client;
/*
* message_type is the type of the message as the sender specified it.
*
*/
-typedef void(*handler_t)(int, uint8_t*, int, uint32_t, uint32_t);
+typedef void (*handler_t)(int, uint8_t *, int, uint32_t, uint32_t);
/* Macro to declare a callback */
-#define IPC_HANDLER(name) \
- static void handle_ ## name (int fd, uint8_t *message, \
- int size, uint32_t message_size, \
- uint32_t message_type)
+#define IPC_HANDLER(name) \
+ static void handle_##name(int fd, uint8_t *message, \
+ int size, uint32_t message_size, \
+ uint32_t message_type)
/**
* Emulates mkdir -p (creates any missing folders)
void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
/**
- * For the workspace "focus" event we send, along the usual "change" field,
- * also the current and previous workspace, in "current" and "old"
- * respectively.
+ * Generates a json workspace event. Returns a dynamically allocated yajl
+ * generator. Free with yajl_gen_free().
*/
-void ipc_send_workspace_focus_event(Con *current, Con *old);
+yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old);
+
+/**
+ * For the workspace events we send, along with the usual "change" field, also
+ * the workspace container in "current". For focus events, we send the
+ * previously focused workspace in "old".
+ */
+void ipc_send_workspace_event(const char *change, Con *current, Con *old);
/**
* For the window events we send, along the usual "change" field,
* For the barconfig update events, we send the serialized barconfig.
*/
void ipc_send_barconfig_update_event(Barconfig *barconfig);
+
+/**
+ * For the binding events, we send the serialized binding struct.
+ */
+void ipc_send_binding_event(const char *event_type, Binding *bind);
* infrastructure, we define a fallback. */
#if !defined(LOG)
void verboselog(char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__((format(printf, 1, 2)));
#define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__)
#endif
#if !defined(ELOG)
void errorlog(char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__((format(printf, 1, 2)));
#define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__)
#endif
#if !defined(DLOG)
void debuglog(char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__((format(printf, 1, 2)));
#define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
* to prevent accidentally using freed memory.
*
*/
-#define I3STRING_FREE(str) \
-do { \
- if (str != NULL) { \
- i3string_free(str); \
- str = NULL; \
- } \
-} while (0)
+#define I3STRING_FREE(str) \
+ do { \
+ if (str != NULL) { \
+ i3string_free(str); \
+ str = NULL; \
+ } \
+ } while (0)
/**
* Returns the UTF-8 encoded version of the i3String.
*
*/
uint32_t get_mod_mask_for(uint32_t keysym,
- xcb_key_symbols_t *symbols,
- xcb_get_modifier_mapping_reply_t *modmap_reply);
+ xcb_key_symbols_t *symbols,
+ xcb_get_modifier_mapping_reply_t *modmap_reply);
/**
* Loads a font for usage, also getting its height. If fallback is true,
*
*/
void draw_text(i3String *text, xcb_drawable_t drawable,
- xcb_gcontext_t gc, int x, int y, int max_width);
+ xcb_gcontext_t gc, int x, int y, int max_width);
/**
* ASCII version of draw_text to print static strings.
*
*/
void draw_text_ascii(const char *text, xcb_drawable_t drawable,
- xcb_gcontext_t gc, int x, int y, int max_width);
+ xcb_gcontext_t gc, int x, int y, int max_width);
/**
* Predict the text width in pixels for the given text. Text must be
*
*/
void debuglog(char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__((format(printf, 1, 2)));
/**
* Logs the given message to stdout while prefixing the current time to it.
*
*/
void errorlog(char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__((format(printf, 1, 2)));
/**
* Logs the given message to stdout while prefixing the current time to it,
*
*/
void verboselog(char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__((format(printf, 1, 2)));
/**
* Deletes the unused log files. Useful if i3 exits immediately, eg.
#pragma once
/**
- * Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT,
+ * Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT,
* TOK_UP, TOK_DOWN from cmdparse.l)
*
*/
-void tree_move(int direction);
+void tree_move(Con *con, int direction);
/*
* Singly-linked List definitions.
*/
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
+#define SLIST_HEAD(name, type) \
+ struct name { \
+ struct type *slh_first; /* first element */ \
+ }
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
+#define SLIST_ENTRY(type) \
+ struct { \
+ struct type *sle_next; /* next element */ \
+ }
/*
* Singly-linked List access methods.
*/
-#define SLIST_FIRST(head) ((head)->slh_first)
-#define SLIST_END(head) NULL
-#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-#define SLIST_FOREACH(var, head, field) \
- for((var) = SLIST_FIRST(head); \
- (var) != SLIST_END(head); \
- (var) = SLIST_NEXT(var, field))
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); \
- ((var) = *(varp)) != SLIST_END(head); \
- (varp) = &SLIST_NEXT((var), field))
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
/*
* Singly-linked List functions.
*/
-#define SLIST_INIT(head) { \
- SLIST_FIRST(head) = SLIST_END(head); \
-}
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- (elm)->field.sle_next = (slistelm)->field.sle_next; \
- (slistelm)->field.sle_next = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.sle_next = (head)->slh_first; \
- (head)->slh_first = (elm); \
-} while (0)
-
-#define SLIST_REMOVE_NEXT(head, elm, field) do { \
- (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- (head)->slh_first = (head)->slh_first->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- if ((head)->slh_first == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } else { \
- struct type *curelm = (head)->slh_first; \
- \
- while (curelm->field.sle_next != (elm)) \
- curelm = curelm->field.sle_next; \
- curelm->field.sle_next = \
- curelm->field.sle_next->field.sle_next; \
- _Q_INVALIDATE((elm)->field.sle_next); \
- } \
-} while (0)
+#define SLIST_INIT(head) \
+ { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+ }
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) \
+ do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+ } while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+ } while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) \
+ do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+ } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) \
+ do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+ } while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) \
+ do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+ } while (0)
/*
* List definitions.
*/
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
+#define LIST_HEAD(name, type) \
+ struct name { \
+ struct type *lh_first; /* first element */ \
+ }
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
+#define LIST_ENTRY(type) \
+ struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+ }
/*
* List access methods
*/
-#define LIST_FIRST(head) ((head)->lh_first)
-#define LIST_END(head) NULL
-#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-#define LIST_FOREACH(var, head, field) \
- for((var) = LIST_FIRST(head); \
- (var)!= LIST_END(head); \
- (var) = LIST_NEXT(var, field))
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST(head); \
+ (var) != LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
/*
* List functions.
*/
-#define LIST_INIT(head) do { \
- LIST_FIRST(head) = LIST_END(head); \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
- (listelm)->field.le_next->field.le_prev = \
- &(elm)->field.le_next; \
- (listelm)->field.le_next = (elm); \
- (elm)->field.le_prev = &(listelm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- (elm)->field.le_next = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &(elm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.le_next = (head)->lh_first) != NULL) \
- (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
- (head)->lh_first = (elm); \
- (elm)->field.le_prev = &(head)->lh_first; \
-} while (0)
-
-#define LIST_REMOVE(elm, field) do { \
- if ((elm)->field.le_next != NULL) \
- (elm)->field.le_next->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = (elm)->field.le_next; \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
-
-#define LIST_REPLACE(elm, elm2, field) do { \
- if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
- (elm2)->field.le_next->field.le_prev = \
- &(elm2)->field.le_next; \
- (elm2)->field.le_prev = (elm)->field.le_prev; \
- *(elm2)->field.le_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
+#define LIST_INIT(head) \
+ do { \
+ LIST_FIRST(head) = LIST_END(head); \
+ } while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) \
+ do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+ } while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+ } while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next; \
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+ } while (0)
+
+#define LIST_REMOVE(elm, field) \
+ do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+ } while (0)
+
+#define LIST_REPLACE(elm, elm2, field) \
+ do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+ } while (0)
/*
* Simple queue definitions.
*/
-#define SIMPLEQ_HEAD(name, type) \
-struct name { \
- struct type *sqh_first; /* first element */ \
- struct type **sqh_last; /* addr of last next element */ \
-}
+#define SIMPLEQ_HEAD(name, type) \
+ struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+ }
-#define SIMPLEQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).sqh_first }
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
-#define SIMPLEQ_ENTRY(type) \
-struct { \
- struct type *sqe_next; /* next element */ \
-}
+#define SIMPLEQ_ENTRY(type) \
+ struct { \
+ struct type *sqe_next; /* next element */ \
+ }
/*
* Simple queue access methods.
*/
-#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
-#define SIMPLEQ_END(head) NULL
-#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
-#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
-#define SIMPLEQ_FOREACH(var, head, field) \
- for((var) = SIMPLEQ_FIRST(head); \
- (var) != SIMPLEQ_END(head); \
- (var) = SIMPLEQ_NEXT(var, field))
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
/*
* Simple queue functions.
*/
-#define SIMPLEQ_INIT(head) do { \
- (head)->sqh_first = NULL; \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
-
-#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (head)->sqh_first = (elm); \
-} while (0)
-
-#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.sqe_next = NULL; \
- *(head)->sqh_last = (elm); \
- (head)->sqh_last = &(elm)->field.sqe_next; \
-} while (0)
-
-#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (listelm)->field.sqe_next = (elm); \
-} while (0)
-
-#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
- if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
+#define SIMPLEQ_INIT(head) \
+ do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+ } while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+ } while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ } while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+ } while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) \
+ do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+ } while (0)
/*
* Tail queue definitions.
*/
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
-}
+#define TAILQ_HEAD(name, type) \
+ struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ }
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first }
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
-}
+#define TAILQ_ENTRY(type) \
+ struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ }
/*
* tail queue access methods
*/
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-#define TAILQ_END(head) NULL
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-#define TAILQ_EMPTY(head) \
- (TAILQ_FIRST(head) == TAILQ_END(head))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
-#define TAILQ_FOREACH(var, head, field) \
- for((var) = TAILQ_FIRST(head); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_NEXT(var, field))
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for((var) = TAILQ_LAST(head, headname); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_PREV(var, headname, field))
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
-#define TAILQ_INIT(head) do { \
- (head)->tqh_first = NULL; \
- (head)->tqh_last = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
- (head)->tqh_first->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (head)->tqh_first = (elm); \
- (elm)->field.tqe_prev = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.tqe_next = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
- (elm)->field.tqe_next->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (listelm)->field.tqe_next = (elm); \
- (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- (elm)->field.tqe_next = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- if (((elm)->field.tqe_next) != NULL) \
- (elm)->field.tqe_next->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
-
-#define TAILQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
- (elm2)->field.tqe_next->field.tqe_prev = \
- &(elm2)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm2)->field.tqe_next; \
- (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
- *(elm2)->field.tqe_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
+#define TAILQ_INIT(head) \
+ do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+ } while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+ } while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ } while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+ } while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+ } while (0)
+
+#define TAILQ_REMOVE(head, elm, field) \
+ do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+ } while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) \
+ do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+ } while (0)
/* Swaps two consecutive elements. 'second' *MUST* follow 'first' */
-#define TAILQ_SWAP(first, second, head, field) do { \
- *((first)->field.tqe_prev) = (second); \
- (second)->field.tqe_prev = (first)->field.tqe_prev; \
- (first)->field.tqe_prev = &((second)->field.tqe_next); \
- (first)->field.tqe_next = (second)->field.tqe_next; \
- if ((second)->field.tqe_next) \
- (second)->field.tqe_next->field.tqe_prev = &((first)->field.tqe_next); \
- (second)->field.tqe_next = first; \
- if ((head)->tqh_last == &((second)->field.tqe_next)) \
- (head)->tqh_last = &((first)->field.tqe_next); \
-} while (0)
+#define TAILQ_SWAP(first, second, head, field) \
+ do { \
+ *((first)->field.tqe_prev) = (second); \
+ (second)->field.tqe_prev = (first)->field.tqe_prev; \
+ (first)->field.tqe_prev = &((second)->field.tqe_next); \
+ (first)->field.tqe_next = (second)->field.tqe_next; \
+ if ((second)->field.tqe_next) \
+ (second)->field.tqe_next->field.tqe_prev = &((first)->field.tqe_next); \
+ (second)->field.tqe_next = first; \
+ if ((head)->tqh_last == &((second)->field.tqe_next)) \
+ (head)->tqh_last = &((first)->field.tqe_next); \
+ } while (0)
/*
* Circular queue definitions.
*/
-#define CIRCLEQ_HEAD(name, type) \
-struct name { \
- struct type *cqh_first; /* first element */ \
- struct type *cqh_last; /* last element */ \
-}
+#define CIRCLEQ_HEAD(name, type) \
+ struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+ }
-#define CIRCLEQ_HEAD_INITIALIZER(head) \
- { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
-#define CIRCLEQ_ENTRY(type) \
-struct { \
- struct type *cqe_next; /* next element */ \
- struct type *cqe_prev; /* previous element */ \
-}
+#define CIRCLEQ_ENTRY(type) \
+ struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+ }
/*
* Circular queue access methods
*/
-#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
-#define CIRCLEQ_LAST(head) ((head)->cqh_last)
-#define CIRCLEQ_END(head) ((void *)(head))
-#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
-#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
-#define CIRCLEQ_EMPTY(head) \
- (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
-
-#define CIRCLEQ_FOREACH(var, head, field) \
- for((var) = CIRCLEQ_FIRST(head); \
- (var) != CIRCLEQ_END(head); \
- (var) = CIRCLEQ_NEXT(var, field))
-
-#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
- for((var) = CIRCLEQ_LAST(head); \
- (var) != CIRCLEQ_END(head); \
- (var) = CIRCLEQ_PREV(var, field))
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
/*
* Circular queue functions.
*/
-#define CIRCLEQ_INIT(head) do { \
- (head)->cqh_first = CIRCLEQ_END(head); \
- (head)->cqh_last = CIRCLEQ_END(head); \
-} while (0)
-
-#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- (elm)->field.cqe_next = (listelm)->field.cqe_next; \
- (elm)->field.cqe_prev = (listelm); \
- if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm); \
- else \
- (listelm)->field.cqe_next->field.cqe_prev = (elm); \
- (listelm)->field.cqe_next = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
- (elm)->field.cqe_next = (listelm); \
- (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
- if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm); \
- else \
- (listelm)->field.cqe_prev->field.cqe_next = (elm); \
- (listelm)->field.cqe_prev = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.cqe_next = (head)->cqh_first; \
- (elm)->field.cqe_prev = CIRCLEQ_END(head); \
- if ((head)->cqh_last == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm); \
- else \
- (head)->cqh_first->field.cqe_prev = (elm); \
- (head)->cqh_first = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.cqe_next = CIRCLEQ_END(head); \
- (elm)->field.cqe_prev = (head)->cqh_last; \
- if ((head)->cqh_first == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm); \
- else \
- (head)->cqh_last->field.cqe_next = (elm); \
- (head)->cqh_last = (elm); \
-} while (0)
-
-#define CIRCLEQ_REMOVE(head, elm, field) do { \
- if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm)->field.cqe_prev; \
- else \
- (elm)->field.cqe_next->field.cqe_prev = \
- (elm)->field.cqe_prev; \
- if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm)->field.cqe_next; \
- else \
- (elm)->field.cqe_prev->field.cqe_next = \
- (elm)->field.cqe_next; \
- _Q_INVALIDATE((elm)->field.cqe_prev); \
- _Q_INVALIDATE((elm)->field.cqe_next); \
-} while (0)
-
-#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
- CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm2); \
- else \
- (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
- if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
- CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm2); \
- else \
- (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
- _Q_INVALIDATE((elm)->field.cqe_prev); \
- _Q_INVALIDATE((elm)->field.cqe_next); \
-} while (0)
+#define CIRCLEQ_INIT(head) \
+ do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) \
+ do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+ } while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) \
+ do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+ } while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) \
+ do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+ } while (0)
#ifndef _sd_printf_attr_
#if __GNUC__ >= 4
-#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+#define _sd_printf_attr_(a, b) __attribute__((format(printf, a, b)))
#else
-#define _sd_printf_attr_(a,b)
+#define _sd_printf_attr_(a, b)
#endif
#endif
#ifndef _sd_hidden_
#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
-#define _sd_hidden_ __attribute__ ((visibility("hidden")))
+#define _sd_hidden_ __attribute__((visibility("hidden")))
#else
#define _sd_hidden_
#endif
This is similar to printk() usage in the kernel.
*/
-#define SD_EMERG "<0>" /* system is unusable */
-#define SD_ALERT "<1>" /* action must be taken immediately */
-#define SD_CRIT "<2>" /* critical conditions */
-#define SD_ERR "<3>" /* error conditions */
-#define SD_WARNING "<4>" /* warning conditions */
-#define SD_NOTICE "<5>" /* normal but significant condition */
-#define SD_INFO "<6>" /* informational */
-#define SD_DEBUG "<7>" /* debug-level messages */
+#define SD_EMERG "<0>" /* system is unusable */
+#define SD_ALERT "<1>" /* action must be taken immediately */
+#define SD_CRIT "<2>" /* critical conditions */
+#define SD_ERR "<3>" /* error conditions */
+#define SD_WARNING "<4>" /* warning conditions */
+#define SD_NOTICE "<5>" /* normal but significant condition */
+#define SD_INFO "<6>" /* informational */
+#define SD_DEBUG "<7>" /* debug-level messages */
/* The first passed file descriptor is fd 3 */
#define SD_LISTEN_FDS_START 3
See sd_notifyf(3) for more information.
*/
-int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3) _sd_hidden_;
+int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2, 3) _sd_hidden_;
/*
Returns > 0 if the system was booted with systemd. Returns < 0 on
* (immediately), the application is reparented to init (process-id 1), which
* correctly handles childs, so we don’t have to do it :-).
*
- * The shell is determined by looking for the SHELL environment variable. If
- * it does not exist, /bin/sh is used.
+ * The shell used to start applications is the system's bourne shell (i.e.,
+ * /bin/sh).
*
* The no_startup_id flag determines whether a startup notification context
* (and ID) should be created, which is the default and encouraged behavior.
*
*/
struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
- xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader);
+ 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
#include "data.h"
#define die(...) errx(EXIT_FAILURE, __VA_ARGS__);
-#define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); }
+#define exit_if_null(pointer, ...) \
+ { \
+ if (pointer == NULL) \
+ die(__VA_ARGS__); \
+ }
#define STARTS_WITH(string, needle) (strncasecmp(string, needle, strlen(needle)) == 0)
-#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? \
- CIRCLEQ_NEXT(elm, field) : NULL)
-#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \
- CIRCLEQ_PREV(elm, field) : NULL)
-#define FOR_TABLE(workspace) \
- for (int cols = 0; cols < (workspace)->cols; cols++) \
- for (int rows = 0; rows < (workspace)->rows; rows++)
-
-#define NODES_FOREACH(head) \
- for (Con *child = (Con*)-1; (child == (Con*)-1) && ((child = 0), true);) \
- TAILQ_FOREACH(child, &((head)->nodes_head), nodes)
-
-#define NODES_FOREACH_REVERSE(head) \
- for (Con *child = (Con*)-1; (child == (Con*)-1) && ((child = 0), true);) \
- TAILQ_FOREACH_REVERSE(child, &((head)->nodes_head), nodes_head, nodes)
+#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL)
+#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL)
+#define FOR_TABLE(workspace) \
+ for (int cols = 0; cols < (workspace)->cols; cols++) \
+ for (int rows = 0; rows < (workspace)->rows; rows++)
+
+#define NODES_FOREACH(head) \
+ for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
+ TAILQ_FOREACH(child, &((head)->nodes_head), nodes)
+
+#define NODES_FOREACH_REVERSE(head) \
+ for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \
+ TAILQ_FOREACH_REVERSE(child, &((head)->nodes_head), nodes_head, nodes)
/* greps the ->nodes of the given head and returns the first node that matches the given condition */
#define GREP_FIRST(dest, head, condition) \
- NODES_FOREACH(head) { \
- if (!(condition)) \
- continue; \
- \
- (dest) = child; \
- break; \
+ NODES_FOREACH(head) { \
+ if (!(condition)) \
+ continue; \
+ \
+ (dest) = child; \
+ break; \
}
-#define FREE(pointer) do { \
+#define FREE(pointer) \
+ do { \
if (pointer != NULL) { \
- free(pointer); \
- pointer = NULL; \
- } \
-} \
-while (0)
+ free(pointer); \
+ pointer = NULL; \
+ } \
+ } while (0)
-#define CALL(obj, member, ...) obj->member(obj, ## __VA_ARGS__)
+#define CALL(obj, member, ...) obj->member(obj, ##__VA_ARGS__)
int min(int a, int b);
int max(int a, int b);
* Returns the next workspace.
*
*/
-Con* workspace_next(void);
+Con *workspace_next(void);
/**
* Returns the previous workspace.
*
*/
-Con* workspace_prev(void);
+Con *workspace_prev(void);
/**
* Returns the next workspace on the same output
*
*/
-Con* workspace_next_on_output(void);
+Con *workspace_next_on_output(void);
/**
* Returns the previous workspace on the same output
*
*/
-Con* workspace_prev_on_output(void);
+Con *workspace_prev_on_output(void);
/**
* Focuses the previously focused workspace.
*/
Con *workspace_back_and_forth_get(void);
-
#if 0
/**
* Assigns the given workspace to the given screen by correctly updating its
#include "data.h"
#include "xcursor.h"
-#define _NET_WM_STATE_REMOVE 0
-#define _NET_WM_STATE_ADD 1
-#define _NET_WM_STATE_TOGGLE 2
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
/** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a
* constant for that. */
-#define XCB_CURSOR_LEFT_PTR 68
+#define XCB_CURSOR_LEFT_PTR 68
#define XCB_CURSOR_SB_H_DOUBLE_ARROW 108
#define XCB_CURSOR_SB_V_DOUBLE_ARROW 116
#define XCB_CURSOR_WATCH 150
/* from X11/keysymdef.h */
-#define XCB_NUM_LOCK 0xff7f
+#define XCB_NUM_LOCK 0xff7f
/* The event masks are defined here because we don’t only set them once but we
need to set slight variations of them (without XCB_EVENT_MASK_ENTER_WINDOW
while rendering the layout) */
/** The XCB_CW_EVENT_MASK for the child (= real window) */
-#define CHILD_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
+#define CHILD_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
XCB_EVENT_MASK_FOCUS_CHANGE)
/** The XCB_CW_EVENT_MASK for its frame */
-#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \
- XCB_EVENT_MASK_BUTTON_RELEASE | \
- XCB_EVENT_MASK_POINTER_MOTION | /* …mouse is moved */ \
- XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \
- XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* …the frame gets destroyed */ \
+#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | /* …mouse is moved */ \
+ XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* …the frame gets destroyed */ \
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | /* …the application tries to resize itself */ \
- XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \
XCB_EVENT_MASK_ENTER_WINDOW) /* …user moves cursor inside our window */
-#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
- XCB_EVENT_MASK_BUTTON_PRESS | \
- XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video \
- projector), the root window gets a \
- ConfigureNotify */ \
- XCB_EVENT_MASK_POINTER_MOTION | \
- XCB_EVENT_MASK_PROPERTY_CHANGE | \
- XCB_EVENT_MASK_ENTER_WINDOW)
+#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video \
+ projector), the root window gets a \
+ ConfigureNotify */ \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
-#define xmacro(atom) xcb_atom_t A_ ## atom;
+#define xmacro(atom) xcb_atom_t A_##atom;
#include "atoms.xmacro"
#undef xmacro
*
*/
xcb_window_t create_window(xcb_connection_t *conn, Rect r, uint16_t depth, xcb_visualid_t visual,
- uint16_t window_class, enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values);
+ uint16_t window_class, enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values);
/**
* Draws a line from x,y to to_x,to_y using the given color
*/
void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r);
-
bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
/**
#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))
+#define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
+#define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
#define ygenalloc() yajl_gen_alloc(NULL)
#define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, client)
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
#include "libi3.h"
#include <math.h>
int logical_px(const int logical) {
const int dpi = (double)root_screen->height_in_pixels * 25.4 /
(double)root_screen->height_in_millimeters;
+ /* There are many misconfigurations out there, i.e. systems with screens
+ * whose dpi is in fact higher than 96 dpi, but not significantly higher,
+ * so software was never adapted. We could tell people to reconfigure their
+ * systems to 96 dpi in order to get the behavior they expect/are used to,
+ * but since we can easily detect this case in code, let’s do it for them.
+ */
+ if ((dpi / 96.0) < 1.25)
+ return logical;
return ceil((dpi / 96.0) * logical);
}
i3Font font;
font.type = FONT_TYPE_NONE;
+ /* No XCB connction, return early because we're just validating the
+ * configuration file. */
+ if (conn == NULL) {
+ return font;
+ }
+
#if PANGO_SUPPORT
/* Try to load a pango font if specified */
if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) {
It tries to start one of the following (in that order):
* $TERMINAL (this is a non-standard variable)
+* x-terminal-emulator (only present on Debian and derivatives)
* urxvt
* rxvt
* terminator
bindsym Mod1+v split v
# enter fullscreen mode for the focused container
-bindsym Mod1+f fullscreen
+bindsym Mod1+f fullscreen toggle
# change container layout (stacked, tabbed, default)
bindsym Mod1+s layout stacking
end
-> call cmd_kill($kill_mode)
+# fullscreen enable|toggle [global]
+# fullscreen disable
# fullscreen [global]
state FULLSCREEN:
- fullscreen_mode = 'global'
- -> call cmd_fullscreen($fullscreen_mode)
+ action = 'disable'
+ -> call cmd_fullscreen($action, "output")
+ action = 'enable', 'toggle'
+ -> FULLSCREEN_MODE
+ action = ''
+ -> FULLSCREEN_COMPAT
+
+state FULLSCREEN_MODE:
+ mode = 'global'
+ -> call cmd_fullscreen($action, $mode)
end
- -> call cmd_fullscreen($fullscreen_mode)
+ -> call cmd_fullscreen($action, "output")
+
+state FULLSCREEN_COMPAT:
+ mode = 'global'
+ -> call cmd_fullscreen("toggle", $mode)
+ end
+ -> call cmd_fullscreen("toggle", "output")
# split v|h|vertical|horizontal
state SPLIT:
state BINDING:
release = '--release'
->
+ whole_window = '--whole-window'
+ ->
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
->
'+'
state BINDCOMMAND:
release = '--release'
->
+ whole_window = '--whole-window'
+ ->
command = string
- -> call cfg_binding($bindtype, $modifiers, $key, $release, $command)
+ -> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command)
################################################################################
# Mode configuration
state MODE_BINDCOMMAND:
release = '--release'
->
+ whole_window = '--whole-window'
+ ->
command = string
- -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $command); MODE
+ -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $whole_window, $command); MODE
################################################################################
# Bar configuration (i3bar)
'hidden_state' -> BAR_HIDDEN_STATE
'id' -> BAR_ID
'modifier' -> BAR_MODIFIER
+ 'wheel_up_cmd' -> BAR_WHEEL_UP_CMD
+ 'wheel_down_cmd' -> BAR_WHEEL_DOWN_CMD
'position' -> BAR_POSITION
'output' -> BAR_OUTPUT
'tray_output' -> BAR_TRAY_OUTPUT
modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift'
-> call cfg_bar_modifier($modifier); BAR
+state BAR_WHEEL_UP_CMD:
+ command = string
+ -> call cfg_bar_wheel_up_cmd($command); BAR
+
+state BAR_WHEEL_DOWN_CMD:
+ command = string
+ -> call cfg_bar_wheel_down_cmd($command); BAR
+
state BAR_POSITION:
position = 'top', 'bottom'
-> call cfg_bar_position($position); BAR
-> call cfg_bar_output($output); BAR
state BAR_TRAY_OUTPUT:
- output = string
+ output = word
-> call cfg_bar_tray_output($output); BAR
state BAR_FONT:
/* Check if any assignments match */
Assignment *current;
- TAILQ_FOREACH (current, &assignments, assignments) {
+ TAILQ_FOREACH(current, &assignments, assignments) {
if (!match_matches_window(&(current->match), window))
continue;
Assignment *assignment_for(i3Window *window, int type) {
Assignment *assignment;
- TAILQ_FOREACH (assignment, &assignments, assignments) {
+ TAILQ_FOREACH(assignment, &assignments, assignments) {
if ((type != A_ANY && (assignment->type & type) == 0) ||
!match_matches_window(&(assignment->match), window))
continue;
*/
#include "all.h"
+#include <xkbcommon/xkbcommon.h>
+
pid_t command_error_nagbar_pid = -1;
/*
struct Mode *mode;
/* Try to find the mode in the list of modes and return it */
- SLIST_FOREACH (mode, &modes, modes) {
+ SLIST_FOREACH(mode, &modes, modes) {
if (strcmp(mode->name, name) == 0)
return mode;
}
*
*/
Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
- const char *release, const char *command, const char *modename) {
+ const char *release, const char *whole_window, const char *command, const char *modename) {
Binding *new_binding = scalloc(sizeof(Binding));
DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release);
new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS);
+ new_binding->whole_window = (whole_window != NULL);
if (strcmp(bindtype, "bindsym") == 0) {
new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
? B_MOUSE
*/
void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
Binding *bind;
- TAILQ_FOREACH (bind, bindings, bindings) {
+ TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type != B_KEYBOARD ||
(bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
(!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
if (!is_release) {
/* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS
* bindings back to B_UPON_KEYRELEASE */
- TAILQ_FOREACH (bind, bindings, bindings) {
+ TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type != input_type)
continue;
if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
}
}
- TAILQ_FOREACH (bind, bindings, bindings) {
+ TAILQ_FOREACH(bind, bindings, bindings) {
/* First compare the modifiers (unless this is a
* B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
* event) */
min_keycode = xcb_get_setup(conn)->min_keycode;
max_keycode = xcb_get_setup(conn)->max_keycode;
- TAILQ_FOREACH (bind, bindings, bindings) {
+ TAILQ_FOREACH(bind, bindings, bindings) {
if (bind->input_type == B_MOUSE) {
int button = atoi(bind->symbol + (sizeof("button") - 1));
bind->keycode = button;
continue;
/* We need to translate the symbol to a keycode */
- keysym = XStringToKeysym(bind->symbol);
- if (keysym == NoSymbol) {
+ keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS);
+ if (keysym == XKB_KEY_NoSymbol) {
ELOG("Could not translate string to key symbol: \"%s\"\n",
bind->symbol);
continue;
DLOG("Switching to mode %s\n", new_mode);
- SLIST_FOREACH (mode, &modes, modes) {
+ SLIST_FOREACH(mode, &modes, modes) {
if (strcasecmp(mode->name, new_mode) != 0)
continue;
*/
void check_for_duplicate_bindings(struct context *context) {
Binding *bind, *current;
- TAILQ_FOREACH (current, bindings, bindings) {
- TAILQ_FOREACH (bind, bindings, bindings) {
+ TAILQ_FOREACH(current, bindings, bindings) {
+ TAILQ_FOREACH(bind, bindings, bindings) {
/* Abort when we reach the current keybinding, only check the
* bindings before */
if (bind == current)
}
/*
- * Runs the given binding and handles parse errors. Returns a CommandResult for
- * running the binding's command. Caller should render tree if
- * needs_tree_render is true. Free with command_result_free().
+ * Creates a dynamically allocated copy of bind.
+ */
+static Binding *binding_copy(Binding *bind) {
+ Binding *ret = smalloc(sizeof(Binding));
+ *ret = *bind;
+ if (bind->symbol != NULL)
+ ret->symbol = strdup(bind->symbol);
+ if (bind->command != NULL)
+ ret->command = strdup(bind->command);
+ if (bind->translated_to != NULL) {
+ ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes);
+ memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes);
+ }
+ return ret;
+}
+
+/*
+ * Frees the binding. If bind is null, it simply returns.
+ */
+void binding_free(Binding *bind) {
+ if (bind == NULL) {
+ return;
+ }
+
+ FREE(bind->symbol);
+ FREE(bind->translated_to);
+ FREE(bind->command);
+ FREE(bind);
+}
+
+/*
+ * Runs the given binding and handles parse errors. If con is passed, it will
+ * execute the command binding with that container selected by criteria.
+ * Returns a CommandResult for running the binding's command. Caller should
+ * render tree if needs_tree_render is true. Free with command_result_free().
*
*/
-CommandResult *run_binding(Binding *bind) {
- /* We need to copy the command since “reload” may be part of the command,
- * and then the memory that bind->command points to may not contain the
+CommandResult *run_binding(Binding *bind, Con *con) {
+ char *command;
+
+ /* We need to copy the binding and command since “reload” may be part of
+ * the command, and then the memory that bind points to may not contain the
* same data anymore. */
- char *command_copy = sstrdup(bind->command);
- CommandResult *result = parse_command(command_copy, NULL);
- free(command_copy);
+ if (con == NULL)
+ command = sstrdup(bind->command);
+ else
+ sasprintf(&command, "[con_id=\"%d\"] %s", con, bind->command);
+
+ Binding *bind_cp = binding_copy(bind);
+ CommandResult *result = parse_command(command, NULL);
+ free(command);
if (result->needs_tree_render)
tree_render();
free(pageraction);
}
- /* TODO: emit event for running a binding */
+ ipc_send_binding_event("run", bind_cp);
+ binding_free(bind_cp);
return result;
}
if (con->parent->type == CT_DOCKAREA)
goto done;
+ /* if the user has bound an action to this click, it should override the
+ * default behavior. */
+ if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) {
+ Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
+ /* clicks over a window decoration will always trigger the binding and
+ * clicks on the inside of the window will only trigger a binding if
+ * the --whole-window flag was given for the binding. */
+ if (bind && (dest == CLICK_DECORATION || bind->whole_window)) {
+ CommandResult *result = run_binding(bind, con);
+
+ /* ASYNC_POINTER eats the event */
+ xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
+ xcb_flush(conn);
+
+ if (result->needs_tree_render)
+ tree_render();
+
+ command_result_free(result);
+
+ return 0;
+ }
+ }
+
/* 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. */
if (ws != focused_workspace)
workspace_show(ws);
- focused_id = XCB_NONE;
/* get the floating con */
Con *floatingcon = con_inside_floating(con);
xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
xcb_flush(conn);
tree_render();
+
return 0;
}
* click coordinates and focus the output's active workspace. */
if (event->event == root) {
Con *output, *ws;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
if (con_is_internal(output) ||
!rect_contains(output->rect, event->event_x, event->event_y))
continue;
/* Check if the click was on the decoration of a child */
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (!rect_contains(child->deco_rect, event->event_x, event->event_y))
continue;
}
TAILQ_INIT(&owindows);
/* copy all_cons */
- TAILQ_FOREACH (con, &all_cons, all_cons) {
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
ow = smalloc(sizeof(owindow));
ow->con = con;
TAILQ_INSERT_TAIL(&owindows, ow, owindows);
}
}
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
}
}
return;
}
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
LOG("should move window to workspace %s\n", name);
/* get the workspace */
- Con *ws = workspace_get(name, NULL);
+ Con *ws = NULL;
+ Con *output = NULL;
+
+ /* first look for a workspace with this name */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
+ }
+
+ /* if the name is plain digits, we interpret this as a "workspace number"
+ * command */
+ if (!ws && name_is_digits(name)) {
+ long parsed_num = ws_name_to_number(name);
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ GREP_FIRST(ws, output_get_content(output),
+ child->num == parsed_num);
+ }
+ }
+
+ /* if no workspace was found, make a new one */
+ if (!ws)
+ ws = workspace_get(name, NULL);
ws = maybe_auto_back_and_forth_workspace(ws);
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
/* get the workspace */
Con *output, *workspace = NULL;
- char *endptr = NULL;
- long parsed_num = strtol(which, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- endptr == which) {
+ long parsed_num = ws_name_to_number(which);
+
+ if (parsed_num == -1) {
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
// TODO: better error message
yerror("Could not parse number");
return;
}
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output),
- child->num == parsed_num);
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(output),
+ child->num == parsed_num);
if (!workspace) {
workspace = workspace_get(which, NULL);
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, workspace, true, false);
}
/* Ensure all the other children have a percentage set. */
Con *child;
- TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
LOG("child->percent = %f (child %p)\n", child->percent, child);
if (child->percent == 0.0)
child->percent = percentage;
LOG("subtract_percent = %f\n", subtract_percent);
/* Ensure that the new percentages are positive and greater than
* 0.05 to have a reasonable minimum size. */
- TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
if (child == current)
continue;
if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) {
current->percent += ((double)ppt / 100.0);
LOG("current->percent after = %f\n", current->percent);
- TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) {
if (child == current)
continue;
child->percent -= subtract_percent;
HANDLE_EMPTY_MATCH;
owindow *current;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
/* Don't handle dock windows (issue #1201) */
if (current->con->window && current->con->window->dock) {
DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con);
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
int border_style = current->con->border_style;
char *end;
restore_open_placeholder_windows(parent);
if (content == JSON_CONTENT_WORKSPACE)
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"restored\"}");
+ ipc_send_workspace_event("restored", parent, NULL);
cmd_output->needs_tree_render = true;
}
DLOG("which=%s\n", which);
+ if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
+ LOG("Cannot switch workspace while in global fullscreen\n");
+ ysuccess(false);
+ return;
+ }
+
if (strcmp(which, "next") == 0)
ws = workspace_next();
else if (strcmp(which, "prev") == 0)
void cmd_workspace_number(I3_CMD, char *which) {
Con *output, *workspace = NULL;
- char *endptr = NULL;
- long parsed_num = strtol(which, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- endptr == which) {
+ if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
+ LOG("Cannot switch workspace while in global fullscreen\n");
+ ysuccess(false);
+ return;
+ }
+
+ long parsed_num = ws_name_to_number(which);
+
+ if (parsed_num == -1) {
LOG("Could not parse initial part of \"%s\" as a number.\n", which);
// TODO: better error message
yerror("Could not parse number");
-
return;
}
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output),
- child->num == parsed_num);
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(output),
+ child->num == parsed_num);
if (!workspace) {
LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num);
*
*/
void cmd_workspace_back_and_forth(I3_CMD) {
+ if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
+ LOG("Cannot switch workspace while in global fullscreen\n");
+ ysuccess(false);
+ return;
+ }
+
workspace_back_and_forth();
cmd_output->needs_tree_render = true;
return;
}
+ if (con_get_fullscreen_con(croot, CF_GLOBAL)) {
+ LOG("Cannot switch workspace while in global fullscreen\n");
+ ysuccess(false);
+ return;
+ }
+
DLOG("should switch to workspace %s\n", name);
if (maybe_back_and_forth(cmd_output, name))
return;
- workspace_show_by_name(name);
+
+ Con *ws = NULL;
+ Con *output = NULL;
+
+ /* first look for a workspace with this name */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
+ }
+
+ /* if the name is only digits, we interpret this as a "workspace number"
+ * command */
+ if (!ws && name_is_digits(name)) {
+ long parsed_num = ws_name_to_number(name);
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ GREP_FIRST(ws, output_get_content(output),
+ child->num == parsed_num);
+ }
+ }
+
+ /* if no workspace was found, make a new one */
+ if (!ws)
+ ws = workspace_get(name, NULL);
+
+ workspace_show(ws);
cmd_output->needs_tree_render = true;
// XXX: default reply for now, make this a better reply
DLOG("Clearing all windows which have that mark first\n");
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons) {
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->mark && strcmp(con->mark, mark) == 0)
FREE(con->mark);
}
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
current->con->mark = sstrdup(mark);
}
void cmd_unmark(I3_CMD, char *mark) {
if (mark == NULL) {
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons) {
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
FREE(con->mark);
}
DLOG("removed all window marks");
} else {
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons) {
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
if (con->mark && strcmp(con->mark, mark) == 0)
FREE(con->mark);
}
Output *output;
// TODO: fix the handling of criteria
- TAILQ_FOREACH (current, &owindows, owindows)
- current_output = get_output_of_con(current->con);
+ TAILQ_FOREACH(current, &owindows, owindows)
+ current_output = get_output_of_con(current->con);
assert(current_output != NULL);
return;
}
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_move_to_workspace(current->con, ws, true, false);
}
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
if (strcmp(floating_mode, "toggle") == 0) {
DLOG("should toggle mode\n");
HANDLE_EMPTY_MATCH;
owindow *current;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
Output *current_output = get_output_of_con(current->con);
if (!current_output) {
ELOG("Cannot get current output. This is a bug in i3.\n");
/* check if we can find a workspace assigned to this output */
bool used_assignment = false;
struct Workspace_Assignment *assignment;
- TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->output, current_output->name) != 0)
continue;
/* check if this workspace is already attached to the tree */
Con *workspace = NULL, *out;
- TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(out),
- !strcasecmp(child->name, assignment->name));
+ TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(out),
+ !strcasecmp(child->name, assignment->name));
if (workspace != NULL)
continue;
create_workspace_on_output(current_output, ws->parent);
/* notify the IPC listeners */
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
+ ipc_send_workspace_event("init", ws, NULL);
}
DLOG("Detaching\n");
/* fix the coordinates of the floating containers */
Con *floating_con;
- TAILQ_FOREACH (floating_con, &(ws->floating_head), floating_windows)
- floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
+ TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
+ floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}");
+ ipc_send_workspace_event("move", ws, NULL);
if (workspace_was_visible) {
/* Focus the moved workspace on the destination output. */
workspace_show(ws);
* 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) {
+ TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
if (ws != previously_visible_ws)
continue;
if (match_is_empty(current_match))
tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ));
else {
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ));
}
if (match_is_empty(current_match))
tree_close_con(kill_mode);
else {
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
tree_close(current->con, kill_mode, false, false);
}
else
window_mode = "floating";
}
- TAILQ_FOREACH (current, &(ws->focus_head), focused) {
+ TAILQ_FOREACH(current, &(ws->focus_head), focused) {
if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) ||
(strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON))
continue;
Con *__i3_scratch = workspace_get("__i3_scratch", NULL);
int count = 0;
owindow *current;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
Con *ws = con_get_workspace(current->con);
/* If no workspace could be found, this was a dock window.
* Just skip it, you cannot focus dock windows. */
}
/*
- * Implementation of 'fullscreen [global]'.
+ * Implementation of 'fullscreen enable|toggle [global]' and
+ * 'fullscreen disable'
*
*/
-void cmd_fullscreen(I3_CMD, char *fullscreen_mode) {
- if (fullscreen_mode == NULL)
- fullscreen_mode = "output";
- DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode);
+void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) {
+ fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT;
+ DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode);
owindow *current;
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
- con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT));
+ if (strcmp(action, "toggle") == 0) {
+ con_toggle_fullscreen(current->con, mode);
+ } else if (strcmp(action, "enable") == 0) {
+ con_enable_fullscreen(current->con, mode);
+ } else if (strcmp(action, "disable") == 0) {
+ con_disable_fullscreen(current->con);
+ }
}
cmd_output->needs_tree_render = true;
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
int px = atoi(move_px);
- /* TODO: make 'move' work with criteria. */
- DLOG("moving in direction %s, px %s\n", direction, move_px);
- if (con_is_floating(focused)) {
- DLOG("floating move with %d pixels\n", px);
- Rect newrect = focused->parent->rect;
- if (strcmp(direction, "left") == 0) {
- newrect.x -= px;
- } else if (strcmp(direction, "right") == 0) {
- newrect.x += px;
- } else if (strcmp(direction, "up") == 0) {
- newrect.y -= px;
- } else if (strcmp(direction, "down") == 0) {
- newrect.y += px;
+ owindow *current;
+ HANDLE_EMPTY_MATCH;
+
+ Con *initially_focused = focused;
+
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ DLOG("moving in direction %s, px %s\n", direction, move_px);
+ if (con_is_floating(current->con)) {
+ DLOG("floating move with %d pixels\n", px);
+ Rect newrect = current->con->parent->rect;
+ if (strcmp(direction, "left") == 0) {
+ newrect.x -= px;
+ } else if (strcmp(direction, "right") == 0) {
+ newrect.x += px;
+ } else if (strcmp(direction, "up") == 0) {
+ newrect.y -= px;
+ } else if (strcmp(direction, "down") == 0) {
+ newrect.y += px;
+ }
+ floating_reposition(current->con->parent, newrect);
+ } else {
+ tree_move(current->con, (strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
+ cmd_output->needs_tree_render = true;
}
- floating_reposition(focused->parent, newrect);
- } else {
- tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN))));
- cmd_output->needs_tree_render = true;
}
+ /* the move command should not disturb focus */
+ if (focused != initially_focused)
+ con_focus(initially_focused);
+
// XXX: default reply for now, make this a better reply
ysuccess(true);
}
if (match_is_empty(current_match))
con_set_layout(focused, layout);
else {
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_set_layout(current->con, layout);
}
if (match_is_empty(current_match))
con_toggle_layout(focused, toggle_mode);
else {
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
con_toggle_layout(current->con, toggle_mode);
}
load_configuration(conn, NULL, true);
x_set_i3_atoms();
/* Send an IPC event just in case the ws names have changed */
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
+ ipc_send_workspace_event("reload", NULL, NULL);
/* Send an update event for the barconfig just in case it has changed */
update_barconfig();
Output *current_output = NULL;
Output *output;
- TAILQ_FOREACH (current, &owindows, owindows)
- current_output = get_output_of_con(current->con);
+ TAILQ_FOREACH(current, &owindows, owindows)
+ current_output = get_output_of_con(current->con);
assert(current_output != NULL);
output = get_output_from_string(current_output, name);
void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) {
int x = atoi(cx);
int y = atoi(cy);
+ bool has_error = false;
- if (!con_is_floating(focused)) {
- ELOG("Cannot change position. The window/container is not floating\n");
- yerror("Cannot change position. The window/container is not floating.");
- return;
- }
+ owindow *current;
+ HANDLE_EMPTY_MATCH;
- if (strcmp(method, "absolute") == 0) {
- focused->parent->rect.x = x;
- focused->parent->rect.y = y;
+ TAILQ_FOREACH(current, &owindows, owindows) {
+ if (!con_is_floating(current->con)) {
+ ELOG("Cannot change position. The window/container is not floating\n");
- DLOG("moving to absolute position %d %d\n", x, y);
- floating_maybe_reassign_ws(focused->parent);
- cmd_output->needs_tree_render = true;
- }
+ if (!has_error) {
+ yerror("Cannot change position of a window/container because it is not floating.");
+ has_error = true;
+ }
- if (strcmp(method, "position") == 0) {
- Rect newrect = focused->parent->rect;
+ continue;
+ }
- DLOG("moving to position %d %d\n", x, y);
- newrect.x = x;
- newrect.y = y;
+ if (strcmp(method, "absolute") == 0) {
+ current->con->parent->rect.x = x;
+ current->con->parent->rect.y = y;
- floating_reposition(focused->parent, newrect);
+ DLOG("moving to absolute position %d %d\n", x, y);
+ floating_maybe_reassign_ws(current->con->parent);
+ cmd_output->needs_tree_render = true;
+ }
+
+ if (strcmp(method, "position") == 0) {
+ Rect newrect = current->con->parent->rect;
+
+ DLOG("moving to position %d %d\n", x, y);
+ newrect.x = x;
+ newrect.y = y;
+
+ floating_reposition(current->con->parent, newrect);
+ }
}
// XXX: default reply for now, make this a better reply
- ysuccess(true);
+ if (!has_error)
+ ysuccess(true);
}
/*
HANDLE_EMPTY_MATCH;
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
scratchpad_move(current->con);
}
if (match_is_empty(current_match)) {
scratchpad_show(NULL);
} else {
- TAILQ_FOREACH (current, &owindows, owindows) {
+ TAILQ_FOREACH(current, &owindows, owindows) {
DLOG("matching: %p / %s\n", current->con, current->con->name);
scratchpad_show(current->con);
}
Con *output, *workspace = NULL;
if (old_name) {
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output),
- !strcasecmp(child->name, 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);
}
}
Con *check_dest = NULL;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(check_dest, output_get_content(output),
- !strcasecmp(child->name, new_name));
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ GREP_FIRST(check_dest, output_get_content(output),
+ !strcasecmp(child->name, new_name));
if (check_dest != NULL) {
// TODO: we should include the new workspace name here and use yajl for
/* Change the name and try to parse it as a number. */
FREE(workspace->name);
workspace->name = sstrdup(new_name);
- char *endptr = NULL;
- long parsed_num = strtol(new_name, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- endptr == new_name)
- workspace->num = -1;
- else
- workspace->num = parsed_num;
+
+ workspace->num = ws_name_to_number(new_name);
LOG("num = %d\n", workspace->num);
/* By re-attaching, the sort order will be correct afterwards. */
cmd_output->needs_tree_render = true;
ysuccess(true);
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}");
+ ipc_send_workspace_event("rename", workspace, NULL);
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+ ewmh_update_current_desktop();
}
/*
bool changed_sth = false;
Barconfig *current = NULL;
- TAILQ_FOREACH (current, &barconfigs, configs) {
+ TAILQ_FOREACH(current, &barconfigs, configs) {
if (bar_id && strcmp(current->id, bar_id) != 0)
continue;
bool changed_sth = false;
Barconfig *current = NULL;
- TAILQ_FOREACH (current, &barconfigs, configs) {
+ TAILQ_FOREACH(current, &barconfigs, configs) {
if (bar_id && strcmp(current->id, bar_id) != 0)
continue;
}
}
+/*
+ * Parses a string (or word, if as_word is true). Extracted out of
+ * parse_command so that it can be used in src/workspace.c for interpreting
+ * workspace commands.
+ *
+ */
+char *parse_string(const char **walk, bool as_word) {
+ const char *beginning = *walk;
+ /* Handle quoted strings (or words). */
+ if (**walk == '"') {
+ beginning++;
+ (*walk)++;
+ while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\'))
+ (*walk)++;
+ } else {
+ if (!as_word) {
+ /* For a string (starting with 's'), the delimiters are
+ * comma (,) and semicolon (;) which introduce a new
+ * operation or command, respectively. Also, newlines
+ * end a command. */
+ while (**walk != ';' && **walk != ',' &&
+ **walk != '\0' && **walk != '\r' &&
+ **walk != '\n')
+ (*walk)++;
+ } else {
+ /* For a word, the delimiters are white space (' ' or
+ * '\t'), closing square bracket (]), comma (,) and
+ * semicolon (;). */
+ while (**walk != ' ' && **walk != '\t' &&
+ **walk != ']' && **walk != ',' &&
+ **walk != ';' && **walk != '\r' &&
+ **walk != '\n' && **walk != '\0')
+ (*walk)++;
+ }
+ }
+ if (*walk == beginning)
+ return NULL;
+
+ char *str = scalloc(*walk - beginning + 1);
+ /* We copy manually to handle escaping of characters. */
+ int inpos, outpos;
+ for (inpos = 0, outpos = 0;
+ inpos < (*walk - beginning);
+ inpos++, outpos++) {
+ /* We only handle escaped double quotes to not break
+ * backwards compatibility with people using \w in
+ * regular expressions etc. */
+ if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
+ inpos++;
+ str[outpos] = beginning[inpos];
+ }
+
+ return str;
+}
+
/*
* Parses and executes the given command. If a caller-allocated yajl_gen is
* passed, a json reply will be generated in the format specified by the ipc
if (strcmp(token->name, "string") == 0 ||
strcmp(token->name, "word") == 0) {
- const char *beginning = walk;
- /* Handle quoted strings (or words). */
- if (*walk == '"') {
- beginning++;
- walk++;
- while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
- walk++;
- } else {
- if (token->name[0] == 's') {
- /* For a string (starting with 's'), the delimiters are
- * comma (,) and semicolon (;) which introduce a new
- * operation or command, respectively. Also, newlines
- * end a command. */
- while (*walk != ';' && *walk != ',' &&
- *walk != '\0' && *walk != '\r' &&
- *walk != '\n')
- walk++;
- } else {
- /* For a word, the delimiters are white space (' ' or
- * '\t'), closing square bracket (]), comma (,) and
- * semicolon (;). */
- while (*walk != ' ' && *walk != '\t' &&
- *walk != ']' && *walk != ',' &&
- *walk != ';' && *walk != '\r' &&
- *walk != '\n' && *walk != '\0')
- walk++;
- }
- }
- if (walk != beginning) {
- char *str = scalloc(walk - beginning + 1);
- /* We copy manually to handle escaping of characters. */
- int inpos, outpos;
- for (inpos = 0, outpos = 0;
- inpos < (walk - beginning);
- inpos++, outpos++) {
- /* We only handle escaped double quotes to not break
- * backwards compatibility with people using \w in
- * regular expressions etc. */
- if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
- inpos++;
- str[outpos] = beginning[inpos];
- }
+ char *str = parse_string(&walk, (token->name[0] != 's'));
+ if (str != NULL) {
if (token->identifier)
push_string(token->identifier, str);
/* If we are at the end of a quoted string, skip the ending
*
*/
#include "all.h"
-
-char *colors[] = {
- "#ff0000",
- "#00FF00",
- "#0000FF",
- "#ff00ff",
- "#00ffff",
- "#ffff00",
- "#aa0000",
- "#00aa00",
- "#0000aa",
- "#aa00aa"};
+#include "yajl_utils.h"
static void con_on_remove_child(Con *con);
new->depth = window->depth;
else
new->depth = XCB_COPY_FROM_PARENT;
- static int cnt = 0;
- DLOG("opening window %d\n", cnt);
-
- /* TODO: remove window coloring after test-phase */
- DLOG("color %s\n", colors[cnt]);
- new->name = strdup(colors[cnt]);
- //uint32_t cp = get_colorpixel(colors[cnt]);
- cnt++;
- if ((cnt % (sizeof(colors) / sizeof(char *))) == 0)
- cnt = 0;
+ DLOG("opening window\n");
TAILQ_INIT(&(new->floating_head));
TAILQ_INIT(&(new->nodes_head));
} else {
if (!ignore_focus) {
/* Get the first tiling container in focus stack */
- TAILQ_FOREACH (loop, &(parent->focus_head), focused) {
+ TAILQ_FOREACH(loop, &(parent->focus_head), focused) {
if (loop->type == CT_FLOATING_CON)
continue;
current = loop;
con->urgent = false;
con_update_parents_urgency(con);
workspace_update_urgent_flag(con_get_workspace(con));
+ ipc_send_window_event("urgent", con);
}
}
TAILQ_REMOVE(&bfs_head, entry, entries);
free(entry);
- TAILQ_FOREACH (child, &(current->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(current->nodes_head), nodes) {
entry = smalloc(sizeof(struct bfs_entry));
entry->con = child;
TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
}
- TAILQ_FOREACH (child, &(current->floating_head), floating_windows) {
+ TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
entry = smalloc(sizeof(struct bfs_entry));
entry->con = child;
TAILQ_INSERT_TAIL(&bfs_head, entry, entries);
*/
Con *con_by_window_id(xcb_window_t window) {
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons)
- if (con->window != NULL && con->window->id == window)
- return con;
+ TAILQ_FOREACH(con, &all_cons, all_cons)
+ if (con->window != NULL && con->window->id == window)
+ return con;
return NULL;
}
*/
Con *con_by_frame_id(xcb_window_t frame) {
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons)
- if (con->frame == frame)
- return con;
+ TAILQ_FOREACH(con, &all_cons, all_cons)
+ if (con->frame == frame)
+ return con;
return NULL;
}
//DLOG("searching con for window %p starting at con %p\n", window, con);
//DLOG("class == %s\n", window->class_class);
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
- TAILQ_FOREACH (match, &(child->swallow_head), matches) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(match, &(child->swallow_head), matches) {
if (!match_matches_window(match, window))
continue;
if (store_match != NULL)
return result;
}
- TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
- TAILQ_FOREACH (match, &(child->swallow_head), matches) {
+ TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
+ TAILQ_FOREACH(match, &(child->swallow_head), matches) {
if (!match_matches_window(match, window))
continue;
if (store_match != NULL)
Con *child;
int children = 0;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes)
- children++;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+ children++;
return children;
}
// with a percentage set we have
double total = 0.0;
int children_with_percent = 0;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->percent > 0.0) {
total += child->percent;
++children_with_percent;
// if there were children without a percentage set, set to a value that
// will make those children proportional to all others
if (children_with_percent != children) {
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->percent <= 0.0) {
if (children_with_percent == 0)
total += (child->percent = 1.0);
// if we got a zero, just distribute the space equally, otherwise
// distribute according to the proportions we got
if (total == 0.0) {
- TAILQ_FOREACH (child, &(con->nodes_head), nodes)
- child->percent = 1.0 / children;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+ child->percent = 1.0 / children;
} else if (total != 1.0) {
- TAILQ_FOREACH (child, &(con->nodes_head), nodes)
- child->percent /= total;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+ child->percent /= total;
}
}
*
*/
void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
- Con *workspace, *fullscreen;
-
if (con->type == CT_WORKSPACE) {
DLOG("You cannot make a workspace fullscreen.\n");
return;
}
DLOG("toggling fullscreen for %p / %s\n", con, con->name);
- if (con->fullscreen_mode == CF_NONE) {
- /* 1: check if there already is a fullscreen con */
- if (fullscreen_mode == CF_GLOBAL)
- fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
- else {
- workspace = con_get_workspace(con);
- fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
- }
- if (fullscreen != NULL) {
- /* Disable fullscreen for the currently fullscreened
- * container and enable it for the one the user wants
- * to have in fullscreen mode. */
- LOG("Disabling fullscreen for (%p/%s) upon user request\n",
- fullscreen, fullscreen->name);
- fullscreen->fullscreen_mode = CF_NONE;
- }
- /* 2: enable fullscreen */
- con->fullscreen_mode = fullscreen_mode;
- } else {
- /* 1: disable fullscreen */
- con->fullscreen_mode = CF_NONE;
- }
+ if (con->fullscreen_mode == CF_NONE)
+ con_enable_fullscreen(con, fullscreen_mode);
+ else
+ con_disable_fullscreen(con);
+}
+
+/*
+ * Sets the specified fullscreen mode for the given container, sends the
+ * “fullscreen_mode” event and changes the XCB fullscreen property of the
+ * container’s window, if any.
+ *
+ */
+static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode) {
+ con->fullscreen_mode = fullscreen_mode;
DLOG("mode now: %d\n", con->fullscreen_mode);
A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
}
+/*
+ * Enables fullscreen mode for the given container, if necessary.
+ *
+ * If the container’s mode is already CF_OUTPUT or CF_GLOBAL, the container is
+ * kept fullscreen but its mode is set to CF_GLOBAL and CF_OUTPUT,
+ * respectively.
+ *
+ * Other fullscreen containers will be disabled first, if they hide the new
+ * one.
+ *
+ */
+void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) {
+ if (con->type == CT_WORKSPACE) {
+ DLOG("You cannot make a workspace fullscreen.\n");
+ return;
+ }
+
+ assert(fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT);
+
+ if (fullscreen_mode == CF_GLOBAL)
+ DLOG("enabling global fullscreen for %p / %s\n", con, con->name);
+ else
+ DLOG("enabling fullscreen for %p / %s\n", con, con->name);
+
+ if (con->fullscreen_mode == fullscreen_mode) {
+ DLOG("fullscreen already enabled for %p / %s\n", con, con->name);
+ return;
+ }
+
+ Con *con_ws = con_get_workspace(con);
+
+ /* Disable any fullscreen container that would conflict the new one. */
+ Con *fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL);
+ if (fullscreen == NULL)
+ fullscreen = con_get_fullscreen_con(con_ws, CF_OUTPUT);
+ if (fullscreen != NULL)
+ con_disable_fullscreen(fullscreen);
+
+ /* Set focus to new fullscreen container. Unless in global fullscreen mode
+ * and on another workspace restore focus afterwards.
+ * Switch to the container’s workspace if mode is global. */
+ Con *cur_ws = con_get_workspace(focused);
+ Con *old_focused = focused;
+ if (fullscreen_mode == CF_GLOBAL && cur_ws != con_ws)
+ workspace_show(con_ws);
+ con_focus(con);
+ if (fullscreen_mode != CF_GLOBAL && cur_ws != con_ws)
+ con_focus(old_focused);
+
+ con_set_fullscreen_mode(con, fullscreen_mode);
+}
+
+/*
+ * Disables fullscreen mode for the given container regardless of the mode, if
+ * necessary.
+ *
+ */
+void con_disable_fullscreen(Con *con) {
+ if (con->type == CT_WORKSPACE) {
+ DLOG("You cannot make a workspace fullscreen.\n");
+ return;
+ }
+
+ DLOG("disabling fullscreen for %p / %s\n", con, con->name);
+
+ if (con->fullscreen_mode == CF_NONE) {
+ DLOG("fullscreen already disabled for %p / %s\n", con, con->name);
+ return;
+ }
+
+ con_set_fullscreen_mode(con, CF_NONE);
+}
+
/*
* Moves the given container to the currently focused container on the given
* workspace.
if (!con_is_leaf(con)) {
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (!child->window)
continue;
}
CALL(parent, on_remove_child);
+
+ ipc_send_window_event("move", con);
}
/*
return next;
do {
before = next;
- TAILQ_FOREACH (child, &(next->focus_head), focused) {
+ TAILQ_FOREACH(child, &(next->focus_head), focused) {
if (child->type == CT_FLOATING_CON)
continue;
/* 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. */
- TAILQ_FOREACH (current, &(con->focus_head), focused) {
+ TAILQ_FOREACH(current, &(con->focus_head), focused) {
if (current->type != CT_FLOATING_CON) {
most = current;
break;
/* 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. */
- TAILQ_FOREACH (current, &(con->focus_head), focused) {
+ TAILQ_FOREACH(current, &(con->focus_head), focused) {
if (current->type != CT_FLOATING_CON) {
most = current;
break;
if (con->type == CT_WORKSPACE) {
if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) {
LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name);
+ yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL);
tree_close(con, DONT_KILL_WINDOW, false, false);
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
+
+ const unsigned char *payload;
+ ylength length;
+ y(get_buf, &payload, &length);
+ ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
+
+ y(free);
}
return;
}
if (con->layout == L_STACKED || con->layout == L_TABBED) {
uint32_t max_width = 0, max_height = 0, deco_height = 0;
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
Rect min = con_minimum_size(child);
deco_height += child->deco_rect.height;
max_width = max(max_width, min.width);
if (con_is_split(con)) {
uint32_t width = 0, height = 0;
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
Rect min = con_minimum_size(child);
if (con->layout == L_SPLITH) {
width += min.width;
/* 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) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (con_has_urgent_child(child))
return true;
}
con_update_parents_urgency(con);
- if (con->urgent == urgent)
- LOG("Urgency flag changed to %d\n", con->urgent);
-
Con *ws;
/* Set the urgency flag on the workspace, if a workspace could be found
* (for dock clients, that is not the case). */
if ((ws = con_get_workspace(con)) != NULL)
workspace_update_urgent_flag(ws);
+
+ if (con->urgent == urgent) {
+ LOG("Urgency flag changed to %d\n", con->urgent);
+ ipc_send_window_event("urgent", con);
+ }
}
/*
/* 2) append representation of children */
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
char *child_txt = con_get_tree_representation(child);
char *tmp_buf;
*
*/
#include "all.h"
-
-/* We need Xlib for XStringToKeysym */
-#include <X11/Xlib.h>
+#include <xkbcommon/xkbcommon.h>
char *current_configpath = NULL;
Config config;
*/
void update_barconfig() {
Barconfig *current;
- TAILQ_FOREACH (current, &barconfigs, configs) {
+ TAILQ_FOREACH(current, &barconfigs, configs) {
ipc_send_barconfig_update_event(current);
}
}
* parse_file().
*
*/
-static void parse_configuration(const char *override_configpath) {
+bool parse_configuration(const char *override_configpath, bool use_nagbar) {
char *path = get_config_path(override_configpath);
LOG("Parsing configfile %s\n", path);
FREE(current_configpath);
current_configpath = path;
- parse_file(path);
+
+ /* initialize default bindings if we're just validating the config file */
+ if (!use_nagbar && bindings == NULL) {
+ bindings = scalloc(sizeof(struct bindings_head));
+ TAILQ_INIT(bindings);
+ }
+
+ return parse_file(path, use_nagbar);
}
/*
while (!TAILQ_EMPTY(bindings)) {
bind = TAILQ_FIRST(bindings);
TAILQ_REMOVE(bindings, bind, bindings);
- FREE(bind->translated_to);
- FREE(bind->command);
- FREE(bind);
+ binding_free(bind);
}
FREE(bindings);
SLIST_REMOVE(&modes, mode, Mode, modes);
/* Invalidate pixmap caches in case font or colors changed */
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons)
- FREE(con->deco_render_params);
+ TAILQ_FOREACH(con, &all_cons, all_cons)
+ FREE(con->deco_render_params);
/* Get rid of the current font */
free_font();
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
- parse_configuration(override_configpath);
+ parse_configuration(override_configpath, true);
if (reload) {
translate_keysyms();
font_pattern = sstrdup(font);
}
-CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
- configure_binding(bindtype, modifiers, key, release, command, DEFAULT_BINDING_MODE);
+CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
+ configure_binding(bindtype, modifiers, key, release, whole_window, command, DEFAULT_BINDING_MODE);
}
/*******************************************************************************
static char *current_mode;
-CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) {
- configure_binding(bindtype, modifiers, key, release, command, current_mode);
+CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
+ configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode);
}
CFGFUN(enter_mode, const char *modename) {
}
if (strcmp(windowtype, "new_window") == 0) {
- DLOG("default tiled border style = %d and border width = %d\n", border_style, border_width);
+ DLOG("default tiled border style = %d and border width = %d (%d physical px)\n",
+ border_style, border_width, logical_px(border_width));
config.default_border = border_style;
- config.default_border_width = border_width;
+ config.default_border_width = logical_px(border_width);
} else {
- DLOG("default floating border style = %d and border width = %d\n", border_style, border_width);
+ DLOG("default floating border style = %d and border width = %d (%d physical px)\n",
+ border_style, border_width, logical_px(border_width));
config.default_floating_border = border_style;
- config.default_floating_border_width = border_width;
+ config.default_floating_border_width = logical_px(border_width);
}
}
* outputs */
struct Workspace_Assignment *assignment;
bool duplicate = false;
- TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcasecmp(assignment->name, workspace) == 0) {
ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
workspace);
current_bar.modifier = M_SHIFT;
}
+CFGFUN(bar_wheel_up_cmd, const char *command) {
+ FREE(current_bar.wheel_up_cmd);
+ current_bar.wheel_up_cmd = sstrdup(command);
+}
+
+CFGFUN(bar_wheel_down_cmd, const char *command) {
+ FREE(current_bar.wheel_down_cmd);
+ current_bar.wheel_down_cmd = sstrdup(command);
+}
+
CFGFUN(bar_position, const char *position) {
current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM);
}
* parse_config and possibly launching i3-nagbar.
*
*/
-void parse_file(const char *f) {
+bool parse_file(const char *f, bool use_nagbar) {
SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
int fd, ret, read_bytes = 0;
struct stat stbuf;
* variables (otherwise we will count them twice, which is bad when
* 'extra' is negative) */
char *bufcopy = sstrdup(buf);
- SLIST_FOREACH (current, &variables, variables) {
+ SLIST_FOREACH(current, &variables, variables) {
int extra = (strlen(current->value) - strlen(current->key));
char *next;
for (next = bufcopy;
destwalk = new;
while (walk < (buf + stbuf.st_size)) {
/* Find the next variable */
- SLIST_FOREACH (current, &variables, variables)
- current->next_match = strcasestr(walk, current->key);
+ SLIST_FOREACH(current, &variables, variables)
+ current->next_match = strcasestr(walk, current->key);
nearest = NULL;
int distance = stbuf.st_size;
- SLIST_FOREACH (current, &variables, variables) {
+ SLIST_FOREACH(current, &variables, variables) {
if (current->next_match == NULL)
continue;
if ((current->next_match - walk) < distance) {
check_for_duplicate_bindings(context);
- if (context->has_errors || context->has_warnings) {
+ if (use_nagbar && (context->has_errors || context->has_warnings)) {
ELOG("FYI: You are using i3 version " I3_VERSION "\n");
if (version == 3)
ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
free(pageraction);
}
+ bool has_errors = context->has_errors;
+
FREE(context->line_copy);
free(context);
free(new);
SLIST_REMOVE_HEAD(&variables, variables);
FREE(current);
}
+
+ return !has_errors;
}
#endif
uint32_t idx = 0;
/* We count to get the index of this workspace because named workspaces
* don’t have the ->num property */
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
Con *ws;
- TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
if (STARTS_WITH(ws->name, "__"))
continue;
}
}
+/*
+ * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of
+ * noninternal workspaces.
+ */
+void ewmh_update_number_of_desktops(void) {
+ Con *output;
+ uint32_t idx = 0;
+
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+ ++idx;
+ }
+ }
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx);
+}
+
+/*
+ * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a
+ * list of NULL-terminated strings in UTF-8 encoding"
+ */
+void ewmh_update_desktop_names(void) {
+ Con *output;
+ int msg_length = 0;
+
+ /* count the size of the property message to set */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+ msg_length += strlen(ws->name) + 1;
+ }
+ }
+
+ char desktop_names[msg_length];
+ int current_position = 0;
+
+ /* fill the buffer with the names of the i3 workspaces */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ for (size_t i = 0; i < strlen(ws->name) + 1; i++) {
+ desktop_names[current_position++] = ws->name[i];
+ }
+ }
+ }
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_DESKTOP_NAMES, A_UTF8_STRING, 8, msg_length, desktop_names);
+}
+
+/*
+ * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that
+ * define the top left corner of each desktop's viewport.
+ */
+void ewmh_update_desktop_viewport(void) {
+ Con *output;
+ int num_desktops = 0;
+ /* count number of desktops */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ num_desktops++;
+ }
+ }
+
+ uint32_t viewports[num_desktops * 2];
+
+ int current_position = 0;
+ /* fill the viewport buffer */
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ viewports[current_position++] = output->rect.x;
+ viewports[current_position++] = output->rect.y;
+ }
+ }
+
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+ A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports);
+}
+
/*
* Updates _NET_ACTIVE_WINDOW with the currently focused window.
*
/* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3");
- xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 19, supported_atoms);
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 23, supported_atoms);
}
*/
static Output *get_screen_at(unsigned int x, unsigned int y) {
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs)
- if (output->rect.x == x && output->rect.y == y)
- return output;
+ TAILQ_FOREACH(output, &outputs, outputs)
+ if (output->rect.x == x && output->rect.y == y)
+ return output;
return NULL;
}
Output *output;
/* Use Rect to encapsulate dimensions, ignoring x/y */
Rect outputs_dimensions = {0, 0, 0, 0};
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
outputs_dimensions.height += output->rect.height;
outputs_dimensions.width += output->rect.width;
}
if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) {
DLOG("Geometry not set, combining children\n");
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height);
nc->rect.width += child->geometry.width;
nc->rect.height = max(nc->rect.height, child->geometry.height);
/* Check if we need to re-assign it to a different workspace because of its
* coordinates and exit if that was done successfully. */
- if (floating_maybe_reassign_ws(nc))
+ if (floating_maybe_reassign_ws(nc)) {
+ ipc_send_window_event("floating", con);
return;
+ }
/* Sanitize coordinates: Check if they are on any output */
- if (get_output_containing(nc->rect.x, nc->rect.y) != NULL)
+ if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) {
+ ipc_send_window_event("floating", con);
return;
+ }
ELOG("No output found at destination coordinates, centering floating window on current ws\n");
nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
+
+ ipc_send_window_event("floating", con);
}
void floating_disable(Con *con, bool automatic) {
if (set_focus)
con_focus(con);
+
+ ipc_send_window_event("floating", con);
}
/*
#include <libsn/sn-monitor.h>
int randr_base = -1;
+int xkb_base = -1;
+int xkb_current_group;
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
since it’d trigger an infinite loop of switching between the different windows when
event = SLIST_NEXT(event, ignore_events);
}
- SLIST_FOREACH (event, &ignore_events, ignore_events) {
+ SLIST_FOREACH(event, &ignore_events, ignore_events) {
if (event->sequence != sequence)
continue;
layout_t layout = (enter_child ? con->parent->layout : con->layout);
if (layout == L_DEFAULT) {
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes)
- if (rect_contains(child->deco_rect, event->event_x, event->event_y)) {
- LOG("using child %p / %s instead!\n", child, child->name);
- con = child;
- break;
- }
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+ if (rect_contains(child->deco_rect, event->event_x, event->event_y)) {
+ LOG("using child %p / %s instead!\n", child, child->name);
+ con = child;
+ break;
+ }
}
#if 0
/* see over which rect the user is */
Con *current;
- TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
if (!rect_contains(current->deco_rect, event->event_x, event->event_y))
continue;
add_ignore_event(event->sequence, -1);
manage_window(event->window, cookie, false);
- x_push_changes(croot);
return;
}
tree_close(con, DONT_KILL_WINDOW, false, false);
tree_render();
- x_push_changes(croot);
ignore_end:
/* If the client (as opposed to i3) destroyed or unmapped a window, an
XCB_ATOM_CARDINAL, 32, 4,
&r);
xcb_flush(conn);
+ } else if (event->type == A_WM_CHANGE_STATE) {
+ /* http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 */
+ Con *con = con_by_window_id(event->window);
+
+ if (con && event->data.data32[0] == 3) {
+ /* this request is so we can play some animiation showing the
+ * window physically moving to the tray before we close it (I
+ * think) */
+ DLOG("Client has requested iconic state. Closing this con. (con = %p)\n", con);
+ tree_close(con, DONT_KILL_WINDOW, false, false);
+ tree_render();
+ } else {
+ DLOG("Not handling WM_CHANGE_STATE request. (window = %d, state = %d)\n", event->window, event->data.data32[0]);
+ }
+
+ } else if (event->type == A__NET_CURRENT_DESKTOP) {
+ /* This request is used by pagers and bars to change the current
+ * desktop likely as a result of some user action. We interpret this as
+ * a request to focus the given workspace. See
+ * http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008
+ * */
+ Con *output;
+ uint32_t idx = 0;
+ DLOG("Request to change current desktop to index %d\n", event->data.data32[0]);
+
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+ Con *ws;
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
+ if (STARTS_WITH(ws->name, "__"))
+ continue;
+
+ if (idx == event->data.data32[0]) {
+ /* data32[1] is a timestamp used to prevent focus race conditions */
+ if (event->data.data32[1])
+ last_timestamp = event->data.data32[1];
+
+ DLOG("Handling request to focus workspace %s\n", ws->name);
+
+ workspace_show(ws);
+ tree_render();
+
+ return;
+ }
+
+ ++idx;
+ }
+ }
+ } else if (event->type == A__NET_CLOSE_WINDOW) {
+ /*
+ * Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW
+ * client message request to the root window.
+ * http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896
+ */
+ Con *con = con_by_window_id(event->window);
+ if (con) {
+ DLOG("Handling _NET_CLOSE_WINDOW request (con = %p)\n", con);
+
+ if (event->data.data32[0])
+ last_timestamp = event->data.data32[0];
+
+ tree_close(con, KILL_WINDOW, false, false);
+ tree_render();
+ } else {
+ DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window);
+ }
} else {
DLOG("unhandled clientmessage\n");
return;
return;
}
+/*
+ * Handles the WM_CLASS property for assignments and criteria selection.
+ *
+ */
+static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+ xcb_atom_t name, xcb_get_property_reply_t *prop) {
+ Con *con;
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
+ return false;
+
+ if (prop == NULL) {
+ prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
+ false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32),
+ NULL);
+
+ if (prop == NULL)
+ return false;
+ }
+
+ window_update_class(con->window, prop, false);
+
+ return true;
+}
+
/* Returns false if the event could not be processed (e.g. the window could not
* be found), true otherwise */
typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
{0, UINT_MAX, handle_normal_hints},
{0, UINT_MAX, handle_clientleader_change},
{0, UINT_MAX, handle_transient_for},
- {0, 128, handle_windowrole_change}};
+ {0, 128, handle_windowrole_change},
+ {0, 128, handle_class_change}};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
/*
property_handlers[4].atom = A_WM_CLIENT_LEADER;
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
property_handlers[6].atom = A_WM_WINDOW_ROLE;
+ property_handlers[7].atom = XCB_ATOM_WM_CLASS;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
*
*/
void handle_event(int type, xcb_generic_event_t *event) {
+ DLOG("event type %d, xkb_base %d\n", type, xkb_base);
if (randr_base > -1 &&
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
handle_screen_change(event);
return;
}
+ if (xkb_base > -1 && type == xkb_base) {
+ DLOG("xkb event, need to handle it.\n");
+
+ xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
+ if (state->xkbType == XCB_XKB_MAP_NOTIFY) {
+ if (event_is_ignored(event->sequence, type)) {
+ DLOG("Ignoring map notify event for sequence %d.\n", state->sequence);
+ } else {
+ DLOG("xkb map notify, sequence %d, time %d\n", state->sequence, state->time);
+ add_ignore_event(event->sequence, type);
+ ungrab_all_keys(conn);
+ translate_keysyms();
+ grab_all_keys(conn, false);
+ }
+ } else if (state->xkbType == XCB_XKB_STATE_NOTIFY) {
+ DLOG("xkb state group = %d\n", state->group);
+
+ /* See The XKB Extension: Library Specification, section 14.1 */
+ /* We check if the current group (each group contains
+ * two levels) has been changed. Mode_switch activates
+ * group XkbGroup2Index */
+ if (xkb_current_group == state->group)
+ return;
+ xkb_current_group = state->group;
+ if (state->group == XCB_XKB_GROUP_1) {
+ DLOG("Mode_switch disabled\n");
+ ungrab_all_keys(conn);
+ grab_all_keys(conn, false);
+ } else {
+ DLOG("Mode_switch enabled\n");
+ grab_all_keys(conn, false);
+ }
+ }
+
+ return;
+ }
+
switch (type) {
case XCB_KEY_PRESS:
case XCB_KEY_RELEASE:
i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c))
i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h)
i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h))
-i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
-i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
+i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
+i3_LIBS = $(XKB_COMMON_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
# When using clang, we use pre-compiled headers to speed up the build. With
# gcc, this actually makes the build slower.
*/
void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
ipc_client *current;
- TAILQ_FOREACH (current, &all_clients, clients) {
+ TAILQ_FOREACH(current, &all_clients, clients) {
/* see if this client is interested in this event */
bool interested = false;
for (int i = 0; i < current->num_events; i++) {
y(map_close);
}
+static void dump_binding(yajl_gen gen, Binding *bind) {
+ y(map_open);
+ ystr("input_code");
+ y(integer, bind->keycode);
+
+ ystr("input_type");
+ ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
+
+ ystr("symbol");
+ if (bind->symbol == NULL)
+ y(null);
+ else
+ ystr(bind->symbol);
+
+ ystr("command");
+ ystr(bind->command);
+
+ ystr("mods");
+ y(array_open);
+ for (int i = 0; i < 8; i++) {
+ if (bind->mods & (1 << i)) {
+ switch (1 << i) {
+ case XCB_MOD_MASK_SHIFT:
+ ystr("shift");
+ break;
+ case XCB_MOD_MASK_LOCK:
+ ystr("lock");
+ break;
+ case XCB_MOD_MASK_CONTROL:
+ ystr("ctrl");
+ break;
+ case XCB_MOD_MASK_1:
+ ystr("Mod1");
+ break;
+ case XCB_MOD_MASK_2:
+ ystr("Mod2");
+ break;
+ case XCB_MOD_MASK_3:
+ ystr("Mod3");
+ break;
+ case XCB_MOD_MASK_4:
+ ystr("Mod4");
+ break;
+ case XCB_MOD_MASK_5:
+ ystr("Mod5");
+ break;
+ }
+ }
+ }
+ y(array_close);
+
+ y(map_close);
+}
+
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
y(map_open);
ystr("id");
y(integer, con->current_border_width);
dump_rect(gen, "rect", con->rect);
+ dump_rect(gen, "deco_rect", con->deco_rect);
dump_rect(gen, "window_rect", con->window_rect);
dump_rect(gen, "geometry", con->geometry);
ystr("name");
if (con->window && con->window->name)
ystr(i3string_as_utf8(con->window->name));
- else
+ else if (con->name != NULL)
ystr(con->name);
+ else
+ y(null);
if (con->type == CT_WORKSPACE) {
ystr("num");
ystr(i3string_as_utf8(con->window->name));
}
+ ystr("transient_for");
+ if (con->window->transient_for == XCB_NONE)
+ y(null);
+ else y(integer, con->window->transient_for);
+
y(map_close);
}
y(array_open);
Con *node;
if (con->type != CT_DOCKAREA || !inplace_restart) {
- TAILQ_FOREACH (node, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(node, &(con->nodes_head), nodes) {
dump_node(gen, node, inplace_restart);
}
}
ystr("floating_nodes");
y(array_open);
- TAILQ_FOREACH (node, &(con->floating_head), floating_windows) {
+ TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
dump_node(gen, node, inplace_restart);
}
y(array_close);
ystr("focus");
y(array_open);
- TAILQ_FOREACH (node, &(con->focus_head), focused) {
+ TAILQ_FOREACH(node, &(con->focus_head), focused) {
y(integer, (long int)node);
}
y(array_close);
ystr("swallows");
y(array_open);
Match *match;
- TAILQ_FOREACH (match, &(con->swallow_head), matches) {
+ TAILQ_FOREACH(match, &(con->swallow_head), matches) {
y(map_open);
if (match->dock != -1) {
ystr("dock");
break;
}
+ if (config->wheel_up_cmd) {
+ ystr("wheel_up_cmd");
+ ystr(config->wheel_up_cmd);
+ }
+
+ if (config->wheel_down_cmd) {
+ ystr("wheel_down_cmd");
+ ystr(config->wheel_down_cmd);
+ }
+
ystr("position");
if (config->position == P_BOTTOM)
ystr("bottom");
Con *focused_ws = con_get_workspace(focused);
Con *output;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
if (con_is_internal(output))
continue;
Con *ws;
- TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) {
+ TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
assert(ws->type == CT_WORKSPACE);
y(map_open);
ystr("num");
- if (ws->num == -1)
- y(null);
- else
- y(integer, ws->num);
+ y(integer, ws->num);
ystr("name");
ystr(ws->name);
y(array_open);
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
y(map_open);
ystr("name");
y(array_open);
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons)
- if (con->mark != NULL)
- ystr(con->mark);
+ TAILQ_FOREACH(con, &all_cons, all_cons)
+ if (con->mark != NULL)
+ ystr(con->mark);
y(array_close);
if (message_size == 0) {
y(array_open);
Barconfig *current;
- TAILQ_FOREACH (current, &barconfigs, configs) {
+ TAILQ_FOREACH(current, &barconfigs, configs) {
ystr(current->id);
}
y(array_close);
strncpy(bar_id, (const char *)message, message_size);
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
Barconfig *current, *config = NULL;
- TAILQ_FOREACH (current, &barconfigs, configs) {
+ TAILQ_FOREACH(current, &barconfigs, configs) {
if (strcmp(current->id, bar_id) != 0)
continue;
ipc_client *current, *client = NULL;
/* Search the ipc_client structure for this connection */
- TAILQ_FOREACH (current, &all_clients, clients) {
+ TAILQ_FOREACH(current, &all_clients, clients) {
if (current->fd != fd)
continue;
/* Delete the client from the list of clients */
ipc_client *current;
- TAILQ_FOREACH (current, &all_clients, clients) {
+ TAILQ_FOREACH(current, &all_clients, clients) {
if (current->fd != w->fd)
continue;
}
/*
- * For the workspace "focus" event we send, along the usual "change" field,
- * also the current and previous workspace, in "current" and "old"
- * respectively.
+ * Generates a json workspace event. Returns a dynamically allocated yajl
+ * generator. Free with yajl_gen_free().
*/
-void ipc_send_workspace_focus_event(Con *current, Con *old) {
+yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) {
setlocale(LC_NUMERIC, "C");
yajl_gen gen = ygenalloc();
y(map_open);
ystr("change");
- ystr("focus");
+ ystr(change);
ystr("current");
- dump_node(gen, current, false);
+ if (current == NULL)
+ y(null);
+ else
+ dump_node(gen, current, false);
ystr("old");
if (old == NULL)
y(map_close);
+ setlocale(LC_NUMERIC, "");
+
+ return gen;
+}
+
+/*
+ * For the workspace events we send, along with the usual "change" field, also
+ * the workspace container in "current". For focus events, we send the
+ * previously focused workspace in "old".
+ */
+void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
+ yajl_gen gen = ipc_marshal_workspace_event(change, current, old);
+
const unsigned char *payload;
ylength length;
y(get_buf, &payload, &length);
ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
+
y(free);
- setlocale(LC_NUMERIC, "");
}
/**
y(free);
setlocale(LC_NUMERIC, "");
}
+
+/*
+ * For the binding events, we send the serialized binding struct.
+ */
+void ipc_send_binding_event(const char *event_type, Binding *bind) {
+ DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode);
+
+ setlocale(LC_NUMERIC, "C");
+
+ yajl_gen gen = ygenalloc();
+
+ y(map_open);
+
+ ystr("change");
+ ystr(event_type);
+
+ ystr("binding");
+ dump_binding(gen, bind);
+
+ y(map_close);
+
+ const unsigned char *payload;
+ ylength length;
+ y(get_buf, &payload, &length);
+
+ ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload);
+
+ y(free);
+ setlocale(LC_NUMERIC, "");
+}
if (bind == NULL)
return;
- CommandResult *result = run_binding(bind);
+ CommandResult *result = run_binding(bind, NULL);
if (result->needs_tree_render)
tree_render();
static Con *to_focus;
static bool parsing_swallows;
static bool parsing_rect;
+static bool parsing_deco_rect;
static bool parsing_window_rect;
static bool parsing_geometry;
static bool parsing_focus;
match_init(current_swallow);
TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches);
} else {
- if (!parsing_rect && !parsing_window_rect && !parsing_geometry) {
+ if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
DLOG("New floating_node\n");
Con *ws = con_get_workspace(json_node);
static int json_end_map(void *ctx) {
LOG("end of map\n");
- if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) {
+ if (!parsing_swallows && !parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) {
/* Set a few default values to simplify manually crafted layout files. */
if (json_node->layout == L_DEFAULT) {
DLOG("Setting layout = L_SPLITH\n");
* workspace called “1”. */
Con *output;
Con *workspace = NULL;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
char *base = sstrdup(json_node->name);
int cnt = 1;
while (workspace != NULL) {
FREE(json_node->name);
asprintf(&(json_node->name), "%s_%d", base, cnt++);
workspace = NULL;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
}
free(base);
/* Set num accordingly so that i3bar will properly sort it. */
json_node->num = ws_name_to_number(json_node->name);
- } else {
- // TODO: remove this in the “next” branch.
- if (json_node->name == NULL || strcmp(json_node->name, "") == 0) {
- json_node->name = sstrdup("#ff0000");
- }
}
LOG("attaching\n");
x_con_init(json_node, json_node->depth);
json_node = json_node->parent;
}
- if (parsing_rect)
- parsing_rect = false;
- if (parsing_window_rect)
- parsing_window_rect = false;
- if (parsing_geometry)
- parsing_geometry = false;
+
+ parsing_rect = false;
+ parsing_deco_rect = false;
+ parsing_window_rect = false;
+ parsing_geometry = false;
return 1;
}
if (parsing_focus) {
/* Clear the list of focus mappings */
struct focus_mapping *mapping;
- TAILQ_FOREACH_REVERSE (mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
+ TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) {
LOG("focus (reverse) %d\n", mapping->old_id);
Con *con;
- TAILQ_FOREACH (con, &(json_node->focus_head), focused) {
+ TAILQ_FOREACH(con, &(json_node->focus_head), focused) {
if (con->old_id != mapping->old_id)
continue;
LOG("got it! %p\n", con);
if (strcasecmp(last_key, "rect") == 0)
parsing_rect = true;
+ if (strcasecmp(last_key, "deco_rect") == 0)
+ parsing_deco_rect = true;
+
if (strcasecmp(last_key, "window_rect") == 0)
parsing_window_rect = true;
to_focus = NULL;
parsing_swallows = false;
parsing_rect = false;
+ parsing_deco_rect = false;
parsing_window_rect = false;
parsing_geometry = false;
parsing_focus = false;
* temporarily for drag_pointer(). */
static struct ev_check *xcb_check;
-static int xkb_event_base;
-
-int xkb_current_group;
-
extern Con *focused;
char **start_argv;
xcb_key_symbols_t *keysyms;
-/* Those are our connections to X11 for use with libXcursor and XKB */
-Display *xlibdpy, *xkbdpy;
-
/* Default shmlog size if not set by user. */
const int default_shmlog_size = 25 * 1024 * 1024;
/* We hope that those are supported and set them to true */
bool xcursor_supported = true;
-bool xkb_supported = true;
-
-/* This will be set to true when -C is used so that functions can behave
- * slightly differently. We don’t want i3-nagbar to be started when validating
- * the config, for example. */
-bool only_check_config = false;
/*
* This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
}
}
-/*
- * When using xmodmap to change the keyboard mapping, this event
- * is only sent via XKB. Therefore, we need this special handler.
- *
- */
-static void xkb_got_event(EV_P_ struct ev_io *w, int revents) {
- DLOG("Handling XKB event\n");
- XkbEvent ev;
-
- /* When using xmodmap, every change (!) gets an own event.
- * Therefore, we just read all events and only handle the
- * mapping_notify once. */
- bool mapping_changed = false;
- while (XPending(xkbdpy)) {
- XNextEvent(xkbdpy, (XEvent *)&ev);
- /* While we should never receive a non-XKB event,
- * better do sanity checking */
- if (ev.type != xkb_event_base)
- continue;
-
- if (ev.any.xkb_type == XkbMapNotify) {
- mapping_changed = true;
- continue;
- }
-
- if (ev.any.xkb_type != XkbStateNotify) {
- ELOG("Unknown XKB event received (type %d)\n", ev.any.xkb_type);
- continue;
- }
-
- /* See The XKB Extension: Library Specification, section 14.1 */
- /* We check if the current group (each group contains
- * two levels) has been changed. Mode_switch activates
- * group XkbGroup2Index */
- if (xkb_current_group == ev.state.group)
- continue;
-
- xkb_current_group = ev.state.group;
-
- if (ev.state.group == XkbGroup2Index) {
- DLOG("Mode_switch enabled\n");
- grab_all_keys(conn, true);
- }
-
- if (ev.state.group == XkbGroup1Index) {
- DLOG("Mode_switch disabled\n");
- ungrab_all_keys(conn);
- grab_all_keys(conn, false);
- }
- }
-
- if (!mapping_changed)
- return;
-
- DLOG("Keyboard mapping changed, updating keybindings\n");
- xcb_key_symbols_free(keysyms);
- keysyms = xcb_key_symbols_alloc(conn);
-
- xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
-
- ungrab_all_keys(conn);
- DLOG("Re-grabbing...\n");
- translate_keysyms();
- grab_all_keys(conn, (xkb_current_group == XkbGroup2Index));
- DLOG("Done\n");
-}
-
/*
* Exit handler which destroys the main_loop. Will trigger cleanup handlers.
*
bool force_xinerama = false;
char *fake_outputs = NULL;
bool disable_signalhandler = false;
+ bool only_check_config = false;
static struct option long_options[] = {
{"no-autostart", no_argument, 0, 'a'},
{"config", required_argument, 0, 'c'},
}
}
+ if (only_check_config) {
+ exit(parse_configuration(override_configpath, false) ? 0 : 1);
+ }
+
/* If the user passes more arguments, we act like i3-msg would: Just send
* the arguments as an IPC message to i3. This allows for nice semantic
* commands such as 'i3 border none'. */
- if (!only_check_config && optind < argc) {
+ if (optind < argc) {
/* We enable verbose mode so that the user knows what’s going on.
* This should make it easier to find mistakes when the user passes
* arguments by mistake. */
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
load_configuration(conn, override_configpath, false);
- if (only_check_config) {
- LOG("Done checking configuration file. Exiting.\n");
- exit(0);
- }
if (config.ipc_socket_path == NULL) {
/* Fall back to a file name in /tmp/ based on the PID */
#include "atoms.xmacro"
#undef xmacro
- /* Initialize the Xlib connection */
- xlibdpy = xkbdpy = XOpenDisplay(NULL);
-
- /* Try to load the X cursors and initialize the XKB extension */
- if (xlibdpy == NULL) {
- ELOG("ERROR: XOpenDisplay() failed, disabling libXcursor/XKB support\n");
- xcursor_supported = false;
- xkb_supported = false;
- } else if (fcntl(ConnectionNumber(xlibdpy), F_SETFD, FD_CLOEXEC) == -1) {
- ELOG("Could not set FD_CLOEXEC on xkbdpy\n");
- return 1;
- } else {
- xcursor_load_cursors();
- /*init_xkb();*/
- }
+ xcursor_load_cursors();
/* Set a cursor for the root window (otherwise the root window will show no
cursor until the first client is launched). */
else
xcb_set_root_cursor(XCURSOR_CURSOR_POINTER);
- if (xkb_supported) {
- int errBase,
- major = XkbMajorVersion,
- minor = XkbMinorVersion;
-
- if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) {
- fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n");
- return 1;
- }
-
- int i1;
- if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) {
- fprintf(stderr, "XKB not supported by X-server\n");
- xkb_supported = false;
- }
- /* end of ugliness */
-
- if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) {
- fprintf(stderr, "Could not set XKB event mask\n");
- return 1;
- }
+ const xcb_query_extension_reply_t *extreply;
+ extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
+ if (!extreply->present) {
+ DLOG("xkb is not present on this server\n");
+ } else {
+ DLOG("initializing xcb-xkb\n");
+ xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
+ xcb_xkb_select_events(conn,
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
+ 0,
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
+ 0xff,
+ 0xff,
+ NULL);
+ xkb_base = extreply->first_event;
}
restore_connect();
x_set_i3_atoms();
ewmh_update_workarea();
- /* Set the _NET_CURRENT_DESKTOP property. */
+ /* Set the ewmh desktop properties. */
ewmh_update_current_desktop();
+ ewmh_update_number_of_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io));
- struct ev_io *xkb = scalloc(sizeof(struct ev_io));
xcb_check = scalloc(sizeof(struct ev_check));
struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare));
ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ);
ev_io_start(main_loop, xcb_watcher);
- if (xkb_supported) {
- ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ);
- ev_io_start(main_loop, xkb);
-
- /* Flush the buffer so that libev can properly get new events */
- XFlush(xkbdpy);
- }
-
ev_check_init(xcb_check, xcb_check_cb);
ev_check_start(main_loop, xcb_check);
/* Autostarting exec-lines */
if (autostart) {
struct Autostart *exec;
- TAILQ_FOREACH (exec, &autostarts, autostarts) {
+ TAILQ_FOREACH(exec, &autostarts, autostarts) {
LOG("auto-starting %s\n", exec->command);
start_application(exec->command, exec->no_startup_id);
}
/* Autostarting exec_always-lines */
struct Autostart *exec_always;
- TAILQ_FOREACH (exec_always, &autostarts_always, autostarts_always) {
+ TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) {
LOG("auto-starting (always!) %s\n", exec_always->command);
start_application(exec_always->command, exec_always->no_startup_id);
}
/* Start i3bar processes for all configured bars */
Barconfig *barconfig;
- TAILQ_FOREACH (barconfig, &barconfigs, configs) {
+ TAILQ_FOREACH(barconfig, &barconfigs, configs) {
char *command = NULL;
sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"",
barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar",
DLOG("Restoring geometry\n");
Con *con;
- TAILQ_FOREACH (con, &all_cons, all_cons)
- if (con->window) {
- DLOG("Re-adding X11 border of %d px\n", con->border_width);
- con->window_rect.width += (2 * con->border_width);
- con->window_rect.height += (2 * con->border_width);
- xcb_set_window_rect(conn, con->window->id, con->window_rect);
- DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y);
- xcb_reparent_window(conn, con->window->id, root,
- con->rect.x, con->rect.y);
- }
+ TAILQ_FOREACH(con, &all_cons, all_cons)
+ if (con->window) {
+ DLOG("Re-adding X11 border of %d px\n", con->border_width);
+ con->window_rect.width += (2 * con->border_width);
+ con->window_rect.height += (2 * con->border_width);
+ xcb_set_window_rect(conn, con->window->id, con->window_rect);
+ DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y);
+ xcb_reparent_window(conn, con->window->id, root,
+ con->rect.x, con->rect.y);
+ }
/* Strictly speaking, this line doesn’t really belong here, but since we
* are syncing, let’s un-register as a window manager first */
if (nc->geometry.width == 0)
nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height};
- if (want_floating) {
- DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
- floating_enable(nc, true);
- }
-
if (motif_border_style != BS_NORMAL) {
DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
if (want_floating) {
}
}
- if (nc->border_style == BS_PIXEL) {
- /* if the border style is BS_PIXEL, explicitly set the border width of
- * the new container */
- nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
+ if (want_floating) {
+ DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
+ /* automatically set the border to the default value if a motif border
+ * was not specified */
+ bool automatic_border = (motif_border_style == BS_NORMAL);
+
+ floating_enable(nc, automatic_border);
}
+ /* explicitly set the border width to the default */
+ nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
+
/* to avoid getting an UnmapNotify event due to reparenting, we temporarily
* declare no interest in any state change event of this window */
values[0] = XCB_NONE;
/* Defer setting focus after the 'new' event has been sent to ensure the
* proper window event sequence. */
- if (set_focus && nc->mapped) {
+ if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
DLOG("Now setting focus.\n");
con_focus(nc);
}
return false;
}
/* if we find a window that is newer than this one, bail */
- TAILQ_FOREACH (con, &all_cons, all_cons) {
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
if ((con->window != NULL) &&
_i3_timercmp(con->window->urgent, window->urgent, > )) {
return false;
return false;
}
/* if we find a window that is older than this one (and not 0), bail */
- TAILQ_FOREACH (con, &all_cons, all_cons) {
+ TAILQ_FOREACH(con, &all_cons, all_cons) {
if ((con->window != NULL) &&
(con->window->urgent.tv_sec != 0) &&
_i3_timercmp(con->window->urgent, window->urgent, < )) {
tree_flatten(croot);
- ipc_send_workspace_focus_event(ws, old_ws);
+ ipc_send_workspace_event("focus", ws, old_ws);
}
/*
- * Moves the current container in the given direction (D_LEFT, D_RIGHT,
+ * Moves the given container in the given direction (D_LEFT, D_RIGHT,
* D_UP, D_DOWN).
*
*/
-void tree_move(int direction) {
+void tree_move(Con *con, int direction) {
position_t position;
Con *target;
DLOG("Moving in direction %d\n", direction);
/* 1: get the first parent with the same orientation */
- Con *con = focused;
if (con->type == CT_WORKSPACE) {
DLOG("Not moving workspace\n");
TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
DLOG("Swapped.\n");
+ ipc_send_window_event("move", con);
return;
}
/* If we couldn't find a place to move it on this workspace,
* try to move it to a workspace on a different output */
move_to_output_directed(con, direction);
+ ipc_send_window_event("move", con);
return;
}
FREE(con->deco_render_params);
tree_flatten(croot);
+ ipc_send_window_event("move", con);
}
Con *output_get_content(Con *output) {
Con *child;
- TAILQ_FOREACH (child, &(output->nodes_head), nodes)
- if (child->type == CT_CON)
- return child;
+ TAILQ_FOREACH(child, &(output->nodes_head), nodes)
+ if (child->type == CT_CON)
+ return child;
return NULL;
}
*/
static Output *get_output_by_id(xcb_randr_output_t id) {
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs)
- if (output->id == id)
- return output;
+ TAILQ_FOREACH(output, &outputs, outputs)
+ if (output->id == id)
+ return output;
return NULL;
}
*/
Output *get_output_by_name(const char *name) {
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs)
- if (output->active &&
- strcasecmp(output->name, name) == 0)
- return output;
+ TAILQ_FOREACH(output, &outputs, outputs)
+ if (output->active &&
+ strcasecmp(output->name, name) == 0)
+ return output;
return NULL;
}
Output *get_first_output(void) {
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs)
- if (output->active)
- return output;
+ TAILQ_FOREACH(output, &outputs, outputs)
+ if (output->active)
+ return output;
die("No usable outputs available.\n");
}
*/
Output *get_output_containing(unsigned int x, unsigned int y) {
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
Output *output;
int lx = rect.x, uy = rect.y;
int rx = rect.x + rect.width, by = rect.y + rect.height;
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
*other;
Output *output,
*best = NULL;
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
/* Search for a Con with that name directly below the root node. There
* might be one from a restored layout. */
- TAILQ_FOREACH (current, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(croot->nodes_head), nodes) {
if (strcmp(current->name, output->name) != 0)
continue;
void init_ws_for_output(Output *output, Con *content) {
/* go through all assignments and move the existing workspaces to this output */
struct Workspace_Assignment *assignment;
- TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->output, output->name) != 0)
continue;
/* check if this workspace actually exists */
Con *workspace = NULL, *out;
- TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(out),
- !strcasecmp(child->name, assignment->name));
+ TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(out),
+ !strcasecmp(child->name, assignment->name));
if (workspace == NULL)
continue;
Con *ws_out_content = output_get_content(workspace_out);
Con *floating_con;
- TAILQ_FOREACH (floating_con, &(workspace->floating_head), floating_windows)
- /* NB: We use output->con here because content is not yet rendered,
+ TAILQ_FOREACH(floating_con, &(workspace->floating_head), floating_windows)
+ /* NB: We use output->con here because content is not yet rendered,
* so it has a rect of {0, 0, 0, 0}. */
- floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect));
+ floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect));
con_detach(workspace);
con_attach(workspace, content, false);
}
/* otherwise, we create the first assigned ws for this output */
- TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->output, output->name) != 0)
continue;
/* Fix the position of all floating windows on this output.
* The 'rect' of each workspace will be updated in src/render.c. */
- TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) {
- TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
+ TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect));
}
}
* the workspaces and their childs depending on output resolution. This is
* only done for workspaces with maximum one child. */
if (config.default_orientation == NO_ORIENTATION) {
- TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) {
+ TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
/* Workspaces with more than one child are left untouched because
* we do not want to change an existing layout. */
if (con_num_children(workspace) > 1)
/* Check for clones, disable the clones and reduce the mode to the
* lowest common mode */
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active || output->to_be_disabled)
continue;
DLOG("output %p / %s, position (%d, %d), checking for clones\n",
* necessary because in the next step, a clone might get disabled. Example:
* LVDS1 active, VGA1 gets activated as a clone of LVDS1 (has no con).
* LVDS1 gets disabled. */
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (output->active && output->con == NULL) {
DLOG("Need to initialize a Con for output %s\n", output->name);
output_init_con(output);
/* Handle outputs which have a new mode or are disabled now (either
* because the user disabled them or because they are clones) */
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (output->to_be_disabled) {
output->active = false;
DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
con_attach(current, first_content, false);
DLOG("Fixing the coordinates of floating containers\n");
Con *floating_con;
- TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows)
- floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
+ TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows)
+ floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect));
DLOG("Done, next\n");
}
DLOG("re-attached all workspaces\n");
/* 3: move the dock clients to the first output */
Con *child;
- TAILQ_FOREACH (child, &(output->con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) {
if (child->type != CT_DOCKAREA)
continue;
DLOG("Handling dock con %p\n", child);
get_first_output();
/* Just go through each active output and assign one workspace */
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
Con *content = output_get_content(output->con);
}
/* Focus the primary screen, if possible */
- TAILQ_FOREACH (output, &outputs, outputs) {
+ TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->primary || !output->con)
continue;
/* Find the content container and ensure that there is exactly one. Also
* check for any non-CT_DOCKAREA clients. */
Con *content = NULL;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->type == CT_CON) {
if (content != NULL) {
DLOG("More than one CT_CON on output container\n");
/* First pass: determine the height of all CT_DOCKAREAs (the sum of their
* children) and figure out how many pixels we have left for the rest */
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->type != CT_DOCKAREA)
continue;
child->rect.height = 0;
- TAILQ_FOREACH (dockchild, &(child->nodes_head), nodes)
- child->rect.height += dockchild->geometry.height;
+ TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes)
+ child->rect.height += dockchild->geometry.height;
height -= child->rect.height;
}
/* Second pass: Set the widths/heights */
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
if (child->type == CT_CON) {
child->rect.x = x;
child->rect.y = y;
Con *child;
int i = 0, assigned = 0;
int total = con_orientation(con) == HORIZ ? rect.width : rect.height;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
double percentage = child->percent > 0.0 ? child->percent : 1.0 / children;
assigned += sizes[i++] = percentage * total;
}
} else if (con->type == CT_ROOT) {
Con *output;
if (!fullscreen) {
- TAILQ_FOREACH (output, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
render_con(output, false);
}
}
* all times. This is important when the user places floating
* windows/containers so that they overlap on another output. */
DLOG("Rendering floating windows:\n");
- TAILQ_FOREACH (output, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(con->nodes_head), nodes) {
if (con_is_internal(output))
continue;
/* Get the active workspace of that output */
Con *workspace = TAILQ_FIRST(&(content->focus_head));
Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
Con *child;
- TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) {
+ TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) {
/* Don’t render floating windows when there is a fullscreen window
* on that workspace. Necessary to make floating fullscreen work
* correctly (ticket #564). */
while (transient_con != NULL &&
transient_con->window != NULL &&
transient_con->window->transient_for != XCB_NONE) {
+ DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n",
+ transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id);
if (transient_con->window->transient_for == fullscreen->window->id) {
is_transient_for = true;
break;
} else {
/* FIXME: refactor this into separate functions: */
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
assert(children > 0);
/* default layout */
/* in a stacking or tabbed container, we ensure the focused client is raised */
if (con->layout == L_STACKED || con->layout == L_TABBED) {
- TAILQ_FOREACH_REVERSE (child, &(con->focus_head), focus_head, focused)
- x_raise_con(child);
+ TAILQ_FOREACH_REVERSE(child, &(con->focus_head), focus_head, focused)
+ x_raise_con(child);
if ((child = TAILQ_FIRST(&(con->focus_head)))) {
/* By rendering the stacked container again, we handle the case
* that we have a non-leaf-container inside the stack. In that
Match *swallows;
int n = 0;
- TAILQ_FOREACH (swallows, &(state->con->swallow_head), matches) {
+ TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) {
char *serialized = NULL;
#define APPEND_REGEX(re_name) \
/* Set the same name as was stored in the layout file. While perhaps
* slightly confusing in the first instant, this brings additional
* clarity to which placeholder is waiting for which actual window. */
- xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
- A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name);
+ if (con->name != NULL)
+ xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder,
+ A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name);
DLOG("Created placeholder window 0x%08x for leaf container %p / %s\n",
placeholder, con, con->name);
}
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
open_placeholder_window(child);
}
- TAILQ_FOREACH (child, &(con->floating_head), floating_windows) {
+ TAILQ_FOREACH(child, &(con->floating_head), floating_windows) {
open_placeholder_window(child);
}
}
*/
void restore_open_placeholder_windows(Con *parent) {
Con *child;
- TAILQ_FOREACH (child, &(parent->nodes_head), nodes) {
+ TAILQ_FOREACH(child, &(parent->nodes_head), nodes) {
open_placeholder_window(child);
}
- TAILQ_FOREACH (child, &(parent->floating_head), floating_windows) {
+ TAILQ_FOREACH(child, &(parent->floating_head), floating_windows) {
open_placeholder_window(child);
}
*/
bool restore_kill_placeholder(xcb_window_t placeholder) {
placeholder_state *state;
- TAILQ_FOREACH (state, &state_head, state) {
+ TAILQ_FOREACH(state, &state_head, state) {
if (state->window != placeholder)
continue;
static void expose_event(xcb_expose_event_t *event) {
placeholder_state *state;
- TAILQ_FOREACH (state, &state_head, state) {
+ TAILQ_FOREACH(state, &state_head, state) {
if (state->window != event->window)
continue;
*/
static void configure_notify(xcb_configure_notify_event_t *event) {
placeholder_state *state;
- TAILQ_FOREACH (state, &state_head, state) {
+ TAILQ_FOREACH(state, &state_head, state) {
if (state->window != event->window)
continue;
* unfocused scratchpad on the current workspace and focus it */
Con *walk_con;
Con *focused_ws = con_get_workspace(focused);
- TAILQ_FOREACH (walk_con, &(focused_ws->floating_head), floating_windows) {
+ TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) {
if (!con && (floating = con_inside_floating(walk_con)) &&
floating->scratchpad_state != SCRATCHPAD_NONE &&
floating != con_inside_floating(focused)) {
* visible scratchpad window on another workspace. In this case we move it
* to the current workspace. */
focused_ws = con_get_workspace(focused);
- TAILQ_FOREACH (walk_con, &all_cons, all_cons) {
+ TAILQ_FOREACH(walk_con, &all_cons, all_cons) {
Con *walk_ws = con_get_workspace(walk_con);
if (!con && walk_ws &&
!con_is_internal(walk_ws) && focused_ws != walk_ws &&
Con *output;
int new_width = -1,
new_height = -1;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
if (output == __i3_output)
continue;
DLOG("output %s's resolution: (%d, %d) %d x %d\n",
DLOG("Fixing coordinates of scratchpad windows\n");
Con *con;
- TAILQ_FOREACH (con, &(__i3_scratch->floating_head), floating_windows) {
+ TAILQ_FOREACH(con, &(__i3_scratch->floating_head), floating_windows) {
floating_fix_coordinates(con, &old_rect, &new_rect);
}
}
/* Open a popup window on each virtual screen */
Output *screen;
xcb_window_t win;
- TAILQ_FOREACH (screen, &outputs, outputs) {
+ TAILQ_FOREACH(screen, &outputs, outputs) {
if (!screen->active)
continue;
win = open_input_window(conn, screen->rect, width, height);
DLOG("Timeout for startup sequence %s\n", id);
struct Startup_Sequence *current, *sequence = NULL;
- TAILQ_FOREACH (current, &startup_sequences, sequences) {
+ TAILQ_FOREACH(current, &startup_sequences, sequences) {
if (strcmp(current->id, id) != 0)
continue;
* the application is reparented to init (process-id 1), which correctly handles
* childs, so we don’t have to do it :-).
*
- * The shell is determined by looking for the SHELL environment variable. If it
- * does not exist, /bin/sh is used.
+ * The shell used to start applications is the system's bourne shell (i.e.,
+ * /bin/sh).
*
* The no_startup_id flag determines whether a startup notification context
* (and ID) should be created, which is the default and encouraged behavior.
/* Get the corresponding internal startup sequence */
const char *id = sn_startup_sequence_get_id(snsequence);
struct Startup_Sequence *current, *sequence = NULL;
- TAILQ_FOREACH (current, &startup_sequences, sequences) {
+ TAILQ_FOREACH(current, &startup_sequences, sequences) {
if (strcmp(current->id, id) != 0)
continue;
}
struct Startup_Sequence *current, *sequence = NULL;
- TAILQ_FOREACH (current, &startup_sequences, sequences) {
+ TAILQ_FOREACH(current, &startup_sequences, sequences) {
if (strcmp(current->id, startup_id) != 0)
continue;
static bool _is_con_mapped(Con *con) {
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes)
- if (_is_con_mapped(child))
- return true;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+ if (_is_con_mapped(child))
+ return true;
return con->mapped;
}
* X11 Errors are returned when the window was already destroyed */
add_ignore_event(cookie.sequence, 0);
}
+ ipc_send_window_event("close", con);
FREE(con->window->class_class);
FREE(con->window->class_instance);
i3string_free(con->window->name);
Con *current;
con->mapped = false;
- TAILQ_FOREACH (current, &(con->nodes_head), nodes)
- mark_unmapped(current);
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes)
+ mark_unmapped(current);
if (con->type == CT_WORKSPACE) {
/* We need to call mark_unmapped on floating nodes aswell since we can
* make containers floating. */
- TAILQ_FOREACH (current, &(con->floating_head), floating_windows)
- mark_unmapped(current);
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+ mark_unmapped(current);
}
}
return true;
Con *focus = con_descend_direction(workspace, direction);
+
+ /* special case: if there was no tiling con to focus and the workspace
+ * has a floating con in the focus stack, focus the top of the focus
+ * stack (which may be floating) */
+ if (focus == workspace)
+ focus = con_descend_focused(workspace);
+
if (focus) {
con_focus(focus);
x_set_warp_to(&(focus->rect));
Con *parent = con->parent;
if (con->type == CT_FLOATING_CON) {
+ if (orientation != HORIZ)
+ return false;
+
/* left/right focuses the previous/next floating container */
- if (orientation == HORIZ) {
- Con *next;
+ Con *next;
+ if (way == 'n')
+ next = TAILQ_NEXT(con, floating_windows);
+ else
+ next = TAILQ_PREV(con, floating_head, floating_windows);
+
+ /* If there is no next/previous container, wrap */
+ if (!next) {
if (way == 'n')
- next = TAILQ_NEXT(con, floating_windows);
+ next = TAILQ_FIRST(&(parent->floating_head));
else
- next = TAILQ_PREV(con, floating_head, floating_windows);
-
- /* If there is no next/previous container, wrap */
- if (!next) {
- if (way == 'n')
- next = TAILQ_FIRST(&(parent->floating_head));
- else
- next = TAILQ_LAST(&(parent->floating_head), floating_head);
- }
-
- /* Still no next/previous container? bail out */
- if (!next)
- return false;
+ next = TAILQ_LAST(&(parent->floating_head), floating_head);
+ }
- con_focus(con_descend_focused(next));
- return true;
- } else {
- /* up/down cycles through the Z-index */
- /* TODO: implement cycling through the z-index */
+ /* Still no next/previous container? bail out */
+ if (!next)
return false;
+
+ /* Raise the floating window on top of other windows preserving
+ * relative stack order */
+ while (TAILQ_LAST(&(parent->floating_head), floating_head) != next) {
+ Con *last = TAILQ_LAST(&(parent->floating_head), floating_head);
+ TAILQ_REMOVE(&(parent->floating_head), last, floating_windows);
+ TAILQ_INSERT_HEAD(&(parent->floating_head), last, floating_windows);
}
+
+ con_focus(con_descend_focused(next));
+ return true;
}
/* If the orientation does not match or there is no other con to focus, we
*/
void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
- DLOG("CLIENT_LEADER not set.\n");
+ DLOG("CLIENT_LEADER not set on window 0x%08x.\n", win->id);
+ win->leader = XCB_NONE;
FREE(prop);
return;
}
*/
void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) {
if (prop == NULL || xcb_get_property_value_length(prop) == 0) {
- DLOG("TRANSIENT_FOR not set.\n");
+ DLOG("TRANSIENT_FOR not set on window 0x%08x.\n", win->id);
+ win->transient_for = XCB_NONE;
FREE(prop);
return;
}
return;
}
- DLOG("Transient for changed to %08x\n", transient_for);
+ DLOG("Transient for changed to 0x%08x (window 0x%08x)\n", transient_for, win->id);
win->transient_for = transient_for;
*
*/
#include "all.h"
+#include "yajl_utils.h"
/* Stores a copy of the name of the last used workspace for the workspace
* back-and-forth switching. */
Con *workspace_get(const char *num, bool *created) {
Con *output, *workspace = NULL;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes)
- GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num));
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
+ GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num));
if (workspace == NULL) {
LOG("Creating new workspace \"%s\"\n", num);
* -1. */
long parsed_num = ws_name_to_number(num);
- TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->name, num) == 0) {
DLOG("Found workspace name assignment to output \"%s\"\n", assignment->output);
GREP_FIRST(output, croot, !strcmp(child->name, assignment->output));
con_attach(workspace, content, false);
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}");
+ ipc_send_workspace_event("init", workspace, NULL);
+ ewmh_update_number_of_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
if (created != NULL)
*created = true;
} else if (created != NULL) {
/* try the configured workspace bindings first to find a free name */
Binding *bind;
- TAILQ_FOREACH (bind, bindings, bindings) {
+ TAILQ_FOREACH(bind, bindings, bindings) {
DLOG("binding with command %s\n", bind->command);
if (strlen(bind->command) < strlen("workspace ") ||
strncasecmp(bind->command, "workspace", strlen("workspace")) != 0)
continue;
DLOG("relevant command = %s\n", bind->command);
- char *target = bind->command + strlen("workspace ");
+ const char *target = bind->command + strlen("workspace ");
while ((*target == ' ' || *target == '\t') && target != '\0')
target++;
/* We check if this is the workspace
strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 ||
strncasecmp(target, "current", strlen("current")) == 0)
continue;
- if (*target == '"')
- target++;
- if (strncasecmp(target, "__", strlen("__")) == 0) {
+ char *target_name = parse_string(&target, false);
+ if (target_name == NULL)
+ continue;
+ if (strncasecmp(target_name, "__", strlen("__")) == 0) {
LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target);
+ free(target_name);
continue;
}
FREE(ws->name);
- ws->name = strdup(target);
- if (ws->name[strlen(ws->name) - 1] == '"')
- ws->name[strlen(ws->name) - 1] = '\0';
+ ws->name = target_name;
DLOG("trying name *%s*\n", ws->name);
/* Ensure that this workspace is not assigned to a different output —
* find a new workspace, etc… */
bool assigned = false;
struct Workspace_Assignment *assignment;
- TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) {
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
if (strcmp(assignment->name, ws->name) != 0 ||
strcmp(assignment->output, output->name) == 0)
continue;
continue;
current = NULL;
- TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
- GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name));
+ TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+ GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name));
exists = (current != NULL);
if (!exists) {
/* Set ->num to the number of the workspace, if the name actually
* is a number or starts with a number */
- char *endptr = NULL;
- long parsed_num = strtol(ws->name, &endptr, 10);
- if (parsed_num == LONG_MIN ||
- parsed_num == LONG_MAX ||
- parsed_num < 0 ||
- endptr == ws->name)
- ws->num = -1;
- else
- ws->num = parsed_num;
+ ws->num = ws_name_to_number(ws->name);
LOG("Used number %d for workspace with name %s\n", ws->num, ws->name);
break;
ws->num = c;
current = NULL;
- TAILQ_FOREACH (out, &(croot->nodes_head), nodes)
- GREP_FIRST(current, output_get_content(out), child->num == ws->num);
+ TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+ GREP_FIRST(current, output_get_content(out), child->num == ws->num);
exists = (current != NULL);
DLOG("result for ws %d: exists = %d\n", c, exists);
Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
Con *current;
- TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
if (current != exclude &&
current->sticky_group != NULL &&
current->window != NULL &&
return recurse;
}
- TAILQ_FOREACH (current, &(con->floating_head), floating_windows) {
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows) {
if (current != exclude &&
current->sticky_group != NULL &&
current->window != NULL &&
/* 1: go through all containers */
/* handle all children and floating windows of this node */
- TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
if (current->sticky_group == NULL) {
workspace_reassign_sticky(current);
continue;
LOG("re-assigned window from src %p to dest %p\n", src, current);
}
- TAILQ_FOREACH (current, &(con->floating_head), floating_windows)
- workspace_reassign_sticky(current);
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+ workspace_reassign_sticky(current);
}
/*
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
Con *con = w->data;
- DLOG("Resetting urgency flag of con %p by timer\n", con);
- con->urgent = false;
- con_update_parents_urgency(con);
- workspace_update_urgent_flag(con_get_workspace(con));
- tree_render();
+ if (con->urgent) {
+ DLOG("Resetting urgency flag of con %p by timer\n", con);
+ con->urgent = false;
+ con_update_parents_urgency(con);
+ workspace_update_urgent_flag(con_get_workspace(con));
+ ipc_send_window_event("urgent", con);
+ tree_render();
+ }
ev_timer_stop(main_loop, con->urgency_timer);
FREE(con->urgency_timer);
/* disable fullscreen for the other workspaces and get the workspace we are
* currently on. */
- TAILQ_FOREACH (current, &(workspace->parent->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes) {
if (current->fullscreen_mode == CF_OUTPUT)
old = current;
current->fullscreen_mode = CF_NONE;
* focus and thereby immediately destroy it */
if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) {
/* focus for now… */
+ next->urgent = false;
con_focus(next);
/* … but immediately reset urgency flags; they will be set to false by
} else
con_focus(next);
- ipc_send_workspace_focus_event(workspace, current);
+ ipc_send_workspace_event("focus", workspace, current);
DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
/* Close old workspace if necessary. This must be done *after* doing
/* check if this workspace is currently visible */
if (!workspace_is_visible(old)) {
LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
+ yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL);
tree_close(old, DONT_KILL_WINDOW, false, false);
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
+
+ const unsigned char *payload;
+ ylength length;
+ y(get_buf, &payload, &length);
+ ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload);
+
+ y(free);
+
+ ewmh_update_number_of_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
}
}
next = TAILQ_NEXT(current, nodes);
} else {
/* If currently a numbered workspace, find next numbered workspace. */
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
/* Skip outputs starting with __, they are internal. */
if (con_is_internal(output))
continue;
- NODES_FOREACH (output_get_content(output)) {
+ NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child->num == -1)
/* Find next named workspace. */
if (!next) {
bool found_current = false;
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
/* Skip outputs starting with __, they are internal. */
if (con_is_internal(output))
continue;
- NODES_FOREACH (output_get_content(output)) {
+ NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child == current) {
/* Find first workspace. */
if (!next) {
- TAILQ_FOREACH (output, &(croot->nodes_head), nodes) {
+ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
/* Skip outputs starting with __, they are internal. */
if (con_is_internal(output))
continue;
- NODES_FOREACH (output_get_content(output)) {
+ NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (!next || (child->num != -1 && child->num < next->num))
prev = NULL;
} else {
/* If numbered workspace, find previous numbered workspace. */
- TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) {
+ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
/* Skip outputs starting with __, they are internal. */
if (con_is_internal(output))
continue;
- NODES_FOREACH_REVERSE (output_get_content(output)) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE || child->num == -1)
continue;
/* Need to check child against current and previous because we
/* Find previous named workspace. */
if (!prev) {
bool found_current = false;
- TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) {
+ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
/* Skip outputs starting with __, they are internal. */
if (con_is_internal(output))
continue;
- NODES_FOREACH_REVERSE (output_get_content(output)) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child == current) {
/* Find last workspace. */
if (!prev) {
- TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) {
+ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) {
/* Skip outputs starting with __, they are internal. */
if (con_is_internal(output))
continue;
- NODES_FOREACH_REVERSE (output_get_content(output)) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (!prev || child->num > prev->num)
next = TAILQ_NEXT(current, nodes);
} else {
/* If currently a numbered workspace, find next numbered workspace. */
- NODES_FOREACH (output_get_content(output)) {
+ NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child->num == -1)
/* Find next named workspace. */
if (!next) {
bool found_current = false;
- NODES_FOREACH (output_get_content(output)) {
+ NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child == current) {
/* Find first workspace. */
if (!next) {
- NODES_FOREACH (output_get_content(output)) {
+ NODES_FOREACH(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (!next || (child->num != -1 && child->num < next->num))
prev = NULL;
} else {
/* If numbered workspace, find previous numbered workspace. */
- NODES_FOREACH_REVERSE (output_get_content(output)) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE || child->num == -1)
continue;
/* Need to check child against current and previous because we
/* Find previous named workspace. */
if (!prev) {
bool found_current = false;
- NODES_FOREACH_REVERSE (output_get_content(output)) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (child == current) {
/* Find last workspace. */
if (!prev) {
- NODES_FOREACH_REVERSE (output_get_content(output)) {
+ NODES_FOREACH_REVERSE(output_get_content(output)) {
if (child->type != CT_WORKSPACE)
continue;
if (!prev || child->num > prev->num)
static bool get_urgency_flag(Con *con) {
Con *child;
- TAILQ_FOREACH (child, &(con->nodes_head), nodes)
- if (child->urgent || get_urgency_flag(child))
- return true;
+ TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+ if (child->urgent || get_urgency_flag(child))
+ return true;
- TAILQ_FOREACH (child, &(con->floating_head), floating_windows)
- if (child->urgent || get_urgency_flag(child))
- return true;
+ TAILQ_FOREACH(child, &(con->floating_head), floating_windows)
+ if (child->urgent || get_urgency_flag(child))
+ return true;
return false;
}
DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent);
if (old_flag != ws->urgent)
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}");
+ ipc_send_workspace_event("urgent", ws, NULL);
}
/*
/* Stores the X11 window ID of the currently focused window */
xcb_window_t focused_id = XCB_NONE;
-/* Because 'focused_id' might be reset to force input focus (after click to
- * raise), we separately keep track of the X11 window ID to be able to always
- * tell whether the focused window actually changed. */
+/* Because 'focused_id' might be reset to force input focus, we separately keep
+ * track of the X11 window ID to be able to always tell whether the focused
+ * window actually changed. */
static xcb_window_t last_focused = XCB_NONE;
/* Stores coordinates to warp mouse pointer to if set */
*/
static con_state *state_for_frame(xcb_window_t window) {
con_state *state;
- CIRCLEQ_FOREACH (state, &state_head, state)
- if (state->id == window)
- return state;
+ CIRCLEQ_FOREACH(state, &state_head, state)
+ if (state->id == window)
+ return state;
/* TODO: better error handling? */
ELOG("No state found\n");
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
}
if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) {
- xcb_rectangle_t rightline = {r->width + br.width + br.x, 0, r->width, r->height};
+ xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height};
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline);
}
if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) {
- xcb_rectangle_t bottomline = {0, r->height + br.height + br.y, r->width, r->height};
+ xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)};
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline);
}
/* 1pixel border needs an additional line at the top */
if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) {
- xcb_rectangle_t topline = {br.x, 0, con->rect.width + br.width + br.x, br.y};
+ xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y};
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline);
}
xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
if (p->parent_layout == L_SPLITH)
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
- {r->width + br.width + br.x, br.y, r->width, r->height + br.height}});
+ {r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}});
else if (p->parent_layout == L_SPLITV)
xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
- {br.x, r->height + br.height + br.y, r->width - (2 * br.x), r->height}});
+ {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
}
}
/* 5: draw two unconnected horizontal lines in border color */
xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
Rect *dr = &(con->deco_rect);
- int deco_diff_l = 2;
- int deco_diff_r = 2;
- if (parent->layout == L_TABBED) {
- if (TAILQ_PREV(con, nodes_head, nodes) != NULL)
+ adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
+ int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
+ int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con-> current_border_width;
+ if (parent->layout == L_TABBED ||
+ (parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
deco_diff_l = 0;
- if (TAILQ_NEXT(con, nodes) != NULL)
deco_diff_r = 0;
}
xcb_segment_t segments[] = {
con_state *state = state_for_frame(con->frame);
if (!leaf) {
- TAILQ_FOREACH (current, &(con->nodes_head), nodes)
- x_deco_recurse(current);
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes)
+ x_deco_recurse(current);
- TAILQ_FOREACH (current, &(con->floating_head), floating_windows)
- x_deco_recurse(current);
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+ x_deco_recurse(current);
if (state->mapped)
xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height);
/* Calculate the height of all window decorations which will be drawn on to
* this frame. */
uint32_t max_y = 0, max_height = 0;
- TAILQ_FOREACH (current, &(con->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
Rect *dr = &(current->deco_rect);
if (dr->y >= max_y && dr->height >= max_height) {
max_y = dr->y;
/* Handle all children and floating windows of this node. We recurse
* in focus order to display the focused client in a stack first when
* switching workspaces (reduces flickering). */
- TAILQ_FOREACH (current, &(con->focus_head), focused)
- x_push_node(current);
+ TAILQ_FOREACH(current, &(con->focus_head), focused)
+ x_push_node(current);
}
/*
}
/* handle all children and floating windows of this node */
- TAILQ_FOREACH (current, &(con->nodes_head), nodes)
- x_push_node_unmaps(current);
+ TAILQ_FOREACH(current, &(con->nodes_head), nodes)
+ x_push_node_unmaps(current);
- TAILQ_FOREACH (current, &(con->floating_head), floating_windows)
- x_push_node_unmaps(current);
+ TAILQ_FOREACH(current, &(con->floating_head), floating_windows)
+ x_push_node_unmaps(current);
}
/*
return false;
Con *current;
- TAILQ_FOREACH (current, &(con->parent->nodes_head), nodes) {
+ TAILQ_FOREACH(current, &(con->parent->nodes_head), nodes) {
if (current == con)
return true;
}
DLOG("-- PUSHING WINDOW STACK --\n");
//DLOG("Disabling EnterNotify\n");
uint32_t values[1] = {XCB_NONE};
- CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) {
+ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
if (state->mapped)
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
}
/* count first, necessary to (re)allocate memory for the bottom-to-top
* stack afterwards */
int cnt = 0;
- CIRCLEQ_FOREACH_REVERSE (state, &state_head, state)
- if (con_has_managed_window(state->con))
- cnt++;
+ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state)
+ if (con_has_managed_window(state->con))
+ cnt++;
/* The bottom-to-top window stack of all windows which are managed by i3.
* Used for x_get_window_stack(). */
xcb_window_t *walk = client_list_windows;
/* X11 correctly represents the stack if we push it from bottom to top */
- CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) {
+ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
if (con_has_managed_window(state->con))
memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t));
walk = client_list_windows;
/* reorder by initial mapping */
- TAILQ_FOREACH (state, &initial_mapping_head, initial_mapping_order) {
+ TAILQ_FOREACH(state, &initial_mapping_head, initial_mapping_order) {
if (con_has_managed_window(state->con))
*walk++ = state->con->window->id;
}
//DLOG("Re-enabling EnterNotify\n");
values[0] = FRAME_EVENT_MASK;
- CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) {
+ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
if (state->mapped)
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
}
values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE);
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
}
- xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp);
+ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME);
if (focused->window != NULL) {
values[0] = CHILD_EVENT_MASK;
xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
if (focused_id == XCB_NONE) {
DLOG("Still no window focused, better set focus to the root window\n");
- xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, last_timestamp);
+ xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
ewmh_update_active_window(XCB_WINDOW_NONE);
focused_id = root;
}
* unmapped, the second one appears under the cursor and therefore gets an
* EnterNotify event. */
values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
- CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) {
+ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
if (!state->unmap_now)
continue;
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
x_push_node_unmaps(con);
/* save the current stack as old stack */
- CIRCLEQ_FOREACH (state, &state_head, state) {
+ CIRCLEQ_FOREACH(state, &state_head, state) {
CIRCLEQ_REMOVE(&old_state_head, state, old_state);
CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state);
}
uint32_t values[] = {FRAME_EVENT_MASK & mask};
con_state *state;
- CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) {
+ CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
if (state->mapped)
xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values);
}
*/
static Output *get_screen_at(unsigned int x, unsigned int y) {
Output *output;
- TAILQ_FOREACH (output, &outputs, outputs)
- if (output->rect.x == x && output->rect.y == y)
- return output;
+ TAILQ_FOREACH(output, &outputs, outputs)
+ if (output->rect.x == x && output->rect.y == y)
+ return output;
return NULL;
}
META.yml
i3-cfg-for-*
-
-Xdummy.so
'AnyEvent::I3' => '0.15',
'X11::XCB' => '0.09',
'Inline' => 0,
+ 'Inline::C' => 0,
'ExtUtils::PkgConfig' => 0,
'Test::More' => '0.94',
'IPC::Run' => 0,
+++ /dev/null
-#!/bin/sh
-# ----------------------------------------------------------------------
-# Copyright (C) 2005-2010 Karl J. Runge <runge@karlrunge.com>
-# All rights reserved.
-#
-# This file is part of Xdummy.
-#
-# Xdummy is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or (at
-# your option) any later version.
-#
-# Xdummy is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Xdummy; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
-# or see <http://www.gnu.org/licenses/>.
-# ----------------------------------------------------------------------
-#
-#
-# Xdummy: an LD_PRELOAD hack to run a stock Xorg(1) or XFree86(1) server
-# with the "dummy" video driver to make it avoid Linux VT switching, etc.
-#
-# Run "Xdummy -help" for more info.
-#
-install=""
-uninstall=""
-runit=1
-prconf=""
-notweak=""
-root=""
-nosudo=""
-xserver=""
-geom=""
-nomodelines=""
-depth=""
-debug=""
-strace=""
-cmdline_config=""
-
-PATH=$PATH:/bin:/usr/bin
-export PATH
-
-program=`basename "$0"`
-
-help () {
- ${PAGER:-more} << END
-$program:
-
- A hack to run a stock Xorg(1) or XFree86(1) X server with the "dummy"
- (RAM-only framebuffer) video driver such that it AVOIDS the Linux VT
- switching, opening device files in /dev, keyboard and mouse conflicts,
- and other problems associated with the normal use of "dummy".
-
- In other words, it tries to make Xorg/XFree86 with the "dummy"
- device driver act more like Xvfb(1).
-
- The primary motivation for the Xdummy script is to provide a virtual X
- server for x11vnc but with more features than Xvfb (or Xvnc); however
- it could be used for other reasons (e.g. better automated testing
- than with Xvfb.) One nice thing is the dummy server supports RANDR
- dynamic resizing while Xvfb does not.
-
- So, for example, x11vnc+Xdummy terminal services are a little better
- than x11vnc+Xvfb.
-
- To achieve this, while running the real Xserver $program intercepts
- system and library calls via the LD_PRELOAD method and modifies
- the behavior to make it work correctly (e.g. avoid the VT stuff.)
- LD_PRELOAD tricks are usually "clever hacks" and so might not work
- in all situations or break when something changes.
-
- WARNING: Take care in using Xdummy, although it never has it is
- possible that it could damage hardware. One can use the -prconf
- option to have it print out the xorg.conf config that it would use
- and then inspect it carefully before actually using it.
-
- This program no longer needs to be run as root as of 12/2009.
- However, if there are problems for certain situations (usually older
- servers) it may perform better if run as root (use the -root option.)
- When running as root remember the previous paragraph and that Xdummy
- comes without any warranty.
-
- gcc/cc and other build tools are required for this script to be able
- to compile the LD_PRELOAD shared object. Be sure they are installed
- on the system. See -install and -uninstall described below.
-
- Your Linux distribution may not install the dummy driver by default,
- e.g:
-
- /usr/lib/xorg/modules/drivers/dummy_drv.so
-
- some have it in a package named xserver-xorg-video-dummy you that
- need to install.
-
-Usage:
-
- $program <${program}-args> <Xserver-args>
-
- (actually, the arguments can be supplied in any order.)
-
-Examples:
-
- $program -install
-
- $program :1
-
- $program -debug :1
-
- $program -tmpdir ~/mytmp :1 -nolisten tcp
-
-startx example:
-
- startx -e bash -- $program :2 -depth 16
-
- (if startx needs to be run as root, you can su(1) to a normal
- user in the bash shell and then launch ~/.xinitrc or ~/.xsession,
- gnome-session, startkde, startxfce4, etc.)
-
-xdm example:
-
- xdm -config /usr/local/dummy/xdm-config -nodaemon
-
- where the xdm-config file has line:
-
- DisplayManager.servers: /usr/local/dummy/Xservers
-
- and /usr/local/dummy/Xservers has lines:
-
- :1 local /usr/local/dummy/Xdummy :1 -debug
- :2 local /usr/local/dummy/Xdummy :2 -debug
-
- (-debug is optional)
-
-gdm/kdm example:
-
- TBD.
-
-Root permission and x11vnc:
-
- Update: as of 12/2009 this program no longer must be run as root.
- So try it as non-root before running it as root and/or the
- following schemes.
-
- In some circumstances X server program may need to be run as root.
- If so, one could run x11vnc as root with -unixpw (it switches
- to the user that logs in) and that may be OK, some other ideas:
-
- - add this to sudo via visudo:
-
- ALL ALL = NOPASSWD: /usr/local/bin/Xdummy
-
- - use this little suid wrapper:
-/*
- * xdummy.c
- *
- cc -o ./xdummy xdummy.c
- sudo cp ./xdummy /usr/local/bin/xdummy
- sudo chown root:root /usr/local/bin/xdummy
- sudo chmod u+s /usr/local/bin/xdummy
- *
- */
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <stdio.h>
-
-int main (int argc, char *argv[]) {
- extern char **environ;
- char str[100];
- sprintf(str, "XDUMMY_UID=%d", (int) getuid());
- putenv(str);
- setuid(0);
- setgid(0);
- execv("/usr/local/bin/Xdummy", argv);
- exit(1);
- return 1;
-}
-
-
-Options:
-
- ${program}-args:
-
- -install Compile the LD_PRELOAD shared object and install it
- next to the $program script file as:
-
- $0.so
-
- When that file exists it is used as the LD_PRELOAD
- shared object without recompiling. Otherwise,
- each time $program is run the LD_PRELOAD shared
- object is compiled as a file in /tmp (or -tmpdir)
-
- If you set the environment variable
- INTERPOSE_GETUID=1 when building, then when
- $program is run as an ordinary user, the shared
- object will interpose getuid() calls and pretend
- to be root. Otherwise it doesn't pretend to
- be root.
-
- You can also set the CFLAGS environment variable
- to anything else you want on the compile cmdline.
-
- -uninstall Remove the file:
-
- $0.so
-
- The LD_PRELOAD shared object will then be compiled
- each time this program is run.
-
- The X server is not started under -install, -uninstall, or -prconf.
-
-
- :N The DISPLAY (e.g. :15) is often the first
- argument. It is passed to the real X server and
- also used by the Xdummy script as an identifier.
-
- -geom geom1[,geom2...] Take the geometry (e.g. 1024x768) or list
- of geometries and insert them into the Screen
- section of the tweaked X server config file.
- Use this to have a different geometry than the
- one(s) in the system config file.
-
- The option -geometry can be used instead of -geom;
- x11vnc calls Xdummy and Xvfb this way.
-
- -nomodelines When you specify -geom/-geometry, $program will
- create Modelines for each geometry and put them
- in the Monitor section. If you do not want this
- then supply -nomodelines.
-
- -depth n Use pixel color depth n (e.g. 8, 16, or 24). This
- makes sure the X config file has a Screen.Display
- subsection of this depth. Note this option is
- ALSO passed to the X server.
-
- -DEPTH n Same as -depth, except not passed to X server.
-
- -tmpdir dir Specify a temporary directory, owned by you and
- only writable by you. This is used in place of
- /tmp/Xdummy.\$USER/.. to place the $program.so
- shared object, tweaked config files, etc.
-
- -nonroot Run in non-root mode (working 12/2009, now default)
-
- -root Run as root (may still be needed in some
- environments.) Same as XDUMMY_RUN_AS_ROOT=1.
-
- -nosudo Do not try to use sudo(1) when re-running as root,
- use su(1) instead.
-
- -xserver path Specify the path to the Xserver to use. Default
- is to try "Xorg" first and then "XFree86". If
- those are not in \$PATH, it tries these locations:
- /usr/bin/Xorg
- /usr/X11R6/bin/Xorg
- /usr/X11R6/bin/XFree86
-
- -n Do not run the command to start the X server,
- just show the command that $program would run.
- The LD_PRELOAD shared object will be built,
- if needed. Also note any XDUMMY* environment
- variables that need to be set.
-
- -prconf Print, to stdout, the tweaked Xorg/XFree86
- config file (-config and -xf86config server
- options, respectively.) The Xserver is not
- started.
-
- -notweak Do not tweak (modify) the Xorg/XFree86 config file
- (system or server command line) at all. The -geom
- and similar config file modifications are ignored.
-
- It is up to you to make sure it is a working
- config file (e.g. "dummy" driver, etc.)
- Perhaps you want to use a file based on the
- -prconf output.
-
- -debug Extra debugging output.
-
- -strace strace(1) the Xserver process (for troubleshooting.)
- -ltrace ltrace(1) instead of strace (can be slow.)
-
- -h, -help Print out this help.
-
-
- Xserver-args:
-
- Most of the Xorg and XFree86 options will work and are simply
- passed along if you supply them. Important ones that may be
- supplied if missing:
-
- :N X Display number for server to use.
-
- vtNN Linux virtual terminal (VT) to use (a VT is currently
- still used, just not switched to and from.)
-
- -config file Driver "dummy" tweaked config file, a
- -xf86config file number of settings are tweaked besides Driver.
-
- If -config/-xf86config is not given, the system one
- (e.g. /etc/X11/xorg.conf) is used. If the system one cannot be
- found, a built-in one is used. Any settings in the config file
- that are not consistent with "dummy" mode will be overwritten
- (unless -notweak is specified.)
-
- Use -config xdummy-builtin to force usage of the builtin config.
-
- If "file" is only a basename (e.g. "xorg.dummy.conf") with no /'s,
- then no tweaking of it is done: the X server will look for that
- basename via its normal search algorithm. If the found file does
- not refer to the "dummy" driver, etc, then the X server will fail.
-
-Notes:
-
- The Xorg/XFree86 "dummy" driver is currently undocumented. It works
- well in this mode, but it is evidently not intended for end-users.
- So it could be removed or broken at any time.
-
- If the display Xserver-arg (e.g. :1) is not given, or ":" is given
- that indicates $program should try to find a free one (based on
- tcp ports.)
-
- If the display virtual terminal, VT, (e.g. vt9) is not given that
- indicates $program should try to find a free one (or guess a high one.)
-
- This program is not completely secure WRT files in /tmp (but it tries
- to a good degree.) Better is to use the -tmpdir option to supply a
- directory only writable by you. Even better is to get rid of users
- on the local machine you do not trust :-)
-
- Set XDUMMY_SET_XV=1 to turn on debugging output for this script.
-
-END
-}
-
-warn() {
- echo "$*" 1>&2
-}
-
-if [ "X$XDUMMY_SET_XV" != "X" ]; then
- set -xv
-fi
-
-if [ "X$XDUMMY_UID" = "X" ]; then
- XDUMMY_UID=`id -u`
- export XDUMMY_UID
-fi
-if [ "X$XDUMMY_UID" = "X0" ]; then
- if [ "X$SUDO_UID" != "X" ]; then
- XDUMMY_UID=$SUDO_UID
- export XDUMMY_UID
- fi
-fi
-
-# check if root=1 first:
-#
-if [ "X$XDUMMY_RUN_AS_ROOT" = "X1" ]; then
- root=1
-fi
-for arg in $*
-do
- if [ "X$arg" = "X-nonroot" ]; then
- root=""
- elif [ "X$arg" = "X-root" ]; then
- root=1
- fi
-done
-
-# See if it really needs to be run as root:
-#
-if [ "X$XDUMMY_SU_EXEC" = "X" -a "X$root" = "X1" -a "X`id -u`" != "X0" ]; then
- # this is to prevent infinite loop in case su/sudo doesn't work:
- XDUMMY_SU_EXEC=1
- export XDUMMY_SU_EXEC
-
- dosu=1
- nosudo=""
-
- for arg in $*
- do
- if [ "X$arg" = "X-nonroot" ]; then
- dosu=""
- elif [ "X$arg" = "X-nosudo" ]; then
- nosudo="1"
- elif [ "X$arg" = "X-help" ]; then
- dosu=""
- elif [ "X$arg" = "X-h" ]; then
- dosu=""
- elif [ "X$arg" = "X-install" ]; then
- dosu=""
- elif [ "X$arg" = "X-uninstall" ]; then
- dosu=""
- elif [ "X$arg" = "X-n" ]; then
- dosu=""
- elif [ "X$arg" = "X-prconf" ]; then
- dosu=""
- fi
- done
- if [ $dosu ]; then
- # we need to restart it with su/sudo:
- if type sudo > /dev/null 2>&1; then
- :
- else
- nosudo=1
- fi
- if [ "X$nosudo" = "X" ]; then
- warn "$program: supply the sudo password to restart as root:"
- if [ "X$XDUMMY_UID" != "X" ]; then
- exec sudo $0 -uid $XDUMMY_UID "$@"
- else
- exec sudo $0 "$@"
- fi
- else
- warn "$program: supply the root password to restart as root:"
- if [ "X$XDUMMY_UID" != "X" ]; then
- exec su -c "$0 -uid $XDUMMY_UID $*"
- else
- exec su -c "$0 $*"
- fi
- fi
- # DONE:
- exit
- fi
-fi
-
-# This will hold the X display, e.g. :20
-#
-disp=""
-args=""
-cmdline_config=""
-
-# Process Xdummy args:
-#
-while [ "X$1" != "X" ]
-do
- if [ "X$1" = "X-config" -o "X$1" = "X-xf86config" ]; then
- cmdline_config="$2"
- fi
- case $1 in
- ":"*) disp=$1
- ;;
- "-install") install=1; runit=""
- ;;
- "-uninstall") uninstall=1; runit=""
- ;;
- "-n") runit=""
- ;;
- "-no") runit=""
- ;;
- "-norun") runit=""
- ;;
- "-prconf") prconf=1; runit=""
- ;;
- "-notweak") notweak=1
- ;;
- "-noconf") notweak=1
- ;;
- "-nonroot") root=""
- ;;
- "-root") root=1
- ;;
- "-nosudo") nosudo=1
- ;;
- "-xserver") xserver="$2"; shift
- ;;
- "-uid") XDUMMY_UID="$2"; shift
- export XDUMMY_UID
- ;;
- "-geom") geom="$2"; shift
- ;;
- "-geometry") geom="$2"; shift
- ;;
- "-nomodelines") nomodelines=1
- ;;
- "-depth") depth="$2"; args="$args -depth $2";
- shift
- ;;
- "-DEPTH") depth="$2"; shift
- ;;
- "-tmpdir") XDUMMY_TMPDIR="$2"; shift
- ;;
- "-debug") debug=1
- ;;
- "-nodebug") debug=""
- ;;
- "-strace") strace=1
- ;;
- "-ltrace") strace=2
- ;;
- "-h") help; exit 0
- ;;
- "-help") help; exit 0
- ;;
- *) args="$args $1"
- ;;
- esac
- shift
-done
-
-# Try to get a username for use in our tmp directory, etc.
-#
-user=""
-if [ X`id -u` = "X0" ]; then
- user=root # this will also be used below for id=0
-elif [ "X$USER" != "X" ]; then
- user=$USER
-elif [ "X$LOGNAME" != "X" ]; then
- user=$LOGNAME
-fi
-
-# Keep trying...
-#
-if [ "X$user" = "X" ]; then
- user=`whoami 2>/dev/null`
-fi
-if [ "X$user" = "X" ]; then
- user=`basename "$HOME"`
-fi
-if [ "X$user" = "X" -o "X$user" = "X." ]; then
- user="u$$"
-fi
-
-if [ "X$debug" = "X1" -a "X$runit" != "X" ]; then
- echo ""
- echo "/usr/bin/env:"
- env | egrep -v '^(LS_COLORS|TERMCAP)' | sort
- echo ""
-fi
-
-# Function to compile the LD_PRELOAD shared object:
-#
-make_so() {
- # extract code embedded in this script into a tmp C file:
- n1=`grep -n '^#code_begin' $0 | head -1 | awk -F: '{print $1}'`
- n2=`grep -n '^#code_end' $0 | head -1 | awk -F: '{print $1}'`
- n1=`expr $n1 + 1`
- dn=`expr $n2 - $n1`
-
- tmp=$tdir/Xdummy.$RANDOM$$.c
- rm -f $tmp
- if [ -e $tmp -o -h $tmp ]; then
- warn "$tmp still exists."
- exit 1
- fi
- touch $tmp || exit 1
- tail -n +$n1 $0 | head -n $dn > $tmp
-
- # compile it to Xdummy.so:
- if [ -f "$SO" ]; then
- mv $SO $SO.$$
- rm -f $SO.$$
- fi
- rm -f $SO
- touch $SO
- if [ ! -f "$SO" ]; then
- SO=$tdir/Xdummy.$user.so
- warn "warning switching LD_PRELOAD shared object to: $SO"
- fi
-
- if [ -f "$SO" ]; then
- mv $SO $SO.$$
- rm -f $SO.$$
- fi
- rm -f $SO
-
- # we assume gcc:
- if [ "X$INTERPOSE_GETUID" = "X1" ]; then
- CFLAGS="$CFLAGS -DINTERPOSE_GETUID"
- fi
- echo "$program:" cc -shared -fPIC $CFLAGS -o $SO $tmp
- cc -shared -fPIC $CFLAGS -o $SO $tmp
- rc=$?
- rm -f $tmp
- if [ $rc != 0 ]; then
- warn "$program: cannot build $SO"
- exit 1
- fi
- if [ "X$debug" != "X" -o "X$install" != "X" ]; then
- warn "$program: created $SO"
- ls -l "$SO"
- fi
-}
-
-# Set tdir to tmp dir for make_so():
-if [ "X$XDUMMY_TMPDIR" != "X" ]; then
- tdir=$XDUMMY_TMPDIR
- mkdir -p $tdir
-else
- tdir="/tmp"
-fi
-
-# Handle -install/-uninstall case:
-SO=$0.so
-if [ "X$install" != "X" -o "X$uninstall" != "X" ]; then
- if [ -e "$SO" -o -h "$SO" ]; then
- warn "$program: removing $SO"
- fi
- if [ -f "$SO" ]; then
- mv $SO $SO.$$
- rm -f $SO.$$
- fi
- rm -f $SO
- if [ -e "$SO" -o -h "$SO" ]; then
- warn "warning: $SO still exists."
- exit 1
- fi
- if [ $install ]; then
- make_so
- if [ ! -f "$SO" ]; then
- exit 1
- fi
- fi
- exit 0
-fi
-
-# We need a tmp directory for the .so, tweaked config file, and for
-# redirecting filenames we cannot create (under -nonroot)
-#
-tack=""
-if [ "X$XDUMMY_TMPDIR" = "X" ]; then
- XDUMMY_TMPDIR="/tmp/Xdummy.$user"
-
- # try to tack on a unique subdir (display number or pid)
- # to allow multiple instances
- #
- if [ "X$disp" != "X" ]; then
- t0=$disp
- else
- t0=$1
- fi
- tack=`echo "$t0" | sed -e 's/^.*://'`
- if echo "$tack" | grep '^[0-9][0-9]*$' > /dev/null; then
- :
- else
- tack=$$
- fi
- if [ "X$tack" != "X" ]; then
- XDUMMY_TMPDIR="$XDUMMY_TMPDIR/$tack"
- fi
-fi
-
-tmp=$XDUMMY_TMPDIR
-if echo "$tmp" | grep '^/tmp' > /dev/null; then
- if [ "X$tmp" != "X/tmp" -a "X$tmp" != "X/tmp/" ]; then
- # clean this subdir of /tmp out, otherwise leave it...
- rm -rf $XDUMMY_TMPDIR
- if [ -e $XDUMMY_TMPDIR ]; then
- warn "$XDUMMY_TMPDIR still exists"
- exit 1
- fi
- fi
-fi
-
-mkdir -p $XDUMMY_TMPDIR
-chmod 700 $XDUMMY_TMPDIR
-if [ "X$tack" != "X" ]; then
- chmod 700 `dirname "$XDUMMY_TMPDIR"` 2>/dev/null
-fi
-
-# See if we can write something there:
-#
-tfile="$XDUMMY_TMPDIR/test.file"
-touch $tfile
-if [ ! -f "$tfile" ]; then
- XDUMMY_TMPDIR="/tmp/Xdummy.$$.$USER"
- warn "warning: setting tmpdir to $XDUMMY_TMPDIR ..."
- rm -rf $XDUMMY_TMPDIR || exit 1
- mkdir -p $XDUMMY_TMPDIR || exit 1
-fi
-rm -f $tfile
-
-export XDUMMY_TMPDIR
-
-# Compile the LD_PRELOAD shared object if needed (needs XDUMMY_TMPDIR)
-#
-if [ ! -f "$SO" ]; then
- SO="$XDUMMY_TMPDIR/Xdummy.so"
- make_so
-fi
-
-# Decide which X server to use:
-#
-if [ "X$xserver" = "X" ]; then
- if type Xorg >/dev/null 2>&1; then
- xserver="Xorg"
- elif type XFree86 >/dev/null 2>&1; then
- xserver="XFree86"
- elif -x /usr/bin/Xorg; then
- xserver="/usr/bin/Xorg"
- elif -x /usr/X11R6/bin/Xorg; then
- xserver="/usr/X11R6/bin/Xorg"
- elif -x /usr/X11R6/bin/XFree86; then
- xserver="/usr/X11R6/bin/XFree86"
- fi
- if [ "X$xserver" = "X" ]; then
- # just let it fail below.
- xserver="/usr/bin/Xorg"
- warn "$program: cannot locate a stock Xserver... assuming $xserver"
- fi
-fi
-
-# See if the binary is suid or not readable under -nonroot mode:
-#
-if [ "X$BASH_VERSION" != "X" ]; then
- xserver_path=`type -p $xserver 2>/dev/null`
-else
- xserver_path=`type $xserver 2>/dev/null | awk '{print $NF}'`
-fi
-if [ -e "$xserver_path" -a "X$root" = "X" -a "X$runit" != "X" ]; then
- if [ ! -r $xserver_path -o -u $xserver_path -o -g $xserver_path ]; then
- # XXX not quite correct with rm -rf $XDUMMY_TMPDIR ...
- # we keep on a filesystem we know root can write to.
- base=`basename "$xserver_path"`
- new="/tmp/$base.$user.bin"
- if [ -e $new ]; then
- snew=`ls -l $new | awk '{print $5}' | grep '^[0-9][0-9]*$'`
- sold=`ls -l $xserver_path | awk '{print $5}' | grep '^[0-9][0-9]*$'`
- if [ "X$snew" != "X" -a "X$sold" != "X" -a "X$sold" != "X$snew" ]; then
- warn "removing different sized copy:"
- ls -l $new $xserver_path
- rm -f $new
- fi
- fi
- if [ ! -e $new -o ! -s $new ]; then
- rm -f $new
- touch $new || exit 1
- chmod 700 $new || exit 1
- if [ ! -r $xserver_path ]; then
- warn ""
- warn "NEED TO COPY UNREADABLE $xserver_path to $new as root:"
- warn ""
- ls -l $xserver_path 1>&2
- warn ""
- warn "This only needs to be done once:"
- warn " cat $xserver_path > $new"
- warn ""
- nos=$nosudo
- if type sudo > /dev/null 2>&1; then
- :
- else
- nos=1
- fi
- if [ "X$nos" = "X1" ]; then
- warn "Please supply root passwd to 'su -c'"
- su -c "cat $xserver_path > $new"
- else
- warn "Please supply the sudo passwd if asked:"
- sudo /bin/sh -c "cat $xserver_path > $new"
- fi
- else
- warn ""
- warn "COPYING SETUID $xserver_path to $new"
- warn ""
- ls -l $xserver_path 1>&2
- warn ""
- cat $xserver_path > $new
- fi
- ls -l $new
- if [ -s $new ]; then
- :
- else
- rm -f $new
- ls -l $new
- exit 1
- fi
- warn ""
- warn "Please restart Xdummy now."
- exit 0
- fi
- if [ ! -O $new ]; then
- warn "file \"$new\" not owned by us!"
- ls -l $new
- exit 1
- fi
- xserver=$new
- fi
-fi
-
-# Work out display:
-#
-if [ "X$disp" != "X" ]; then
- :
-elif [ "X$1" != "X" ]; then
- if echo "$1" | grep '^:[0-9]' > /dev/null; then
- disp=$1
- shift
- elif [ "X$1" = "X:" ]; then
- # ":" means for us to find one.
- shift
- fi
-fi
-if [ "X$disp" = "X" -o "X$disp" = "X:" ]; then
- # try to find an open display port:
- # (tcp outdated...)
- ports=`netstat -ant | grep LISTEN | awk '{print $4}' | sed -e 's/^.*://'`
- n=0
- while [ $n -le 20 ]
- do
- port=`printf "60%02d" $n`
- if echo "$ports" | grep "^${port}\$" > /dev/null; then
- :
- else
- disp=":$n"
- warn "$program: auto-selected DISPLAY $disp"
- break
- fi
- n=`expr $n + 1`
- done
-fi
-
-# Work out which vt to use, try to find/guess an open one if necessary.
-#
-vt=""
-for arg in $*
-do
- if echo "$arg" | grep '^vt' > /dev/null; then
- vt=$arg
- break
- fi
-done
-if [ "X$vt" = "X" ]; then
- if [ "X$user" = "Xroot" ]; then
- # root can user fuser(1) to see if it is in use:
- if type fuser >/dev/null 2>&1; then
- # try /dev/tty17 thru /dev/tty32
- n=17
- while [ $n -le 32 ]
- do
- dev="/dev/tty$n"
- if fuser $dev >/dev/null 2>&1; then
- :
- else
- vt="vt$n"
- warn "$program: auto-selected VT $vt => $dev"
- break
- fi
- n=`expr $n + 1`
- done
- fi
- fi
- if [ "X$vt" = "X" ]; then
- # take a wild guess...
- vt=vt16
- warn "$program: selected fallback VT $vt"
- fi
-else
- vt=""
-fi
-
-# Decide flavor of Xserver:
-#
-stype=`basename "$xserver"`
-if echo "$stype" | grep -i xfree86 > /dev/null; then
- stype=xfree86
-else
- stype=xorg
-fi
-
-tweak_config() {
- in="$1"
- config2="$XDUMMY_TMPDIR/xdummy_modified_xconfig.conf"
- if [ "X$disp" != "X" ]; then
- d=`echo "$disp" | sed -e 's,/,,g' -e 's/:/_/g'`
- config2="$config2$d"
- fi
-
- # perl script to tweak the config file... add/delete options, etc.
- #
- env XDUMMY_GEOM=$geom \
- XDUMMY_DEPTH=$depth \
- XDUMMY_NOMODELINES=$nomodelines \
- perl > $config2 < $in -e '
- $n = 0;
- $geom = $ENV{XDUMMY_GEOM};
- $depth = $ENV{XDUMMY_DEPTH};
- $nomodelines = $ENV{XDUMMY_NOMODELINES};
- $mode_str = "";
- $videoram = "24000";
- $HorizSync = "30.0 - 130.0";
- $VertRefresh = "50.0 - 250.0";
- if ($geom ne "") {
- my $tmp = "";
- foreach $g (split(/,/, $geom)) {
- $tmp .= "\"$g\" ";
- if (!$nomodelines && $g =~ /(\d+)x(\d+)/) {
- my $w = $1;
- my $h = $2;
- $mode_str .= " Modeline \"$g\" ";
- my $dot = sprintf("%.2f", $w * $h * 70 * 1.e-6);
- $mode_str .= $dot;
- $mode_str .= " " . $w;
- $mode_str .= " " . int(1.02 * $w);
- $mode_str .= " " . int(1.10 * $w);
- $mode_str .= " " . int(1.20 * $w);
- $mode_str .= " " . $h;
- $mode_str .= " " . int($h + 1);
- $mode_str .= " " . int($h + 3);
- $mode_str .= " " . int($h + 20);
- $mode_str .= "\n";
- }
- }
- $tmp =~ s/\s*$//;
- $geom = $tmp;
- }
- while (<>) {
- if ($ENV{XDUMMY_NOTWEAK}) {
- print $_;
- next;
- }
- $n++;
- if (/^\s*#/) {
- # pass comments straight thru
- print;
- next;
- }
- if (/^\s*Section\s+(\S+)/i) {
- # start of Section
- $sect = $1;
- $sect =~ s/\W//g;
- $sect =~ y/A-Z/a-z/;
- $sects{$sect} = 1;
- print;
- next;
- }
- if (/^\s*EndSection/i) {
- # end of Section
- if ($sect eq "serverflags") {
- if (!$got_DontVTSwitch) {
- print " ##Xdummy:##\n";
- print " Option \"DontVTSwitch\" \"true\"\n";
- }
- if (!$got_AllowMouseOpenFail) {
- print " ##Xdummy:##\n";
- print " Option \"AllowMouseOpenFail\" \"true\"\n";
- }
- if (!$got_PciForceNone) {
- print " ##Xdummy:##\n";
- print " Option \"PciForceNone\" \"true\"\n";
- }
- } elsif ($sect eq "device") {
- if (!$got_Driver) {
- print " ##Xdummy:##\n";
- print " Driver \"dummy\"\n";
- }
- if (!$got_VideoRam) {
- print " ##Xdummy:##\n";
- print " VideoRam $videoram\n";
- }
- } elsif ($sect eq "screen") {
- if ($depth ne "" && !got_DefaultDepth) {
- print " ##Xdummy:##\n";
- print " DefaultDepth $depth\n";
- }
- if ($got_Monitor eq "") {
- print " ##Xdummy:##\n";
- print " Monitor \"Monitor0\"\n";
- }
- } elsif ($sect eq "monitor") {
- if (!got_HorizSync) {
- print " ##Xdummy:##\n";
- print " HorizSync $HorizSync\n";
- }
- if (!got_VertRefresh) {
- print " ##Xdummy:##\n";
- print " VertRefresh $VertRefresh\n";
- }
- if (!$nomodelines) {
- print " ##Xdummy:##\n";
- print $mode_str;
- }
- }
- $sect = "";
- print;
- next;
- }
-
- if (/^\s*SubSection\s+(\S+)/i) {
- # start of Section
- $subsect = $1;
- $subsect =~ s/\W//g;
- $subsect =~ y/A-Z/a-z/;
- $subsects{$subsect} = 1;
- if ($sect eq "screen" && $subsect eq "display") {
- $got_Modes = 0;
- }
- print;
- next;
- }
- if (/^\s*EndSubSection/i) {
- # end of SubSection
- if ($sect eq "screen") {
- if ($subsect eq "display") {
- if ($depth ne "" && !$set_Depth) {
- print " ##Xdummy:##\n";
- print " Depth\t$depth\n";
- }
- if ($geom ne "" && ! $got_Modes) {
- print " ##Xdummy:##\n";
- print " Modes\t$geom\n";
- }
- }
- }
- $subsect = "";
- print;
- next;
- }
-
- $l = $_;
- $l =~ s/#.*$//;
- if ($sect eq "serverflags") {
- if ($l =~ /^\s*Option.*DontVTSwitch/i) {
- $_ =~ s/false/true/ig;
- $got_DontVTSwitch = 1;
- }
- if ($l =~ /^\s*Option.*AllowMouseOpenFail/i) {
- $_ =~ s/false/true/ig;
- $got_AllowMouseOpenFail = 1;
- }
- if ($l =~ /^\s*Option.*PciForceNone/i) {
- $_ =~ s/false/true/ig;
- $got_PciForceNone= 1;
- }
- }
- if ($sect eq "module") {
- if ($l =~ /^\s*Load.*\b(dri|fbdevhw)\b/i) {
- $_ = "##Xdummy## $_";
- }
- }
- if ($sect eq "monitor") {
- if ($l =~ /^\s*HorizSync/i) {
- $got_HorizSync = 1;
- }
- if ($l =~ /^\s*VertRefresh/i) {
- $got_VertRefresh = 1;
- }
- }
- if ($sect eq "device") {
- if ($l =~ /^(\s*Driver)\b/i) {
- $_ = "$1 \"dummy\"\n";
- $got_Driver = 1;
- }
- if ($l =~ /^\s*VideoRam/i) {
- $got_VideoRam= 1;
- }
- }
- if ($sect eq "inputdevice") {
- if ($l =~ /^\s*Option.*\bDevice\b/i) {
- print " ##Xdummy:##\n";
- $_ = " Option \"Device\" \"/dev/dilbert$n\"\n";
- }
- }
- if ($sect eq "screen") {
- if ($l =~ /^\s*DefaultDepth\s+(\d+)/i) {
- if ($depth ne "") {
- print " ##Xdummy:##\n";
- $_ = " DefaultDepth\t$depth\n";
- }
- $got_DefaultDepth = 1;
- }
- if ($l =~ /^\s*Monitor\s+(\S+)/i) {
- $got_Monitor = $1;
- $got_Monitor =~ s/"//g;
- }
- if ($subsect eq "display") {
- if ($geom ne "") {
- if ($l =~ /^(\s*Modes)\b/i) {
- print " ##Xdummy:##\n";
- $_ = "$1 $geom\n";
- $got_Modes = 1;
- }
- }
- if ($l =~ /^\s*Depth\s+(\d+)/i) {
- my $d = $1;
- if (!$set_Depth && $depth ne "") {
- $set_Depth = 1;
- if ($depth != $d) {
- print " ##Xdummy:##\n";
- $_ = " Depth\t$depth\n";
- }
- }
- }
- }
- }
- print;
- }
- if ($ENV{XDUMMY_NOTWEAK}) {
- exit;
- }
- # create any crucial sections that are missing:
- if (! exists($sects{serverflags})) {
- print "\n##Xdummy:##\n";
- print "Section \"ServerFlags\"\n";
- print " Option \"DontVTSwitch\" \"true\"\n";
- print " Option \"AllowMouseOpenFail\" \"true\"\n";
- print " Option \"PciForceNone\" \"true\"\n";
- print "EndSection\n";
- }
- if (! exists($sects{device})) {
- print "\n##Xdummy:##\n";
- print "Section \"Device\"\n";
- print " Identifier \"Videocard0\"\n";
- print " Driver \"dummy\"\n";
- print " VideoRam $videoram\n";
- print "EndSection\n";
- }
- if (! exists($sects{monitor})) {
- print "\n##Xdummy:##\n";
- print "Section \"Monitor\"\n";
- print " Identifier \"Monitor0\"\n";
- print " HorizSync $HorizSync\n";
- print " VertRefresh $VertRefresh\n";
- print "EndSection\n";
- }
- if (! exists($sects{screen})) {
- print "\n##Xdummy:##\n";
- print "Section \"Screen\"\n";
- print " Identifier \"Screen0\"\n";
- print " Device \"Videocard0\"\n";
- if ($got_Monitor ne "") {
- print " Monitor \"$got_Monitor\"\n";
- } else {
- print " Monitor \"Monitor0\"\n";
- }
- if ($depth ne "") {
- print " DefaultDepth $depth\n";
- } else {
- print " DefaultDepth 24\n";
- }
- print " SubSection \"Display\"\n";
- print " Viewport 0 0\n";
- print " Depth 24\n";
- if ($got_Modes) {
- ;
- } elsif ($geom ne "") {
- print " Modes $geom\n";
- } else {
- print " Modes \"1280x1024\" \"1024x768\" \"800x600\"\n";
- }
- print " EndSubSection\n";
- print "EndSection\n";
- }
-';
-}
-
-# Work out config file and tweak it.
-#
-if [ "X$cmdline_config" = "X" ]; then
- :
-elif [ "X$cmdline_config" = "Xxdummy-builtin" ]; then
- :
-elif echo "$cmdline_config" | grep '/' > /dev/null; then
- :
-else
- # ignore basename only case (let server handle it)
- cmdline_config=""
- notweak=1
-fi
-
-config=$cmdline_config
-
-if [ "X$notweak" = "X1" -a "X$root" = "X" -a -f "$cmdline_config" ]; then
- # if not root we need to copy (but not tweak) the specified config.
- XDUMMY_NOTWEAK=1
- export XDUMMY_NOTWEAK
- notweak=""
-fi
-
-if [ ! $notweak ]; then
- # tweaked config will be put in $config2:
- config2=""
- if [ "X$config" = "X" ]; then
- # use the default one:
- if [ "X$stype" = "Xxorg" ]; then
- config=/etc/X11/xorg.conf
- else
- if [ -f "/etc/X11/XF86Config-4" ]; then
- config="/etc/X11/XF86Config-4"
- else
- config="/etc/X11/XF86Config"
- fi
- fi
- if [ ! -f "$config" ]; then
- for c in /etc/X11/xorg.conf /etc/X11/XF86Config-4 /etc/X11/XF86Config
- do
- if [ -f $c ]; then
- config=$c
- break
- fi
- done
- fi
- fi
-
- if [ "X$config" = "Xxdummy-builtin" ]; then
- config=""
- fi
-
- if [ ! -f "$config" ]; then
- config="$XDUMMY_TMPDIR/xorg.conf"
- warn "$program: using minimal built-in xorg.conf settings."
- cat > $config <<END
-
-Section "ServerLayout"
- Identifier "Layout0"
- Screen 0 "Screen0"
- InputDevice "Keyboard0" "CoreKeyboard"
- InputDevice "Mouse0" "CorePointer"
-EndSection
-
-Section "Files"
-EndSection
-
-Section "Module"
- Load "dbe"
- Load "extmod"
- Load "freetype"
- Load "glx"
-EndSection
-
-Section "InputDevice"
- Identifier "Mouse0"
- Driver "mouse"
- Option "Protocol" "auto"
- Option "Device" "/dev/psaux"
- Option "Emulate3Buttons" "no"
- Option "ZAxisMapping" "4 5"
-EndSection
-
-Section "InputDevice"
- Identifier "Keyboard0"
- Driver "kbd"
-EndSection
-
-Section "Monitor"
- Identifier "Monitor0"
- VendorName "Unknown"
- ModelName "Unknown"
- HorizSync 30.0 - 130.0
- VertRefresh 50.0 - 250.0
- Option "DPMS"
-EndSection
-
-Section "Device"
- Identifier "Device0"
- Driver "foovideo"
- VendorName "foovideo Corporation"
-EndSection
-
-Section "Screen"
- Identifier "Screen0"
- Device "Device0"
- Monitor "Monitor0"
- DefaultDepth 24
- SubSection "Display"
- Depth 24
- Modes "1280x1024"
- EndSubSection
-EndSection
-
-END
- fi
-
- if [ -f "$config" ]; then
- tweak_config $config
- fi
-
- # now we need to get our tweaked config file onto the command line:
- if [ "X$cmdline_config" = "X" ]; then
- # append to cmdline (FUBAR will be substituted below.)
- if [ "X$stype" = "Xxorg" ]; then
- args="$args -config FUBAR"
- else
- args="$args -xf86config FUBAR"
- fi
- fi
- if [ "X$config2" != "X" ]; then
- # or modify $args:
- c2=$config2
- if [ "X$root" = "X" ]; then
- # ordinary user cannot use absolute path.
- c2=`basename $config2`
- fi
- args=`echo "$args" | sed \
- -e "s,-config *[^ ][^ ]*,-config $c2,g" \
- -e "s,-xf86config *[^ ][^ ]*,-xf86config $c2,g"`
- fi
-fi
-
-if [ $prconf ]; then
- warn ""
- warn "Printing out the Xorg/XFree86 server config file:"
- warn ""
- if [ "X$config2" = "X" ]; then
- warn "NO CONFIG GENERATED."
- exit 1
- else
- cat "$config2"
- fi
- exit 0
-fi
-
-if [ $debug ]; then
- XDUMMY_DEBUG=1
- export XDUMMY_DEBUG
-fi
-if [ $root ]; then
- XDUMMY_ROOT=1
- export XDUMMY_ROOT
-fi
-
-# Finally, run it:
-#
-if [ "X$debug" != "X" -o "X$runit" = "X" ]; then
- if [ ! $runit ]; then
- echo ""
- echo "/usr/bin/env:"
- env | egrep -v '^(LS_COLORS|TERMCAP)' | sort
- echo ""
- echo "XDUMMY*:"
- env | grep '^XDUMMY' | sort
- echo ""
- fi
- warn ""
- warn "The command to run is:"
- warn ""
- so=$SO
- pwd=`pwd`
- if echo "$so" | grep '^\./' > /dev/null; then
- so=`echo "$so" | sed -e "s,^\.,$pwd,"`
- fi
- if echo "$so" | grep '/' > /dev/null; then
- :
- else
- so="$pwd/$so"
- fi
- warn "env LD_PRELOAD=$so $xserver $disp $args $vt"
- warn ""
- if [ ! $runit ]; then
- exit 0
- fi
-fi
-
-if [ $strace ]; then
- if [ "X$strace" = "X2" ]; then
- ltrace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
- else
- strace -f env LD_PRELOAD=$SO $xserver $disp $args $vt
- fi
-else
- exec env LD_PRELOAD=$SO $xserver $disp $args $vt
-fi
-
-exit $?
-
-#########################################################################
-
-code() {
-#code_begin
-#include <stdio.h>
-#define O_ACCMODE 0003
-#define O_RDONLY 00
-#define O_WRONLY 01
-#define O_RDWR 02
-#define O_CREAT 0100 /* not fcntl */
-#define O_EXCL 0200 /* not fcntl */
-#define O_NOCTTY 0400 /* not fcntl */
-#define O_TRUNC 01000 /* not fcntl */
-#define O_APPEND 02000
-#define O_NONBLOCK 04000
-#define O_NDELAY O_NONBLOCK
-#define O_SYNC 010000
-#define O_FSYNC O_SYNC
-#define O_ASYNC 020000
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <linux/vt.h>
-#include <linux/kd.h>
-
-#define __USE_GNU
-#include <dlfcn.h>
-
-static char tmpdir[4096];
-static char str1[4096];
-static char str2[4096];
-
-static char devs[256][1024];
-static int debug = -1;
-static int root = -1;
-static int changed_uid = 0;
-static int saw_fonts = 0;
-static int saw_lib_modules = 0;
-
-static time_t start = 0;
-
-void check_debug(void) {
- if (debug < 0) {
- if (getenv("XDUMMY_DEBUG") != NULL) {
- debug = 1;
- } else {
- debug = 0;
- }
- /* prevent other processes using the preload: */
- putenv("LD_PRELOAD=");
- }
-}
-void check_root(void) {
- if (root < 0) {
- /* script tells us if we are root */
- if (getenv("XDUMMY_ROOT") != NULL) {
- root = 1;
- } else {
- root = 0;
- }
- }
-}
-
-void check_uid(void) {
- if (start == 0) {
- start = time(NULL);
- if (debug) fprintf(stderr, "START: %u\n", (unsigned int) start);
- return;
- } else if (changed_uid == 0) {
- if (saw_fonts || time(NULL) > start + 20) {
- if (getenv("XDUMMY_UID")) {
- int uid = atoi(getenv("XDUMMY_UID"));
- if (debug) fprintf(stderr, "SETREUID: %d saw_fonts=%d\n", uid, saw_fonts);
- if (uid >= 0) {
- /* this will simply fail in -nonroot mode: */
- setreuid(uid, -1);
- }
- }
- changed_uid = 1;
- }
- }
-}
-
-#define CHECKIT if (debug < 0) check_debug(); \
- if (root < 0) check_root(); \
- check_uid();
-
-static void set_tmpdir(void) {
- char *s;
- static int didset = 0;
- if (didset) {
- return;
- }
- s = getenv("XDUMMY_TMPDIR");
- if (! s) {
- s = "/tmp";
- }
- tmpdir[0] = '\0';
- strcat(tmpdir, s);
- strcat(tmpdir, "/");
- didset = 1;
-}
-
-static char *tmpdir_path(const char *path) {
- char *str;
- set_tmpdir();
- strcpy(str2, path);
- str = str2;
- while (*str) {
- if (*str == '/') {
- *str = '_';
- }
- str++;
- }
- strcpy(str1, tmpdir);
- strcat(str1, str2);
- return str1;
-}
-
-int open(const char *pathname, int flags, unsigned short mode) {
- int fd;
- char *store_dev = NULL;
- static int (*real_open)(const char *, int , unsigned short) = NULL;
-
- CHECKIT
- if (! real_open) {
- real_open = (int (*)(const char *, int , unsigned short))
- dlsym(RTLD_NEXT, "open");
- }
-
- if (strstr(pathname, "lib/modules/")) {
- /* not currently used. */
- saw_lib_modules = 1;
- }
-
- if (!root) {
- if (strstr(pathname, "/dev/") == pathname) {
- store_dev = strdup(pathname);
- }
- if (strstr(pathname, "/dev/tty") == pathname && strcmp(pathname, "/dev/tty")) {
- pathname = tmpdir_path(pathname);
- if (debug) fprintf(stderr, "OPEN: %s -> %s (as FIFO)\n", store_dev, pathname);
- /* we make it a FIFO so ioctl on it does not fail */
- unlink(pathname);
- mkfifo(pathname, 0666);
- } else if (0) {
- /* we used to handle more /dev files ... */
- fd = real_open(pathname, O_WRONLY|O_CREAT, 0777);
- close(fd);
- }
- }
-
- fd = real_open(pathname, flags, mode);
-
- if (debug) fprintf(stderr, "OPEN: %s %d %d fd=%d\n", pathname, flags, mode, fd);
-
- if (! root) {
- if (store_dev) {
- if (fd < 256) {
- strcpy(devs[fd], store_dev);
- }
- free(store_dev);
- }
- }
-
- return(fd);
-}
-
-int open64(const char *pathname, int flags, unsigned short mode) {
- int fd;
-
- CHECKIT
- if (debug) fprintf(stderr, "OPEN64: %s %d %d\n", pathname, flags, mode);
-
- fd = open(pathname, flags, mode);
- return(fd);
-}
-
-int rename(const char *oldpath, const char *newpath) {
- static int (*real_rename)(const char *, const char *) = NULL;
-
- CHECKIT
- if (! real_rename) {
- real_rename = (int (*)(const char *, const char *))
- dlsym(RTLD_NEXT, "rename");
- }
-
- if (debug) fprintf(stderr, "RENAME: %s %s\n", oldpath, newpath);
-
- if (root) {
- return(real_rename(oldpath, newpath));
- }
-
- if (strstr(oldpath, "/var/log") == oldpath) {
- if (debug) fprintf(stderr, "RENAME: returning 0\n");
- return 0;
- }
- return(real_rename(oldpath, newpath));
-}
-
-FILE *fopen(const char *pathname, const char *mode) {
- static FILE* (*real_fopen)(const char *, const char *) = NULL;
- char *str;
-
- if (! saw_fonts) {
- if (strstr(pathname, "/fonts/")) {
- if (strstr(pathname, "fonts.dir")) {
- saw_fonts = 1;
- } else if (strstr(pathname, "fonts.alias")) {
- saw_fonts = 1;
- }
- }
- }
-
- CHECKIT
- if (! real_fopen) {
- real_fopen = (FILE* (*)(const char *, const char *))
- dlsym(RTLD_NEXT, "fopen");
- }
-
- if (debug) fprintf(stderr, "FOPEN: %s %s\n", pathname, mode);
-
- if (strstr(pathname, "xdummy_modified_xconfig.conf")) {
- /* make our config appear to be in /etc/X11, etc. */
- char *q = strrchr(pathname, '/');
- if (q != NULL && getenv("XDUMMY_TMPDIR") != NULL) {
- strcpy(str1, getenv("XDUMMY_TMPDIR"));
- strcat(str1, q);
- if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str1);
- pathname = str1;
- }
- }
-
- if (root) {
- return(real_fopen(pathname, mode));
- }
-
- str = (char *) pathname;
- if (strstr(pathname, "/var/log") == pathname) {
- str = tmpdir_path(pathname);
- if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str);
- }
- return(real_fopen(str, mode));
-}
-
-
-#define RETURN0 if (debug) \
- {fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return 0;
-#define RETURN1 if (debug) \
- {fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return -1;
-
-int ioctl(int fd, int req, void *ptr) {
- static int closed_xf86Info_consoleFd = 0;
- static int (*real_ioctl)(int, int , void *) = NULL;
-
- CHECKIT
- if (! real_ioctl) {
- real_ioctl = (int (*)(int, int , void *))
- dlsym(RTLD_NEXT, "open");
- }
- if (debug) fprintf(stderr, "IOCTL: %d 0x%x %p\n", fd, req, ptr);
-
- /* based on xorg-x11-6.8.1-dualhead.patch */
- if (req == VT_GETMODE) {
- /* close(xf86Info.consoleFd) */
- if (0 && ! closed_xf86Info_consoleFd) {
- /* I think better not to close it... */
- close(fd);
- closed_xf86Info_consoleFd = 1;
- }
- RETURN0
- } else if (req == VT_SETMODE) {
- RETURN0
- } else if (req == VT_GETSTATE) {
- RETURN0
- } else if (req == KDSETMODE) {
- RETURN0
- } else if (req == KDSETLED) {
- RETURN0
- } else if (req == KDGKBMODE) {
- RETURN0
- } else if (req == KDSKBMODE) {
- RETURN0
- } else if (req == VT_ACTIVATE) {
- RETURN0
- } else if (req == VT_WAITACTIVE) {
- RETURN0
- } else if (req == VT_RELDISP) {
- if (ptr == (void *) 1) {
- RETURN1
- } else if (ptr == (void *) VT_ACKACQ) {
- RETURN0
- }
- }
-
- return(real_ioctl(fd, req, ptr));
-}
-
-typedef void (*sighandler_t)(int);
-#define SIGUSR1 10
-#define SIG_DFL ((sighandler_t)0)
-
-sighandler_t signal(int signum, sighandler_t handler) {
- static sighandler_t (*real_signal)(int, sighandler_t) = NULL;
-
- CHECKIT
- if (! real_signal) {
- real_signal = (sighandler_t (*)(int, sighandler_t))
- dlsym(RTLD_NEXT, "signal");
- }
-
- if (debug) fprintf(stderr, "SIGNAL: %d %p\n", signum, handler);
-
- if (signum == SIGUSR1) {
- if (debug) fprintf(stderr, "SIGNAL: skip SIGUSR1\n");
- return SIG_DFL;
- }
-
- return(real_signal(signum, handler));
-}
-
-int close(int fd) {
- static int (*real_close)(int) = NULL;
-
- CHECKIT
- if (! real_close) {
- real_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
- }
-
- if (debug) fprintf(stderr, "CLOSE: %d\n", fd);
- if (!root) {
- if (fd < 256) {
- devs[fd][0] = '\0';
- }
- }
- return(real_close(fd));
-}
-
-struct stat {
- int foo;
-};
-
-int stat(const char *path, struct stat *buf) {
- static int (*real_stat)(const char *, struct stat *) = NULL;
-
- CHECKIT
- if (! real_stat) {
- real_stat = (int (*)(const char *, struct stat *))
- dlsym(RTLD_NEXT, "stat");
- }
-
- if (debug) fprintf(stderr, "STAT: %s\n", path);
-
- return(real_stat(path, buf));
-}
-
-int stat64(const char *path, struct stat *buf) {
- static int (*real_stat64)(const char *, struct stat *) = NULL;
-
- CHECKIT
- if (! real_stat64) {
- real_stat64 = (int (*)(const char *, struct stat *))
- dlsym(RTLD_NEXT, "stat64");
- }
-
- if (debug) fprintf(stderr, "STAT64: %s\n", path);
-
- return(real_stat64(path, buf));
-}
-
-int chown(const char *path, uid_t owner, gid_t group) {
- static int (*real_chown)(const char *, uid_t, gid_t) = NULL;
-
- CHECKIT
- if (! real_chown) {
- real_chown = (int (*)(const char *, uid_t, gid_t))
- dlsym(RTLD_NEXT, "chown");
- }
-
- if (root) {
- return(real_chown(path, owner, group));
- }
-
- if (debug) fprintf(stderr, "CHOWN: %s %d %d\n", path, owner, group);
-
- if (strstr(path, "/dev") == path) {
- if (debug) fprintf(stderr, "CHOWN: return 0\n");
- return 0;
- }
-
- return(real_chown(path, owner, group));
-}
-
-extern int *__errno_location (void);
-#ifndef ENODEV
-#define ENODEV 19
-#endif
-
-int ioperm(unsigned long from, unsigned long num, int turn_on) {
- static int (*real_ioperm)(unsigned long, unsigned long, int) = NULL;
-
- CHECKIT
- if (! real_ioperm) {
- real_ioperm = (int (*)(unsigned long, unsigned long, int))
- dlsym(RTLD_NEXT, "ioperm");
- }
- if (debug) fprintf(stderr, "IOPERM: %d %d %d\n", (int) from, (int) num, turn_on);
- if (root) {
- return(real_ioperm(from, num, turn_on));
- }
- if (from == 0 && num == 1024 && turn_on == 1) {
- /* we want xf86EnableIO to fail */
- if (debug) fprintf(stderr, "IOPERM: setting ENODEV.\n");
- *__errno_location() = ENODEV;
- return -1;
- }
- return 0;
-}
-
-int iopl(int level) {
- static int (*real_iopl)(int) = NULL;
-
- CHECKIT
- if (! real_iopl) {
- real_iopl = (int (*)(int)) dlsym(RTLD_NEXT, "iopl");
- }
- if (debug) fprintf(stderr, "IOPL: %d\n", level);
- if (root) {
- return(real_iopl(level));
- }
- return 0;
-}
-
-#ifdef INTERPOSE_GETUID
-
-/*
- * we got things to work w/o pretending to be root.
- * so we no longer interpose getuid(), etc.
- */
-
-uid_t getuid(void) {
- static uid_t (*real_getuid)(void) = NULL;
- CHECKIT
- if (! real_getuid) {
- real_getuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid");
- }
- if (root) {
- return(real_getuid());
- }
- if (debug) fprintf(stderr, "GETUID: 0\n");
- return 0;
-}
-uid_t geteuid(void) {
- static uid_t (*real_geteuid)(void) = NULL;
- CHECKIT
- if (! real_geteuid) {
- real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
- }
- if (root) {
- return(real_geteuid());
- }
- if (debug) fprintf(stderr, "GETEUID: 0\n");
- return 0;
-}
-uid_t geteuid_kludge1(void) {
- static uid_t (*real_geteuid)(void) = NULL;
- CHECKIT
- if (! real_geteuid) {
- real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid");
- }
- if (debug) fprintf(stderr, "GETEUID: 0 saw_libmodules=%d\n", saw_lib_modules);
- if (root && !saw_lib_modules) {
- return(real_geteuid());
- } else {
- saw_lib_modules = 0;
- return 0;
- }
-}
-
-uid_t getuid32(void) {
- static uid_t (*real_getuid32)(void) = NULL;
- CHECKIT
- if (! real_getuid32) {
- real_getuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid32");
- }
- if (root) {
- return(real_getuid32());
- }
- if (debug) fprintf(stderr, "GETUID32: 0\n");
- return 0;
-}
-uid_t geteuid32(void) {
- static uid_t (*real_geteuid32)(void) = NULL;
- CHECKIT
- if (! real_geteuid32) {
- real_geteuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid32");
- }
- if (root) {
- return(real_geteuid32());
- }
- if (debug) fprintf(stderr, "GETEUID32: 0\n");
- return 0;
-}
-
-gid_t getgid(void) {
- static gid_t (*real_getgid)(void) = NULL;
- CHECKIT
- if (! real_getgid) {
- real_getgid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid");
- }
- if (root) {
- return(real_getgid());
- }
- if (debug) fprintf(stderr, "GETGID: 0\n");
- return 0;
-}
-gid_t getegid(void) {
- static gid_t (*real_getegid)(void) = NULL;
- CHECKIT
- if (! real_getegid) {
- real_getegid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid");
- }
- if (root) {
- return(real_getegid());
- }
- if (debug) fprintf(stderr, "GETEGID: 0\n");
- return 0;
-}
-gid_t getgid32(void) {
- static gid_t (*real_getgid32)(void) = NULL;
- CHECKIT
- if (! real_getgid32) {
- real_getgid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid32");
- }
- if (root) {
- return(real_getgid32());
- }
- if (debug) fprintf(stderr, "GETGID32: 0\n");
- return 0;
-}
-gid_t getegid32(void) {
- static gid_t (*real_getegid32)(void) = NULL;
- CHECKIT
- if (! real_getegid32) {
- real_getegid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid32");
- }
- if (root) {
- return(real_getegid32());
- }
- if (debug) fprintf(stderr, "GETEGID32: 0\n");
- return 0;
-}
-#endif
-
-#if 0
-/* maybe we need to interpose on strcmp someday... here is the template */
-int strcmp(const char *s1, const char *s2) {
- static int (*real_strcmp)(const char *, const char *) = NULL;
- CHECKIT
- if (! real_strcmp) {
- real_strcmp = (int (*)(const char *, const char *)) dlsym(RTLD_NEXT, "strcmp");
- }
- if (debug) fprintf(stderr, "STRCMP: '%s' '%s'\n", s1, s2);
- return(real_strcmp(s1, s2));
-}
-#endif
-
-#code_end
-}
use IO::Handle;
# these are shipped with the testsuite
use lib qw(lib);
-use StartXDummy;
+use StartXServer;
use StatusLine;
use TestWorker;
# the following modules are not shipped with Perl
my %timings;
my $help = 0;
-# Number of tests to run in parallel. Important to know how many Xdummy
+# Number of tests to run in parallel. Important to know how many Xephyr
# instances we need to start (unless @displays are given). Defaults to
# num_cores * 2.
my $parallel = undef;
coverage => 0,
restart => 0,
);
-my $keep_xdummy_output = 0;
+my $keep_xserver_output = 0;
my $result = GetOptions(
"coverage-testing" => \$options{coverage},
- "keep-xdummy-output" => \$keep_xdummy_output,
+ "keep-xserver-output" => \$keep_xserver_output,
"valgrind" => \$options{valgrind},
"strace" => \$options{strace},
"xtrace" => \$options{xtrace},
die "$binary is not an executable" unless -x $binary;
}
+qx(Xephyr -help 2>&1);
+die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?;
+
@displays = split(/,/, join(',', @displays));
@displays = map { s/ //g; $_ } @displays;
my $numtests = scalar @testfiles;
-# No displays specified, let’s start some Xdummy instances.
+# No displays specified, let’s start some Xephyr instances.
if (@displays == 0) {
- @displays = start_xdummy($parallel, $numtests, $keep_xdummy_output);
+ @displays = start_xserver($parallel, $numtests, $keep_xserver_output);
}
# 1: create an output directory for this test-run
# connect to all displays for two reasons:
# 1: check if the display actually works
# 2: keep the connection open so that i3 is not the only client. this prevents
-# the X server from exiting (Xdummy will restart it, but not quick enough
-# sometimes)
+# the X server from exiting
my @single_worker;
for my $display (@displays) {
my $screen;
# Read previous timing information, if available. We will be able to roughly
# predict the test duration and schedule a good order for the tests.
-my $timingsjson = StartXDummy::slurp('.last_run_timings.json');
+my $timingsjson = StartXServer::slurp('.last_run_timings.json');
%timings = %{decode_json($timingsjson)} if length($timingsjson) > 0;
# Re-order the files so that those which took the longest time in the previous
if ($numtests == 1) {
say '';
say 'Test output:';
- say StartXDummy::slurp($logfile);
+ say StartXServer::slurp($logfile);
}
END { cleanup() }
=head1 EXAMPLE
-To run the whole testsuite on a reasonable number of Xdummy instances (your
+To run the whole testsuite on a reasonable number of Xephyr instances (your
running X11 will not be touched), run:
./complete-run.pl
# Run tests on the second X server
./complete-run.pl -d :1
- # Run four tests in parallel on some Xdummy servers
+ # Run four tests in parallel on some Xephyr servers
./complete-run.pl -d :1,:2,:3,:4
Note that it is not necessary to specify this anymore. If omitted,
-complete-run.pl will start (num_cores * 2) Xdummy instances.
+complete-run.pl will start (num_cores * 2) Xephyr instances.
=item B<--valgrind>
=item B<--parallel>
-Number of Xdummy instances to start (if you don’t want to start num_cores * 2
+Number of Xephyr instances to start (if you don’t want to start num_cores * 2
instances for some reason).
- # Run all tests on a single Xdummy instance
+ # Run all tests on a single Xephyr instance
./complete-run.pl -p 1
bindsym Mod1+v split v
# Fullscreen (Mod1+f)
-bindsym Mod1+f fullscreen
+bindsym Mod1+f fullscreen toggle
# Stacking (Mod1+s)
bindsym Mod1+s layout stacking
+++ /dev/null
-package StartXDummy;
-# vim:ts=4:sw=4:expandtab
-
-use strict;
-use warnings;
-use Exporter 'import';
-use Time::HiRes qw(sleep);
-use v5.10;
-
-our @EXPORT = qw(start_xdummy);
-
-my @pids;
-my $x_socketpath = '/tmp/.X11-unix/X';
-
-# reads in a whole file
-sub slurp {
- open(my $fh, '<', shift) or return '';
- local $/;
- <$fh>;
-}
-
-# forks an Xdummy or Xdmx process
-sub fork_xserver {
- my $keep_xdummy_output = shift;
- my $displaynum = shift;
- my $pid = fork();
- die "Could not fork: $!" unless defined($pid);
- if ($pid == 0) {
- # Child, close stdout/stderr, then start Xdummy.
- if (!$keep_xdummy_output) {
- close STDOUT;
- close STDERR;
- }
-
- exec @_;
- exit 1;
- }
- push(@complete_run::CLEANUP, sub {
- kill(15, $pid);
- # Unlink the X11 socket, Xdmx seems to leave it there.
- unlink($x_socketpath . $displaynum);
- });
-
- push @pids, $pid;
-
- return $x_socketpath . $displaynum;
-}
-
-# Blocks until the socket paths specified in the given array reference actually
-# exist.
-sub wait_for_x {
- my ($sockets_waiting) = @_;
-
- # Wait until Xdmx actually runs. Pretty ugly solution, but as long as we
- # can’t socket-activate X11…
- while (1) {
- @$sockets_waiting = grep { ! -S $_ } @$sockets_waiting;
- last unless @$sockets_waiting;
- sleep 0.1;
- }
-}
-
-=head2 start_xdummy($parallel)
-
-Starts C<$parallel> (or number of cores * 2 if undef) Xdummy processes (see
-the file ./Xdummy) and returns two arrayrefs: a list of X11 display numbers to
-the Xdummy processes and a list of PIDs of the processes.
-
-=cut
-
-sub start_xdummy {
- my ($parallel, $numtests, $keep_xdummy_output) = @_;
-
- my @displays = ();
- my @childpids = ();
-
- $SIG{CHLD} = sub {
- my $child = waitpid -1, POSIX::WNOHANG;
- @pids = grep { $_ != $child } @pids;
- return unless @pids == 0;
- print STDERR "All Xdummy processes died.\n";
- print STDERR "Use ./complete-run.pl --parallel 1 --keep-xdummy-output\n";
- print STDERR "";
- print STDERR "A frequent cause for this is missing the DUMMY Xorg module,\n";
- print STDERR "package xserver-xorg-video-dummy on Debian.\n";
- exit 1;
- };
-
- # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have
- # _SC_NPROCESSORS_CONF.
- my $cpuinfo = slurp('/proc/cpuinfo');
- my $num_cores = scalar grep { /model name/ } split("\n", $cpuinfo);
- # If /proc/cpuinfo does not exist, we fall back to 2 cores.
- $num_cores ||= 2;
-
- # If unset, we use num_cores * 2.
- $parallel ||= ($num_cores * 2);
-
- # If we are running a small number of tests, don’t over-parallelize.
- $parallel = $numtests if $numtests < $parallel;
-
- # First get the last used display number, then increment it by one.
- # Effectively falls back to 1 if no X server is running.
- my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
- $displaynum++;
-
- say "Starting $parallel Xdummy instances, starting at :$displaynum...";
-
- my @sockets_waiting;
- for (1 .. $parallel) {
- # We use -config /dev/null to prevent Xdummy from using the system
- # Xorg configuration. The tests should be independant from the
- # actual system X configuration.
- my $socket = fork_xserver($keep_xdummy_output, $displaynum,
- './Xdummy', ":$displaynum", '-config', '/dev/null',
- '-configdir', '/dev/null', '-nolisten', 'tcp');
- push(@displays, ":$displaynum");
- push(@sockets_waiting, $socket);
- $displaynum++;
- }
-
- wait_for_x(\@sockets_waiting);
-
- return @displays;
-}
-
-1
--- /dev/null
+package StartXServer;
+# vim:ts=4:sw=4:expandtab
+
+use strict;
+use warnings;
+use Exporter 'import';
+use Time::HiRes qw(sleep);
+use v5.10;
+
+our @EXPORT = qw(start_xserver);
+
+my @pids;
+my $x_socketpath = '/tmp/.X11-unix/X';
+
+# reads in a whole file
+sub slurp {
+ open(my $fh, '<', shift) or return '';
+ local $/;
+ <$fh>;
+}
+
+# forks an X server process
+sub fork_xserver {
+ my $keep_xserver_output = shift;
+ my $displaynum = shift;
+ my $pid = fork();
+ die "Could not fork: $!" unless defined($pid);
+ if ($pid == 0) {
+ # Child, close stdout/stderr, then start Xephyr
+ if (!$keep_xserver_output) {
+ close STDOUT;
+ close STDERR;
+ }
+
+ exec @_;
+ exit 1;
+ }
+ push(@complete_run::CLEANUP, sub {
+ kill(15, $pid);
+ # Unlink the X11 socket, Xdmx seems to leave it there.
+ unlink($x_socketpath . $displaynum);
+ });
+
+ push @pids, $pid;
+
+ return $x_socketpath . $displaynum;
+}
+
+# Blocks until the socket paths specified in the given array reference actually
+# exist.
+sub wait_for_x {
+ my ($sockets_waiting) = @_;
+
+ # Wait until Xdmx actually runs. Pretty ugly solution, but as long as we
+ # can’t socket-activate X11…
+ while (1) {
+ @$sockets_waiting = grep { ! -S $_ } @$sockets_waiting;
+ last unless @$sockets_waiting;
+ sleep 0.1;
+ }
+}
+
+=head2 start_xserver($parallel)
+
+Starts C<$parallel> (or number of cores * 2 if undef) Xephyr processes (see
+http://www.freedesktop.org/wiki/Software/Xephyr/) and returns two arrayrefs: a
+list of X11 display numbers to the Xephyr processes and a list of PIDs of the
+processes.
+
+=cut
+
+sub start_xserver {
+ my ($parallel, $numtests, $keep_xserver_output) = @_;
+
+ my @displays = ();
+ my @childpids = ();
+
+ $SIG{CHLD} = sub {
+ my $child = waitpid -1, POSIX::WNOHANG;
+ @pids = grep { $_ != $child } @pids;
+ return unless @pids == 0;
+ print STDERR "All X server processes died.\n";
+ print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n";
+ exit 1;
+ };
+
+ # Yeah, I know it’s non-standard, but Perl’s POSIX module doesn’t have
+ # _SC_NPROCESSORS_CONF.
+ my $cpuinfo = slurp('/proc/cpuinfo');
+ my $num_cores = scalar grep { /model name/ } split("\n", $cpuinfo);
+ # If /proc/cpuinfo does not exist, we fall back to 2 cores.
+ $num_cores ||= 2;
+
+ # If unset, we use num_cores * 2.
+ $parallel ||= ($num_cores * 2);
+
+ # If we are running a small number of tests, don’t over-parallelize.
+ $parallel = $numtests if $numtests < $parallel;
+
+ # First get the last used display number, then increment it by one.
+ # Effectively falls back to 1 if no X server is running.
+ my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*');
+ $displaynum++;
+
+ say "Starting $parallel Xephyr instances, starting at :$displaynum...";
+
+ my @sockets_waiting;
+ for (1 .. $parallel) {
+ my $socket = fork_xserver($keep_xserver_output, $displaynum,
+ 'Xephyr', ":$displaynum", '-screen', '1280x800',
+ '-nolisten', 'tcp');
+ push(@displays, ":$displaynum");
+ push(@sockets_waiting, $socket);
+ $displaynum++;
+ }
+
+ wait_for_x(\@sockets_waiting);
+
+ return @displays;
+}
+
+1
$args{name} //= 'Window ' . counter_window();
my $window = $x->root->create_child(%args);
+ $window->add_hint('input');
if ($before_map) {
# TODO: investigate why _create is not needed
cmd 'focus left';
is($x->input_focus, $window->id, 'fullscreen window still focused');
+################################################################################
+# Verify that changing workspace while in global fullscreen does not work.
+################################################################################
+
+$tmp = fresh_workspace;
+$window = open_window;
+
+cmd 'fullscreen global';
+is($x->input_focus, $window->id, 'window focused');
+is(focused_ws(), $tmp, 'workspace selected');
+
+$other = get_unused_workspace;
+cmd "workspace $other";
+is($x->input_focus, $window->id, 'window still focused');
+is(focused_ws(), $tmp, 'workspace still selected');
+
+# leave global fullscreen so that is does not interfere with the other tests
+$window->fullscreen(0);
+sync_with_i3;
+
################################################################################
# Verify that fullscreening a window on a second workspace and moving it onto
# the first workspace unfullscreens the first window.
is(fullscreen_windows($tmp), 1, 'one fullscreen window on ws');
is($x->input_focus, $swindow->id, 'fullscreen window focused');
+################################################################################
+# Verify that command ‘fullscreen enable’ works and is idempotent.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$window = open_window;
+is($x->input_focus, $window->id, 'window focused');
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+cmd 'fullscreen enable';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace');
+
+cmd 'fullscreen enable';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 1, 'still one fullscreen window on workspace');
+
+$window->fullscreen(0);
+sync_with_i3;
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+################################################################################
+# Verify that command ‘fullscreen enable global’ works and is idempotent.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$window = open_window;
+is($x->input_focus, $window->id, 'window focused');
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+cmd 'fullscreen enable global';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace');
+
+cmd 'fullscreen enable global';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 1, 'still one fullscreen window on workspace');
+
+$window->fullscreen(0);
+sync_with_i3;
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+################################################################################
+# Verify that command ‘fullscreen disable’ works and is idempotent.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$window = open_window;
+is($x->input_focus, $window->id, 'window focused');
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+$window->fullscreen(1);
+sync_with_i3;
+is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace');
+
+cmd 'fullscreen disable';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+cmd 'fullscreen disable';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 0, 'still no fullscreen window on workspace');
+
+################################################################################
+# Verify that command ‘fullscreen toggle’ works.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$window = open_window;
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+cmd 'fullscreen toggle';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace');
+
+cmd 'fullscreen toggle';
+is($x->input_focus, $window->id, 'window still focused');
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+################################################################################
+# Verify that a window’s fullscreen is disabled when another one is enabled
+# on the same workspace. The new fullscreen window should be focused.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$window = open_window;
+$other = open_window;
+
+is($x->input_focus, $other->id, 'other window focused');
+is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace');
+
+cmd 'fullscreen enable';
+is($x->input_focus, $other->id, 'other window focused');
+is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace');
+
+cmd '[id="' . $window->id . '"] fullscreen enable';
+is($x->input_focus, $window->id, 'window focused');
+is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace');
+
+################################################################################
+# Verify that when a global fullscreen is enabled the window is focused and
+# its workspace is selected, so that disabling the fullscreen keeps the window
+# focused and visible.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$window = open_window;
+
+is($x->input_focus, $window->id, 'window focused');
+
+cmd 'workspace ' . get_unused_workspace;
+isnt($x->input_focus, $window->id, 'window not focused');
+isnt(focused_ws(), $tmp, 'workspace not selected');
+
+cmd '[id="' . $window->id . '"] fullscreen enable global';
+is($x->input_focus, $window->id, 'window focused');
+
+cmd 'fullscreen disable';
+is($x->input_focus, $window->id, 'window still focused');
+is(focused_ws(), $tmp, 'workspace selected');
+
done_testing;
test_resize;
################################################################################
-# Check if we can position a floating window out of bounds. The XDummy screen
+# Check if we can position a floating window out of bounds. The Xephyr screen
# is 1280x1024, so x=2864, y=893 is out of bounds.
################################################################################
}
+################################################################################
+# Verify that transient_for can be set and unset.
+################################################################################
+
+$tmp = fresh_workspace;
+
+$fwindow = open_window({ dont_map => 1 });
+$fwindow->transient_for($right);
+$fwindow->map;
+
+wait_for_map($fwindow);
+
+my $floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0];
+is($floating_con->{window_properties}->{transient_for}, $right->id, 'WM_TRANSIENT_FOR properly parsed');
+
+$x->delete_property($fwindow->id, $x->atom(name => 'WM_TRANSIENT_FOR')->id);
+$x->flush;
+
+sync_with_i3;
+
+$floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0];
+is($floating_con->{window_properties}->{transient_for}, undef, 'WM_TRANSIENT_FOR properly removed');
+
done_testing;
# Workspaces requests and events
################################
-my $focused = get_ws(focused_ws());
+my $old_ws = get_ws(focused_ws());
# Events
workspace => sub {
my ($event) = @_;
if ($event->{change} eq 'init') {
- $init->send(1);
+ $init->send($event);
} elsif ($event->{change} eq 'focus') {
- # Check that we have the old and new workspace
- $focus->send(
- $event->{current}->{name} == '2' &&
- $event->{old}->{name} == $focused->{name}
- );
+ $focus->send($event);
} elsif ($event->{change} eq 'empty') {
- $empty->send(1);
+ $empty->send($event);
}
}
})->recv;
}
);
-ok($init->recv, 'Workspace "init" event received');
-ok($focus->recv, 'Workspace "focus" event received');
-ok($empty->recv, 'Workspace "empty" event received');
+my $init_event = $init->recv;
+my $focus_event = $focus->recv;
+my $empty_event = $empty->recv;
+
+my $current_ws = get_ws(focused_ws());
+
+ok($init_event, 'workspace "init" event received');
+is($init_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the initted workspace con');
+
+ok($focus_event, 'workspace "focus" event received');
+is($focus_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con');
+is($focus_event->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last');
+
+ok($empty_event, 'workspace "empty" event received');
+is($empty_event->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con');
done_testing;
type => 'root',
id => $ignore,
rect => $ignore,
+ deco_rect => $ignore,
window_rect => $ignore,
geometry => $ignore,
swallows => $ignore,
is(@{$first->{nodes}}, 0, 'first container has no children');
is(@{$second->{nodes}}, 0, 'second container has no children (yet)');
- my $old_name = $second->{name};
+ my $old_id = $second->{id};
cmd $args{split_command};
cmd 'open';
$second = $content->[1];
is(@{$first->{nodes}}, 0, 'first container has no children');
- isnt($second->{name}, $old_name, 'second container was replaced');
+ isnt($second->{id}, $old_id, 'second container was replaced');
is($second->{layout}, 'splith', 'orientation is horizontal');
is(@{$second->{nodes}}, 2, 'second container has 2 children');
- is($second->{nodes}->[0]->{name}, $old_name, 'found old second container');
+ is($second->{nodes}->[0]->{id}, $old_id, 'found old second container');
}
verify_split_layout(split_command => 'split h');
is($floatcon[0]->{rect}->{x}, $center_x, "moved to center at position $center_x x");
is($floatcon[0]->{rect}->{y}, $center_y, "moved to center at position $center_y y");
+# Make sure the command works with criteria
+open_floating_window;
+
+@floatcon = @{get_ws($tmp)->{floating_nodes}};
+
+cmd '[con_id="' . $floatcon[0]->{nodes}[0]->{id} . '"] move position 25 px 30 px';
+cmd '[con_id="' . $floatcon[1]->{nodes}[0]->{id} . '"] move position 35 px 40 px';
+
+@floatcon = @{get_ws($tmp)->{floating_nodes}};
+
+is($floatcon[0]->{rect}->{x}, 25, 'moved to position 25 x with criteria');
+is($floatcon[0]->{rect}->{y}, 30, 'moved to position 30 y with criteria');
+
+is($floatcon[1]->{rect}->{x}, 35, 'moved to position 35 x with criteria');
+is($floatcon[1]->{rect}->{y}, 40, 'moved to position 40 y with criteria');
+
done_testing;
my ($msg) = @_;
my @ws = @{$i3->get_workspaces->recv};
- my @nums = map { $_->{num} } grep { defined($_->{num}) } @ws;
+ my @nums = map { $_->{num} } grep { $_->{num} != -1 } @ws;
my @sorted = sort @nums;
is_deeply(\@nums, \@sorted, $msg);
done_testing;
};
+# http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
+# > Clients using the Globally Active model can only use a SetInputFocus request
+# > to acquire the input focus when they do not already have it on receipt of one
+# > of the following events:
+# > * ButtonPress
+# > * ButtonRelease
+# > * Passive-grabbed KeyPress
+# > * Passive-grabbed KeyRelease
+#
+# Since managing a window happens on a MapNotify (which is absent from this
+# list), the window cannot accept input focus, so we should not try to focus
+# the window at all.
subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
fresh_workspace;
$window->map;
- ok(recv_take_focus($window), 'got ClientMessage with WM_TAKE_FOCUS atom');
+ ok(!recv_take_focus($window), 'did not receive ClientMessage');
done_testing;
};
exit_gracefully($pid);
+################################################################################
+# 5: now with a binding that contains multiple commands
+################################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym Mod1+1 workspace 3; exec foo
+EOT
+
+$pid = launch_with_config($config);
+
+@names = @{get_workspace_names()};
+is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3 without ;exec foo');
+
+exit_gracefully($pid);
+
done_testing;
# echo its $DESKTOP_STARTUP_ID. We (blockingly) read the variable into
# $startup_id in the testcase.
my $tmp = tmpnam();
-mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp";
+mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!";
cmd qq|exec echo \$DESKTOP_STARTUP_ID >$tmp|;
my $expected = <<'EOT';
cfg_enter_mode(meh)
-cfg_mode_binding(bindsym, Mod1,Shift, x, (null), resize grow)
-cfg_mode_binding(bindcode, Mod1, 44, (null), resize shrink)
-cfg_mode_binding(bindsym, Mod1, x, --release, exec foo)
+cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), resize grow)
+cfg_mode_binding(bindcode, Mod1, 44, (null), (null), resize shrink)
+cfg_mode_binding(bindsym, Mod1, x, --release, (null), exec foo)
EOT
is(parser_calls($config),
$expected = <<'EOT';
cfg_enter_mode(yo)
-cfg_mode_binding(bindsym, (null), x, (null), resize shrink left)
+cfg_mode_binding(bindsym, (null), x, (null), (null), resize shrink left)
ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', '}'
ERROR: CONFIG: (in file <stdin>)
ERROR: CONFIG: Line 1: mode "yo" {
$expected = <<'EOT';
cfg_bar_output(LVDS-1)
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
ERROR: CONFIG: (in file <stdin>)
ERROR: CONFIG: Line 1: bar {
ERROR: CONFIG: Line 2: output LVDS-1
cmd 'workspace 2';
is(current_desktop_index, 2, "Open and view empty");
+#########################################################
+# Test the _NET_CURRENT_DESKTOP client request
+# This request is sent by pagers and bars to switch the current desktop (which
+# is like an ersatz workspace) to the given index
+#########################################################
+
+sub send_current_desktop_request {
+ my ($idx) = @_;
+
+ my $msg = pack "CCSLLLLLL",
+ X11::XCB::CLIENT_MESSAGE, # response_type
+ 32, # format
+ 0, # sequence
+ 0,
+ $_NET_CURRENT_DESKTOP,
+ $idx, # data32[0] (the desktop index)
+ 0, # data32[1] (can be a timestamp)
+ 0, # data32[2]
+ 0, # data32[3]
+ 0; # data32[4]
+
+ $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
+}
+
+send_current_desktop_request(1);
+is(current_desktop_index, 1, 'current desktop request switched to desktop 1');
+# note that _NET_CURRENT_DESKTOP is an index and that in this case, workspace 1
+# is at index 1 as a convenience for the test
+is(focused_ws, '1', 'current desktop request switched to workspace 1');
+
+send_current_desktop_request(0);
+is(current_desktop_index, 0, 'current desktop request switched to desktop 0');
+is(focused_ws, '0', 'current desktop request switched to workspace 0');
+
exit_gracefully($pid);
done_testing;
my $event = $cond->recv;
is($event->{change}, 'empty', '"Empty" event received upon workspace switch');
+ is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
};
################################################################################
my $event = $cond->recv;
is($event->{change}, 'empty', '"Empty" event received upon window close');
+ is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
};
}
my @tiled = @{$wscontent->{nodes}};
ok(@tiled == 1, 'one tiled container opened');
+is($tiled[0]->{current_border_width}, 5, 'tiled current border width set to 5');
is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*5, 'tiled border width 5');
my @floating = @{$wscontent->{floating_nodes}};
ok(@floating == 1, 'one floating container opened');
+is($floating[0]->{nodes}[0]->{current_border_width}, 10, 'floating current border width set to 10');
is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*10, 'floating border width 10');
exit_gracefully($pid);
@tiled = @{$wscontent->{nodes}};
ok(@tiled == 1, 'one tiled container opened');
+is($tiled[0]->{current_border_width}, 3, 'tiled current border width set to 3');
is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*3, 'tiled border width 3');
@floating = @{$wscontent->{floating_nodes}};
ok(@floating == 1, 'one floating container opened');
+is($floating[0]->{nodes}[0]->{current_border_width}, 7, 'floating current border width set to 7');
is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*7, 'floating border width 7');
exit_gracefully($pid);
+#####################################################################
+# 3: make sure normal border widths work as well
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_float normal 6
+new_window normal 4
+EOT
+
+$pid = launch_with_config($config);
+
+$tmp = fresh_workspace;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+
+$tilewindow = open_window;
+$floatwindow = open_floating_window;
+
+$wscontent = get_ws($tmp);
+
+@tiled = @{$wscontent->{nodes}};
+ok(@tiled == 1, 'one tiled container opened');
+is($tiled[0]->{current_border_width}, 4, 'tiled current border width set to 4');
+is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*4, 'tiled border width 4');
+
+@floating = @{$wscontent->{floating_nodes}};
+ok(@floating == 1, 'one floating container opened');
+is($floating[0]->{nodes}[0]->{current_border_width}, 6, 'floating current border width set to 6');
+is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*6, 'floating border width 6');
+
+exit_gracefully($pid);
+
done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the window::floating event works correctly. This event should be
+# emitted when a window transitions to or from the floating state.
+# Bug still in: 4.8-7-gf4a8253
+use i3test;
+
+my $i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+my $cv = AnyEvent->condvar;
+
+$i3->subscribe({
+ window => sub {
+ my ($event) = @_;
+ $cv->send($event) if $event->{change} eq 'floating';
+ }
+ })->recv;
+
+my $t;
+$t = AnyEvent->timer(
+ after => 0.5,
+ cb => sub {
+ $cv->send(0);
+ }
+);
+
+my $win = open_window();
+
+cmd '[id="' . $win->{id} . '"] floating enable';
+my $e = $cv->recv;
+
+isnt($e, 0, 'floating a container should send an ipc window event');
+is($e->{container}->{window}, $win->{id}, 'the event should contain information about the window');
+is($e->{container}->{floating}, 'user_on', 'the container should be floating');
+
+$cv = AnyEvent->condvar;
+cmd '[id="' . $win->{id} . '"] floating disable';
+my $e = $cv->recv;
+
+isnt($e, 0, 'disabling floating on a container should send an ipc window event');
+is($e->{container}->{window}, $win->{id}, 'the event should contain information about the window');
+is($e->{container}->{floating}, 'user_off', 'the container should not be floating');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests that the ipc close event works properly
+#
+# Bug still in: 4.8-7-gf4a8253
+use i3test;
+
+my $i3 = i3(get_socket_path());
+$i3->connect()->recv;
+
+my $cv;
+my $t;
+
+sub reset_test {
+ $cv = AE::cv;
+ $t = AE::timer(0.5, 0, sub { $cv->send(0); });
+}
+
+reset_test;
+
+$i3->subscribe({
+ window => sub {
+ my ($e) = @_;
+ if ($e->{change} eq 'close') {
+ $cv->send($e->{container});
+ }
+ },
+ })->recv;
+
+my $window = open_window;
+
+cmd 'kill';
+my $con = $cv->recv;
+
+ok($con, 'closing a window should send the window::close event');
+is($con->{window}, $window->{id}, 'the event should contain information about the window');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests that the ipc window::move event works properly
+#
+# Bug still in: 4.8-7-gf4a8253
+use i3test;
+
+my $i3 = i3(get_socket_path());
+$i3->connect()->recv;
+
+my $cv;
+my $t;
+
+sub reset_test {
+ $cv = AE::cv;
+ $t = AE::timer(0.5, 0, sub { $cv->send(0); });
+}
+
+reset_test;
+
+$i3->subscribe({
+ window => sub {
+ my ($e) = @_;
+ if ($e->{change} eq 'move') {
+ $cv->send($e->{container});
+ }
+ },
+ })->recv;
+
+my $dummy_window = open_window;
+my $window = open_window;
+
+cmd 'move right';
+my $con = $cv->recv;
+
+ok($con, 'moving a window should emit the window::move event');
+is($con->{window}, $window->{id}, 'the event should contain info about the window');
+
+reset_test;
+
+cmd 'move to workspace ws_new';
+$con = $cv->recv;
+
+ok($con, 'moving a window to a different workspace should emit the window::move event');
+is($con->{window}, $window->{id}, 'the event should contain info about the window');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Correctly handle WM_CHANGE_STATE requests for the iconic state
+# See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4
+# Ticket: #1279
+# Bug still in: 4.8-7-gf4a8253
+use i3test;
+
+sub send_iconic_state_request {
+ my ($win) = @_;
+
+ my $msg = pack "CCSLLLLLL",
+ X11::XCB::CLIENT_MESSAGE, # response_type
+ 32, # format
+ 0, # sequence
+ $win->id, # window
+ $x->atom(name => 'WM_CHANGE_STATE')->id, # message type
+ 3, # data32[0]
+ 0, # data32[1]
+ 0, # data32[2]
+ 0, # data32[3]
+ 0; # data32[4]
+
+ $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
+}
+
+my $ws = fresh_workspace;
+my $win = open_window;
+
+send_iconic_state_request($win);
+sync_with_i3;
+
+is(@{get_ws($ws)->{nodes}}, 0, 'When a window requests the iconic state, the container should be closed');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the `move [direction]` command works with criteria
+# Bug still in: 4.8-16-g6888a1f
+use i3test;
+
+my $ws = fresh_workspace;
+
+my $win1 = open_window;
+my $win2 = open_window;
+my $win3 = open_window;
+
+# move win1 from the left to the right
+cmd '[id="' . $win1->{id} . '"] move right';
+
+# now they should be switched, with win2 still being focused
+my $ws_con = get_ws($ws);
+
+# win2 should be on the left
+is($ws_con->{nodes}[0]->{window}, $win2->{id}, 'the `move [direction]` command should work with criteria');
+is($x->input_focus, $win3->{id}, 'it should not disturb focus');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that `workspace {N}` acts like `workspace number {N}` when N is a plain
+# digit, and likewise for `move to workspace {N}`.
+# Ticket: #1238
+# Bug still in: 4.8-16-g3f5a0f0
+use i3test;
+
+cmd 'workspace 5:foo';
+open_window;
+fresh_workspace;
+cmd 'workspace 5';
+
+is(focused_ws, '5:foo',
+ 'a command to switch to a workspace with a bare number should switch to a workspace of that number');
+
+fresh_workspace;
+my $win = open_window;
+cmd '[id="' . $win->{id} . '"] move to workspace 5';
+
+is(@{get_ws('5:foo')->{nodes}}, 2,
+ 'a command to move a container to a workspace with a bare number should move that container to a workspace of that number');
+
+fresh_workspace;
+cmd 'workspace 7';
+open_window;
+cmd 'workspace 7:foo';
+$win = open_window;
+
+cmd 'workspace 7';
+is(focused_ws, '7',
+ 'a workspace with a name that is a matching plain number should be preferred when switching');
+
+cmd '[id="' . $win->{id} . '"] move to workspace 7';
+is(@{get_ws('7')->{nodes}}, 2,
+ 'a workspace with a name that is a matching plain number should be preferred when moving');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the window::urgent event works correctly. The window::urgent event
+# should be emitted when a window becomes urgent or loses its urgent status.
+#
+use i3test;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+force_display_urgency_hint 0ms
+EOT
+
+my $i3 = i3(get_socket_path());
+$i3->connect()->recv;
+
+my $cv;
+$i3->subscribe({
+ window => sub {
+ my ($event) = @_;
+ $cv->send($event) if $event->{change} eq 'urgent';
+ }
+})->recv;
+
+my $t;
+$t = AnyEvent->timer(
+ after => 0.5,
+ cb => sub {
+ $cv->send(0);
+ }
+);
+
+$cv = AnyEvent->condvar;
+fresh_workspace;
+my $win = open_window;
+my $dummy_win = open_window;
+
+$win->add_hint('urgency');
+my $event = $cv->recv;
+
+isnt($event, 0, 'an urgent con should emit the window::urgent event');
+is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
+is($event->{container}->{urgent}, 1, 'the container should be urgent');
+
+$cv = AnyEvent->condvar;
+$win->delete_hint('urgency');
+my $event = $cv->recv;
+
+isnt($event, 0, 'an urgent con should emit the window::urgent event');
+is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
+is($event->{container}->{urgent}, 0, 'the container should not be urgent');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the EWMH specified property _NET_DESKTOP_NAMES is updated properly
+# on the root window. We interpret this as a list of the open workspace names.
+# Ticket: #1241
+use i3test;
+
+sub get_desktop_names {
+ # Make sure that i3 pushed its changes to X11 before querying.
+ sync_with_i3;
+
+ my $cookie = $x->get_property(
+ 0,
+ $x->get_root_window(),
+ $x->atom(name => '_NET_DESKTOP_NAMES')->id,
+ $x->atom(name => 'UTF8_STRING')->id,
+ 0,
+ 4096,
+ );
+
+ my $reply = $x->get_property_reply($cookie->{sequence});
+
+ return 0 if $reply->{value_len} == 0;
+
+ # the property is a null-delimited list of utf8 strings ;;
+ return split /\0/, $reply->{value};
+}
+
+cmd 'workspace foo';
+
+my @expected_names = ('foo');
+my @desktop_names = get_desktop_names;
+
+is_deeply(\@desktop_names, \@expected_names, '_NET_DESKTOP_NAMES should be an array of the workspace names');
+
+# open a new workspace and see that the property is updated correctly
+open_window;
+cmd 'workspace bar';
+
+@desktop_names = get_desktop_names;
+@expected_names = ('foo', 'bar');
+
+is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a new workspace appears');
+
+# rename the workspace and see that the property is updated correctly
+cmd 'rename workspace bar to baz';
+
+@desktop_names = get_desktop_names;
+@expected_names = ('foo', 'baz');
+
+is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a workspace is renamed');
+
+# empty a workspace and see that the property is updated correctly
+cmd 'workspace foo';
+
+@desktop_names = get_desktop_names;
+@expected_names = ('foo');
+
+is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a workspace is emptied');
+
+done_testing;
# Bug still in: 4.8-26-gf96ec19
use i3test;
use File::Temp qw(tempfile);
-use List::MoreUtils qw(uniq);
use IO::Handle;
my $ws = fresh_workspace;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# This is a regression test for a bug where a normal floating default border is
+# not applied when the default tiling border is set to a pixel value.
+# Ticket: #1305
+# Bug still in: 4.8-62-g7381b50
+use i3test i3_autostart => 0;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+new_window pixel 5
+new_float normal
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+
+my $float_window = open_floating_window;
+
+my @floating = @{get_ws($ws)->{floating_nodes}};
+
+is($floating[0]->{nodes}[0]->{border}, 'normal', 'default floating border is `normal`');
+
+exit_gracefully($pid);
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Check whether the -C option works without a display and doesn't
+# accidentally start the nagbar.
+#
+use i3test i3_autostart => 0;
+use File::Temp qw(tempfile);
+
+sub check_config {
+ my ($config) = @_;
+ my ($fh, $tmpfile) = tempfile(UNLINK => 1);
+ print $fh $config;
+ my $output = qx(DISPLAY= ../i3 -C -c $tmpfile 2>&1);
+ my $retval = $?;
+ $fh->flush;
+ close($fh);
+ return ($retval >> 8, $output);
+}
+
+################################################################################
+# 1: test with a bogus configuration file
+################################################################################
+
+my $cfg = <<EOT;
+# i3 config file (v4)
+i_am_an_unknown_config option
+EOT
+
+my ($ret, $out) = check_config($cfg);
+is($ret, 1, "exit code == 1");
+like($out, qr/ERROR: *CONFIG: *[Ee]xpected.*tokens/, 'bogus config file');
+
+################################################################################
+# 2: test with a valid configuration file
+################################################################################
+
+my $cfg = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+EOT
+
+my ($ret, $out) = check_config($cfg);
+is($ret, 0, "exit code == 0");
+is($out, "", 'valid config file');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that changes to WM_CLASS are internally processed by i3 by updating the
+# cached property and running assignments. This allows the property to be used
+# in criteria selection
+# Ticket: #1052
+# Bug still in: 4.8-73-g6bf7f8e
+use i3test i3_autostart => 0;
+use X11::XCB qw(PROP_MODE_REPLACE);
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [class="Special"] mark special_class_mark
+EOT
+
+my $pid = launch_with_config($config);
+
+sub change_window_class {
+ my ($window, $class) = @_;
+ my $atomname = $x->atom(name => 'WM_CLASS');
+ my $atomtype = $x->atom(name => 'STRING');
+ $x->change_property(
+ PROP_MODE_REPLACE,
+ $window->id,
+ $atomname->id,
+ $atomtype->id,
+ 8,
+ length($class) + 1,
+ $class
+ );
+ sync_with_i3;
+}
+
+my $ws = fresh_workspace;
+
+my $win = open_window;
+
+change_window_class($win, "special\0Special");
+
+my $con = @{get_ws_content($ws)}[0];
+
+is($con->{window_properties}->{class}, 'Special',
+ 'The container class should be updated when a window changes class');
+
+is($con->{window_properties}->{instance}, 'special',
+ 'The container instance should be updated when a window changes instance');
+
+# The mark `special_class_mark` is added in a `for_window` assignment in the
+# config for testing purposes
+is($con->{mark}, 'special_class_mark',
+ 'A `for_window` assignment should run for a match when the window changes class');
+
+exit_gracefully($pid);
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that focusing floating windows with the command `focus [direction]`
+# promotes the focused window to the top of the rendering stack.
+# Ticket: #1322
+# Bug still in: 4.8-88-gcc09348
+use i3test;
+
+my $ws = fresh_workspace;
+
+my $win1 = open_floating_window;
+my $win2 = open_floating_window;
+my $win3 = open_floating_window;
+
+# it's a good idea to do this a few times because of the implementation
+for my $i (1 .. 3) {
+ cmd 'focus left';
+ my $ws_con = get_ws($ws);
+ is($ws_con->{floating_nodes}[-1]->{nodes}[0]->{id}, get_focused($ws),
+ "focus left put the focused window on top of the floating windows (try $i)");
+}
+
+for my $i (1 .. 3) {
+ cmd 'focus right';
+ my $ws_con = get_ws($ws);
+ is($ws_con->{floating_nodes}[-1]->{nodes}[0]->{id}, get_focused($ws),
+ "focus right put the focused window on top of the floating windows (try $i)");
+}
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the binding event works properly
+# Ticket: #1210
+use i3test i3_autostart => 0;
+
+my $keysym = 't';
+my $command = 'nop';
+my @mods = ('Shift', 'Ctrl');
+my $binding_symbol = join("+", @mods) . "+$keysym";
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym $binding_symbol $command
+EOT
+
+SKIP: {
+ qx(which xdotool 2> /dev/null);
+
+ skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
+
+ skip "AnyEvent::I3 too old (need >= 0.16)", 1 if $AnyEvent::I3::VERSION < 0.16;
+
+ my $pid = launch_with_config($config);
+
+ my $i3 = i3(get_socket_path());
+ $i3->connect->recv;
+
+ my $cv = AE::cv;
+ my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+ $i3->subscribe({
+ binding => sub {
+ $cv->send(shift);
+ }
+ })->recv;
+
+ qx(xdotool key $binding_symbol);
+
+ my $e = $cv->recv;
+
+ does_i3_live;
+
+ diag "Event:\n", Dumper($e);
+
+ ok($e,
+ 'the binding event should emit when user input triggers an i3 binding event');
+
+ is($e->{change}, 'run',
+ 'the `change` field should indicate this binding has run');
+
+ ok($e->{binding},
+ 'the `binding` field should be a hash that contains information about the binding');
+
+ is($e->{binding}->{input_type}, 'keyboard',
+ 'the input_type field should be the input type of the binding (keyboard or mouse)');
+
+ note 'the `mods` field should contain the symbols for the modifiers of the binding';
+ foreach (@mods) {
+ ok(grep(/$_/i, @{$e->{binding}->{mods}}), "`mods` contains the modifier $_");
+ }
+
+ is($e->{binding}->{command}, $command,
+ 'the `command` field should contain the command the binding ran');
+
+ is($e->{binding}->{input_code}, 0,
+ 'the input_code should be the specified code if the key was bound with bindcode, and otherwise zero');
+
+ exit_gracefully($pid);
+
+}
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the binding event works properly
+# Ticket: #1210
+use i3test i3_autostart => 0;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+bindsym r reload
+EOT
+
+SKIP: {
+ qx(which xdotool 2> /dev/null);
+
+ skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
+
+ my $pid = launch_with_config($config);
+
+ my $i3 = i3(get_socket_path());
+ $i3->connect->recv;
+
+ qx(xdotool key r);
+
+ does_i3_live;
+
+ exit_gracefully($pid);
+
+}
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test _NET_CLOSE_WINDOW requests to close a window.
+# See http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896
+# Ticket: #1396
+# Bug still in: 4.8-116-gbb1f857
+use i3test;
+
+sub send_close_window_request {
+ my ($win) = @_;
+
+ my $msg = pack "CCSLLLLLL",
+ X11::XCB::CLIENT_MESSAGE, # response_type
+ 32, # format
+ 0, # sequence
+ $win->id, # window
+ $x->atom(name => '_NET_CLOSE_WINDOW')->id, # message type
+ 0, # data32[0]
+ 0, # data32[1]
+ 0, # data32[2]
+ 0, # data32[3]
+ 0; # data32[4]
+
+ $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
+}
+
+my $ws = fresh_workspace;
+my $win = open_window;
+
+send_close_window_request($win);
+sync_with_i3;
+
+is(@{get_ws($ws)->{nodes}}, 0, 'When a pager sends a _NET_CLOSE_WINDOW request for a window, the container should be closed');
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Ensure that `focus [direction]` will focus an existing floating con when no
+# tiling con exists on the output in [direction] when focusing across outputs
+# Bug still in: 4.7.2-204-g893dbae
+use i3test i3_autostart => 0;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+workspace ws_left output fake-0
+workspace ws_right output fake-1
+
+mouse_warping none
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
+
+my $pid = launch_with_config($config);
+
+cmd 'workspace ws_left';
+my $win = open_window();
+
+cmd 'floating enable';
+cmd 'focus output right';
+cmd 'focus left';
+
+is($x->input_focus, $win->id,
+ 'Focusing across outputs with `focus [direction]` should focus an existing floating con when no tiling con exists on the output in [direction].');
+
+exit_gracefully($pid);
+
+done_testing;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test that the EWMH specified property _NET_DESKTOP_VIEWPORT is updated
+# properly on the root window. We interpret this as a list of x/y coordinate
+# pairs for the upper left corner of the respective outputs of the workspaces
+# Ticket: #1241
+use i3test i3_autostart => 0;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+workspace 0 output fake-0
+workspace 1 output fake-1
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
+
+my $pid = launch_with_config($config);
+
+sub get_desktop_viewport {
+ # Make sure that i3 pushed its changes to X11 before querying.
+ sync_with_i3;
+
+ my $cookie = $x->get_property(
+ 0,
+ $x->get_root_window(),
+ $x->atom(name => '_NET_DESKTOP_VIEWPORT')->id,
+ $x->atom(name => 'CARDINAL')->id,
+ 0,
+ 4096
+ );
+
+ my $reply = $x->get_property_reply($cookie->{sequence});
+
+ return 0 if $reply->{value_len} == 0;
+
+ my $len = $reply->{length};
+
+ return unpack ("L$len", $reply->{value});
+}
+
+# initialize the workspaces
+cmd 'workspace 1';
+cmd 'workspace 0';
+
+my @expected_viewport = (0, 0, 1024, 0);
+my @desktop_viewport = get_desktop_viewport;
+
+is_deeply(\@desktop_viewport, \@expected_viewport,
+ '_NET_DESKTOP_VIEWPORT should be an array of x/y coordinate pairs for the upper left corner of the respective outputs of the workspaces');
+
+cmd 'workspace 0';
+open_window;
+cmd 'workspace 3';
+
+@expected_viewport = (0, 0, 0, 0, 1024, 0);
+@desktop_viewport = get_desktop_viewport;
+
+is_deeply(\@desktop_viewport, \@expected_viewport,
+ 'it should be updated when a new workspace appears');
+
+cmd 'rename workspace 3 to 2';
+
+@expected_viewport = (0, 0, 0, 0, 1024, 0);
+@desktop_viewport = get_desktop_viewport;
+
+is_deeply(\@desktop_viewport, \@expected_viewport,
+ 'it should stay up to date when a workspace is renamed');
+
+cmd 'workspace 0';
+
+@expected_viewport = (0, 0, 1024, 0);
+@desktop_viewport = get_desktop_viewport;
+
+is_deeply(\@desktop_viewport, \@expected_viewport,
+ 'it should be updated when a workspace is emptied');
+
+exit_gracefully($pid);
+
+done_testing;
/*
* Singly-linked List definitions.
*/
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
+#define SLIST_HEAD(name, type) \
+ struct name { \
+ struct type *slh_first; /* first element */ \
+ }
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
+#define SLIST_ENTRY(type) \
+ struct { \
+ struct type *sle_next; /* next element */ \
+ }
/*
* Singly-linked List access methods.
*/
-#define SLIST_FIRST(head) ((head)->slh_first)
-#define SLIST_END(head) NULL
-#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-#define SLIST_FOREACH(var, head, field) \
- for((var) = SLIST_FIRST(head); \
- (var) != SLIST_END(head); \
- (var) = SLIST_NEXT(var, field))
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); \
- ((var) = *(varp)) != SLIST_END(head); \
- (varp) = &SLIST_NEXT((var), field))
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
/*
* Singly-linked List functions.
*/
-#define SLIST_INIT(head) { \
- SLIST_FIRST(head) = SLIST_END(head); \
-}
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- (elm)->field.sle_next = (slistelm)->field.sle_next; \
- (slistelm)->field.sle_next = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.sle_next = (head)->slh_first; \
- (head)->slh_first = (elm); \
-} while (0)
-
-#define SLIST_REMOVE_NEXT(head, elm, field) do { \
- (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- (head)->slh_first = (head)->slh_first->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- if ((head)->slh_first == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } else { \
- struct type *curelm = (head)->slh_first; \
- \
- while (curelm->field.sle_next != (elm)) \
- curelm = curelm->field.sle_next; \
- curelm->field.sle_next = \
- curelm->field.sle_next->field.sle_next; \
- _Q_INVALIDATE((elm)->field.sle_next); \
- } \
-} while (0)
+#define SLIST_INIT(head) \
+ { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+ }
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) \
+ do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+ } while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+ } while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) \
+ do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+ } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) \
+ do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+ } while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) \
+ do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+ } while (0)
/*
* List definitions.
*/
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
+#define LIST_HEAD(name, type) \
+ struct name { \
+ struct type *lh_first; /* first element */ \
+ }
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
+#define LIST_ENTRY(type) \
+ struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+ }
/*
* List access methods
*/
-#define LIST_FIRST(head) ((head)->lh_first)
-#define LIST_END(head) NULL
-#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-#define LIST_FOREACH(var, head, field) \
- for((var) = LIST_FIRST(head); \
- (var)!= LIST_END(head); \
- (var) = LIST_NEXT(var, field))
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST(head); \
+ (var) != LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
/*
* List functions.
*/
-#define LIST_INIT(head) do { \
- LIST_FIRST(head) = LIST_END(head); \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
- (listelm)->field.le_next->field.le_prev = \
- &(elm)->field.le_next; \
- (listelm)->field.le_next = (elm); \
- (elm)->field.le_prev = &(listelm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- (elm)->field.le_next = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &(elm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.le_next = (head)->lh_first) != NULL) \
- (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
- (head)->lh_first = (elm); \
- (elm)->field.le_prev = &(head)->lh_first; \
-} while (0)
-
-#define LIST_REMOVE(elm, field) do { \
- if ((elm)->field.le_next != NULL) \
- (elm)->field.le_next->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = (elm)->field.le_next; \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
-
-#define LIST_REPLACE(elm, elm2, field) do { \
- if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
- (elm2)->field.le_next->field.le_prev = \
- &(elm2)->field.le_next; \
- (elm2)->field.le_prev = (elm)->field.le_prev; \
- *(elm2)->field.le_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
+#define LIST_INIT(head) \
+ do { \
+ LIST_FIRST(head) = LIST_END(head); \
+ } while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) \
+ do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+ } while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+ } while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) \
+ do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next; \
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+ } while (0)
+
+#define LIST_REMOVE(elm, field) \
+ do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+ } while (0)
+
+#define LIST_REPLACE(elm, elm2, field) \
+ do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+ } while (0)
/*
* Simple queue definitions.
*/
-#define SIMPLEQ_HEAD(name, type) \
-struct name { \
- struct type *sqh_first; /* first element */ \
- struct type **sqh_last; /* addr of last next element */ \
-}
+#define SIMPLEQ_HEAD(name, type) \
+ struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+ }
-#define SIMPLEQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).sqh_first }
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
-#define SIMPLEQ_ENTRY(type) \
-struct { \
- struct type *sqe_next; /* next element */ \
-}
+#define SIMPLEQ_ENTRY(type) \
+ struct { \
+ struct type *sqe_next; /* next element */ \
+ }
/*
* Simple queue access methods.
*/
-#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
-#define SIMPLEQ_END(head) NULL
-#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
-#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
-#define SIMPLEQ_FOREACH(var, head, field) \
- for((var) = SIMPLEQ_FIRST(head); \
- (var) != SIMPLEQ_END(head); \
- (var) = SIMPLEQ_NEXT(var, field))
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
/*
* Simple queue functions.
*/
-#define SIMPLEQ_INIT(head) do { \
- (head)->sqh_first = NULL; \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
-
-#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (head)->sqh_first = (elm); \
-} while (0)
-
-#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.sqe_next = NULL; \
- *(head)->sqh_last = (elm); \
- (head)->sqh_last = &(elm)->field.sqe_next; \
-} while (0)
-
-#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (listelm)->field.sqe_next = (elm); \
-} while (0)
-
-#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
- if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
+#define SIMPLEQ_INIT(head) \
+ do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+ } while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+ } while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ } while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+ } while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) \
+ do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+ } while (0)
/*
* Tail queue definitions.
*/
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
-}
+#define TAILQ_HEAD(name, type) \
+ struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ }
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first }
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
-}
+#define TAILQ_ENTRY(type) \
+ struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ }
/*
* tail queue access methods
*/
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-#define TAILQ_END(head) NULL
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-#define TAILQ_EMPTY(head) \
- (TAILQ_FIRST(head) == TAILQ_END(head))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
-#define TAILQ_FOREACH(var, head, field) \
- for((var) = TAILQ_FIRST(head); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_NEXT(var, field))
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for((var) = TAILQ_LAST(head, headname); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_PREV(var, headname, field))
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
/*
* Tail queue functions.
*/
-#define TAILQ_INIT(head) do { \
- (head)->tqh_first = NULL; \
- (head)->tqh_last = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
- (head)->tqh_first->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (head)->tqh_first = (elm); \
- (elm)->field.tqe_prev = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.tqe_next = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
- (elm)->field.tqe_next->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (listelm)->field.tqe_next = (elm); \
- (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- (elm)->field.tqe_next = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- if (((elm)->field.tqe_next) != NULL) \
- (elm)->field.tqe_next->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
-
-#define TAILQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
- (elm2)->field.tqe_next->field.tqe_prev = \
- &(elm2)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm2)->field.tqe_next; \
- (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
- *(elm2)->field.tqe_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
+#define TAILQ_INIT(head) \
+ do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+ } while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+ } while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ } while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+ } while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) \
+ do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+ } while (0)
+
+#define TAILQ_REMOVE(head, elm, field) \
+ do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+ } while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) \
+ do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+ } while (0)
/*
* Circular queue definitions.
*/
-#define CIRCLEQ_HEAD(name, type) \
-struct name { \
- struct type *cqh_first; /* first element */ \
- struct type *cqh_last; /* last element */ \
-}
+#define CIRCLEQ_HEAD(name, type) \
+ struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+ }
-#define CIRCLEQ_HEAD_INITIALIZER(head) \
- { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
-#define CIRCLEQ_ENTRY(type) \
-struct { \
- struct type *cqe_next; /* next element */ \
- struct type *cqe_prev; /* previous element */ \
-}
+#define CIRCLEQ_ENTRY(type) \
+ struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+ }
/*
* Circular queue access methods
*/
-#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
-#define CIRCLEQ_LAST(head) ((head)->cqh_last)
-#define CIRCLEQ_END(head) ((void *)(head))
-#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
-#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
-#define CIRCLEQ_EMPTY(head) \
- (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
-
-#define CIRCLEQ_FOREACH(var, head, field) \
- for((var) = CIRCLEQ_FIRST(head); \
- (var) != CIRCLEQ_END(head); \
- (var) = CIRCLEQ_NEXT(var, field))
-
-#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
- for((var) = CIRCLEQ_LAST(head); \
- (var) != CIRCLEQ_END(head); \
- (var) = CIRCLEQ_PREV(var, field))
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
/*
* Circular queue functions.
*/
-#define CIRCLEQ_INIT(head) do { \
- (head)->cqh_first = CIRCLEQ_END(head); \
- (head)->cqh_last = CIRCLEQ_END(head); \
-} while (0)
-
-#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- (elm)->field.cqe_next = (listelm)->field.cqe_next; \
- (elm)->field.cqe_prev = (listelm); \
- if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm); \
- else \
- (listelm)->field.cqe_next->field.cqe_prev = (elm); \
- (listelm)->field.cqe_next = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
- (elm)->field.cqe_next = (listelm); \
- (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
- if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm); \
- else \
- (listelm)->field.cqe_prev->field.cqe_next = (elm); \
- (listelm)->field.cqe_prev = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.cqe_next = (head)->cqh_first; \
- (elm)->field.cqe_prev = CIRCLEQ_END(head); \
- if ((head)->cqh_last == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm); \
- else \
- (head)->cqh_first->field.cqe_prev = (elm); \
- (head)->cqh_first = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.cqe_next = CIRCLEQ_END(head); \
- (elm)->field.cqe_prev = (head)->cqh_last; \
- if ((head)->cqh_first == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm); \
- else \
- (head)->cqh_last->field.cqe_next = (elm); \
- (head)->cqh_last = (elm); \
-} while (0)
-
-#define CIRCLEQ_REMOVE(head, elm, field) do { \
- if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm)->field.cqe_prev; \
- else \
- (elm)->field.cqe_next->field.cqe_prev = \
- (elm)->field.cqe_prev; \
- if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm)->field.cqe_next; \
- else \
- (elm)->field.cqe_prev->field.cqe_next = \
- (elm)->field.cqe_next; \
- _Q_INVALIDATE((elm)->field.cqe_prev); \
- _Q_INVALIDATE((elm)->field.cqe_next); \
-} while (0)
-
-#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
- CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm2); \
- else \
- (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
- if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
- CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm2); \
- else \
- (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
- _Q_INVALIDATE((elm)->field.cqe_prev); \
- _Q_INVALIDATE((elm)->field.cqe_next); \
-} while (0)
+#define CIRCLEQ_INIT(head) \
+ do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) \
+ do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) \
+ do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) \
+ do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+ } while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) \
+ do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+ } while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) \
+ do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+ } while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) \
+ do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+ } while (0)
printf("first: %d\n", e->abc);
e = TAILQ_LAST(&head, objhead);
printf("last: %d\n", e->abc);
- TAILQ_FOREACH (e, &head, entry) {
+ TAILQ_FOREACH(e, &head, entry) {
printf(" %d\n", e->abc);
}
printf("again, but reverse:\n");
- TAILQ_FOREACH_REVERSE (e, &head, objhead, entry) {
+ TAILQ_FOREACH_REVERSE(e, &head, objhead, entry) {
printf(" %d\n", e->abc);
}
printf("done\n\n");