]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 31 Jan 2015 21:42:54 +0000 (22:42 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 31 Jan 2015 21:42:54 +0000 (22:42 +0100)
148 files changed:
.clang-format
DEPENDS
common.mk
debian/changelog
debian/control
debian/i3-wm.manpages
debian/patches/manpage-x-terminal-emulator.patch [deleted file]
debian/patches/series [deleted file]
debian/patches/use-x-terminal-emulator.patch [deleted file]
docs/hacking-howto
docs/i3bar-protocol
docs/ipc
docs/testsuite
docs/userguide
i3-config-wizard/i3-config-wizard.mk
i3-config-wizard/main.c
i3-config-wizard/xcb.h
i3-input/i3-input.h
i3-nagbar/i3-nagbar.h
i3-save-tree
i3-sensible-editor
i3-sensible-pager
i3-sensible-terminal
i3.config
i3.config.keycodes
i3.xsession.desktop
i3bar/i3bar.mk
i3bar/include/common.h
i3bar/include/config.h
i3bar/include/ipc.h
i3bar/include/outputs.h
i3bar/include/trayclients.h
i3bar/include/util.h
i3bar/include/workspaces.h
i3bar/include/xcb.h
i3bar/src/child.c
i3bar/src/config.c
i3bar/src/ipc.c
i3bar/src/outputs.c
i3bar/src/workspaces.c
i3bar/src/xcb.c
include/atoms.xmacro
include/bindings.h
include/commands.h
include/commands_parser.h
include/con.h
include/config.h
include/config_directives.h
include/config_parser.h
include/data.h
include/ewmh.h
include/floating.h
include/handlers.h
include/i3.h
include/i3/ipc.h
include/ipc.h
include/libi3.h
include/log.h
include/move.h
include/queue.h
include/sd-daemon.h
include/startup.h
include/util.h
include/workspace.h
include/xcb.h
include/yajl_utils.h
libi3/dpi.c
libi3/font.c
man/i3-sensible-terminal.man
man/i3.man
parser-specs/commands.spec
parser-specs/config.spec
src/assignments.c
src/bindings.c
src/click.c
src/commands.c
src/commands_parser.c
src/con.c
src/config.c
src/config_directives.c
src/config_parser.c
src/ewmh.c
src/fake_outputs.c
src/floating.c
src/handlers.c
src/i3.mk
src/ipc.c
src/key_press.c
src/load_layout.c
src/main.c
src/manage.c
src/match.c
src/move.c
src/output.c
src/randr.c
src/render.c
src/restore_layout.c
src/scratchpad.c
src/sighandler.c
src/startup.c
src/tree.c
src/window.c
src/workspace.c
src/x.c
src/xinerama.c
testcases/.gitignore
testcases/Makefile.PL
testcases/Xdummy [deleted file]
testcases/complete-run.pl
testcases/i3-test.config
testcases/lib/StartXDummy.pm [deleted file]
testcases/lib/StartXServer.pm [new file with mode: 0644]
testcases/lib/i3test.pm
testcases/t/100-fullscreen.t
testcases/t/112-floating-resize.t
testcases/t/114-client-leader.t
testcases/t/115-ipc-workspaces.t
testcases/t/116-nestedcons.t
testcases/t/122-split.t
testcases/t/124-move.t
testcases/t/139-ws-numbers.t
testcases/t/158-wm_take_focus.t
testcases/t/172-start-on-named-ws.t
testcases/t/175-startup-notification.t
testcases/t/201-config-parser.t
testcases/t/217-NET_CURRENT_DESKTOP.t
testcases/t/227-ipc-workspace-empty.t
testcases/t/228-border-widths.t
testcases/t/231-ipc-floating-event.t [new file with mode: 0644]
testcases/t/231-ipc-window-close.t [new file with mode: 0644]
testcases/t/231-ipc-window-move.t [new file with mode: 0644]
testcases/t/231-wm-change-state.t [new file with mode: 0644]
testcases/t/232-cmd-move-criteria.t [new file with mode: 0644]
testcases/t/232-cmd-workspace-number-selection.t [new file with mode: 0644]
testcases/t/232-ipc-window-urgent.t [new file with mode: 0644]
testcases/t/234-ewmh-desktop-names.t [new file with mode: 0644]
testcases/t/234-layout-restore-output.t
testcases/t/234-regress-default-floating-border.t [new file with mode: 0644]
testcases/t/235-check-config-no-x.t [new file with mode: 0644]
testcases/t/235-wm-class-change-handler.t [new file with mode: 0644]
testcases/t/236-floating-focus-raise.t [new file with mode: 0644]
testcases/t/238-ipc-binding-event.t [new file with mode: 0644]
testcases/t/238-regress-reload-bindsym.t [new file with mode: 0644]
testcases/t/239-net-close-window-request.t [new file with mode: 0644]
testcases/t/520-regress-focus-direction-floating.t [new file with mode: 0644]
testcases/t/521-ewmh-desktop-viewport.t [new file with mode: 0644]
tests/queue.h
tests/swap.c

index 57948c311cf2244721becc17300163d4addc30a8..1d84013239de1922edcb58688c0800226db9b46b 100644 (file)
@@ -7,5 +7,4 @@ AlwaysBreakBeforeMultilineStrings: false
 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
diff --git a/DEPENDS b/DEPENDS
index 8a9f9dcaa263b6393495d90ec5bd498cc05fb846..d5dae1429d05192a5d0bb56671354d34933b3cb4 100644 (file)
--- a/DEPENDS
+++ b/DEPENDS
@@ -4,25 +4,27 @@
    "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
@@ -35,6 +37,6 @@
  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.
index b086bc8535ff5f349bebcfb3d0036a69f608aeb5..b9e15a286fc27d335e4ae8c12a36ec8188dbd972 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -92,6 +92,7 @@ else
 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)
@@ -105,9 +106,10 @@ XCB_WM_LIBS   := $(call ldflags_for_lib, xcb-icccm,xcb-icccm)
 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)
index ac79c8bbeab598a9b717c688c3338fd5083bf821..5e3d174babb7da8bdced7e810007cb485455bb4a 100644 (file)
@@ -1,8 +1,15 @@
-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
 
index 97b11a5ee400d92ff30c2c219ab3f4fdf722cb8a..cc9660f7260bccd8472ef07b81310c13efbc37f0 100644 (file)
@@ -10,6 +10,9 @@ Build-Depends: debhelper (>= 7.0.50~),
                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,
@@ -21,7 +24,7 @@ Build-Depends: debhelper (>= 7.0.50~),
                libcairo2-dev,
                libpango1.0-dev,
                libpod-simple-perl
-Standards-Version: 3.9.4
+Standards-Version: 3.9.5
 Homepage: http://i3wm.org/
 
 Package: i3
@@ -38,7 +41,7 @@ Architecture: any
 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
index 58569b8711e2ad11c1f5e2e54f86f2b0e39bcc2f..1953b6a85a0185e68aafebe19567fee080cf8a03 100644 (file)
@@ -9,4 +9,5 @@ man/i3-sensible-pager.1
 man/i3-sensible-editor.1
 man/i3-sensible-terminal.1
 man/i3-dmenu-desktop.1
+man/i3-save-tree.1
 man/i3bar.1
diff --git a/debian/patches/manpage-x-terminal-emulator.patch b/debian/patches/manpage-x-terminal-emulator.patch
deleted file mode 100644 (file)
index 17b8ee4..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-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
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644 (file)
index 08d60ae..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-use-x-terminal-emulator.patch
-manpage-x-terminal-emulator.patch
diff --git a/debian/patches/use-x-terminal-emulator.patch b/debian/patches/use-x-terminal-emulator.patch
deleted file mode 100644 (file)
index 9616162..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-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
index f4e3f0318f40f91a1e0def7530cac04a6166b64f..aca6e92bf6f496bbc5272169ab279b557232c0e9 100644 (file)
@@ -951,6 +951,8 @@ For a short introduction into using git, see
 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.
index 927631549fe9969622824b53c4435e6af08b77c1..0ca2fd82c9e3d3e0233d359dcdfe682e1c063588 100644 (file)
@@ -148,7 +148,7 @@ min_width::
         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::
index c976870583011878dd61e02c3cb40a9b594ceaad..ff7c8aaeb0f185299520dc5f271460060a8d13be 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -1,7 +1,7 @@
 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
@@ -156,7 +156,7 @@ following properties:
 
 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.
@@ -316,6 +316,10 @@ window_rect (map)::
        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.
@@ -613,7 +617,7 @@ you can register to an event.
 *Example:*
 ---------------------------------
 type: SUBSCRIBE
-payload: [ "workspace", "focus" ]
+payload: [ "workspace", "output" ]
 ---------------------------------
 
 
@@ -638,6 +642,9 @@ window (3)::
 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:*
 --------------------------------------------------------------------
@@ -661,15 +668,16 @@ if ($is_event) {
 
 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:*
 ---------------------
@@ -717,9 +725,13 @@ This event consists of a single serialized map containing a property
 +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
@@ -745,6 +757,47 @@ This event consists of a single serialized map reporting on options from 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]]
@@ -754,15 +807,14 @@ all this on your own). This list names some (if you wrote one, please let me
 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::
@@ -770,4 +822,4 @@ 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
index 6c3a36d9c78e9f253a8b0371eee4be55f0f29c31..61a7221962af1161010cd23d7440fb549733874f 100644 (file)
@@ -74,6 +74,9 @@ client, simply called +cpan+. It comes with every Perl installation and can be
 used to install the testsuite. Many users prefer to use the more modern
 +cpanminus+ instead, though (because it asks no questions and just works):
 
+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
@@ -102,7 +105,7 @@ more testcases. Also, it takes care of starting up a separate instance of i3
 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+
 ---------------------------------------
@@ -146,12 +149,11 @@ $ less latest/i3-log-for-04-floating.t
 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
 
@@ -175,10 +177,9 @@ manager.
 === 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
 --------------------------------------------
@@ -197,7 +198,6 @@ conventionally named subfolder +t+:
 │   │   ├── omitted for brevity
 │   │   ├── ...
 │   │   └── 74-regress-focus-toggle.t
-│   └── Xdummy
 --------------------------------------------
 
 == Anatomy of a testcase
index ff46d62f132b00febbda5829e864ce1f731cfb95..d6afa334957b23898b6b208068b8de2a57ad3447 100644 (file)
@@ -91,7 +91,7 @@ To display a window in fullscreen mode or to go out of fullscreen mode again,
 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
 
@@ -153,6 +153,7 @@ to upgrade to a newer version of i3) you can use +$mod+Shift+r+.
 === Exiting i3
 
 To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+.
+By default, a dialog will ask you to confirm if you really want to quit.
 
 === Floating
 
@@ -366,7 +367,7 @@ bindcode [--release] [Modifiers+]keycode command
 *Examples*:
 --------------------------------
 # Fullscreen
-bindsym $mod+f fullscreen
+bindsym $mod+f fullscreen toggle
 
 # Restart
 bindsym $mod+Shift+r restart
@@ -393,6 +394,40 @@ umlauts or special characters 'and' having some comfortably reachable key
 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
@@ -1069,6 +1104,27 @@ bar {
 
 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,
@@ -1446,9 +1502,13 @@ Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+
 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*:
 --------------
@@ -1469,7 +1529,7 @@ bindsym $mod+x layout toggle
 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
index e759b4bd8c9fae4c17e85a0367febdbd4b7e3566..1dab64526165e73c1642172c459ac4e33f9e8d3a 100644 (file)
@@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-config-wizard
 
 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)
 
index a76c211e42504a83edf35c3a98186ec56576c9b5..c09d459d8d67cf0495e64955bec2c0d3e11ddafd 100644 (file)
@@ -43,6 +43,9 @@
 #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>
@@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap;
 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();
 
@@ -250,12 +255,24 @@ static char *next_state(const cmdp_token *token) {
              * 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));
@@ -642,8 +659,14 @@ static void handle_button_press(xcb_button_press_event_t *event) {
 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)
@@ -797,6 +820,16 @@ int main(int argc, char *argv[]) {
         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);
 
index d51f979a84d5c621445d17ccb32337663164e3bc..f3e204e8ecf2c60891e56f64d04096a827cef6fa 100644 (file)
@@ -1,8 +1,8 @@
 #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
index 104296cf6945db1267914e8378d26ec4094fad91..fcf9edc3d3138ac24a644cee60adb8f85b9e6bdf 100644 (file)
@@ -3,12 +3,12 @@
 #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;
index 9aac709b551783c0c3d515402aee1d4102606265..fc076d728f8a099f80d9875c721d86eb42cc342d 100644 (file)
@@ -3,15 +3,15 @@
 #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
 
index add4c8c1c305ee8cc635fc6d4ff0cb24fad1097f..c64fc72a09ae119b6cd3bc8615781c3595be1551 100755 (executable)
@@ -92,6 +92,7 @@ my %allowed_keys = map { ($_, 1) } qw(
     name
     geometry
     window_properties
+    mark
 );
 
 sub strip_containers {
index b3afceb7fa691ce3df5b8eeff19c97da48bdb4d3..4e7456b705129a9ea022d073efe51b6e21946abb 100755 (executable)
@@ -10,7 +10,7 @@
 
 # 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
index df463251dffea61737ba9511e607f9cebb972d3b..5ad786062405a9c4a571ca6a1f54cf60ee366ade 100755 (executable)
@@ -12,7 +12,7 @@
 # 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
index fddefae10048f0f546e4b0404e32caeae0c7d1f4..747a9280aa6e307f77d155aeaab74717e5f3f6a0 100755 (executable)
@@ -5,11 +5,11 @@
 # 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
index 30b3f6a86a0703d1143b1731e669e12637281d69..d7e96fe9c01d185da6d007d02d0aff5be9b42ffc 100644 (file)
--- a/i3.config
+++ b/i3.config
@@ -75,7 +75,7 @@ bindsym Mod1+h split h
 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
index 27398515b1de5757b0815ed16434c29ea2b59ef0..93528a54eeafd50f31983a9d98aebc2141fb9f90 100644 (file)
@@ -69,7 +69,7 @@ bindcode $mod+43 split h
 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
index d1e3dc2da6e59ca28686ddd93def9b6c26f9a192..d1340053449e6df8666591bf349b51a6bd93e45c 100644 (file)
@@ -5,3 +5,4 @@ Exec=i3
 TryExec=i3
 Type=Application
 X-LightDM-DesktopName=i3
+DesktopNames=i3
index 067802502f28ddc31aca70b1a39a6f21545e1440..53227a8e10fbb29d0468389bc1bb1a65c2fefa99 100644 (file)
@@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar
 
 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)
 
index d63780dc70c23732cfcab3b6851686c18fdae268..e8b6be0a57ec7e2788a9a68a9eb4ed161cfceb8a 100644 (file)
@@ -15,9 +15,9 @@
 
 typedef struct rect_t rect;
 
-struct ev_loopmain_loop;
-char            *statusline;
-char            *statusline_buffer;
+struct ev_loop *main_loop;
+char *statusline;
+char *statusline_buffer;
 
 struct rect_t {
     int x;
@@ -27,6 +27,7 @@ struct rect_t {
 };
 
 typedef enum {
+    /* First value to make it the default. */
     ALIGN_LEFT,
     ALIGN_CENTER,
     ALIGN_RIGHT
index a2c16887bde80497cea14aeba6c1f7129459787c..2c39930531aea2752df810a84d2ade9d1de3f640 100644 (file)
@@ -18,27 +18,32 @@ typedef enum {
 } 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;
index 5de23878d6a31fbfcc3e3d69549312ea120aa6e2..c357be89e8efc7087a57a8a9a1cbc7e25b495607 100644 (file)
@@ -29,7 +29,7 @@ void destroy_connection(void);
  * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
  *
  */
-int i3_send_msg(uint32_t type, const charpayload);
+int i3_send_msg(uint32_t type, const char *payload);
 
 /*
  * Subscribe to all the i3-events, we need
index 9f6add11cfcd9737c45d7b66d5bd510f4f8e3c8e..bd41e7767d247d3b75f1f014788f195008fdb4cd 100644 (file)
@@ -16,7 +16,7 @@
 typedef struct i3_output i3_output;
 
 SLIST_HEAD(outputs_head, i3_output);
-struct outputs_head *outputs;
+struct outputs_headoutputs;
 
 /*
  * Start parsing the received json-string
@@ -37,18 +37,19 @@ void init_outputs(void);
 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 */
 };
index 7a7e537e0027514cc07fc3fdd42e6ba4f3a288a9..1720ec3b860d68c2f4f5e86bcc7c2ce1ec8919a6 100644 (file)
@@ -14,9 +14,9 @@ typedef struct trayclient trayclient;
 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 */
 };
index 9ffd4467f56ceb3f5078e0432b0c569dd3565b60..cb0903c1e48fe5ef27b34310fa7478e8907730c4 100644 (file)
 
 /* 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)
index d3d23c8422decde8d6e332fe15bae67533c8f384..b9cefa8405bff15ac3a82ccb01d67f53c10c2eac 100644 (file)
@@ -30,15 +30,15 @@ void parse_workspaces_json(char *json);
 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 */
 };
index 2740f330b964b91af4e9be334ebf494517371568..32b52f4d1f5a3670bedd7197b446faf0b29cffb7 100644 (file)
 
 #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;
index 8645a664e19cd98d09c51096dc1772b8a0b6ee85..8d5e999ebf12b2de47294fde89dd4fef9c84563e 100644 (file)
@@ -188,12 +188,12 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
         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);
@@ -233,7 +233,7 @@ static int stdin_end_map(void *context) {
     /* 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);
@@ -243,7 +243,7 @@ static int stdin_end_map(void *context) {
 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);
     }
index 809b1ab4cfbec09840f919f3ae0bf42ab2e1efac..bb322619b427a3b33c1db31d22442ead6e145521 100644 (file)
@@ -112,6 +112,20 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
         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);
@@ -246,9 +260,9 @@ void parse_config_json(char *json) {
  *
  */
 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);
index 6ff839792be2b3367beee2a3630bea98ffd2d115..3eb50beb34bb817c231947f70b64f091d1e4b3d6 100644 (file)
@@ -67,7 +67,7 @@ void got_output_reply(char *reply) {
     reconfig_windows(false);
 
     i3_output *o_walk;
-    SLIST_FOREACH (o_walk, outputs, slist) {
+    SLIST_FOREACH(o_walk, outputs, slist) {
         kick_tray_clients(o_walk);
     }
 
index 5508a5a2340e252da481e3cc66f95d2fffbb7ded..02b718d3ae42418ddbcd8899f8aa312cfec5e278 100644 (file)
@@ -296,7 +296,7 @@ i3_output *get_output_by_name(char *name) {
     if (name == NULL) {
         return NULL;
     }
-    SLIST_FOREACH (walk, outputs, slist) {
+    SLIST_FOREACH(walk, outputs, slist) {
         if (!strcmp(walk->name, name)) {
             break;
         }
index 91307b0cb43d78cf69c482ced9c55ad88604b3af..e8184d400c4fc560987bb0d143f465fb0cc77761 100644 (file)
@@ -266,9 +266,9 @@ void free_workspaces(void) {
     }
     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);
             }
index 2d33919e785ab28318e97142d0362a2940084b7f..6f5f0ca2096c07c095ea769519c450b5649eaca3 100644 (file)
@@ -8,6 +8,7 @@
  *
  */
 #include <xcb/xcb.h>
+#include <xcb/xkb.h>
 #include <xcb/xproto.h>
 #include <xcb/xcb_aux.h>
 
@@ -63,8 +64,7 @@ static i3Font font;
 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
@@ -128,7 +128,7 @@ void refresh_statusline(void) {
     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;
 
@@ -156,7 +156,7 @@ void refresh_statusline(void) {
 
         /* 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;
     }
@@ -168,19 +168,38 @@ void refresh_statusline(void) {
         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. */
@@ -190,8 +209,8 @@ void refresh_statusline(void) {
             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) } });
         }
     }
 }
@@ -206,7 +225,7 @@ void hide_bars(void) {
     }
 
     i3_output *walk;
-    SLIST_FOREACH (walk, outputs, slist) {
+    SLIST_FOREACH(walk, outputs, slist) {
         if (!walk->active) {
             continue;
         }
@@ -231,7 +250,7 @@ void unhide_bars(void) {
 
     cont_child();
 
-    SLIST_FOREACH (walk, outputs, slist) {
+    SLIST_FOREACH(walk, outputs, slist) {
         if (walk->bar == XCB_NONE) {
             continue;
         }
@@ -303,7 +322,7 @@ void handle_button(xcb_button_press_event_t *event) {
     /* 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;
         }
@@ -315,7 +334,7 @@ void handle_button(xcb_button_press_event_t *event) {
     }
 
     /* 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;
         }
@@ -338,7 +357,7 @@ void handle_button(xcb_button_press_event_t *event) {
         /* 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));
@@ -351,7 +370,7 @@ void handle_button(xcb_button_press_event_t *event) {
         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;
 
@@ -370,6 +389,14 @@ void handle_button(xcb_button_press_event_t *event) {
              * 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;
 
@@ -380,6 +407,14 @@ void handle_button(xcb_button_press_event_t *event) {
              * 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;
 
@@ -387,7 +422,7 @@ void handle_button(xcb_button_press_event_t *event) {
             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;
@@ -398,7 +433,7 @@ void handle_button(xcb_button_press_event_t *event) {
             /* 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;
                 }
@@ -446,6 +481,39 @@ void handle_button(xcb_button_press_event_t *event) {
     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
@@ -455,12 +523,12 @@ void handle_button(xcb_button_press_event_t *event) {
 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++;
@@ -544,7 +612,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
 
             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) {
@@ -562,7 +630,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             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);
@@ -650,12 +718,12 @@ static void handle_destroy_notify(xcb_destroy_notify_event_t *event) {
     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;
 
@@ -679,12 +747,12 @@ static void handle_map_notify(xcb_map_notify_event_t *event) {
     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;
 
@@ -707,12 +775,12 @@ static void handle_unmap_notify(xcb_unmap_notify_event_t *event) {
     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;
 
@@ -739,11 +807,11 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
         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;
@@ -802,12 +870,12 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
 
     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++;
@@ -854,7 +922,66 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
     }
 
     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();
@@ -900,76 +1027,6 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
 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'.
@@ -1053,44 +1110,23 @@ char *init_xcb_early() {
  *
  */
 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;
 }
 
 /*
@@ -1098,13 +1134,14 @@ void register_xkb_keyevents() {
  *
  */
 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);
 }
 
 /*
@@ -1280,7 +1317,7 @@ void init_tray_colors(void) {
 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);
@@ -1435,7 +1472,7 @@ void reconfig_windows(bool redraw_bars) {
     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? */
@@ -1461,6 +1498,12 @@ void reconfig_windows(bool redraw_bars) {
              * */
             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;
             }
@@ -1597,7 +1640,7 @@ void reconfig_windows(bool redraw_bars) {
                  * 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();
@@ -1686,7 +1729,7 @@ void draw_bars(bool unhide) {
     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;
@@ -1716,29 +1759,29 @@ void draw_bars(bool unhide) {
              * 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;
@@ -1854,7 +1897,7 @@ void draw_bars(bool unhide) {
  */
 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;
         }
index 90b026165ec00d75da9dab709e45afacecbd03ed..2755d6cb83cf0b3148ae62266065113dc8e09da2 100644 (file)
@@ -16,7 +16,11 @@ xmacro(_NET_WM_STRUT_PARTIAL)
 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)
@@ -34,3 +38,4 @@ xmacro(I3_PID)
 xmacro(_NET_REQUEST_FRAME_EXTENTS)
 xmacro(_NET_FRAME_EXTENTS)
 xmacro(_MOTIF_WM_HINTS)
+xmacro(WM_CHANGE_STATE)
index e51f5e9924c039307fcf28532c1678c91b28bdcd..19345f8c82ad6c6144d8c7bbde53f849bceb86eb 100644 (file)
@@ -24,7 +24,7 @@ const char *DEFAULT_BINDING_MODE;
  *
  */
 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)
@@ -61,9 +61,15 @@ void switch_mode(const char *new_mode);
 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);
index cb687890008c868fa54d62d366f53a2f78deb922..780a9e8eb781893dd3fb69c8642783b98c73e676 100644 (file)
@@ -187,10 +187,10 @@ void cmd_focus_level(I3_CMD, char *level);
 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]]'.
index 6e531e9bd43985d13476e1d47ffc98f6634df6b8..cfa44dd5a5a03621fd04fd9f081018c297ba0b6d 100644 (file)
@@ -44,6 +44,14 @@ struct CommandResult {
     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
index b8fd60adc06865bda11e38f276bad15c1f603bb3..5d761f684ada5ebe7661fd6b34edbbae57426d45 100644 (file)
@@ -18,7 +18,6 @@
  */
 Con *con_new_skeleton(Con *parent, i3Window *window);
 
-
 /* A wrapper for con_new_skeleton, to retain the old con_new behaviour
  *
  */
@@ -173,6 +172,18 @@ void con_fix_percent(Con *con);
  */
 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.
index 71b37a80e2261f01682678c5cff2c1df9badff51..dea26d96344548a5573ebdb6de2813b4a1ccc32e 100644 (file)
@@ -241,10 +241,13 @@ struct Barconfig {
     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 {
@@ -258,8 +261,17 @@ struct Barconfig {
         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.
@@ -314,6 +326,20 @@ struct Barconfig {
     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.
  *
index 9dedad15a6b62a7e1bcb799d53d41e399d32676b..0f1a6620ef1200f47b2f8b0738a7ab4589a4b24e 100644 (file)
@@ -24,7 +24,7 @@ uint32_t modifiers_from_str(const char *str);
  * 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
@@ -61,10 +61,10 @@ CFGFUN(color_single, const char *colorclass, const char *color);
 CFGFUN(floating_modifier, const char *modifiers);
 CFGFUN(new_window, const char *windowtype, const char *border, const long width);
 CFGFUN(workspace, const char *workspace, const char *output);
-CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command);
+CFGFUN(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);
@@ -73,6 +73,8 @@ CFGFUN(bar_id, const char *bar_id);
 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);
index e18e5cf2a892180436c870b0261ab7035ec8026f..9fc3bf2ffb5ea63e2b3e22bef34a69f9472dd9d5 100644 (file)
@@ -33,7 +33,10 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context);
 
 /**
  * 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);
index 6ac228ec8dcdff252f928985c72e5e70f80af7fb..8f2c197df3e11e4a9234323a5fd8b3fd6049b289 100644 (file)
@@ -47,34 +47,42 @@ typedef struct Match Match;
 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)
 };
 
@@ -247,6 +255,11 @@ struct Binding {
         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 */
@@ -267,7 +280,6 @@ struct Binding {
      * This is an array of number_keycodes size. */
     xcb_keycode_t *translated_to;
 
-
     /** Command, like in command mode */
     char *command;
 
@@ -367,7 +379,9 @@ struct Window {
     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;
@@ -407,7 +421,9 @@ struct Match {
         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:
@@ -419,7 +435,9 @@ struct Match {
      *            (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;
 
@@ -450,10 +468,10 @@ struct Assignment {
      *
      */
     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 */
@@ -470,7 +488,9 @@ struct Assignment {
 };
 
 /** 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.
@@ -596,7 +616,7 @@ struct Con {
     TAILQ_ENTRY(Con) floating_windows;
 
     /** callbacks */
-    void(*on_remove_child)(Con *);
+    void (*on_remove_child)(Con *);
 
     enum {
         /* Not a scratchpad window. */
index 46d6c98575b574295a14783fbaf3e4e79e2f8408..3b5806285b4f66badec03fd7d2bb513601cb997d 100644 (file)
  */
 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.
  *
index fa3bdcc3fc132774f512497ad7ccfa89cf5f47c4..bea5f7a2885c5474afa5f037e140a4f3c4d15804 100644 (file)
 #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
@@ -164,8 +164,8 @@ typedef enum {
  *
  */
 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
index db7d06b5fec7e172b1b8a488ec0752c7faf0987c..82f6b982fc43474c5717e5c48748a04409e8102c 100644 (file)
@@ -13,6 +13,7 @@
 #include <xcb/randr.h>
 
 extern int randr_base;
+extern int xkb_base;
 
 /**
  * Adds the given sequence to the list of events which are ignored.
index 4ed0d8d54f1c62796503fa5f79f168a7fb2e3738..5ca875417bb163c386e5da8d462ffcc6d1a2c486 100644 (file)
@@ -13,6 +13,7 @@
 #include <sys/resource.h>
 
 #include <xcb/xcb_keysyms.h>
+#include <xcb/xkb.h>
 
 #include <X11/XKBlib.h>
 
@@ -42,7 +43,7 @@ extern xcb_key_symbols_t *keysyms;
 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;
index 94a39904a7d5bb1232142015cd6b3a7e0c915fa3..f1b50dec011b1e07548628e35b713013eb3d48dd 100644 (file)
@@ -17,7 +17,7 @@ typedef struct i3_ipc_header {
     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
@@ -25,31 +25,31 @@ typedef struct i3_ipc_header {
  */
 
 /** 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
@@ -57,46 +57,49 @@ typedef struct i3_ipc_header {
  */
 
 /** 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)
index 5fa4334a6620a62816aa2a9a5180ebc0a1feda85..96a60a1f29d27ea7f3b9ea719269ea269fef35f5 100644 (file)
 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;
 
 /*
@@ -42,13 +42,13 @@ typedef struct 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)
@@ -89,11 +89,17 @@ void ipc_shutdown(void);
 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,
@@ -105,3 +111,8 @@ void ipc_send_window_event(const char *property, Con *con);
  * 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);
index dc5d537f7fecef0289a2a104ec38f60dfd91bd92..7c0ead32be1df03170396c11ba09e606aefe3f2e 100644 (file)
@@ -72,17 +72,17 @@ struct Font {
  * 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
 
@@ -167,13 +167,13 @@ void i3string_free(i3String *str);
  * 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.
@@ -285,8 +285,8 @@ uint32_t aio_get_mod_mask_for(uint32_t keysym, xcb_key_symbols_t *symbols);
  *
  */
 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,
@@ -338,14 +338,14 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background
  *
  */
 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
index 2400092ba2b64244092e0aba2e5a0eca1f4e3da0..a5086dbee5ae78fa3327017763882d3bbdac39e5 100644 (file)
@@ -78,14 +78,14 @@ void set_verbosity(bool _verbose);
  *
  */
 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,
@@ -93,7 +93,7 @@ void errorlog(char *fmt, ...)
  *
  */
 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.
index 5c8a7d20f2ad7ce2d0d55271548af987a945b909..939665ec41f09b73cb2be7cce20dc598a5dfb5fe 100644 (file)
@@ -10,8 +10,8 @@
 #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);
index 2307149b6c2529b394eebdf0a3c127b8049f8ea2..9fb9ba5e7160f24619cec4946004e9b0bf9acbcc 100644 (file)
 /*
  * 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)
index 8746a3ad9bc2c41364911ff36dfd935369cc194c..543580ecca73156844b92d7ed1d454ef73e5870f 100644 (file)
@@ -68,15 +68,15 @@ extern "C" {
 
 #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
@@ -89,14 +89,14 @@ extern "C" {
 
   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
@@ -242,7 +242,7 @@ int sd_notify(int unset_environment, const char *state) _sd_hidden_;
 
   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
index fb017103bbb68c1c31cbc7a8bbcea9789246d2f8..2f28baa772f9000918c9a6e341579d0f6a94c056 100644 (file)
@@ -21,8 +21,8 @@
  * (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.
@@ -49,7 +49,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata);
  *
  */
 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
index 514a47c2a76cd6f9dd72e19c08f8ee6a924b2296..dec681168b49ee851995dd3d88444405d598e257 100644 (file)
 #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);
index 463ccf19a8c1723d4704e96896b3610fbaa1bc63..9ee6f156674976275f37220d46e4aa594b8459b8 100644 (file)
@@ -68,25 +68,25 @@ void workspace_show_by_name(const char *num);
  * Returns the next workspace.
  *
  */
-Conworkspace_next(void);
+Con *workspace_next(void);
 
 /**
  * Returns the previous workspace.
  *
  */
-Conworkspace_prev(void);
+Con *workspace_prev(void);
 
 /**
  * Returns the next workspace on the same output
  *
  */
-Conworkspace_next_on_output(void);
+Con *workspace_next_on_output(void);
 
 /**
  * Returns the previous workspace on the same output
  *
  */
-Conworkspace_prev_on_output(void);
+Con *workspace_prev_on_output(void);
 
 /**
  * Focuses the previously focused workspace.
@@ -100,7 +100,6 @@ void workspace_back_and_forth(void);
  */
 Con *workspace_back_and_forth_get(void);
 
-
 #if 0
 /**
  * Assigns the given workspace to the given screen by correctly updating its
index 4df7f6397765b261a6844a105996aa94ddb511f3..9f4ea91fd8f29733ff4549ad2d8fcaa9af386ece 100644 (file)
 #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
 
@@ -65,7 +65,7 @@ extern unsigned int xcb_numlock_mask;
  *
  */
 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
@@ -108,7 +108,6 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window);
  */
 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);
 
 /**
index 747e0ca046b97a938392abda2b0f69fcaa42cc06..e8422aabd2d051294d3fedcd007b94658d0722b5 100644 (file)
@@ -14,8 +14,8 @@
 #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)
index 090a65600004dbdd83b80edea8379de4419b5b95..37d5b21502e5882df374933070497d5e73ae0484 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * 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>
 
@@ -12,5 +19,13 @@ extern xcb_screen_t *root_screen;
 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);
 }
index fc868e60bb95eb0e413ac93079df41c4051cec90..7670335fa805fb62f0869fae3b181c000d8a287d 100644 (file)
@@ -167,6 +167,12 @@ i3Font load_font(const char *pattern, const bool fallback) {
     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:"))) {
index 49d8831c770eeac221343e1fe99b4b7a34a6fd1c..d1ad5198edaa8bd9fa30f63f3056d5f93a34149e 100644 (file)
@@ -22,6 +22,7 @@ is appropriate for the distribution.
 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
index ea232fcf0069a87e99beea7bbb43a6e1d3da9067..203b42eea26e57ab7379fd180d50c8a4651b6146 100644 (file)
@@ -230,7 +230,7 @@ bindsym Mod1+h split h
 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
index e3da62c91aae2b2ca39c6d19f6163a06b91b3f6b..82348df79c9c47d6b5dc9f352eceaaabb4b5100f 100644 (file)
@@ -156,12 +156,28 @@ state KILL:
   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:
index f1021b26f9cad6d151c29a26d94da7cce60c10e7..95c206fe4ae293166e2db6426f8f6c906f838348 100644 (file)
@@ -278,6 +278,8 @@ state FONT:
 state BINDING:
   release = '--release'
       ->
+  whole_window = '--whole-window'
+      ->
   modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
       ->
   '+'
@@ -288,8 +290,10 @@ state BINDING:
 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
@@ -333,8 +337,10 @@ state MODE_BINDING:
 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)
@@ -358,6 +364,8 @@ state BAR:
   '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
@@ -403,6 +411,14 @@ state BAR_MODIFIER:
   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
@@ -412,7 +428,7 @@ state BAR_OUTPUT:
       -> call cfg_bar_output($output); BAR
 
 state BAR_TRAY_OUTPUT:
-  output = string
+  output = word
       -> call cfg_bar_tray_output($output); BAR
 
 state BAR_FONT:
index 2545bde0b881623db96c68716ef9c47855b27554..96834f648539d4bcb06c1778a4b6be65117845f4 100644 (file)
@@ -23,7 +23,7 @@ void run_assignments(i3Window *window) {
 
     /* 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;
 
@@ -72,7 +72,7 @@ void run_assignments(i3Window *window) {
 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;
index b17ec13cd957ddf6488083eda306cade20d2a92d..8f9767e69943e26a6df3b9cb091cfd38e5f47bf6 100644 (file)
@@ -8,6 +8,8 @@
  */
 #include "all.h"
 
+#include <xkbcommon/xkbcommon.h>
+
 pid_t command_error_nagbar_pid = -1;
 
 /*
@@ -25,7 +27,7 @@ static struct Mode *mode_from_name(const char *name) {
     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;
     }
@@ -47,10 +49,11 @@ static struct Mode *mode_from_name(const char *name) {
  *
  */
 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
@@ -104,7 +107,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
  */
 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))
@@ -133,7 +136,7 @@ static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_
     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)
@@ -141,7 +144,7 @@ static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_
         }
     }
 
-    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) */
@@ -248,7 +251,7 @@ void translate_keysyms(void) {
     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;
@@ -263,8 +266,8 @@ void translate_keysyms(void) {
             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;
@@ -304,7 +307,7 @@ void switch_mode(const char *new_mode) {
 
     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;
 
@@ -334,8 +337,8 @@ void switch_mode(const char *new_mode) {
  */
 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)
@@ -379,18 +382,57 @@ 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().
+ * 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();
@@ -414,7 +456,8 @@ CommandResult *run_binding(Binding *bind) {
         free(pageraction);
     }
 
-    /* TODO: emit event for running a binding */
+    ipc_send_binding_event("run", bind_cp);
+    binding_free(bind_cp);
 
     return result;
 }
index 2cf02178a0b160cace1058c751016dc6244c80cd..86a63ea68ce4cce090bb45f636e52b94d0098a2f 100644 (file)
@@ -177,6 +177,29 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     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. */
@@ -191,7 +214,6 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
 
     if (ws != focused_workspace)
         workspace_show(ws);
-    focused_id = XCB_NONE;
 
     /* get the floating con */
     Con *floatingcon = con_inside_floating(con);
@@ -300,6 +322,7 @@ done:
     xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
     xcb_flush(conn);
     tree_render();
+
     return 0;
 }
 
@@ -330,7 +353,7 @@ int handle_button_press(xcb_button_press_event_t *event) {
          * 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;
@@ -358,7 +381,7 @@ int handle_button_press(xcb_button_press_event_t *event) {
 
     /* 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;
 
index 73db2802978e738b9e13071bd4ea0173be9c5f29..498c25c85b7aa5a33578219bf637e30cd8c877c8 100644 (file)
@@ -273,7 +273,7 @@ void cmd_criteria_init(I3_CMD) {
     }
     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);
@@ -324,7 +324,7 @@ void cmd_criteria_match_windows(I3_CMD) {
         }
     }
 
-    TAILQ_FOREACH (current, &owindows, owindows) {
+    TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
     }
 }
@@ -448,7 +448,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
         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);
     }
@@ -475,7 +475,7 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
 
     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);
     }
@@ -513,13 +513,33 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 
     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);
     }
@@ -550,21 +570,18 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
     /* 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);
@@ -574,7 +591,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
 
     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);
     }
@@ -728,7 +745,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char
 
     /* 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;
@@ -740,7 +757,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char
     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)) {
@@ -758,7 +775,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char
     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;
@@ -786,7 +803,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
     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);
@@ -823,7 +840,7 @@ void cmd_border(I3_CMD, char *border_style_str, char *border_width) {
 
     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;
@@ -927,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) {
     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;
 }
@@ -941,6 +958,12 @@ void cmd_workspace(I3_CMD, char *which) {
 
     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)
@@ -969,22 +992,24 @@ void cmd_workspace(I3_CMD, char *which) {
 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);
@@ -1007,6 +1032,12 @@ void cmd_workspace_number(I3_CMD, char *which) {
  *
  */
 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;
@@ -1025,10 +1056,39 @@ void cmd_workspace_name(I3_CMD, char *name) {
         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
@@ -1043,7 +1103,7 @@ void cmd_mark(I3_CMD, char *mark) {
     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);
     }
@@ -1053,7 +1113,7 @@ void cmd_mark(I3_CMD, char *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);
     }
@@ -1070,13 +1130,13 @@ void cmd_mark(I3_CMD, char *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);
         }
@@ -1116,8 +1176,8 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
     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);
 
@@ -1147,7 +1207,7 @@ void cmd_move_con_to_output(I3_CMD, char *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);
     }
@@ -1168,7 +1228,7 @@ void cmd_floating(I3_CMD, char *floating_mode) {
 
     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");
@@ -1198,7 +1258,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
     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");
@@ -1228,15 +1288,15 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
             /* 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;
 
@@ -1253,7 +1313,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
                 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");
 
@@ -1271,10 +1331,10 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
 
         /* 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);
@@ -1285,7 +1345,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
          * 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;
 
@@ -1313,7 +1373,7 @@ void cmd_split(I3_CMD, char *direction) {
     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));
         }
@@ -1350,7 +1410,7 @@ void cmd_kill(I3_CMD, char *kill_mode_str) {
     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);
         }
@@ -1418,7 +1478,7 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) {
             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;
@@ -1480,7 +1540,7 @@ void cmd_focus(I3_CMD) {
     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. */
@@ -1538,20 +1598,26 @@ void cmd_focus(I3_CMD) {
 }
 
 /*
- * 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;
@@ -1567,26 +1633,36 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) {
     // 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);
 }
@@ -1622,7 +1698,7 @@ void cmd_layout(I3_CMD, char *layout_str) {
     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);
         }
@@ -1649,7 +1725,7 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) {
     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);
         }
@@ -1685,7 +1761,7 @@ void cmd_reload(I3_CMD) {
     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();
 
@@ -1745,8 +1821,8 @@ void cmd_focus_output(I3_CMD, char *name) {
     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);
@@ -1779,34 +1855,46 @@ void cmd_focus_output(I3_CMD, char *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);
 }
 
 /*
@@ -1856,7 +1944,7 @@ void cmd_move_scratchpad(I3_CMD) {
 
     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);
     }
@@ -1877,7 +1965,7 @@ void cmd_scratchpad_show(I3_CMD) {
     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);
         }
@@ -1906,9 +1994,9 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
 
     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);
     }
@@ -1922,9 +2010,9 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
     }
 
     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
@@ -1937,15 +2025,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
     /* 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. */
@@ -1959,7 +2040,10 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
     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();
 }
 
 /*
@@ -1984,7 +2068,7 @@ bool cmd_bar_mode(char *bar_mode, char *bar_id) {
 
     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;
 
@@ -2027,7 +2111,7 @@ bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) {
 
     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;
 
index 31729676a383f86b95e53863f4e02e6569b148b9..f325a048afc6ebf6859ee81347fafd7c8dc4de28 100644 (file)
@@ -204,6 +204,61 @@ static void next_state(const cmdp_token *token) {
     }
 }
 
+/*
+ * 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
@@ -262,48 +317,8 @@ CommandResult *parse_command(const char *input, yajl_gen gen) {
 
             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
index 42eb74f7612c761761972f8ef75b7bd8a620480f..38ea0585e17f0887e178d0304f04ea6153a62e85 100644 (file)
--- a/src/con.c
+++ b/src/con.c
  *
  */
 #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);
 
@@ -59,16 +48,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) {
         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));
@@ -142,7 +122,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
     } 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;
@@ -231,6 +211,7 @@ void con_focus(Con *con) {
         con->urgent = false;
         con_update_parents_urgency(con);
         workspace_update_urgent_flag(con_get_workspace(con));
+        ipc_send_window_event("urgent", con);
     }
 }
 
@@ -388,13 +369,13 @@ Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) {
         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);
@@ -460,9 +441,9 @@ bool con_inside_focused(Con *con) {
  */
 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;
 }
 
@@ -473,9 +454,9 @@ Con *con_by_window_id(xcb_window_t window) {
  */
 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;
 }
 
@@ -490,8 +471,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
     //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)
@@ -503,8 +484,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) {
             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)
@@ -527,8 +508,8 @@ int con_num_children(Con *con) {
     Con *child;
     int children = 0;
 
-    TAILQ_FOREACH (child, &(con->nodes_head), nodes)
-        children++;
+    TAILQ_FOREACH(child, &(con->nodes_head), nodes)
+    children++;
 
     return children;
 }
@@ -547,7 +528,7 @@ void con_fix_percent(Con *con) {
     // 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;
@@ -557,7 +538,7 @@ void con_fix_percent(Con *con) {
     // 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);
@@ -570,11 +551,11 @@ void con_fix_percent(Con *con) {
     // 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;
     }
 }
 
@@ -585,37 +566,27 @@ void con_fix_percent(Con *con) {
  *
  */
 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);
 
@@ -638,6 +609,79 @@ void con_toggle_fullscreen(Con *con, int 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.
@@ -807,7 +851,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
 
     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;
 
@@ -832,6 +876,8 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
     }
 
     CALL(parent, on_remove_child);
+
+    ipc_send_window_event("move", con);
 }
 
 /*
@@ -1003,7 +1049,7 @@ Con *con_descend_tiling_focused(Con *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;
 
@@ -1038,7 +1084,7 @@ Con *con_descend_direction(Con *con, direction_t direction) {
             /* Wrong orientation. We use the last focused con. Within that con,
              * we recurse to chose the left/right con or at least the last
              * focused one. */
-            TAILQ_FOREACH (current, &(con->focus_head), focused) {
+            TAILQ_FOREACH(current, &(con->focus_head), focused) {
                 if (current->type != CT_FLOATING_CON) {
                     most = current;
                     break;
@@ -1063,7 +1109,7 @@ Con *con_descend_direction(Con *con, direction_t direction) {
             /* Wrong orientation. We use the last focused con. Within that con,
              * we recurse to chose the top/bottom con or at least the last
              * focused one. */
-            TAILQ_FOREACH (current, &(con->focus_head), focused) {
+            TAILQ_FOREACH(current, &(con->focus_head), focused) {
                 if (current->type != CT_FLOATING_CON) {
                     most = current;
                     break;
@@ -1390,8 +1436,15 @@ static void con_on_remove_child(Con *con) {
     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;
     }
@@ -1432,7 +1485,7 @@ Rect con_minimum_size(Con *con) {
     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);
@@ -1449,7 +1502,7 @@ Rect con_minimum_size(Con *con) {
     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;
@@ -1544,7 +1597,7 @@ bool con_has_urgent_child(Con *con) {
 
     /* 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;
     }
@@ -1603,14 +1656,16 @@ void con_set_urgency(Con *con, bool urgent) {
 
     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);
+    }
 }
 
 /*
@@ -1657,7 +1712,7 @@ char *con_get_tree_representation(Con *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;
index 7e88a9ef6830f8a8e8f7b1a768bc83337913582e..b41f0e1b7eb2f909aaf7afe9141ddf8c586bc71f 100644 (file)
@@ -11,9 +11,7 @@
  *
  */
 #include "all.h"
-
-/* We need Xlib for XStringToKeysym */
-#include <X11/Xlib.h>
+#include <xkbcommon/xkbcommon.h>
 
 char *current_configpath = NULL;
 Config config;
@@ -36,7 +34,7 @@ void ungrab_all_keys(xcb_connection_t *conn) {
  */
 void update_barconfig() {
     Barconfig *current;
-    TAILQ_FOREACH (current, &barconfigs, configs) {
+    TAILQ_FOREACH(current, &barconfigs, configs) {
         ipc_send_barconfig_update_event(current);
     }
 }
@@ -114,12 +112,19 @@ static char *get_config_path(const char *override_configpath) {
  * 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);
 }
 
 /*
@@ -142,9 +147,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
             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);
@@ -204,8 +207,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
 
         /* 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();
@@ -262,7 +265,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     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();
index e45db0e277fdfc0e9120f3f1ef1a72ec4586af35..e8fdfe77ec5a68b40bbb3e505c60aeae055377e3 100644 (file)
@@ -171,8 +171,8 @@ CFGFUN(font, const char *font) {
     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);
 }
 
 /*******************************************************************************
@@ -181,8 +181,8 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co
 
 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) {
@@ -271,13 +271,15 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width)
     }
 
     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);
     }
 }
 
@@ -334,7 +336,7 @@ CFGFUN(workspace, const char *workspace, const char *output) {
      * 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);
@@ -459,6 +461,16 @@ CFGFUN(bar_modifier, const char *modifier) {
         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);
 }
index 21cd06fb5a6e5d12b85e04249612a3b284f918ce..24cebcece9707755087d83928a49030629ecea63 100644 (file)
@@ -840,7 +840,7 @@ static char *migrate_config(char *input, off_t size) {
  * 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;
@@ -917,7 +917,7 @@ void parse_file(const char *f) {
      * 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;
@@ -937,11 +937,11 @@ void parse_file(const char *f) {
     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) {
@@ -1000,7 +1000,7 @@ void parse_file(const char *f) {
 
     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");
@@ -1030,6 +1030,8 @@ void parse_file(const char *f) {
         free(pageraction);
     }
 
+    bool has_errors = context->has_errors;
+
     FREE(context->line_copy);
     free(context);
     free(new);
@@ -1042,6 +1044,8 @@ void parse_file(const char *f) {
         SLIST_REMOVE_HEAD(&variables, variables);
         FREE(current);
     }
+
+    return !has_errors;
 }
 
 #endif
index 986523dd3e9db2aef47b05319ba282a40535f596..0746a5e189e6e710e965060ef43d1ad1ffcaeb21 100644 (file)
@@ -24,9 +24,9 @@ void ewmh_update_current_desktop(void) {
     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;
 
@@ -40,6 +40,102 @@ 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) {
+    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.
  *
@@ -138,5 +234,5 @@ void ewmh_setup_hints(void) {
     /* 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);
 }
index 548c21c6b871d07fae2a61127c442fac9020f872..4f2740992b9540639b0fd5009a1a95382487129e 100644 (file)
@@ -20,9 +20,9 @@ static int num_screens;
  */
 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;
 }
index 7a8df508cb83f17c5e537aaade8e0010928b3a94..8a2fde2c745c96e7d479dca5dafa35e6244309f6 100644 (file)
@@ -21,7 +21,7 @@ static Rect total_outputs_dimensions(void) {
     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;
     }
@@ -205,7 +205,7 @@ void floating_enable(Con *con, bool automatic) {
     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);
@@ -298,16 +298,22 @@ void floating_enable(Con *con, bool automatic) {
 
     /* 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) {
@@ -351,6 +357,8 @@ void floating_disable(Con *con, bool automatic) {
 
     if (set_focus)
         con_focus(con);
+
+    ipc_send_window_event("floating", con);
 }
 
 /*
index c217cbbcc80916eec4a1490b8ed4df73e3920921..569a8ec3deb1b36a0875f255a5062ca9c5c7cc00 100644 (file)
@@ -21,6 +21,8 @@
 #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
@@ -62,7 +64,7 @@ bool event_is_ignored(const int sequence, const int response_type) {
             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;
 
@@ -163,12 +165,12 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) {
     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
@@ -231,7 +233,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
 
     /* 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;
 
@@ -282,7 +284,6 @@ static void handle_map_request(xcb_map_request_event_t *event) {
     add_ignore_event(event->sequence, -1);
 
     manage_window(event->window, cookie, false);
-    x_push_changes(croot);
     return;
 }
 
@@ -487,7 +488,6 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
 
     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
@@ -791,6 +791,71 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             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;
@@ -1045,6 +1110,30 @@ static void handle_focus_in(xcb_focus_in_event_t *event) {
     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);
@@ -1062,7 +1151,8 @@ static struct property_handler_t property_handlers[] = {
     {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))
 
 /*
@@ -1080,6 +1170,7 @@ void property_handlers_init(void) {
     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) {
@@ -1115,12 +1206,50 @@ 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:
index 0325d82a95ee765ea17ab5bc846857c96f2e4353..8c7634643ab7cc10292abda8869e24dfd435556d 100644 (file)
--- a/src/i3.mk
+++ b/src/i3.mk
@@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3
 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.
index 66c28bdc730d83ab063cb106144978807799c713..7dbb6632cb88191f77a86cfe398efaeff1c57297 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -74,7 +74,7 @@ bool mkdirp(const char *path) {
  */
 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++) {
@@ -151,6 +151,60 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) {
     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");
@@ -293,14 +347,17 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     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");
@@ -337,6 +394,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
             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);
     }
 
@@ -344,7 +406,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     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);
         }
     }
@@ -352,14 +414,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool 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);
@@ -386,7 +448,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     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");
@@ -512,6 +574,16 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
             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");
@@ -591,19 +663,16 @@ IPC_HANDLER(get_workspaces) {
     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);
@@ -656,7 +725,7 @@ IPC_HANDLER(get_outputs) {
     y(array_open);
 
     Output *output;
-    TAILQ_FOREACH (output, &outputs, outputs) {
+    TAILQ_FOREACH(output, &outputs, outputs) {
         y(map_open);
 
         ystr("name");
@@ -710,9 +779,9 @@ IPC_HANDLER(get_marks) {
     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);
 
@@ -766,7 +835,7 @@ IPC_HANDLER(get_bar_config) {
     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);
@@ -786,7 +855,7 @@ IPC_HANDLER(get_bar_config) {
     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;
 
@@ -852,7 +921,7 @@ IPC_HANDLER(subscribe) {
     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;
 
@@ -932,7 +1001,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
 
         /* 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;
 
@@ -1051,21 +1120,23 @@ int ipc_create_socket(const char *filename) {
 }
 
 /*
- * 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)
@@ -1075,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
 
     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, "");
 }
 
 /**
@@ -1132,3 +1216,33 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig) {
     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, "");
+}
index 56021da024b793ac95d1648b57774bb298b3d51e..95e5079eef44101e8a5cf97c1b88a00a4b4d84fe 100644 (file)
@@ -30,7 +30,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
     if (bind == NULL)
         return;
 
-    CommandResult *result = run_binding(bind);
+    CommandResult *result = run_binding(bind, NULL);
 
     if (result->needs_tree_render)
         tree_render();
index 5a66828d26eaab0233a5d3e7c6006a947b52dd97..ccd71c3736db139623305628fdc40694847ba4b1 100644 (file)
@@ -24,6 +24,7 @@ static Con *json_node;
 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;
@@ -47,7 +48,7 @@ static int json_start_map(void *ctx) {
         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);
@@ -68,7 +69,7 @@ static int json_start_map(void *ctx) {
 
 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");
@@ -98,26 +99,21 @@ static int json_end_map(void *ctx) {
              * 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");
@@ -126,12 +122,11 @@ static int json_end_map(void *ctx) {
         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;
 }
 
@@ -146,10 +141,10 @@ static int json_end_array(void *ctx) {
     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);
@@ -180,6 +175,9 @@ static int json_key(void *ctx, const unsigned char *val, size_t len) {
     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;
 
@@ -553,6 +551,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) {
     to_focus = NULL;
     parsing_swallows = false;
     parsing_rect = false;
+    parsing_deco_rect = false;
     parsing_window_rect = false;
     parsing_geometry = false;
     parsing_focus = false;
index 66090dae207c021ec70d0e883044c1d8faa9ad4c..b696e031a2dfc60c34cc8c4eda5b4af17575d26f 100644 (file)
@@ -36,10 +36,6 @@ int listen_fds;
  * 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;
@@ -70,9 +66,6 @@ struct ev_loop *main_loop;
 
 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;
 
@@ -94,12 +87,6 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment
 
 /* 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.
@@ -166,73 +153,6 @@ void main_set_x11_cb(bool enable) {
     }
 }
 
-/*
- * 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.
  *
@@ -276,6 +196,7 @@ int main(int argc, char *argv[]) {
     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'},
@@ -441,10 +362,14 @@ int main(int argc, char *argv[]) {
         }
     }
 
+    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. */
@@ -567,10 +492,6 @@ int main(int argc, char *argv[]) {
     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 */
@@ -597,21 +518,7 @@ int main(int argc, char *argv[]) {
 #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). */
@@ -620,27 +527,22 @@ int main(int argc, char *argv[]) {
     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();
@@ -769,25 +671,19 @@ int main(int argc, char *argv[]) {
     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);
 
@@ -888,7 +784,7 @@ int main(int argc, char *argv[]) {
     /* 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);
         }
@@ -896,14 +792,14 @@ int main(int argc, char *argv[]) {
 
     /* 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",
index a7002bf3abc33c3c4aa1baa559859bb211a45a0f..b7ea5e69e1aa3ce08e8ba864ef97fb368148d51a 100644 (file)
@@ -56,16 +56,16 @@ void restore_geometry(void) {
     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 */
@@ -436,11 +436,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     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) {
@@ -450,12 +445,18 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
         }
     }
 
-    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;
@@ -511,7 +512,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
 
     /* 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);
     }
index a810068ea5a54e857909860cb1c37fcef4bcc0b4..dc4d422f27550d9754ea791977d00c899148c0c6 100644 (file)
@@ -136,7 +136,7 @@ bool match_matches_window(Match *match, i3Window *window) {
             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;
@@ -151,7 +151,7 @@ bool match_matches_window(Match *match, i3Window *window) {
             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, < )) {
index 42510d520823ec98e4dfcbb0edd116bb282ca444..1999a1fe901853d06924df49f5c026cc4f7977e2 100644 (file)
@@ -128,22 +128,21 @@ static void move_to_output_directed(Con *con, direction_t direction) {
 
     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");
@@ -206,6 +205,7 @@ void tree_move(int direction) {
                 TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
 
                 DLOG("Swapped.\n");
+                ipc_send_window_event("move", con);
                 return;
             }
 
@@ -213,6 +213,7 @@ void tree_move(int direction) {
                 /*  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;
             }
 
@@ -264,4 +265,5 @@ end:
     FREE(con->deco_render_params);
 
     tree_flatten(croot);
+    ipc_send_window_event("move", con);
 }
index b037335a02621d3fa4ae9eefa187f7925635ec6b..6499c65df77d46dea56fb8c75b432ebedb1dad61 100644 (file)
@@ -18,9 +18,9 @@
 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;
 }
index 0755c0d73fda3a640b8842e3fe57d1bb241171e2..a4a0f6fd6d36fe7c626d836e7bc23d8c34573cdb 100644 (file)
@@ -37,9 +37,9 @@ static bool randr_disabled = false;
  */
 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;
 }
@@ -50,10 +50,10 @@ static Output *get_output_by_id(xcb_randr_output_t id) {
  */
 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;
 }
@@ -65,9 +65,9 @@ Output *get_output_by_name(const char *name) {
 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");
 }
@@ -79,7 +79,7 @@ Output *get_first_output(void) {
  */
 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",
@@ -104,7 +104,7 @@ bool contained_by_output(Rect rect) {
     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",
@@ -163,7 +163,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far
          *other;
     Output *output,
         *best = NULL;
-    TAILQ_FOREACH (output, &outputs, outputs) {
+    TAILQ_FOREACH(output, &outputs, outputs) {
         if (!output->active)
             continue;
 
@@ -256,7 +256,7 @@ void output_init_con(Output *output) {
 
     /* 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;
 
@@ -355,15 +355,15 @@ void output_init_con(Output *output) {
 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;
 
@@ -401,10 +401,10 @@ void init_ws_for_output(Output *output, Con *content) {
         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);
@@ -436,7 +436,7 @@ void init_ws_for_output(Output *output, Con *content) {
     }
 
     /* 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;
 
@@ -478,8 +478,8 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
 
     /* 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));
         }
     }
@@ -488,7 +488,7 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
      * 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)
@@ -640,7 +640,7 @@ void randr_query_outputs(void) {
 
     /* 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",
@@ -681,7 +681,7 @@ void randr_query_outputs(void) {
      * 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);
@@ -691,7 +691,7 @@ void randr_query_outputs(void) {
 
     /* 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);
@@ -731,8 +731,8 @@ void randr_query_outputs(void) {
                     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");
@@ -745,7 +745,7 @@ void randr_query_outputs(void) {
 
                 /* 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);
@@ -788,7 +788,7 @@ void randr_query_outputs(void) {
     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);
@@ -799,7 +799,7 @@ void randr_query_outputs(void) {
     }
 
     /* Focus the primary screen, if possible */
-    TAILQ_FOREACH (output, &outputs, outputs) {
+    TAILQ_FOREACH(output, &outputs, outputs) {
         if (!output->primary || !output->con)
             continue;
 
index 2f39b08282e94d9df1fb50f9d88c1c63cf7d20ab..7f92d4d441071601c7871e19b7f57992fec390d9 100644 (file)
@@ -41,7 +41,7 @@ static void render_l_output(Con *con) {
     /* 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");
@@ -77,19 +77,19 @@ static void render_l_output(Con *con) {
 
     /* 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;
@@ -232,7 +232,7 @@ void render_con(Con *con, bool render_fullscreen) {
         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;
         }
@@ -256,7 +256,7 @@ void render_con(Con *con, bool render_fullscreen) {
     } 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);
             }
         }
@@ -266,7 +266,7 @@ void render_con(Con *con, bool render_fullscreen) {
          * 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 */
@@ -278,7 +278,7 @@ void render_con(Con *con, bool render_fullscreen) {
             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). */
@@ -298,6 +298,8 @@ void render_con(Con *con, bool render_fullscreen) {
                     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;
@@ -331,7 +333,7 @@ void render_con(Con *con, bool render_fullscreen) {
     } 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 */
@@ -438,8 +440,8 @@ void render_con(Con *con, bool render_fullscreen) {
 
         /* 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
index ba82f76f26275dc35357c68f5be9873ace39211c..3a657c90bf4123de34315aa6f66aab4c76d6997f 100644 (file)
@@ -137,7 +137,7 @@ static void update_placeholder_contents(placeholder_state *state) {
 
     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)                                                                                                                        \
@@ -197,8 +197,9 @@ static void open_placeholder_window(Con *con) {
         /* 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);
 
@@ -222,10 +223,10 @@ static void open_placeholder_window(Con *con) {
     }
 
     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);
     }
 }
@@ -239,10 +240,10 @@ static void open_placeholder_window(Con *con) {
  */
 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);
     }
 
@@ -258,7 +259,7 @@ void restore_open_placeholder_windows(Con *parent) {
  */
 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;
 
@@ -277,7 +278,7 @@ bool restore_kill_placeholder(xcb_window_t placeholder) {
 
 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;
 
@@ -305,7 +306,7 @@ static void expose_event(xcb_expose_event_t *event) {
  */
 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;
 
index 8ca81eae01f18f8308fd98f87e3f2aeee8140285..3b7c2a72b009131e616cd57cf0665af3499c46bc 100644 (file)
@@ -116,7 +116,7 @@ void scratchpad_show(Con *con) {
      * 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)) {
@@ -134,7 +134,7 @@ void scratchpad_show(Con *con) {
      * 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 &&
@@ -257,7 +257,7 @@ void scratchpad_fix_resolution(void) {
     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",
@@ -288,7 +288,7 @@ void scratchpad_fix_resolution(void) {
 
     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);
     }
 }
index 7733386395a5b87a92bd4197b2e13e0af2b2c371..8b5bfed4c015745390dd4a645645dfc87b4619d4 100644 (file)
@@ -253,7 +253,7 @@ static void open_popups() {
     /* 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);
index a5d8e117340300c43c71aa9ed5dba57ec6257a31..ebe8c1d9439601a426bee70aa512d95c70a7a44f 100644 (file)
@@ -37,7 +37,7 @@ static void startup_timeout(EV_P_ ev_timer *w, int revents) {
     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;
 
@@ -123,8 +123,8 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) {
  * 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.
@@ -219,7 +219,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) {
     /* 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;
 
@@ -309,7 +309,7 @@ struct Startup_Sequence *startup_sequence_get(i3Window *cwindow,
     }
 
     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;
 
index da73e14d76fc12b996a76ed3283cdb7410e62f28..a6b15122b42c55dd58dd261dc3646adaec67ae6d 100644 (file)
@@ -167,9 +167,9 @@ Con *tree_open_con(Con *con, i3Window *window) {
 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;
 }
@@ -255,6 +255,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
              * 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);
@@ -489,13 +490,13 @@ static void mark_unmapped(Con *con) {
     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);
     }
 }
 
@@ -581,6 +582,13 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
             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));
@@ -591,33 +599,38 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
     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
index 538f4629fd9f86fc109e71f8f256ef5378bdbe8a..e406752a54c63fa5bd2f2b77ddb96419cef407c3 100644 (file)
@@ -125,7 +125,8 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bo
  */
 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;
     }
@@ -149,7 +150,8 @@ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) {
  */
 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;
     }
@@ -160,7 +162,7 @@ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop)
         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;
 
index 739b0e0a9c14d82b74cca40bc2a1d2de59521da6..a30566339f246b40e10de9932d1eac7f82a1ab6e 100644 (file)
@@ -11,6 +11,7 @@
  *
  */
 #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. */
@@ -44,8 +45,8 @@ static void _workspace_apply_default_orientation(Con *ws) {
 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);
@@ -59,7 +60,7 @@ Con *workspace_get(const char *num, bool *created) {
          * -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));
@@ -91,7 +92,10 @@ Con *workspace_get(const char *num, bool *created) {
 
         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) {
@@ -117,13 +121,13 @@ Con *create_workspace_on_output(Output *output, Con *content) {
 
     /* 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
@@ -139,16 +143,16 @@ Con *create_workspace_on_output(Output *output, Con *content) {
             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 —
@@ -156,7 +160,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
          * 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;
@@ -169,22 +173,14 @@ Con *create_workspace_on_output(Output *output, Con *content) {
             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;
@@ -201,8 +197,8 @@ Con *create_workspace_on_output(Output *output, Con *content) {
             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);
@@ -245,7 +241,7 @@ bool workspace_is_visible(Con *ws) {
 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 &&
@@ -257,7 +253,7 @@ Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
             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 &&
@@ -284,7 +280,7 @@ static void workspace_reassign_sticky(Con *con) {
     /* 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;
@@ -312,8 +308,8 @@ static void workspace_reassign_sticky(Con *con) {
         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);
 }
 
 /*
@@ -325,11 +321,14 @@ static void workspace_reassign_sticky(Con *con) {
 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);
@@ -344,7 +343,7 @@ static void _workspace_show(Con *workspace) {
 
     /* 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;
@@ -385,6 +384,7 @@ static void _workspace_show(Con *workspace) {
      * 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
@@ -410,7 +410,7 @@ static void _workspace_show(Con *workspace) {
     } 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
@@ -422,8 +422,19 @@ static void _workspace_show(Con *workspace) {
         /* 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();
         }
     }
 
@@ -472,11 +483,11 @@ Con *workspace_next(void) {
         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)
@@ -493,11 +504,11 @@ Con *workspace_next(void) {
     /* 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) {
@@ -512,11 +523,11 @@ Con *workspace_next(void) {
 
     /* 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))
@@ -544,11 +555,11 @@ Con *workspace_prev(void) {
             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
@@ -563,11 +574,11 @@ Con *workspace_prev(void) {
     /* 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) {
@@ -582,11 +593,11 @@ Con *workspace_prev(void) {
 
     /* 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)
@@ -613,7 +624,7 @@ Con *workspace_next_on_output(void) {
         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)
@@ -629,7 +640,7 @@ Con *workspace_next_on_output(void) {
     /* 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) {
@@ -643,7 +654,7 @@ Con *workspace_next_on_output(void) {
 
     /* 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))
@@ -671,7 +682,7 @@ Con *workspace_prev_on_output(void) {
             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
@@ -685,7 +696,7 @@ Con *workspace_prev_on_output(void) {
     /* 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) {
@@ -699,7 +710,7 @@ Con *workspace_prev_on_output(void) {
 
     /* 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)
@@ -742,13 +753,13 @@ Con *workspace_back_and_forth_get(void) {
 
 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;
 }
@@ -764,7 +775,7 @@ void workspace_update_urgent_flag(Con *ws) {
     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);
 }
 
 /*
diff --git a/src/x.c b/src/x.c
index 9e3e55bc5453ab258333ed1b0553f595b2ae2774..b39c19d002f2515d7dd1843b640ea047dac4dc4a 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -15,9 +15,9 @@
 /* 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 */
@@ -76,9 +76,9 @@ TAILQ_HEAD(initial_mapping_head, con_state) initial_mapping_head =
  */
 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");
@@ -430,16 +430,16 @@ void x_draw_decoration(Con *con) {
             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);
         }
 
@@ -453,10 +453,10 @@ void x_draw_decoration(Con *con) {
             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)}});
         }
     }
 
@@ -473,12 +473,12 @@ void x_draw_decoration(Con *con) {
     /* 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[] = {
@@ -579,11 +579,11 @@ void x_deco_recurse(Con *con) {
     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);
@@ -620,7 +620,7 @@ void x_push_node(Con *con) {
         /* 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;
@@ -800,8 +800,8 @@ void x_push_node(Con *con) {
     /* 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);
 }
 
 /*
@@ -845,11 +845,11 @@ static void x_push_node_unmaps(Con *con) {
     }
 
     /* 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);
 }
 
 /*
@@ -862,7 +862,7 @@ static bool is_con_attached(Con *con) {
         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;
     }
@@ -893,7 +893,7 @@ void x_push_changes(Con *con) {
     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);
     }
@@ -904,9 +904,9 @@ void x_push_changes(Con *con) {
     /* 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(). */
@@ -921,7 +921,7 @@ void x_push_changes(Con *con) {
     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));
 
@@ -952,7 +952,7 @@ void x_push_changes(Con *con) {
         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;
         }
@@ -985,7 +985,7 @@ void x_push_changes(Con *con) {
 
     //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);
     }
@@ -1023,7 +1023,7 @@ void x_push_changes(Con *con) {
                     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);
@@ -1041,7 +1041,7 @@ void x_push_changes(Con *con) {
 
     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;
     }
@@ -1057,7 +1057,7 @@ void x_push_changes(Con *con) {
      * 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);
@@ -1067,7 +1067,7 @@ void x_push_changes(Con *con) {
     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);
     }
@@ -1155,7 +1155,7 @@ void x_mask_event_mask(uint32_t mask) {
     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);
     }
index fead802a21f317b57bd0c6767c2ab6f4454254a8..9e412e0330db13a788068c1bcb3c34534047fb40 100644 (file)
@@ -23,9 +23,9 @@ static int num_screens;
  */
 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;
 }
index 294f0daef79b134b469575167daf246a0dc152fe..c11c55636aecd695af5fb8fba34b753ec7f36a7e 100644 (file)
@@ -8,4 +8,3 @@ inc
 META.yml
 i3-cfg-for-*
 -
-Xdummy.so
index 6bc80d8f3ded97c86f5a76b4e9db7e830c3787b7..f2c15013f5246b7e9346819d60365818d4fd68dd 100755 (executable)
@@ -11,6 +11,7 @@ WriteMakefile(
         'AnyEvent::I3' => '0.15',
         'X11::XCB'     => '0.09',
         'Inline'       => 0,
+        'Inline::C'    => 0,
         'ExtUtils::PkgConfig' => 0,
         'Test::More'   => '0.94',
         'IPC::Run' => 0,
diff --git a/testcases/Xdummy b/testcases/Xdummy
deleted file mode 100755 (executable)
index 638a7b3..0000000
+++ /dev/null
@@ -1,1930 +0,0 @@
-#!/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
-}
index 7ca89016f6ea45443372cba02cd50ac6f9c93d22..61f2ef52451115e3051e3a6d88d0945fb41350f7 100755 (executable)
@@ -19,7 +19,7 @@ use Time::HiRes qw(time);
 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
@@ -43,7 +43,7 @@ sub Log { say $log "@_" }
 
 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;
@@ -55,11 +55,11 @@ my %options = (
     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},
@@ -86,6 +86,9 @@ foreach my $binary (@binaries) {
     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;
 
@@ -97,9 +100,9 @@ my @testfiles = @ARGV;
 
 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
@@ -115,8 +118,7 @@ symlink("$outdir", "latest") or die "Could not symlink latest to $outdir";
 # 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;
@@ -131,7 +133,7 @@ for my $display (@displays) {
 
 # 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
@@ -220,7 +222,7 @@ printf("\t%s with %.2f seconds\n", $_, $timings{$_})
 if ($numtests == 1) {
     say '';
     say 'Test output:';
-    say StartXDummy::slurp($logfile);
+    say StartXServer::slurp($logfile);
 }
 
 END { cleanup() }
@@ -346,7 +348,7 @@ complete-run.pl [files...]
 
 =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
 
@@ -365,11 +367,11 @@ will parallelize the tests:
   # 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>
 
@@ -392,8 +394,8 @@ Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.
 
 =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
index 513dda3679b242c9236874fdd4624d82543cda4e..9d0b7d66b15e528286314623d15c0939832cf6e5 100644 (file)
@@ -20,7 +20,7 @@ bindsym Mod1+h split h
 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
diff --git a/testcases/lib/StartXDummy.pm b/testcases/lib/StartXDummy.pm
deleted file mode 100644 (file)
index 592feb8..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-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
diff --git a/testcases/lib/StartXServer.pm b/testcases/lib/StartXServer.pm
new file mode 100644 (file)
index 0000000..032f58c
--- /dev/null
@@ -0,0 +1,122 @@
+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
index a6b982bafd8e18b0b281ccc58ab104236950a1a5..212e78fd64f79b3aa8f58b296465860379f5bd74 100644 (file)
@@ -337,6 +337,7 @@ sub open_window {
     $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
index 206116ee171eed08091e3900ed30331c783a1a1f..458fff935497e69f9d228ca078293eeb58fd918c 100644 (file)
@@ -191,6 +191,26 @@ is($x->input_focus, $window->id, 'fullscreen window focused');
 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.
@@ -235,4 +255,131 @@ $swindow = open_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;
index ec690b5eb4f66d0f03fc442b538c9c81c11d702e..a739005132224d8952ede8cb38ffd9c459875e80 100644 (file)
@@ -69,7 +69,7 @@ cmd 'border 1pixel';
 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.
 ################################################################################
 
index 63e92c3ca422c6bf8644efd21e269ba980cc3034..ec8bff268d876a14e3543e7ef8eae3610cbf1df5 100644 (file)
@@ -99,4 +99,27 @@ is($x->input_focus, $child->id, "Child window focused");
 
 }
 
+################################################################################
+# 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;
index 29837430b34d06c0bd37865ed4cc432c561ed829..2bcc6d602f7ec4b57c317f111926a29c81926235 100644 (file)
@@ -23,7 +23,7 @@ $i3->connect()->recv;
 # Workspaces requests and events
 ################################
 
-my $focused = get_ws(focused_ws());
+my $old_ws = get_ws(focused_ws());
 
 # Events
 
@@ -36,15 +36,11 @@ $i3->subscribe({
     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;
@@ -61,8 +57,20 @@ $t = AnyEvent->timer(
     }
 );
 
-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;
index eb2fe1447007508c6f18c0b22674c60e1b584bfc..d9ff1c39fbf53fe624424339697a460ad7229b93 100644 (file)
@@ -54,6 +54,7 @@ my $expected = {
     type => 'root',
     id => $ignore,
     rect => $ignore,
+    deco_rect => $ignore,
     window_rect => $ignore,
     geometry => $ignore,
     swallows => $ignore,
index 6afdd806fc52bbcd82b48430d33b35b50ab55f28..e9d069385c3895ad2bb9a337c6c642209c32a042 100644 (file)
@@ -48,7 +48,7 @@ sub verify_split_layout {
 
     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';
@@ -60,10 +60,10 @@ sub verify_split_layout {
     $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');
index 28207a3c607b8e553cbc4ceddaf573effa0b4350..88a36a5b333fc67d38da8019247a4fa5662b3b4a 100644 (file)
@@ -245,4 +245,20 @@ my $center_y = int($x->root->rect->height/2) - int($floatcon[0]->{rect}->{height
 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;
index 6829a1478cc078d643fc5fe450e8b569b6795383..f76ee04bf8e6006adb42dc182db7bc4898002299 100644 (file)
@@ -24,7 +24,7 @@ sub check_order {
     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);
index ba03913a8f524ce8c2355f1bf52c21f09397d882..050e11623f95e250b4bfe6e7fe0e15e4c1261a47 100644 (file)
@@ -59,6 +59,18 @@ subtest 'Window without WM_TAKE_FOCUS', sub {
     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;
 
@@ -74,7 +86,7 @@ subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
 
     $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;
 };
index 09b708be85d09d3ff0353d49ddbd72b288745048..8b00abac08569b47f2a583114a4100a49ac26ae2 100644 (file)
@@ -88,4 +88,22 @@ is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3 without whitespace');
 
 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;
index a85320ea84c8c4d7590ac0139878febb5bff8d81..9f0c046ab2dd0dd51d8cc62ff8adb780605df2e1 100644 (file)
@@ -84,7 +84,7 @@ is_num_children($first_ws, 0, 'no containers on this workspace yet');
 # 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|;
 
index 874a25ec3f3f34f6354612c4a1c507035f8c6c60..86ef731f58d69013223db30b420b4d483c7c4e51 100644 (file)
@@ -50,9 +50,9 @@ EOT
 
 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),
@@ -618,7 +618,7 @@ EOT
 
 $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" {
@@ -645,7 +645,7 @@ EOT
 
 $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
index fe2ea675f62fd922368371fdd4fcac321e24ebf4..7ca6fbc26214258d60a8f50993f13c218fde265a 100644 (file)
@@ -71,6 +71,40 @@ is(current_desktop_index, 1, "Open on 0 and view 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;
index 185910e8886a37ff5433e1c210616c1093e9c3e1..0c67423eaf4cfe19839a96866ccf07fdaedb3985 100644 (file)
@@ -50,6 +50,7 @@ subtest 'Workspace empty event upon switch', sub {
 
     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');
 };
 
 ################################################################################
@@ -116,6 +117,7 @@ subtest 'Workspace empty event upon window close', sub {
 
     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');
 };
 
 }
index e236fe606d0867d278d9740ee1b87b8dba39f8f1..be1e7f855ba900e419713db99b20a1fc1a6c02cd 100644 (file)
@@ -47,10 +47,12 @@ my $wscontent = get_ws($tmp);
 
 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);
@@ -80,12 +82,49 @@ $wscontent = get_ws($tmp);
 
 @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;
diff --git a/testcases/t/231-ipc-floating-event.t b/testcases/t/231-ipc-floating-event.t
new file mode 100644 (file)
index 0000000..c2de64e
--- /dev/null
@@ -0,0 +1,59 @@
+#!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;
diff --git a/testcases/t/231-ipc-window-close.t b/testcases/t/231-ipc-window-close.t
new file mode 100644 (file)
index 0000000..3483cf4
--- /dev/null
@@ -0,0 +1,52 @@
+#!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;
diff --git a/testcases/t/231-ipc-window-move.t b/testcases/t/231-ipc-window-move.t
new file mode 100644 (file)
index 0000000..117d27f
--- /dev/null
@@ -0,0 +1,61 @@
+#!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;
diff --git a/testcases/t/231-wm-change-state.t b/testcases/t/231-wm-change-state.t
new file mode 100644 (file)
index 0000000..92992b4
--- /dev/null
@@ -0,0 +1,49 @@
+#!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;
diff --git a/testcases/t/232-cmd-move-criteria.t b/testcases/t/232-cmd-move-criteria.t
new file mode 100644 (file)
index 0000000..22a2eb4
--- /dev/null
@@ -0,0 +1,37 @@
+#!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;
diff --git a/testcases/t/232-cmd-workspace-number-selection.t b/testcases/t/232-cmd-workspace-number-selection.t
new file mode 100644 (file)
index 0000000..bda0564
--- /dev/null
@@ -0,0 +1,52 @@
+#!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;
diff --git a/testcases/t/232-ipc-window-urgent.t b/testcases/t/232-ipc-window-urgent.t
new file mode 100644 (file)
index 0000000..2ac9ecb
--- /dev/null
@@ -0,0 +1,68 @@
+#!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;
diff --git a/testcases/t/234-ewmh-desktop-names.t b/testcases/t/234-ewmh-desktop-names.t
new file mode 100644 (file)
index 0000000..d95965c
--- /dev/null
@@ -0,0 +1,75 @@
+#!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;
index d407289aea8f61e060b642a05e65f624e65219eb..bc90131d76c73e8023ebc9988ce61572254049c0 100644 (file)
@@ -19,7 +19,6 @@
 # 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;
diff --git a/testcases/t/234-regress-default-floating-border.t b/testcases/t/234-regress-default-floating-border.t
new file mode 100644 (file)
index 0000000..d5994f5
--- /dev/null
@@ -0,0 +1,43 @@
+#!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;
diff --git a/testcases/t/235-check-config-no-x.t b/testcases/t/235-check-config-no-x.t
new file mode 100644 (file)
index 0000000..614d6b3
--- /dev/null
@@ -0,0 +1,60 @@
+#!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;
diff --git a/testcases/t/235-wm-class-change-handler.t b/testcases/t/235-wm-class-change-handler.t
new file mode 100644 (file)
index 0000000..e6cacde
--- /dev/null
@@ -0,0 +1,70 @@
+#!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;
diff --git a/testcases/t/236-floating-focus-raise.t b/testcases/t/236-floating-focus-raise.t
new file mode 100644 (file)
index 0000000..4be8713
--- /dev/null
@@ -0,0 +1,44 @@
+#!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;
diff --git a/testcases/t/238-ipc-binding-event.t b/testcases/t/238-ipc-binding-event.t
new file mode 100644 (file)
index 0000000..6931fe2
--- /dev/null
@@ -0,0 +1,88 @@
+#!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;
diff --git a/testcases/t/238-regress-reload-bindsym.t b/testcases/t/238-regress-reload-bindsym.t
new file mode 100644 (file)
index 0000000..6d5d12c
--- /dev/null
@@ -0,0 +1,45 @@
+#!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;
diff --git a/testcases/t/239-net-close-window-request.t b/testcases/t/239-net-close-window-request.t
new file mode 100644 (file)
index 0000000..20c3f84
--- /dev/null
@@ -0,0 +1,49 @@
+#!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;
diff --git a/testcases/t/520-regress-focus-direction-floating.t b/testcases/t/520-regress-focus-direction-floating.t
new file mode 100644 (file)
index 0000000..ccef49e
--- /dev/null
@@ -0,0 +1,48 @@
+#!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;
diff --git a/testcases/t/521-ewmh-desktop-viewport.t b/testcases/t/521-ewmh-desktop-viewport.t
new file mode 100644 (file)
index 0000000..9e36090
--- /dev/null
@@ -0,0 +1,95 @@
+#!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;
index cc129da7db3ae73b3ff8fb44ade668635d1e1bc2..0b3a9c0be9ee5028bbfaab47a9923f14cf460d09 100644 (file)
 /*
  * 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)
index d6bf9a245615b7e203264a3101eaf76c6dfb216b..abc3b3b525d130145ba544b71d01de6d50598810 100644 (file)
@@ -24,11 +24,11 @@ void dump() {
     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");