]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Thu, 30 Jul 2015 20:27:15 +0000 (22:27 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Thu, 30 Jul 2015 20:27:15 +0000 (22:27 +0200)
197 files changed:
.gitignore
.travis.yml
CONTRIBUTING.md
DEPENDS
Makefile
common.mk
debian/changelog
debian/i3-wm.docs
docs/asciidoc-git.conf
docs/debugging
docs/hacking-howto
docs/ipc
docs/stacklimit.png [deleted file]
docs/testsuite
docs/userguide
generate-command-parser.pl
i3-config-wizard/i3-config-wizard.mk
i3-config-wizard/main.c
i3-dmenu-desktop
i3-dump-log/i3-dump-log.mk
i3-dump-log/main.c
i3-input/i3-input.mk
i3-input/main.c
i3-migrate-config-to-v4
i3-msg/i3-msg.mk
i3-msg/main.c
i3-nagbar/i3-nagbar.mk
i3-nagbar/main.c
i3-save-tree
i3-sensible-pager
i3-sensible-terminal
i3bar/i3bar.mk
i3bar/include/child.h
i3bar/include/common.h
i3bar/include/config.h
i3bar/include/ipc.h
i3bar/include/mode.h
i3bar/include/outputs.h
i3bar/include/parse_json_header.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/main.c
i3bar/src/mode.c
i3bar/src/outputs.c
i3bar/src/parse_json_header.c
i3bar/src/workspaces.c
i3bar/src/xcb.c
include/all.h
include/assignments.h
include/atoms.xmacro
include/bindings.h
include/click.h
include/cmdparse.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/debug.h
include/display_version.h
include/ewmh.h
include/fake_outputs.h
include/floating.h
include/handlers.h
include/i3.h
include/i3/ipc.h
include/ipc.h
include/key_press.h
include/libi3.h
include/load_layout.h
include/log.h
include/main.h
include/manage.h
include/match.h
include/move.h
include/output.h
include/randr.h
include/regex.h
include/render.h
include/resize.h
include/restore_layout.h
include/scratchpad.h
include/shmlog.h
include/sighandler.h
include/startup.h
include/tree.h
include/util.h
include/window.h
include/workspace.h
include/x.h
include/xcb.h
include/xcb_compat.h
include/xcursor.h
include/xinerama.h
include/yajl_utils.h
libi3/dpi.c
libi3/fake_configure_notify.c
libi3/font.c
libi3/get_colorpixel.c
libi3/get_config_path.c [new file with mode: 0644]
libi3/get_mod_mask.c
libi3/get_process_filename.c
libi3/get_visualtype.c
libi3/ipc_connect.c
libi3/ipc_recv_message.c
libi3/ipc_send_message.c
libi3/resolve_tilde.c [new file with mode: 0644]
libi3/root_atom_contents.c
libi3/safewrappers.c
libi3/string.c
libi3/strndup.c
libi3/ucs2_conversion.c
parser-specs/commands.spec
parser-specs/config.spec
release.sh
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/debug.c
src/display_version.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/log.c
src/main.c
src/manage.c
src/match.c
src/move.c
src/output.c
src/randr.c
src/regex.c
src/render.c
src/resize.c
src/restore_layout.c
src/scratchpad.c
src/sighandler.c
src/startup.c
src/tree.c
src/util.c
src/version.c [new file with mode: 0644]
src/window.c
src/workspace.c
src/x.c
src/xcb.c
src/xcursor.c
src/xinerama.c
testcases/Makefile.PL
testcases/complete-run.pl
testcases/lib/i3test.pm
testcases/t/113-urgent.t
testcases/t/141-resize.t
testcases/t/165-for_window.t
testcases/t/171-config-migrate.t
testcases/t/175-startup-notification.t
testcases/t/177-bar-config.t
testcases/t/185-scratchpad.t
testcases/t/187-commands-parser.t
testcases/t/201-config-parser.t
testcases/t/210-mark-unmark.t
testcases/t/231-ipc-floating-event.t
testcases/t/232-cmd-move-criteria.t
testcases/t/232-ipc-window-urgent.t
testcases/t/235-check-config-no-x.t
testcases/t/235-wm-class-change-handler.t
testcases/t/240-focus-on-window-activation.t [new file with mode: 0644]
testcases/t/240-tabbed-floating-disable-crash.t [new file with mode: 0644]
testcases/t/241-consistent-center.t [new file with mode: 0644]
testcases/t/242-no-focus.t [new file with mode: 0644]
testcases/t/243-move-to-mark.t [new file with mode: 0644]
testcases/t/243-net-wm-state-hidden.t [new file with mode: 0644]
testcases/t/245-move-position-mouse.t [new file with mode: 0644]
testcases/t/246-window-decoration-focus.t [new file with mode: 0644]
testcases/t/247-config-line-continuation.t [new file with mode: 0644]
testcases/t/523-move-position-center.t [new file with mode: 0644]
testcases/t/524-move.t [new file with mode: 0644]
testcases/t/525-i3bar-mouse-bindings.t [new file with mode: 0644]
tests/queue.h [deleted file]
tests/swap.c [deleted file]
tests/test_table.c [deleted file]

index 1d4c1678aa4ed2abc89b7e5d856be089a3741022..617421ac1e92f3edc338c7b7c75d868e6696a608 100644 (file)
@@ -33,3 +33,4 @@ docs/*.html
 i3-command-parser.stamp
 i3-config-parser.stamp
 .clang_complete
+LAST_VERSION
index 733daeac39117ae0f04587d31ddf8f4e3522588b..e3e45656b0509d618bad2228209ff8923745a76d 100644 (file)
@@ -32,7 +32,7 @@ before_install:
   - sudo cp /tmp/pin /etc/apt/preferences.d/trustypin
   - sudo apt-get update
   - sudo apt-get install -t trusty libc6 libc6-dev
-  - sudo apt-get install --no-install-recommends devscripts equivs
+  - sudo apt-get install --no-install-recommends devscripts equivs xdotool
   - sudo apt-get install -t utopic clang-format-3.5
   - clang-format-3.5 --version
 install:
index 7a5e0c5fda2b947e1db0c2f11c3a4dab67283d3f..39835c14f56a9cf3b552673c6e36fee17be31c85 100644 (file)
@@ -9,6 +9,11 @@ and https://github.com/i3/i3lock
 1. Read http://i3wm.org/docs/debugging.html
 2. Make sure you include a link to your logfile in your report (section 3).
 3. Make sure you include the i3 version number in your report (section 1).
+4. Please be aware that we cannot support compatibility issues with
+   closed-source software, as digging into compatibility problems without
+   having access to the source code is too time-consuming. Additionally,
+   experience has shown that often, the software in question is responsible for
+   the issue. Please raise an issue with the software in question, not i3.
 
 # Pull requests
 
@@ -18,3 +23,10 @@ and https://github.com/i3/i3lock
 * Use the `next` branch for developing and sending your pull request.
 * Use `clang-format` to format your code.
 * Run the testsuite, see http://i3wm.org/docs/testsuite.html
+
+# Finding something to do
+
+* Find a [reproducible bug](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Areproducible+label%3Abug+) from the issue tracker. These issues have been reviewed and confirmed by a project contributor.
+* Find an [accepted enhancement](https://github.com/i3/i3/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3Aaccepted+label%3Aenhancement) from the issue tracker. These have been approved and are ok to start working on.
+
+There's a very good [overview of the codebase](http://i3wm.org/docs/hacking-howto.html) available to get you started.
diff --git a/DEPENDS b/DEPENDS
index d5dae1429d05192a5d0bb56671354d34933b3cb4..0882f1475be4ac96afaefb1611f7b94b42086ddf 100644 (file)
--- a/DEPENDS
+++ b/DEPENDS
@@ -7,29 +7,30 @@
 ┌──────────────┬────────┬────────┬────────────────────────────────────────┐
 │ 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/       │
+│ pkg-config   │ 0.25   │ 0.28   │ http://pkgconfig.freedesktop.org/      │
+│ libxcb       │ 1.1.93 │ 1.11   │ 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/     │
+│ xkbcommon    │ 0.4.0  │ 0.5.0  │ http://xkbcommon.org/                  │
+│ xkbcommon-x11│ 0.4.0  │ 0.5.0  │ http://xkbcommon.org/                  │
+│ util-cursor³⁴│ 0.0.99 │ 0.1.2  │ http://xcb.freedesktop.org/dist/       │
+│ util-wm⁴     │ 0.3.8  │ 0.3.8  │ http://xcb.freedesktop.org/dist/       │
+│ util-keysyms⁴│ 0.3.8  │ 0.4.0  │ http://xcb.freedesktop.org/dist/       │
+│ libev        │ 4.0    │ 4.19   │ http://libev.schmorp.de/               │
+│ yajl         │ 2.0.1  │ 2.1.0  │ http://lloyd.github.com/yajl/          │
+│ asciidoc     │ 8.3.0  │ 8.6.8  │ 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/                   │
+│ PCRE         │ 8.12   │ 8.35   │ 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/              │
+│ pango        │ 1.30.0 | 1.36.8 │ http://www.pango.org/                  │
+│ cairo        │ 1.12.2 │ 1.14.0 │ 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
- ³ xcb-util-cursor, to be precise. Might be considered part of xcb-util, or not
  :-).
+ ³ xcb-util-cursor, to be precise.
⁴ Depending on your distribution, this might be considered part of xcb-util.
 
  i3bar, i3-msg, i3-input, i3-nagbar and i3-config-wizard do not introduce any
  new dependencies.
index 1fed4df1ac59185a4540413db3c5b9e436f15344..fd3021017650d11c1daa6cfed7ee327a853b7dc8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,12 @@ include i3-dump-log/i3-dump-log.mk
 include docs/docs.mk
 include man/man.mk
 
+# Update $(TOPDIR)/LAST_VERSION if it differs from $I3_VERSION
+CACHED_VERSION := '$(shell [ -f $(TOPDIR)/LAST_VERSION ] && cat $(TOPDIR)/LAST_VERSION)'
+ifneq ($(CACHED_VERSION),$(I3_VERSION))
+$(shell echo -n ${I3_VERSION} > $(TOPDIR)/LAST_VERSION)
+endif
+
 real-all: $(ALL_TARGETS)
 
 install: $(INSTALL_TARGETS)
index b9e15a286fc27d335e4ae8c12a36ec8188dbd972..c568fb60dba42d557e73ac33c167a46b7c6a2230 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -1,11 +1,14 @@
 UNAME=$(shell uname)
 DEBUG=1
-COVERAGE=0
 INSTALL=install
 LN=ln
+PKG_CONFIG=pkg-config
 ifndef PREFIX
   PREFIX=/usr
 endif
+ifndef EXEC_PREFIX
+  EXEC_PREFIX=$(PREFIX)
+endif
 ifndef SYSCONFDIR
   ifeq ($(PREFIX),/usr)
     SYSCONFDIR=/etc
@@ -60,7 +63,7 @@ I3_CPPFLAGS += -DI3__FILE__=__FILE__
 
 ## Libraries flags
 
-ifeq ($(shell which pkg-config 2>/dev/null 1>/dev/null || echo 1),1)
+ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null 1>/dev/null || echo 1),1)
 $(error "pkg-config was not found")
 endif
 
@@ -74,15 +77,15 @@ endif
 #
 # We redirect stderr to /dev/null because pkg-config prints an error if support
 # for gnome-config was enabled but gnome-config is not actually installed.
-cflags_for_lib = $(shell pkg-config --silence-errors --cflags $(1) 2>/dev/null)
-ldflags_for_lib = $(shell pkg-config --exists 2>/dev/null $(1) && pkg-config --libs $(1) 2>/dev/null || echo -l$(2))
+cflags_for_lib = $(shell $(PKG_CONFIG) --silence-errors --cflags $(1) 2>/dev/null)
+ldflags_for_lib = $(shell $(PKG_CONFIG) --exists 2>/dev/null $(1) && $(PKG_CONFIG) --libs $(1) 2>/dev/null || echo -l$(2))
 
 # XCB common stuff
 XCB_CFLAGS  := $(call cflags_for_lib, xcb)
 XCB_CFLAGS  += $(call cflags_for_lib, xcb-event)
 XCB_LIBS    := $(call ldflags_for_lib, xcb,xcb)
 XCB_LIBS    += $(call ldflags_for_lib, xcb-event,xcb-event)
-ifeq ($(shell pkg-config --exists xcb-util 2>/dev/null || echo 1),1)
+ifeq ($(shell $(PKG_CONFIG) --exists xcb-util 2>/dev/null || echo 1),1)
 XCB_CFLAGS  += $(call cflags_for_lib, xcb-atom)
 XCB_CFLAGS  += $(call cflags_for_lib, xcb-aux)
 XCB_LIBS    += $(call ldflags_for_lib, xcb-atom,xcb-atom)
@@ -125,7 +128,7 @@ LIBEV_LIBS   := $(call ldflags_for_lib, libev,ev)
 
 # libpcre
 PCRE_CFLAGS := $(call cflags_for_lib, libpcre)
-ifeq ($(shell pkg-config --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1)
+ifeq ($(shell $(PKG_CONFIG) --atleast-version=8.10 libpcre 2>/dev/null && echo 1),1)
 I3_CPPFLAGS += -DPCRE_HAS_UCP=1
 endif
 PCRE_LIBS   := $(call ldflags_for_lib, libpcre,pcre)
index cc0e532875100a733545d5415e0a7a2693d63b9c..023eeac10b6e000570cfc0c624f2ca04c1248cd9 100644 (file)
@@ -1,8 +1,20 @@
-i3-wm (4.10.1-1) experimental; urgency=medium
+i3-wm (4.10.3-1) experimental; urgency=medium
 
   * NOT YET RELEASED.
 
- -- Michael Stapelberg <stapelberg@debian.org>  Sun, 29 Mar 2015 18:08:13 +0200
+ -- Michael Stapelberg <stapelberg@debian.org>  Thu, 16 Apr 2015 09:08:30 +0200
+
+i3-wm (4.10.2-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Thu, 16 Apr 2015 09:02:53 +0200
+
+i3-wm (4.10.1-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Sun, 29 Mar 2015 18:54:07 +0200
 
 i3-wm (4.10-1) experimental; urgency=medium
 
index 11a695ba7ea5093f58a54c3d87da812b326cd142..437175b310fef4fe748500dad6d538f367be92d2 100644 (file)
@@ -8,7 +8,6 @@ docs/snapping.png
 docs/two_columns.png
 docs/two_terminals.png
 docs/modes.png
-docs/stacklimit.png
 docs/ipc.html
 docs/multi-monitor.html
 docs/wsbar.html
index 3d42bca8a9999309c3e8b961d9c377de5896585f..36bdb6f5a0cac50ac424537636e140b4799587aa 100644 (file)
@@ -647,7 +647,7 @@ endif::doctype-manpage[]
 </div>\r
 {disable-javascript%<div id="footnotes"><hr /></div>}\r
 <div id="footer" lang="de">\r
-© 2009-2014 Michael Stapelberg, <a href="http://i3wm.org/impress.html">Impressum</a>
+© 2009 Michael Stapelberg, <a href="http://i3wm.org/impress.html">Impressum</a>
 </div>\r
 </body>\r
 </html>\r
index 1253b0c50fc2b405f8279d36211c7c02014174fd..cf61813d4789b52c1c4791f4b28f92698c73e4b2 100644 (file)
@@ -10,10 +10,10 @@ Thank you for being interested in debugging i3. It really means
 something to us to get your bug fixed. If you have any questions about the
 process and/or need further help, do not hesitate to contact us!
 
-== Verify you are using i3 ≥ 4.7
+== Verify you are using i3 ≥ 4.10
 
-Only the latest major version of i3 is supported, i.e. version 4.7 currently.
-To verify which version you are running, use:
+Only the latest major version of i3 is supported. To verify which version
+you are running, use:
 
 ---------------
 $ i3 --moreversion 2>&- || i3 --version
@@ -67,8 +67,37 @@ fly:
 i3-msg 'debuglog on; shmlog on; reload'
 ---------------------------------------
 
+== Reproducing the problem
+
+Before submitting an issue, please make sure to close down on the problem as
+much as you can yourself. Here are some steps you should consider:
+
+* Find a deterministic, reliable way to reproduce the problem and provide it
+  with your bug report.
+* Try using the default i3 config to reproduce the problem. If the issue does
+  not appear with the default config, gradually adapt it to track down what 
+  change(s) to the config introduce the problem.
+* Reproduce the problem with a minimal setup, i.e., only use as few applications,
+  windows and steps as necessary.
+* In addition, try to stick to applications that are common and, even more
+  importantly, free / open source.
+* Before obtaining the log file, restart i3 in-place, execute the steps to
+  reproduce the problem and then save the logs. This keeps the log file as
+  small as possible and necessary.
+
+Please be aware that we cannot support compatibility issues with closed-source
+software, as digging into compatibility problems without having access to the
+source code is too time-consuming. Additionally, experience has shown that
+often, the software in question is responsible for the issue. Please raise an
+issue with the software in question, not i3.
+
 == Obtaining the debug logfile
 
+Please note that log files may contain sensitive data such as window titles.
+The best way to avoid submitting such information is to only run the necessary
+applications to reproduce the behavior when saving the log file. This will also
+make analyzing the log file easier.
+
 No matter whether i3 misbehaved in some way without crashing or whether it just
 crashed, the logfile provides all information necessary to debug the problem.
 
@@ -121,3 +150,19 @@ When debugging with us in IRC, be prepared to use a so called nopaste service
 such as http://nopaste.info or http://pastebin.com because pasting large
 amounts of text in IRC sometimes leads to incomplete lines (servers have line
 length limitations) or flood kicks.
+
+== Debugging i3bar
+
+To debug i3bar problems, add +verbose yes+ to all +bar {}+ blocks in your i3 config
+and then restart all i3bar instances like this:
+
+---------------------------------------------------------------------
+$ killall i3bar
+$ for c in $(i3-msg -t get_bar_config | python -c \
+      'import json,sys;print("\n".join(json.load(sys.stdin)))'); do \
+    (i3bar --bar_id=$c >i3bar.$c.log 2>&1) & \
+  done;
+---------------------------------------------------------------------
+
+There will now be +i3bar.*.log+ files in your current directory that you can provide
+in your bug report.
index 12d6b14ee1b2da3406d4b78e7960c66288207749..e6313f7d6837fd85da3059116fd73e962e9751fe 100644 (file)
@@ -404,10 +404,14 @@ can reconfigure themselves).
 
 == _NET_WM_STATE
 
-Only the _NET_WM_STATE_FULLSCREEN atom is handled. It calls
-``toggle_fullscreen()'' for the specific client which just configures the
-client to use the whole screen on which it currently is. Also, it is set as
-fullscreen_client for the i3Screen.
+Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
+are handled.
+
+The former calls ``toggle_fullscreen()'' for the specific client which just
+configures the client to use the whole screen on which it currently is.
+Also, it is set as fullscreen_client for the i3Screen.
+
+The latter is used to set, read and display urgency hints.
 
 == WM_NAME
 
@@ -629,8 +633,8 @@ unmapped if it should not be visible anymore. +WM_STATE+ will be set to
 +x_draw_decoration+ draws window decorations. It is run for every leaf
 container (representing an actual X11 window) and for every non-leaf container
 which is in a stacked/tabbed container (because stacked/tabbed containers
-display a window decoration for split containers, which at the moment just says
-"another container").
+display a window decoration for split containers, which consists of a representation
+of the child container's names.
 
 Then, parameters are collected to be able to determine whether this decoration
 drawing is actually necessary or was already done. This saves a substantial
@@ -686,9 +690,9 @@ all commands.
 In earlier versions of i3, interpreting these commands was done using lex and
 yacc, but experience has shown that lex and yacc are not well suited for our
 command language. Therefore, starting from version 4.2, we use a custom parser
-for user commands (not yet for the configuration file).
+for user commands and the configuration file.
 The input specification for this parser can be found in the file
-+parser-specs/commands.spec+. Should you happen to use Vim as an editor, use
++parser-specs/*.spec+. Should you happen to use Vim as an editor, use
 :source parser-specs/highlighting.vim to get syntax highlighting for this file
 (highlighting files for other editors are welcome).
 
@@ -729,11 +733,14 @@ features. This is its definition:
 # workspace next|prev|next_on_output|prev_on_output
 # workspace back_and_forth
 # workspace <name>
+# workspace number <number>
 state WORKSPACE:
   direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
       -> call cmd_workspace($direction)
   'back_and_forth'
       -> call cmd_workspace_back_and_forth()
+  'number'
+      -> WORKSPACE_NUMBER
   workspace = string
       -> call cmd_workspace_name($workspace)
 ----------------------------------------------------------------
@@ -772,6 +779,10 @@ workspace <name>::
        single quotes), but just called string. Other possible tokens are word
        (the same as string, but stops matching at a whitespace) and end
        (matches the end of the input).
+workspace number <number>::
+        The workspace command has to be followed by the keyword +number+. It
+        then transitions into the state +WORKSPACE_NUMBER+, where the actual
+        parameter will be read.
 
 === Introducing a new command
 
@@ -952,18 +963,21 @@ http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_
 or, for more documentation, see http://git-scm.com/documentation
 
 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.
+accepted. A good way for this is to open an issue and asking for opinions on it.
+Even for accepted features, this can be a good way to refine an idea upfront. However,
+we don't want to see certain features in i3, e.g., switching window focus in an
+Alt+Tab like way.
 
 When working on bugfixes, please make sure you mention that you are working on
-it in the corresponding bugreport at https://github.com/i3/i3/issues In case
-there is no bugreport yet, please create one.
+it in the corresponding bug report at https://github.com/i3/i3/issues. In case
+there is no bug report yet, please create one.
 
-After you are done, please submit your work for review at https://github.com/i3/i3
+After you are done, please submit your work for review as a pull request at
+https://github.com/i3/i3.
 
 Do not send emails to the mailing list or any author directly, and don’t submit
 them in the bugtracker, since all reviews should be done in public at
-http://cr.i3wm.org/. In order to make your review go as fast as possible, you
+https://github.com/i3/i3. In order to make your review go as fast as possible, you
 could have a look at previous reviews and see what the common mistakes are.
 
 === Which branch to use?
index ff7c8aaeb0f185299520dc5f271460060a8d13be..4462e772ba405cfc5332bea42eed400922085076 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -520,21 +520,23 @@ statusline::
        Text color to be used for the statusline.
 separator::
        Text color to be used for the separator.
-focused_workspace_text/focused_workspace_bg::
-       Text color/background color for a workspace button when the workspace
+focused_workspace_text/focused_workspace_bg/focused_workspace_border::
+       Text/background/border color for a workspace button when the workspace
        has focus.
-active_workspace_text/active_workspace_bg::
-       Text color/background color for a workspace button when the workspace
+active_workspace_text/active_workspace_bg/active_workspace_border::
+       Text/background/border color for a workspace button when the workspace
        is active (visible) on some output, but the focus is on another one.
        You can only tell this apart from the focused workspace when you are
        using multiple monitors.
-inactive_workspace_text/inactive_workspace_bg::
-       Text color/background color for a workspace button when the workspace
+inactive_workspace_text/inactive_workspace_bg/inactive_workspace_border::
+       Text/background/border color for a workspace button when the workspace
        does not have focus and is not active (visible) on any output. This
        will be the case for most workspaces.
-urgent_workspace_text/urgent_workspace_bar::
-       Text color/background color for workspaces which contain at least one
+urgent_workspace_text/urgent_workspace_bg/urgent_workspace_border::
+       Text/background/border color for workspaces which contain at least one
        window with the urgency hint set.
+binding_mode_text/binding_mode_bg/binding_mode_border::
+        Text/background/border color for the binding mode indicator.
 
 
 *Example of configured bars:*
@@ -580,11 +582,14 @@ human_readable (string)::
        build date and branch name. When you need to display the i3 version to
        your users, use the human-readable version whenever possible (since
        this is what +i3 --version+ displays, too).
+loaded_config_file_name (string)::
+       The current config path.
 
 *Example:*
 -------------------
 {
    "human_readable" : "4.2-169-gf80b877 (2012-08-05, branch \"next\")",
+   "loaded_config_file_name" : "/home/hwangcc23/.i3/config",
    "minor" : 2,
    "patch" : 0,
    "major" : 4
@@ -822,4 +827,7 @@ Python::
        * https://github.com/whitelynx/i3ipc (not maintained)
        * https://github.com/ziberna/i3-py (not maintained)
 Ruby::
-       * http://github.com/badboy/i3-ipc
+       * https://github.com/veelenga/i3ipc-ruby
+       * https://github.com/badboy/i3-ipc (not maintained)
+Rust::
+       * https://github.com/tmerr/i3ipc-rs
diff --git a/docs/stacklimit.png b/docs/stacklimit.png
deleted file mode 100644 (file)
index ab5bed7..0000000
Binary files a/docs/stacklimit.png and /dev/null differ
index 29a352185da2f2f39ec2e24cf47224671fda86d6..8fdb9635b9648fcc6e8e7e1b380a80801033c8e8 100644 (file)
@@ -160,6 +160,27 @@ $ ./complete-run.pl --parallel=1 --keep-xserver-output
 This will show the output of Xephyr, which is the X server implementation we
 use for testing.
 
+==== Coverage testing
+
+Coverage testing is possible with +lcov+, the front-end for GCC's coverage
+testing tool +gcov+. The testcases can generate a nice html report that tells
+you which functions and lines were covered during a run of the tests. You can
+use this tool to judge how effective your tests are.
+
+To use test coverage tools, first compile with coverage enabled.
+
+---------------------------------------------------
+COVERAGE=1 make
+---------------------------------------------------
+
+Then run the tests with the +--coverage-testing+ flag.
+
+---------------------------------------------------
+./complete-run.pl --coverage-testing
+---------------------------------------------------
+
+Then open +latest/i3-coverage/index.html+ in your web browser.
+
 ==== IPC interface
 
 The testsuite makes extensive use of the IPC (Inter-Process Communication)
index 820f539521a53531f756e250459bce8f98886221..e21553815601c5bebd60c5793c22595afd6863f5 100644 (file)
@@ -256,8 +256,9 @@ workspace node’s orientation will be changed to +vertical+. The terminal windo
 you moved down is directly attached to the workspace and appears on the bottom
 of the screen. A new (horizontal) container was created to accommodate the
 other two terminal windows. You will notice this when switching to tabbed mode
-(for example). You would end up having one tab called "another container" and
-the other one being the terminal window you moved down.
+(for example). You would end up having one tab with a representation of the split
+container (e.g., "H[urxvt firefox]") and the other one being the terminal window
+you moved down.
 
 [[configuring]]
 == Configuring i3
@@ -319,7 +320,7 @@ and fall back to a working font.
 *Syntax*:
 ------------------------------
 font <X core font description>
-font pango:[family list] [style options] [size]
+font pango:<family list> [<style options>] <size>
 ------------------------------
 
 *Examples*:
@@ -360,8 +361,8 @@ after the keys have been released.
 
 *Syntax*:
 ----------------------------------
-bindsym [--release] [Modifiers+]keysym command
-bindcode [--release] [Modifiers+]keycode command
+bindsym [--release] [<Modifiers>+]<keysym> command
+bindcode [--release] [<Modifiers>+]<keycode> command
 ----------------------------------
 
 *Examples*:
@@ -403,14 +404,17 @@ 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 [--release] [--whole-window] [Modifiers+]button[n] command
-----------------------------------
+-------------------------------------------------------------------------------
+bindsym [--release] [--border] [--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. If the +--release+ flag is given, it will run when the mouse
-button is released.
+window. If the +--release+ flag is given, it will run when the mouse button
+is released.
+
+If the +--whole-window+ flag is given, the binding will also run when any part
+of the window is clicked, with the exception of the border. To have a bind run
+when the border is clicked, specify the +--border+ flag.
 
 *Examples*:
 --------------------------------
@@ -447,7 +451,7 @@ ratio will be preserved).
 
 *Syntax*:
 --------------------------------
-floating_modifier <Modifiers>
+floating_modifier <Modifier>
 --------------------------------
 
 *Example*:
@@ -486,9 +490,9 @@ With the +default_orientation+ configuration directive, you can override that
 behavior.
 
 *Syntax*:
-----------------------------------------------
-default_orientation <horizontal|vertical|auto>
-----------------------------------------------
+--------------------------------------------
+default_orientation horizontal|vertical|auto
+--------------------------------------------
 
 *Example*:
 ----------------------------
@@ -499,17 +503,11 @@ default_orientation vertical
 
 This option determines in which mode new containers on workspace level will
 start.
-///////////////////////////////
-See also <<stack-limit>>.
-//////////////////////////////
 
 *Syntax*:
 ---------------------------------------------
-workspace_layout <default|stacking|tabbed>
+workspace_layout default|stacking|tabbed
 ---------------------------------------------
-/////////////////////////////////////////////
-new_container stack-limit <cols|rows> <value>
-/////////////////////////////////////////////
 
 *Example*:
 ---------------------
@@ -519,18 +517,20 @@ workspace_layout tabbed
 === Border style for new windows
 
 This option determines which border style new windows will have. The default is
-"normal". Note that new_float applies only to windows which are starting out as
-floating windows, e.g. dialog windows.
++normal+. Note that new_float applies only to windows which are starting out as
+floating windows, e.g., dialog windows, but not windows that are floated later on.
 
 *Syntax*:
 ---------------------------------------------
-new_window <normal|1pixel|none|pixel>
-new_float <normal|1pixel|none|pixel>
+new_window normal|none|pixel
+new_window normal|pixel <px>
+new_float normal|none|pixel
+new_float normal|pixel <px>
 ---------------------------------------------
 
 *Example*:
 ---------------------
-new_window 1pixel
+new_window pixel
 ---------------------
 
 The "normal" and "pixel" border styles support an optional border width in
@@ -553,9 +553,9 @@ You can hide vertical borders adjacent to the screen edges using
 to waste even two pixels in displayspace. Default is none.
 
 *Syntax*:
-----------------------------
-hide_edge_borders <none|vertical|horizontal|both>
-----------------------------
+-----------------------------------------------
+hide_edge_borders none|vertical|horizontal|both
+-----------------------------------------------
 
 *Example*:
 ----------------------
@@ -564,14 +564,16 @@ hide_edge_borders vertical
 
 === Arbitrary commands for specific windows (for_window)
 
+[[for_window]]
+
 With the +for_window+ command, you can let i3 execute any command when it
 encounters a specific window. This can be used to set windows to floating or to
 change their border style, for example.
 
 *Syntax*:
------------------------------
-for_window <criteria> command
------------------------------
+-------------------------------
+for_window <criteria> <command>
+-------------------------------
 
 *Examples*:
 ------------------------------------------------
@@ -579,7 +581,7 @@ for_window <criteria> command
 for_window [class="XTerm"] floating enable
 
 # Make all urxvts use a 1-pixel border:
-for_window [class="urxvt"] border 1pixel
+for_window [class="urxvt"] border pixel 1
 
 # A less useful, but rather funny example:
 # makes the window floating as soon as I change
@@ -589,6 +591,27 @@ for_window [title="x200: ~/work"] floating enable
 
 The valid criteria are the same as those for commands, see <<command_criteria>>.
 
+=== Don't focus window upon opening
+
+[[no_focus]]
+
+When a new window appears, it will be focused. The +no_focus+ directive allows preventing
+this from happening and can be used in combination with <<command_criteria>>.
+
+Note that this does not apply to all cases, e.g., when feeding data into a running application
+causing it to request being focused. To configure the behavior in such cases, refer to
+<<focus_on_window_activation>>.
+
+*Syntax*:
+-------------------
+no_focus <criteria>
+-------------------
+
+*Example*:
+-------------------------------
+no_focus [window_role="pop-up"]
+-------------------------------
+
 === Variables
 
 As you learned in the section about keyboard bindings, you will have
@@ -597,9 +620,9 @@ yourself some typing and be able to change the modifier you use later,
 variables can be handy.
 
 *Syntax*:
---------------
-set $name value
---------------
+-------------------
+set $<name> <value>
+-------------------
 
 *Example*:
 ------------------------
@@ -635,7 +658,7 @@ considered.
 
 *Syntax*:
 ------------------------------------------------------------
-assign <criteria> [→] workspace
+assign <criteria> [→] [workspace] <workspace>
 ------------------------------------------------------------
 
 *Examples*:
@@ -704,10 +727,10 @@ and +,+ (comma): they chain commands together in i3, so you need to use quoted
 strings if they appear in your command.
 
 *Syntax*:
--------------------
-exec [--no-startup-id] command
-exec_always [--no-startup-id] command
--------------------
+---------------------------------------
+exec [--no-startup-id] <command>
+exec_always [--no-startup-id] <command>
+---------------------------------------
 
 *Examples*:
 --------------------------------
@@ -731,9 +754,9 @@ or when starting (e.g., by default it will use 1 for the first screen, 2 for
 the second screen and so on).
 
 *Syntax*:
-----------------------------------
+-------------------------------------
 workspace <workspace> output <output>
-----------------------------------
+-------------------------------------
 
 The 'output' is the name of the RandR output you attach your screen to. On a
 laptop, you might have VGA1 and LVDS1 as output names. You can see the
@@ -753,9 +776,9 @@ workspace "2: vim" output VGA1
 You can change all colors which i3 uses to draw the window decorations.
 
 *Syntax*:
---------------------------------------------
-colorclass border background text indicator
---------------------------------------------
+------------------------------------------------------
+<colorclass> <border> <background> <text> <indicator>
+------------------------------------------------------
 
 Where colorclass can be one of:
 
@@ -777,9 +800,9 @@ windows. This color will be used to paint the window on top of which the client
 will be rendered.
 
 *Syntax*:
------------------------
-client.background color
------------------------
+-------------------------
+client.background <color>
+-------------------------
 
 Only clients that do not cover the whole area of this window expose the color
 used to paint it.
@@ -841,9 +864,9 @@ still be useful inside the currently active window (for example to click on
 links in your browser window).
 
 *Syntax*:
-----------------------------
-focus_follows_mouse <yes|no>
-----------------------------
+--------------------------
+focus_follows_mouse yes|no
+--------------------------
 
 *Example*:
 ----------------------
@@ -861,9 +884,9 @@ be warped. +none+ disables warping entirely, whereas +output+ is the default
 behavior described above.
 
 *Syntax*:
----------------------------
-mouse_warping <output|none>
----------------------------
+-------------------------
+mouse_warping output|none
+-------------------------
 
 *Example*:
 ------------------
@@ -885,9 +908,9 @@ There are three things which are possible to do in this situation:
 3. Leave fullscreen mode.
 
 *Syntax*:
--------------------------------------------------
-popup_during_fullscreen <smart|ignore|leave_fullscreen>
--------------------------------------------------
+-----------------------------------------------------
+popup_during_fullscreen smart|ignore|leave_fullscreen
+-----------------------------------------------------
 
 *Example*:
 ------------------------------
@@ -908,9 +931,9 @@ parent+ to switch to different containers, you can use the
 will always wrap.
 
 *Syntax*:
------------------------------
-force_focus_wrapping <yes|no>
------------------------------
+---------------------------
+force_focus_wrapping yes|no
+---------------------------
 
 *Example*:
 ------------------------
@@ -930,9 +953,9 @@ For people who cannot modify their +~/.xsession+ to add the
 +--force-xinerama+ commandline parameter, a configuration option is provided:
 
 *Syntax*:
------------------------
-force_xinerama <yes|no>
------------------------
+---------------------
+force_xinerama yes|no
+---------------------
 
 *Example*:
 ------------------
@@ -952,9 +975,9 @@ mod+2 because somebody sent you a message. You don’t need to remember where yo
 came from now, you can just press $mod+2 again to switch back to "1: www".
 
 *Syntax*:
---------------------------------------
-workspace_auto_back_and_forth <yes|no>
---------------------------------------
+------------------------------------
+workspace_auto_back_and_forth yes|no
+------------------------------------
 
 *Example*:
 ---------------------------------
@@ -985,6 +1008,90 @@ force_display_urgency_hint <timeout> ms
 force_display_urgency_hint 500 ms
 ---------------------------------
 
+=== Delaying exiting on zero displays
+
+Outputs may disappear momentarily and come back later. For example,
+using a docking station that does not announce the undock (e.g. ACPI Undock 
+event triggered through manually pushing a button before actually ejecting 
+the notebook). During the removal of the notebook from the docking station,
+all outputs disappear momentarily.
+
+To prevent i3 from exiting when no output is available momentarily, you can 
+tell i3 to delay a certain time first and check available outputs again using 
+the +delay_exit_on_zero_displays+ directive. Setting the value to 0 disables 
+this feature.
+
+The default is 500ms.
+
+*Syntax*:
+----------------------------------------
+delay_exit_on_zero_displays <timeout> ms
+----------------------------------------
+
+*Example*:
+----------------------------------
+delay_exit_on_zero_displays 500 ms
+----------------------------------
+
+=== Focus on window activation
+
+[[focus_on_window_activation]]
+
+If a window is activated, e.g., via +google-chrome www.google.com+, it may request
+to take focus. Since this may not preferable, different reactions can be configured.
+
+Note that this may not affect windows that are being opened. To prevent new windows
+from being focused, see <<no_focus>>.
+
+*Syntax*:
+--------------------------------------------------
+focus_on_window_activation smart|urgent|focus|none
+--------------------------------------------------
+
+The different modes will act as follows:
+
+smart::
+    This is the default behavior. If the window requesting focus is on an active
+    workspace, it will receive the focus. Otherwise, the urgency hint will be set.
+urgent::
+    The window will always be marked urgent, but the focus will not be stolen.
+focus::
+    The window will always be focused and not be marked urgent.
+none::
+    The window will neither be focused, nor be marked urgent.
+
+=== Drawing marks on window decoration
+
+If activated, marks on windows are drawn in their window decoration. However,
+any mark starting with an underscore in its name (+_+) will not be drawn even if
+this option is activated.
+
+The default for this option is +yes+.
+
+*Syntax*:
+-----------------
+show_marks yes|no
+-----------------
+
+*Example*:
+--------------
+show_marks yes
+--------------
+
+[[line_continuation]]
+
+=== Line continuation
+
+Config files support line continuation, meaning when you end a line in a
+backslash character (`\`), the line-break will be ignored by the parser. This
+feature can be used to create more readable configuration files.
+
+*Examples*:
+-------------------
+bindsym Mod1+f \
+fullscreen toggle
+-------------------
+
 == Configuring i3bar
 
 The bar at the bottom of your monitor is drawn by a separate process called
@@ -1027,9 +1134,9 @@ The specified command will be passed to +sh -c+, so you can use globbing and
 have to have correct quoting etc.
 
 *Syntax*:
-----------------------
-i3bar_command command
-----------------------
+-----------------------
+i3bar_command <command>
+-----------------------
 
 *Example*:
 -------------------------------------------------
@@ -1046,17 +1153,23 @@ right hand side of the bar. This is useful to display system information like
 your current IP address, battery status or date/time.
 
 The specified command will be passed to +sh -c+, so you can use globbing and
-have to have correct quoting etc.
+have to have correct quoting etc. Note that for signal handling, depending on
+your shell (users of dash(1) are known to be affected), you have to use the
+shell’s exec command so that signals are passed to your program, not to the
+shell.
 
 *Syntax*:
-----------------------
-status_command command
-----------------------
+------------------------
+status_command <command>
+------------------------
 
 *Example*:
 -------------------------------------------------
 bar {
     status_command i3status --config ~/.i3status.conf
+
+    # For dash(1) users who want signal handling to work:
+    status_command exec ~/.bin/my_status_command
 }
 -------------------------------------------------
 
@@ -1092,11 +1205,11 @@ The default mode is dock mode; in hide mode, the default modifier is Mod4 (usual
 the windows key). The default value for the hidden_state is hide.
 
 *Syntax*:
-----------------
-mode <dock|hide|invisible>
-hidden_state <hide|show>
+-------------------------
+mode dock|hide|invisible
+hidden_state hide|show
 modifier <Modifier>
-----------------
+------------------------
 
 *Example*:
 ----------------
@@ -1112,23 +1225,41 @@ 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.
+default behavior. This is useful, e.g., for disabling the scroll wheel action
+or running scripts that implement custom behavior for these buttons.
+
+A button is always named +button<n>+, where 1 to 5 are default buttons as follows and higher
+numbers can be special buttons on devices offering more buttons:
+
+button1::
+    Left mouse button.
+button2::
+    Middle mouse button.
+button3::
+    Right mouse button.
+button4::
+    Scroll wheel up.
+button5::
+    Scroll wheel down.
+
+Please note that the old +wheel_up_cmd+ and +wheel_down_cmd+ commands are deprecated
+and will be removed in a future release. We strongly recommend using the more general
++bindsym+ with +button4+ and +button5+ instead.
 
 *Syntax*:
----------------------
-wheel_up_cmd <command>
-wheel_down_cmd <command>
----------------------
+----------------------------
+bindsym button<n> <command>
+----------------------------
 
 *Example*:
----------------------
+---------------------------------------------------------
 bar {
-    wheel_up_cmd nop
-    wheel_down_cmd exec ~/.i3/scripts/custom_wheel_down
+    # disable clicking on workspace buttons
+    bindsym button1 nop
+    # execute custom script when scrolling downwards
+    bindsym button5 exec ~/.i3/scripts/custom_wheel_down
 }
----------------------
+---------------------------------------------------------
 
 === Bar ID
 
@@ -1156,9 +1287,9 @@ This option determines in which edge of the screen i3bar should show up.
 The default is bottom.
 
 *Syntax*:
----------------------
-position <top|bottom>
----------------------
+-------------------
+position top|bottom
+-------------------
 
 *Example*:
 ---------------------
@@ -1211,9 +1342,9 @@ You can configure on which output (monitor) the icons should be displayed or
 you can turn off the functionality entirely.
 
 *Syntax*:
--------------------------
-tray_output <none|primary|output>
--------------------------
+---------------------------------
+tray_output none|primary|<output>
+---------------------------------
 
 *Example*:
 -------------------------
@@ -1223,7 +1354,9 @@ bar {
 }
 
 # show tray icons on the primary monitor
-tray_output primary
+bar {
+    tray_output primary
+}
 
 # show tray icons on the big monitor
 bar {
@@ -1236,6 +1369,23 @@ Note that you might not have a primary output configured yet. To do so, run:
 xrandr --output <output> --primary
 -------------------------
 
+=== Tray padding
+
+The tray is shown on the right-hand side of the bar. By default, a padding of 2
+pixels is used for the upper, lower and right-hand side of the tray area and
+between the individual icons.
+
+*Syntax*:
+-------------------------
+tray_padding <px> [px]
+-------------------------
+
+*Example*:
+-------------------------
+# Obey Fitts's law
+tray_padding 0
+-------------------------
+
 === Font
 
 Specifies the font to be used in the bar. See <<fonts>>.
@@ -1278,9 +1428,9 @@ you want to display a statusline-only bar containing additional information.
 The default is to show workspace buttons.
 
 *Syntax*:
---------------------------
-workspace_buttons <yes|no>
---------------------------
+------------------------
+workspace_buttons yes|no
+------------------------
 
 *Example*:
 ------------------------
@@ -1303,9 +1453,9 @@ workspaces to "1:I", "2:II", "3:III", "4:IV", ...
 The default is to display the full name within the workspace button.
 
 *Syntax*:
-----------------------------------
-strip_workspace_numbers <yes|no>
-----------------------------------
+------------------------------
+strip_workspace_numbers yes|no
+------------------------------
 
 *Example*:
 ----------------------------
@@ -1324,9 +1474,9 @@ For an example of a +mode+ definition, see <<resizingconfig>>.
 The default is to show the mode indicator.
 
 *Syntax*:
--------------------------------
-binding_mode_indicator <yes|no>
--------------------------------
+-----------------------------
+binding_mode_indicator yes|no
+-----------------------------
 
 *Example*:
 -----------------------------
@@ -1360,7 +1510,10 @@ inactive_workspace::
        will be the case for most workspaces.
 urgent_workspace::
        Border, background and text color for a workspace button when the workspace
-       contains a window with the urgency hint set. Also applies to +mode+ indicators.
+       contains a window with the urgency hint set.
+binding_mode::
+        Border, background and text color for the binding mode indicator. If not used,
+        the colors will be taken from +urgent_workspace+.
 
 *Syntax*:
 ----------------------------------------
@@ -1369,7 +1522,7 @@ colors {
     statusline <color>
     separator <color>
 
-    colorclass <border> <background> <text>
+    <colorclass> <border> <background> <text>
 }
 ----------------------------------------
 
@@ -1385,6 +1538,7 @@ bar {
         active_workspace   #333333 #5f676a #ffffff
         inactive_workspace #333333 #222222 #888888
         urgent_workspace   #2f343a #900000 #ffffff
+        binding_mode       #2f343a #900000 #ffffff
     }
 }
 --------------------------------------
@@ -1401,6 +1555,8 @@ do this is to use the +i3-msg+ utility:
 i3-msg border none
 --------------------------
 
+[[command_chaining]]
+
 Commands can be chained by using +;+ (a semicolon). So, to move a window to a
 specific workspace and immediately switch to that workspace, you can configure
 the following keybinding:
@@ -1445,6 +1601,10 @@ instance::
        Compares the window instance (the first part of WM_CLASS)
 window_role::
        Compares the window role (WM_WINDOW_ROLE).
+window_type::
+        Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
+        +normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
+        +popup_menu+ and +toolti+.
 id::
        Compares the X11 window ID, which you can get via +xwininfo+ for example.
 title::
@@ -1453,14 +1613,16 @@ urgent::
        Compares the urgent state of the window. Can be "latest" or "oldest".
        Matches the latest or oldest urgent window, respectively.
        (The following aliases are also available: newest, last, recent, first)
+workspace::
+       Compares the workspace name of the workspace the window belongs to.
 con_mark::
        Compares the mark set for this container, see <<vim_like_marks>>.
 con_id::
        Compares the i3-internal container ID, which you can get via the IPC
        interface. Handy for scripting.
 
-The criteria +class+, +instance+, +role+, +title+ and +mark+ are actually
-regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
+The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are
+actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for
 information on how to use them.
 
 [[exec]]
@@ -1477,9 +1639,9 @@ and +,+ (comma): they chain commands together in i3, so you need to use quoted
 strings if they appear in your command.
 
 *Syntax*:
-------------------------------
-exec [--no-startup-id] command
-------------------------------
+--------------------------------
+exec [--no-startup-id] <command>
+--------------------------------
 
 *Example*:
 ------------------------------
@@ -1512,9 +1674,9 @@ orientation will be changed (if it does not have more than one window). Use
 to splith or vice-versa.
 
 *Syntax*:
----------------------------
-split <vertical|horizontal>
----------------------------
+-------------------------
+split vertical|horizontal
+-------------------------
 
 *Example*:
 ------------------------------
@@ -1537,10 +1699,10 @@ Likewise, to make the current window floating (or tiling again) use +floating
 enable+ respectively +floating disable+ (or +floating toggle+):
 
 *Syntax*:
---------------
-layout <default|tabbed|stacking|splitv|splith>
+--------------------------------------------
+layout default|tabbed|stacking|splitv|splith
 layout toggle [split|all]
---------------
+--------------------------------------------
 
 *Examples*:
 --------------
@@ -1561,15 +1723,17 @@ bindsym $mod+f fullscreen toggle
 bindsym $mod+t floating toggle
 --------------
 
-=== Focusing/Moving containers
+[[_focusing_moving_containers]]
 
-To change the focus, use the focus command: +focus left+, +focus right+, +focus
-down+ and +focus up+.
+=== Focusing containers
 
-There are a few special parameters you can use for the focus command:
+To change focus, you can use the +focus+ command. The following options are
+available:
 
+left|right|up|down::
+        Sets focus to the nearest container in the given direction.
 parent::
-       Sets focus to the +Parent Container+ of the current +Container+.
+       Sets focus to the parent container of the current container.
 child::
        The opposite of +focus parent+, sets the focus to the last focused
        child container.
@@ -1583,23 +1747,16 @@ output::
        Followed by a direction or an output name, this will focus the
        corresponding output.
 
-For moving, use +move left+, +move right+, +move down+ and +move up+.
-
 *Syntax*:
------------------------------------
-focus <left|right|down|up>
-focus <parent|child|floating|tiling|mode_toggle>
-focus output <<left|right|down|up>|output>
-move <left|right|down|up> [<px> px]
-move [absolute] position [[<px> px] [<px> px]|center]
------------------------------------
-
-Note that the amount of pixels you can specify for the +move+ command is only
-relevant for floating containers. The default amount is 10 pixels.
+----------------------------------------------
+focus left|right|down|up
+focus parent|child|floating|tiling|mode_toggle
+focus output left|right|up|down|<output>
+----------------------------------------------
 
 *Examples*:
-----------------------
-# Focus container on the left, bottom, top, right:
+-------------------------------------------------
+# Focus container on the left, bottom, top, right
 bindsym $mod+j focus left
 bindsym $mod+k focus down
 bindsym $mod+l focus up
@@ -1616,8 +1773,33 @@ bindsym $mod+x focus output right
 
 # Focus the big output
 bindsym $mod+x focus output HDMI-2
+-------------------------------------------------
+
+=== Moving containers
+
+Use the +move+ command to move a container.
 
-# Move container to the left, bottom, top, right:
+*Syntax*:
+-----------------------------------------------------
+# Moves the container into the given direction.
+# The optional pixel argument specifies how far the
+# container should be moved if it is floating and
+# defaults to 10 pixels.
+move <left|right|down|up> [<px> px]
+
+# Moves the container either to a specific location
+# or to the center of the screen. If 'absolute' is
+# used, it is moved to the center of all outputs.
+move [absolute] position [[<px> px] [<px> px]|center]
+
+# Moves the container to the current position of the
+# mouse cursor. Only affects floating containers.
+move position mouse
+-----------------------------------------------------
+
+*Examples*:
+-------------------------------------------------------
+# Move container to the left, bottom, top, right
 bindsym $mod+j move left
 bindsym $mod+k move down
 bindsym $mod+l move up
@@ -1627,10 +1809,12 @@ bindsym $mod+semicolon move right
 # move more than the default
 bindsym $mod+j move left 20 px
 
-# Move floating container to the center
-# of all outputs
+# Move floating container to the center of all outputs
 bindsym $mod+c move absolute position center
-----------------------
+
+# Move container to the current position of the cursor
+bindsym $mod+m move position mouse
+-------------------------------------------------------
 
 === Changing (named) workspaces/moving to workspaces
 
@@ -1661,14 +1845,14 @@ workspace using +move container to workspace back_and_forth+.
 
 *Syntax*:
 -----------------------------------
-workspace <next|prev|next_on_output|prev_on_output>
+workspace next|prev|next_on_output|prev_on_output
 workspace back_and_forth
 workspace <name>
 workspace number <name>
 
 move [window|container] [to] workspace <name>
 move [window|container] [to] workspace number <name>
-move [window|container] [to] workspace <prev|next|current>
+move [window|container] [to] workspace prev|next|current
 -----------------------------------
 
 *Examples*:
@@ -1760,10 +1944,10 @@ To move a container to another RandR output (addressed by names like +LVDS1+ or
 +right+, +up+ or +down+), there are two commands:
 
 *Syntax*:
---------------------------------------------------------
-move container to output <<left|right|down|up>|<output>>
-move workspace to output <<left|right|down|up>|<output>>
---------------------------------------------------------
+----------------------------------------------------
+move container to output left|right|down|up|<output>
+move workspace to output left|right|down|up|<output>
+----------------------------------------------------
 
 *Examples*:
 --------------------------------------------------------
@@ -1775,6 +1959,26 @@ bindsym $mod+x move workspace to output right
 bindsym $mod+x move container to output VGA1
 --------------------------------------------------------
 
+=== Moving containers/workspaces to marks
+
+To move a container to another container with a specific mark (see <<vim_like_marks>>),
+you can use the following command.
+
+The window will be moved right after the marked container in the tree, i.e., it ends up
+in the same position as if you had opened a new window when the marked container was
+focused. If the mark is on a split container, the window will appear as a new child
+after the currently focused child within that container.
+
+*Syntax*:
+------------------------------------
+move window|container to mark <mark>
+------------------------------------
+
+*Example*:
+--------------------------------------------------------
+for_window [instance="tabme"] move window to mark target
+--------------------------------------------------------
+
 [[resizingconfig]]
 
 === Resizing containers/windows
@@ -1783,9 +1987,9 @@ If you want to resize containers/windows using your keyboard, you can use the
 +resize+ command:
 
 *Syntax*:
----------------------------------------------------------
-resize <grow|shrink> <direction> [<px> px [or <ppt> ppt]]
----------------------------------------------------------
+-------------------------------------------------------
+resize grow|shrink <direction> [<px> px [or <ppt> ppt]]
+-------------------------------------------------------
 
 Direction can either be one of +up+, +down+, +left+ or +right+. Or you can be
 less specific and use +width+ or +height+, in which case i3 will take/give
@@ -1864,11 +2068,17 @@ window, you cannot simply bind it to a key.  +i3-input+ is a tool created
 for this purpose: It lets you input a command and sends the command to i3. It
 can also prefix this command and display a custom prompt for the input dialog.
 
+The additional +--toggle+ option will remove the mark if the window already has
+this mark, add it if the window has none or replace the current mark if it has
+another mark.
+
+Refer to +show_marks+ if you don't want marks to be shown in the window decoration.
+
 *Syntax*:
 ------------------------------
-mark identifier
+mark [--toggle] <identifier>
 [con_mark="identifier"] focus
-unmark identifier
+unmark <identifier>
 ------------------------------
 
 *Example (in a terminal)*:
@@ -1893,52 +2103,63 @@ Alternatively, if you do not want to mess with +i3-input+, you could create
 seperate bindings for a specific set of labels and then only use those labels.
 ///////////////////////////////////////////////////////////////////
 
-=== Changing border style
+=== Window title format
 
-To change the border of the current client, you can use +border normal+ to use the normal
-border (including window title), +border 1pixel+ to use a 1-pixel border (no window title)
-and +border none+ to make the client borderless.
+By default, i3 will simply print the X11 window title. Using +title_format+,
+this can be customized by setting the format to the desired output. This
+directive supports
+https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]
+and the following placeholders which will be replaced:
 
-There is also +border toggle+ which will toggle the different border styles.
++%title+::
+    The X11 window title (_NET_WM_NAME or WM_NAME as fallback).
+
+Using the <<for_window>> directive, you can set the title format for any window
+based on <<command_criteria>>.
+
+*Syntax*:
+---------------------
+title_format <format>
+---------------------
 
 *Examples*:
-----------------------------
-bindsym $mod+t border normal
-bindsym $mod+y border 1pixel
-bindsym $mod+u border none
-----------------------------
+-------------------------------------------------------------------------------------
+# give the focused window a prefix
+bindsym $mod+p title_format "Important | %title"
 
-[[stack-limit]]
+# print all window titles bold
+for_window [class=".*"] title_format "<b>%title</b>"
 
-///////////////////////////////////////////////////////////////////////////////
-TODO: not yet implemented
-=== Changing the stack-limit of a container
+# print window titles of firefox windows red
+for_window [class="(?i)firefox"] title_format "<span foreground='red'>%title</span>"
+-------------------------------------------------------------------------------------
 
-If you have a single container with a lot of windows inside it (say, more than
-10), the default layout of a stacking container can get a little unhandy.
-Depending on your screen’s size, you might end up with only half of the title
-lines being actually used, wasting a lot of screen space.
+=== Changing border style
 
-Using the +stack-limit+ command, you can limit the number of rows or columns
-in a stacking container. i3 will create columns or rows (depending on what
-you limited) automatically as needed.
+To change the border of the current client, you can use +border normal+ to use the normal
+border (including window title), +border pixel 1+ to use a 1-pixel border (no window title)
+and +border none+ to make the client borderless.
 
-*Syntax*:
---------------------------------
-stack-limit <cols|rows> <value>
---------------------------------
+There is also +border toggle+ which will toggle the different border styles.
 
-*Examples*:
--------------------
-# I always want to have two window titles in one line
-stack-limit cols 2
+*Syntax*:
+-----------------------------------------------
+border normal|pixel [<n>]
+border none|toggle
 
-# Not more than 5 rows in this stacking container
-stack-limit rows 5
--------------------
+# legacy syntax, equivalent to "border pixel 1"
+border 1pixel
+-----------------------------------------------
 
-image:stacklimit.png[Container limited to two columns]
-///////////////////////////////////////////////////////////////////////////////
+*Examples*:
+----------------------------------------------
+# use window title, but no border
+bindsym $mod+t border normal 0
+# use no window title and a thick border
+bindsym $mod+y border pixel 3
+# use neither window title nor border
+bindsym $mod+u border none
+----------------------------------------------
 
 [[shmlog]]
 
@@ -1954,7 +2175,7 @@ discarded and a new one will be started.
 *Syntax*:
 ------------------------------
 shmlog <size_in_bytes>
-shmlog <on|off|toggle>
+shmlog on|off|toggle
 ------------------------------
 
 *Examples*:
@@ -1975,9 +2196,9 @@ command does not activate shared memory logging (shmlog), and as such is most
 likely useful in combination with the above-described <<shmlog>> command.
 
 *Syntax*:
-------------------------
-debuglog <on|off|toggle>
-------------------------
+----------------------
+debuglog on|off|toggle
+----------------------
 
 *Examples*:
 ------------------------
index 9b5ef562941779da30ff5e6386f178bd36c9650d..f40bb390100b1ff3c26a748e4c84473a54e0317d 100755 (executable)
@@ -2,7 +2,7 @@
 # vim:ts=4:sw=4:expandtab
 #
 # i3 - an improved dynamic tiling window manager
-# © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
 #
 # generate-command-parser.pl: script to generate parts of the command parser
 # from its specification file parser-specs/commands.spec.
index 1dab64526165e73c1642172c459ac4e33f9e8d3a..900893f5e85297824fc0ae1ee0621d9c826b7d5a 100644 (file)
@@ -20,8 +20,8 @@ i3-config-wizard/i3-config-wizard: libi3.a $(i3_config_wizard_OBJECTS)
 
 install-i3-config-wizard: i3-config-wizard/i3-config-wizard
        echo "[i3-config-wizard] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-config-wizard/i3-config-wizard $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
+       $(INSTALL) -m 0755 i3-config-wizard/i3-config-wizard $(DESTDIR)$(EXEC_PREFIX)/bin/
 
 clean-i3-config-wizard:
        echo "[i3-config-wizard] Clean"
index 35770dc9fa807c38feda80b71c3c97425e5a12f5..4c1d96971ec0d5cc27978b0ed24c219ba82252d1 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-config-wizard: Program to convert configs using keycodes to configs using
  *                   keysyms.
@@ -461,38 +461,6 @@ void errorlog(char *fmt, ...) {
 void debuglog(char *fmt, ...) {
 }
 
-/*
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-static char *resolve_tilde(const char *path) {
-    static glob_t globbuf;
-    char *head, *tail, *result;
-
-    tail = strchr(path, '/');
-    head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
-
-    int res = glob(head, GLOB_TILDE, NULL, &globbuf);
-    free(head);
-    /* no match, or many wildcard matches are bad */
-    if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
-        result = strdup(path);
-    else if (res != 0) {
-        err(1, "glob() failed");
-    } else {
-        head = globbuf.gl_pathv[0];
-        result = calloc(1, strlen(head) + (tail ? strlen(tail) : 0) + 1);
-        strncpy(result, head, strlen(head));
-        if (tail)
-            strncat(result, tail, strlen(tail));
-    }
-    globfree(&globbuf);
-
-    return result;
-}
-
 /*
  * Handles expose events, that is, draws the window contents.
  *
@@ -514,17 +482,23 @@ static int handle_expose() {
         set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
 
         txt(logical_px(10), 2, "You have not configured i3 yet.");
-        txt(logical_px(10), 3, "Do you want me to generate ~/.i3/config?");
-        txt(logical_px(85), 5, "Yes, generate ~/.i3/config");
-        txt(logical_px(85), 7, "No, I will use the defaults");
+        txt(logical_px(10), 3, "Do you want me to generate a config at");
+
+        char *msg;
+        sasprintf(&msg, "%s?", config_path);
+        txt(logical_px(10), 4, msg);
+        free(msg);
+
+        txt(logical_px(85), 6, "Yes, generate the config");
+        txt(logical_px(85), 8, "No, I will use the defaults");
 
         /* green */
         set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
-        txt(logical_px(25), 5, "<Enter>");
+        txt(logical_px(25), 6, "<Enter>");
 
         /* red */
         set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
-        txt(logical_px(31), 7, "<ESC>");
+        txt(logical_px(31), 8, "<ESC>");
     }
 
     if (current_step == STEP_GENERATE) {
@@ -534,7 +508,7 @@ static int handle_expose() {
         txt(logical_px(85), 4, "Win as default modifier");
         txt(logical_px(85), 5, "Alt as default modifier");
         txt(logical_px(10), 7, "Afterwards, press");
-        txt(logical_px(85), 9, "to write ~/.i3/config");
+        txt(logical_px(85), 9, "to write the config");
         txt(logical_px(85), 10, "to abort");
 
         /* the not-selected modifier */
@@ -772,7 +746,7 @@ static void finish() {
 }
 
 int main(int argc, char *argv[]) {
-    config_path = resolve_tilde("~/.i3/config");
+    char *xdg_config_home;
     socket_path = getenv("I3SOCK");
     char *pattern = "pango:monospace 8";
     char *patternbold = "pango:monospace bold 8";
@@ -806,20 +780,29 @@ int main(int argc, char *argv[]) {
         }
     }
 
-    /* Check if the destination config file does not exist but the path is
-     * writable. If not, exit now, this program is not useful in that case. */
-    struct stat stbuf;
-    if (stat(config_path, &stbuf) == 0) {
-        printf("The config file \"%s\" already exists. Exiting.\n", config_path);
+    char *path = get_config_path(NULL, false);
+    if (path != NULL) {
+        printf("The config file \"%s\" already exists. Exiting.\n", path);
+        free(path);
         return 0;
     }
 
-    /* Create ~/.i3 if it does not yet exist */
-    char *config_dir = resolve_tilde("~/.i3");
+    /* Always write to $XDG_CONFIG_HOME/i3/config by default. */
+    if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
+        xdg_config_home = "~/.config";
+
+    xdg_config_home = resolve_tilde(xdg_config_home);
+    sasprintf(&config_path, "%s/i3/config", xdg_config_home);
+
+    /* Create $XDG_CONFIG_HOME/i3 if it does not yet exist */
+    char *config_dir;
+    struct stat stbuf;
+    sasprintf(&config_dir, "%s/i3", xdg_config_home);
     if (stat(config_dir, &stbuf) != 0)
-        if (mkdir(config_dir, 0755) == -1)
-            err(1, "mkdir(%s) failed", config_dir);
+        if (!mkdirp(config_dir))
+            err(EXIT_FAILURE, "mkdirp(%s) failed", config_dir);
     free(config_dir);
+    free(xdg_config_home);
 
     int fd;
     if ((fd = open(config_path, O_CREAT | O_RDWR, 0644)) == -1) {
index cc72f1016429780c2e13a2ef8378c119701cb9e0..a0549ead7dbb81c37991cc748f782ef2eace0f33 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env perl
 # vim:ts=4:sw=4:expandtab
 #
-# © 2012-2014 Michael Stapelberg
+# © 2012 Michael Stapelberg
 #
 # No dependencies except for perl ≥ v5.10
 
@@ -45,7 +45,7 @@ my $result = GetOptions(
     'dmenu=s' => \$dmenu_cmd,
     'entry-type=s' => \@entry_types,
     'version' => sub {
-        say "dmenu-desktop 1.5 © 2012-2013 Michael Stapelberg";
+        say "dmenu-desktop 1.5 © 2012 Michael Stapelberg";
         exit 0;
     },
     'help' => sub {
index bbce356fddd78eba858f993acb91c77d2724d6d0..7e5d4499fab7dc5d8f3dde55f401437b0d9f8f02 100644 (file)
@@ -20,8 +20,8 @@ i3-dump-log/i3-dump-log: libi3.a $(i3_dump_log_OBJECTS)
 
 install-i3-dump-log: i3-dump-log/i3-dump-log
        echo "[i3-dump-log] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-dump-log/i3-dump-log $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
+       $(INSTALL) -m 0755 i3-dump-log/i3-dump-log $(DESTDIR)$(EXEC_PREFIX)/bin/
 
 clean-i3-dump-log:
        echo "[i3-dump-log] Clean"
index 1b0d593c5e1c7322c11c210e739adfade5f50030..137554a4f1fefc36539c5ca8c40b5357b32a7e2a 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-dump-log/main.c: Dumps the i3 SHM log to stdout.
  *
index 03f4e0a6e5f53fbe994e102a90b8769c7e902db4..be3e28331d263d02d820be553ae4e0d82508b491 100644 (file)
@@ -20,8 +20,8 @@ i3-input/i3-input: libi3.a $(i3_input_OBJECTS)
 
 install-i3-input: i3-input/i3-input
        echo "[i3-input] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-input/i3-input $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
+       $(INSTALL) -m 0755 i3-input/i3-input $(DESTDIR)$(EXEC_PREFIX)/bin/
 
 clean-i3-input:
        echo "[i3-input] Clean"
index 996fc0ea9f08958a3b563a359e85ad6c7388b798..6736aad3a97052e3d671029275b9510c28935860 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-input/main.c: Utility which lets the user input commands and sends them
  *                  to i3.
@@ -314,6 +314,50 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
     return 1;
 }
 
+static xcb_rectangle_t get_window_position(void) {
+    xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8)};
+
+    xcb_get_input_focus_reply_t *input_focus = NULL;
+    xcb_get_geometry_reply_t *geometry = NULL;
+    xcb_translate_coordinates_reply_t *coordinates = NULL;
+
+    /* In rare cases, the window holding the input focus might disappear while we are figuring out its
+     * position. To avoid this, we grab the server in the meantime. */
+    xcb_grab_server(conn);
+
+    input_focus = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
+    if (input_focus == NULL || input_focus->focus == XCB_NONE) {
+        DLOG("Failed to receive the current input focus or no window has the input focus right now.\n");
+        goto free_resources;
+    }
+
+    geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL);
+    if (geometry == NULL) {
+        DLOG("Failed to received window geometry.\n");
+        goto free_resources;
+    }
+
+    coordinates = xcb_translate_coordinates_reply(
+        conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL);
+    if (coordinates == NULL) {
+        DLOG("Failed to translate coordinates.\n");
+        goto free_resources;
+    }
+
+    DLOG("Determined coordinates of window with input focus at x = %i / y = %i.\n", coordinates->dst_x, coordinates->dst_y);
+    result.x += coordinates->dst_x;
+    result.y += coordinates->dst_y;
+
+free_resources:
+    xcb_ungrab_server(conn);
+    xcb_flush(conn);
+
+    FREE(input_focus);
+    FREE(geometry);
+    FREE(coordinates);
+    return result;
+}
+
 int main(int argc, char *argv[]) {
     format = strdup("%s");
     socket_path = getenv("I3SOCK");
@@ -402,15 +446,17 @@ int main(int argc, char *argv[]) {
     if (prompt != NULL)
         prompt_offset = predict_text_width(prompt);
 
+    const xcb_rectangle_t win_pos = get_window_position();
+
     /* Open an input window */
     win = xcb_generate_id(conn);
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                                                                          /* the window id */
-        root,                                                                         /* parent == root */
-        logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8), /* dimensions */
-        0,                                                                            /* X11 border = 0, we draw our own */
+        win,                                                 /* the window id */
+        root,                                                /* parent == root */
+        win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
+        0,                                                   /* X11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
index b78378be1c65a68225a38d8ebace7b5be0845a3d..5da4e26ed447d7b30316adec7fd81af1bf6b9f22 100755 (executable)
@@ -117,7 +117,6 @@ for my $line (@lines) {
 
     # new_container changed only the statement name to workspace_layout
     if ($statement eq 'new_container') {
-        # TODO: new_container stack-limit
         print "workspace_layout$parameters\n";
         next;
     }
@@ -184,7 +183,6 @@ sub convert_command {
         restart
         reload
         exit
-        stack-limit
     );
 
     my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
index fda56dad003e524c797da9f9e9954319283deca3..277c43e7b788407013dccf69f408330e802cc1e4 100644 (file)
@@ -20,8 +20,8 @@ i3-msg/i3-msg: libi3.a $(i3_msg_OBJECTS)
 
 install-i3-msg: i3-msg/i3-msg
        echo "[i3-msg] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-msg/i3-msg $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
+       $(INSTALL) -m 0755 i3-msg/i3-msg $(DESTDIR)$(EXEC_PREFIX)/bin/
 
 clean-i3-msg:
        echo "[i3-msg] Clean"
index 354e8af90d61b0b939cbb7d241a0ac8a53cd2ebe..6a6186d8288d6e71679742f7ee90a2b92f34260e 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-msg/main.c: Utility which sends messages to a running i3-instance using
  * IPC via UNIX domain sockets.
index e54aa6544876739b6289edec0dad9f12acb5c825..aba3c09ab49223fce156f0a61cf5b83d307a5dc4 100644 (file)
@@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar
 
 i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c)
 i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h)
-i3_nagbar_CFLAGS   = $(XCB_CFLAGS) $(PANGO_CFLAGS)
-i3_nagbar_LIBS     = $(XCB_LIBS) $(PANGO_LIBS)
+i3_nagbar_CFLAGS   = $(XCB_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS)
+i3_nagbar_LIBS     = $(XCB_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS)
 
 i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o)
 
@@ -20,8 +20,8 @@ i3-nagbar/i3-nagbar: libi3.a $(i3_nagbar_OBJECTS)
 
 install-i3-nagbar: i3-nagbar/i3-nagbar
        echo "[i3-nagbar] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3-nagbar/i3-nagbar $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
+       $(INSTALL) -m 0755 i3-nagbar/i3-nagbar $(DESTDIR)$(EXEC_PREFIX)/bin/
 
 clean-i3-nagbar:
        echo "[i3-nagbar] Clean"
index bf6d1cca41f29a6ba39bec468c677ef3571b2734..aca70ab115efd4c2021a81b37dff41068f3db26b 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-nagbar is a utility which displays a nag message, for example in the case
  * when the user has an error in their configuration file.
@@ -27,6 +27,7 @@
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
 #include <xcb/xcb_event.h>
+#include <xcb/randr.h>
 
 #include "libi3.h"
 #include "i3-nagbar.h"
@@ -288,6 +289,60 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
     return 1;
 }
 
+/**
+ * Return the position and size the i3-nagbar window should use.
+ * This will be the primary output or a fallback if it cannot be determined.
+ */
+static xcb_rectangle_t get_window_position(void) {
+    /* Default values if we cannot determine the primary output or its CRTC info. */
+    xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + logical_px(8) + logical_px(8)};
+
+    xcb_randr_get_screen_resources_current_cookie_t rcookie = xcb_randr_get_screen_resources_current(conn, root);
+    xcb_randr_get_output_primary_cookie_t pcookie = xcb_randr_get_output_primary(conn, root);
+
+    xcb_randr_get_output_primary_reply_t *primary = NULL;
+    xcb_randr_get_screen_resources_current_reply_t *res = NULL;
+
+    if ((primary = xcb_randr_get_output_primary_reply(conn, pcookie, NULL)) == NULL) {
+        DLOG("Could not determine the primary output.\n");
+        goto free_resources;
+    }
+
+    if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
+        goto free_resources;
+    }
+
+    xcb_randr_get_output_info_reply_t *output =
+        xcb_randr_get_output_info_reply(conn,
+                                        xcb_randr_get_output_info(conn, primary->output, res->config_timestamp),
+                                        NULL);
+    if (output == NULL || output->crtc == XCB_NONE)
+        goto free_resources;
+
+    xcb_randr_get_crtc_info_reply_t *crtc =
+        xcb_randr_get_crtc_info_reply(conn,
+                                      xcb_randr_get_crtc_info(conn, output->crtc, res->config_timestamp),
+                                      NULL);
+    if (crtc == NULL)
+        goto free_resources;
+
+    DLOG("Found primary output on position x = %i / y = %i / w = %i / h = %i.\n",
+         crtc->x, crtc->y, crtc->width, crtc->height);
+    if (crtc->width == 0 || crtc->height == 0) {
+        DLOG("Primary output is not active, ignoring it.\n");
+        goto free_resources;
+    }
+
+    result.x = crtc->x;
+    result.y = crtc->y;
+    goto free_resources;
+
+free_resources:
+    FREE(res);
+    FREE(primary);
+    return result;
+}
+
 int main(int argc, char *argv[]) {
     /* The following lines are a terribly horrible kludge. Because terminal
      * emulators have different ways of interpreting the -e command line
@@ -410,16 +465,18 @@ int main(int argc, char *argv[]) {
     font = load_font(pattern, true);
     set_font(&font);
 
+    xcb_rectangle_t win_pos = get_window_position();
+
     /* Open an input window */
     win = xcb_generate_id(conn);
 
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                                                                         /* the window id */
-        root,                                                                        /* parent == root */
-        50, 50, 500, font.height + logical_px(8) + logical_px(8) /* 8 px padding */, /* dimensions */
-        0,                                                                           /* x11 border = 0, we draw our own */
+        win,                                                 /* the window id */
+        root,                                                /* parent == root */
+        win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
+        0,                                                   /* x11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
index 289fd8c6980d4e8aa25cc64d9390947459846ee3..18a1a38068b7da28b9b4efd69c92da4bbb6e4a72 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env perl
 # vim:ts=4:sw=4:expandtab
 #
-# © 2013-2014 Michael Stapelberg
+# © 2013 Michael Stapelberg
 #
 # Requires perl ≥ v5.10, AnyEvent::I3 and JSON::XS
 
index 5ad786062405a9c4a571ca6a1f54cf60ee366ade..ce71686bd01182ec79651f2d46c078fe7ce36c11 100755 (executable)
@@ -11,7 +11,7 @@
 # Hopefully one of these is installed (no flamewars about preference please!):
 # 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
+for pager in $PAGER less most w3m pg i3-sensible-editor; do
     if command -v $pager > /dev/null 2>&1; then
         exec $pager "$@"
     fi
index dd4b075ac981740144011de201ebcb5acba232c3..c80e5ee211b785e1f4dc59efc4501c73d5b44bcb 100755 (executable)
@@ -8,7 +8,7 @@
 # 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
+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology; do
     if command -v $terminal > /dev/null 2>&1; then
         exec $terminal "$@"
     fi
index 53227a8e10fbb29d0468389bc1bb1a65c2fefa99..737b0b6999db1aed5e9dbe75d95fe4515bad08bf 100644 (file)
@@ -20,8 +20,8 @@ i3bar/i3bar: libi3.a $(i3bar_OBJECTS)
 
 install-i3bar: i3bar/i3bar
        echo "[i3bar] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
-       $(INSTALL) -m 0755 i3bar/i3bar $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
+       $(INSTALL) -m 0755 i3bar/i3bar $(DESTDIR)$(EXEC_PREFIX)/bin/
 
 clean-i3bar:
        echo "[i3bar] Clean"
index b87cfaacacffd8d78ff8679cf71dd8fe7e32c998..873a34651a2a735248a96a549eb29fe327d52b0a 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * child.c: Getting input for the statusline
  *
index 22b9a28ac81d39e9d7a3a24cf40f2ba900839fe7..90da9388a60b128b34ed2b929df8263c17984e2c 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  */
 #pragma once
index fdc37445ab4944549742ca7c580fa4f488e51de0..1ce5dfa6e4bebaaf930114d61081498a1f77ce55 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * config.c: Parses the configuration (received from i3).
  *
@@ -22,10 +22,16 @@ typedef enum { M_DOCK = 0,
                M_HIDE = 1,
                M_INVISIBLE = 2 } bar_display_mode_t;
 
+typedef struct binding_t {
+    int input_code;
+    char *command;
+
+    TAILQ_ENTRY(binding_t) bindings;
+} binding_t;
+
 typedef struct config_t {
     int modifier;
-    char *wheel_up_cmd;
-    char *wheel_down_cmd;
+    TAILQ_HEAD(bindings_head, binding_t) bindings;
     position_t position;
     int verbose;
     struct xcb_color_strings_t colors;
@@ -37,6 +43,7 @@ typedef struct config_t {
     char *fontname;
     i3String *separator_symbol;
     char *tray_output;
+    int tray_padding;
     int num_outputs;
     char **outputs;
 
index a60a1ebae1603e44ae0f93847e58ebc6d4fc9f00..686c0322d31c9bd119f9df6627d44dda557df1e4 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * ipc.c: Communicating with i3
  *
index 37e8e01793d75fea581bd564f58a6155770ddaf0..828d4906a695033aad2fe8fa2132b1b5a48fbfc1 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * mode.c: Handle "mode" event and show current binding mode in the bar
  *
index 73fbf33f32caf5a9ad674a0f18ebd3e903850797..e6605e1f73da2b7892e6be88b7360c31118f94e4 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * outputs.c: Maintaining the outputs list
  *
index ef13cf78dd7564c4b686e3ad0e9d958f9f581b81..41e97de2f994ef24a754148e9f676c12446efd60 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * parse_json_header.c: Parse the JSON protocol header to determine
  *                      protocol version and features.
index 1720ec3b860d68c2f4f5e86bcc7c2ce1ec8919a6..694faa483a7ac5682812094503d888ff2362f055 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  */
 #pragma once
index ac13799851c097d08a4c638cf53eb8dda8a44933..ba08cf763b79e587d9b1f3f4cad95fec4c778592 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #pragma once
index 1b8c6c729bd813ba6afb4c26f3e6af1ff5adcea2..5ed84de86e426dc3f9f70dd318a3b4d79dbfb9c2 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * workspaces.c: Maintaining the workspace lists
  *
index bb37e7d517921066922fca075ff6b591421c7834..835105e7fe7bde2a7315c9bb659cbce66a854697 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * xcb.c: Communicating with X
  *
@@ -40,6 +40,9 @@ struct xcb_color_strings_t {
     char *urgent_ws_bg;
     char *urgent_ws_fg;
     char *urgent_ws_border;
+    char *binding_mode_bg;
+    char *binding_mode_fg;
+    char *binding_mode_border;
 };
 
 typedef struct xcb_colors_t xcb_colors_t;
index e2638e7bd863225887b41b0e94808ea091af87f8..e369c54deb190a0e3651c34e0fb3cab7e1d5525c 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * child.c: Getting input for the statusline
  *
index f7abaee7dff9f9aa65abab5af1fc36a3c4c65cfb..65447e7ecaa14bf0f11af8d634f262261c29c28f 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2011 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * config.c: Parses the configuration (received from i3).
  *
@@ -20,6 +20,7 @@
 #include "common.h"
 
 static char *cur_key;
+static bool parsing_bindings;
 
 /*
  * Parse a key.
@@ -34,6 +35,14 @@ static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t
     strncpy(cur_key, (const char *)keyVal, keyLen);
     cur_key[keyLen] = '\0';
 
+    if (strcmp(cur_key, "bindings") == 0)
+        parsing_bindings = true;
+
+    return 1;
+}
+
+static int config_end_array_cb(void *params_) {
+    parsing_bindings = false;
     return 1;
 }
 
@@ -63,6 +72,27 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
     if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path"))
         return 1;
 
+    if (parsing_bindings) {
+        if (strcmp(cur_key, "command") == 0) {
+            binding_t *binding = TAILQ_LAST(&(config.bindings), bindings_head);
+            if (binding == NULL) {
+                ELOG("There is no binding to put the current command onto. This is a bug in i3.\n");
+                return 0;
+            }
+
+            if (binding->command != NULL) {
+                ELOG("The binding for input_code = %d already has a command. This is a bug in i3.\n", binding->input_code);
+                return 0;
+            }
+
+            sasprintf(&(binding->command), "%.*s", len, val);
+            return 1;
+        }
+
+        ELOG("Unknown key \"%s\" while parsing bar bindings.\n", cur_key);
+        return 0;
+    }
+
     if (!strcmp(cur_key, "mode")) {
         DLOG("mode = %.*s, len = %d\n", len, val, len);
         config.hide_on_modifier = (len == 4 && !strncmp((const char *)val, "dock", strlen("dock")) ? M_DOCK
@@ -112,17 +142,25 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
         return 1;
     }
 
+    /* This key was sent in <= 4.10.2. We keep it around to avoid breakage for
+     * users updating from that version and restarting i3bar before i3. */
     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);
+        binding_t *binding = scalloc(sizeof(binding_t));
+        binding->input_code = 4;
+        sasprintf(&(binding->command), "%.*s", len, val);
+        TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
         return 1;
     }
 
+    /* This key was sent in <= 4.10.2. We keep it around to avoid breakage for
+     * users updating from that version and restarting i3bar before i3. */
     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);
+        binding_t *binding = scalloc(sizeof(binding_t));
+        binding->input_code = 5;
+        sasprintf(&(binding->command), "%.*s", len, val);
+        TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
         return 1;
     }
 
@@ -191,6 +229,9 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
     COLOR(urgent_workspace_border, urgent_ws_border);
     COLOR(urgent_workspace_bg, urgent_ws_bg);
     COLOR(urgent_workspace_text, urgent_ws_fg);
+    COLOR(binding_mode_border, binding_mode_border);
+    COLOR(binding_mode_bg, binding_mode_bg);
+    COLOR(binding_mode_text, binding_mode_fg);
 
     printf("got unexpected string %.*s for cur_key = %s\n", len, val, cur_key);
 
@@ -229,11 +270,40 @@ static int config_boolean_cb(void *params_, int val) {
     return 0;
 }
 
+/*
+ * Parse an integer value
+ *
+ */
+static int config_integer_cb(void *params_, long long val) {
+    if (parsing_bindings) {
+        if (strcmp(cur_key, "input_code") == 0) {
+            binding_t *binding = scalloc(sizeof(binding_t));
+            binding->input_code = val;
+            TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
+
+            return 1;
+        }
+
+        ELOG("Unknown key \"%s\" while parsing bar bindings.\n", cur_key);
+        return 0;
+    }
+
+    if (!strcmp(cur_key, "tray_padding")) {
+        DLOG("tray_padding = %lld\n", val);
+        config.tray_padding = val;
+        return 1;
+    }
+
+    return 0;
+}
+
 /* A datastructure to pass all these callbacks to yajl */
 static yajl_callbacks outputs_callbacks = {
     .yajl_null = config_null_cb,
     .yajl_boolean = config_boolean_cb,
+    .yajl_integer = config_integer_cb,
     .yajl_string = config_string_cb,
+    .yajl_end_array = config_end_array_cb,
     .yajl_map_key = config_map_key_cb,
 };
 
@@ -246,6 +316,8 @@ void parse_config_json(char *json) {
     yajl_status state;
     handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
 
+    TAILQ_INIT(&(config.bindings));
+
     state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
 
     /* FIXME: Proper error handling for JSON parsing */
@@ -286,5 +358,8 @@ void free_colors(struct xcb_color_strings_t *colors) {
     FREE_COLOR(focus_ws_fg);
     FREE_COLOR(focus_ws_bg);
     FREE_COLOR(focus_ws_border);
+    FREE_COLOR(binding_mode_fg);
+    FREE_COLOR(binding_mode_bg);
+    FREE_COLOR(binding_mode_border);
 #undef FREE_COLOR
 }
index 15a26d7b4eb42b502fa3ca1a45d01cee90bc42d3..eb48afeaaa2f2432bee7904b52ddda6c6b30fb4b 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * ipc.c: Communicating with i3
  *
index a9a972dbe6c12a37a5893b830bf7244b5ef34bca..32425319ed3c5afc59a9516d1f434a208ad59fbf 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  */
 #include <stdio.h>
@@ -111,7 +111,7 @@ int main(int argc, char **argv) {
                 socket_path = expand_path(optarg);
                 break;
             case 'v':
-                printf("i3bar version " I3_VERSION " © 2010-2014 Axel Wagner and contributors\n");
+                printf("i3bar version " I3_VERSION " © 2010 Axel Wagner and contributors\n");
                 exit(EXIT_SUCCESS);
                 break;
             case 'b':
index 0ff8ba4f0239fd3fa70d6706e108d40a8144349d..7f7537af7d086e6db8168df6943932fa6e5530f9 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * mode.c: Handle mode event and show current binding mode in the bar
  *
@@ -32,7 +32,7 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
 
     if (!strcmp(params->cur_key, "change")) {
         /* Save the name */
-        params->mode->name = i3string_from_utf8_with_length((const char *)val, len);
+        params->mode->name = i3string_from_markup_with_length((const char *)val, len);
         /* Save its rendered width */
         params->mode->width = predict_text_width(params->mode->name);
 
index 8f5c95dc2f16d7e8ffb93b763099a8cc0b65b03e..b49ff53f6892bbb7ee69fb409c0b755d4dfbeb75 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * outputs.c: Maintaining the outputs list
  *
index f5fb84ab147befeb838296fbbfa153c99ed35bfb..1cd951065c8e379f123d08671b12c769e8468e8f 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * parse_json_header.c: Parse the JSON protocol header to determine
  *                      protocol version and features.
index e30325265bb36835b2b5bdac5f8a7a76b54d2386..961a41f5da7913c4be7558204f546e1bcc91919f 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * workspaces.c: Maintaining the workspace lists
  *
@@ -106,7 +106,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
 
     if (!strcmp(params->cur_key, "name")) {
         const char *ws_name = (const char *)val;
-        params->workspaces_walk->canonical_name = strndup(ws_name, len);
+        params->workspaces_walk->canonical_name = sstrndup(ws_name, len);
 
         if (config.strip_ws_numbers && params->workspaces_walk->num >= 0) {
             /* Special case: strip off the workspace number */
index ba57b9fa397d11a8fabc238da2727ba09c4095ab..f31d711a80a2b05a2d8d509cb0eb8ab308386395 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3bar - an xcb-based status- and ws-bar for i3
- * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
+ * © 2010 Axel Wagner and contributors (see also: LICENSE)
  *
  * xcb.c: Communicating with X
  *
@@ -60,6 +60,9 @@ xcb_connection_t *conn;
 /* The font we'll use */
 static i3Font font;
 
+/* Icon size (based on font size) */
+int icon_size;
+
 /* Overall height of the bar (based on font size) */
 int bar_height;
 
@@ -103,6 +106,9 @@ struct xcb_colors_t {
     uint32_t focus_ws_bg;
     uint32_t focus_ws_fg;
     uint32_t focus_ws_border;
+    uint32_t binding_mode_bg;
+    uint32_t binding_mode_fg;
+    uint32_t binding_mode_border;
 };
 struct xcb_colors_t colors;
 
@@ -148,7 +154,7 @@ int get_tray_width(struct tc_head *trayclients) {
     TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
         if (!trayclient->mapped)
             continue;
-        tray_width += font.height + logical_px(2);
+        tray_width += icon_size + logical_px(config.tray_padding);
     }
     if (tray_width > 0)
         tray_width += logical_px(tray_loff_px);
@@ -368,6 +374,18 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
     PARSE_COLOR(focus_ws_border, "#4c7899");
 #undef PARSE_COLOR
 
+#define PARSE_COLOR_FALLBACK(name, fallback)                                                 \
+    do {                                                                                     \
+        colors.name = new_colors->name ? get_colorpixel(new_colors->name) : colors.fallback; \
+    } while (0)
+
+    /* For the binding mode indicator colors, we don't hardcode a default.
+     * Instead, we fall back to urgent_ws_* colors. */
+    PARSE_COLOR_FALLBACK(binding_mode_fg, urgent_ws_fg);
+    PARSE_COLOR_FALLBACK(binding_mode_bg, urgent_ws_bg);
+    PARSE_COLOR_FALLBACK(binding_mode_border, urgent_ws_border);
+#undef PARSE_COLOR_FALLBACK
+
     init_tray_colors();
     xcb_flush(xcb_connection);
 }
@@ -420,7 +438,7 @@ void handle_button(xcb_button_press_event_t *event) {
         int offset = walk->rect.w - statusline_width - tray_width - logical_px(sb_hoff_px);
 
         x = original_x - offset;
-        if (x >= 0) {
+        if (x >= 0 && (size_t)x < statusline_width) {
             struct status_block *block;
             int sep_offset_remainder = 0;
 
@@ -442,25 +460,27 @@ void handle_button(xcb_button_press_event_t *event) {
         x = original_x;
     }
 
+    /* If a custom command was specified for this mouse button, it overrides
+     * the default behavior. */
+    binding_t *binding;
+    TAILQ_FOREACH(binding, &(config.bindings), bindings) {
+        if (binding->input_code != event->detail)
+            continue;
+
+        i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, binding->command);
+        return;
+    }
+
     if (cur_ws == NULL) {
         DLOG("No workspace active?\n");
         return;
     }
-
     switch (event->detail) {
         case 4:
             /* Mouse wheel up. We select the previous ws, if any.
              * If there is no more workspace, don’t even send the workspace
              * command, otherwise (with workspace auto_back_and_forth) we’d end
              * up on the wrong workspace. */
-
-            /* If `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;
 
@@ -471,14 +491,6 @@ 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;
 
@@ -591,8 +603,8 @@ static void configure_trayclients(void) {
             clients++;
 
             DLOG("Configuring tray window %08x to x=%d\n",
-                 trayclient->win, output->rect.w - (clients * (font.height + logical_px(2))));
-            uint32_t x = output->rect.w - (clients * (font.height + logical_px(2)));
+                 trayclient->win, output->rect.w - (clients * (icon_size + logical_px(config.tray_padding))));
+            uint32_t x = output->rect.w - (clients * (icon_size + logical_px(config.tray_padding)));
             xcb_configure_window(xcb_connection,
                                  trayclient->win,
                                  XCB_CONFIG_WINDOW_X,
@@ -702,16 +714,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             xcb_reparent_window(xcb_connection,
                                 client,
                                 output->bar,
-                                output->rect.w - font.height - 2,
-                                2);
+                                output->rect.w - icon_size - logical_px(config.tray_padding),
+                                logical_px(config.tray_padding));
             /* We reconfigure the window to use a reasonable size. The systray
              * specification explicitly says:
              *   Tray icons may be assigned any size by the system tray, and
              *   should do their best to cope with any size effectively
              */
             mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
-            values[0] = font.height;
-            values[1] = font.height;
+            values[0] = icon_size;
+            values[1] = icon_size;
             xcb_configure_window(xcb_connection,
                                  client,
                                  mask,
@@ -941,10 +953,10 @@ static void handle_configure_request(xcb_configure_request_event_t *event) {
                 continue;
 
             xcb_rectangle_t rect;
-            rect.x = output->rect.w - (clients * (font.height + 2));
-            rect.y = 2;
-            rect.width = font.height;
-            rect.height = font.height;
+            rect.x = output->rect.w - (clients * (icon_size + logical_px(config.tray_padding)));
+            rect.y = logical_px(config.tray_padding);
+            rect.width = icon_size;
+            rect.height = icon_size;
 
             DLOG("This is a tray window. x = %d\n", rect.x);
             fake_configure_notify(xcb_connection, rect, event->window, 0);
@@ -1215,6 +1227,7 @@ void init_xcb_late(char *fontname) {
     set_font(&font);
     DLOG("Calculated font height: %d\n", font.height);
     bar_height = font.height + 2 * logical_px(ws_voff_px);
+    icon_size = bar_height - 2 * logical_px(config.tray_padding);
 
     if (config.separator_symbol)
         separator_symbol_width = predict_text_width(config.separator_symbol);
@@ -1792,6 +1805,8 @@ void reconfig_windows(bool redraw_bars) {
 void draw_bars(bool unhide) {
     DLOG("Drawing bars...\n");
     int workspace_width = 0;
+    /* Is the currently-rendered statusline using short_text items? */
+    bool rendered_statusline_is_short = false;
 
     refresh_statusline(false);
 
@@ -1888,11 +1903,11 @@ void draw_bars(bool unhide) {
         if (binding.name && !config.disable_binding_mode_indicator) {
             workspace_width += logical_px(ws_spacing_px);
 
-            uint32_t fg_color = colors.urgent_ws_fg;
-            uint32_t bg_color = colors.urgent_ws_bg;
+            uint32_t fg_color = colors.binding_mode_fg;
+            uint32_t bg_color = colors.binding_mode_bg;
             uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
 
-            uint32_t vals_border[] = {colors.urgent_ws_border, colors.urgent_ws_border};
+            uint32_t vals_border[] = {colors.binding_mode_border, colors.binding_mode_border};
             xcb_change_gc(xcb_connection,
                           outputs_walk->bargc,
                           mask,
@@ -1941,8 +1956,15 @@ void draw_bars(bool unhide) {
             uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - 2 * logical_px(sb_hoff_px);
 
             /* If the statusline is too long, try to use short texts. */
-            if (statusline_width > max_statusline_width)
+            if (statusline_width > max_statusline_width) {
+                /* If the currently rendered statusline is long, render a short status line */
                 refresh_statusline(true);
+                rendered_statusline_is_short = true;
+            } else if (rendered_statusline_is_short) {
+                /* If the currently rendered statusline is short, render a long status line */
+                refresh_statusline(false);
+                rendered_statusline_is_short = false;
+            }
 
             /* Luckily we already prepared a seperate pixmap containing the rendered
              * statusline, we just have to copy the relevant parts to the relevant
index a355d3d2b65a8e1ef08bb9a97556942a4b8c12bc..85397d59a3d8cad3d6205dc8baee4ed4cdf09e50 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * This header file includes all relevant files of i3 and the most often used
  * system header files. This reduces boilerplate (the amount of code duplicated
index b83ee03faab0c3252e49c1f625048c417b9bb96c..22ebafc345a4a38cf7a54921ef83b1157d46a63d 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * assignments.c: Assignments for specific windows (for_window).
  *
index 59dab6eddd1c1eeeff16b3b5b26d5992b137ad85..00a346db7b74fff780754c497b6bbb9b7ce17939 100644 (file)
@@ -5,13 +5,19 @@ xmacro(_NET_WM_MOVERESIZE)
 xmacro(_NET_WM_STATE_FULLSCREEN)
 xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
 xmacro(_NET_WM_STATE_MODAL)
+xmacro(_NET_WM_STATE_HIDDEN)
 xmacro(_NET_WM_STATE)
 xmacro(_NET_WM_WINDOW_TYPE)
+xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
 xmacro(_NET_WM_WINDOW_TYPE_DOCK)
 xmacro(_NET_WM_WINDOW_TYPE_DIALOG)
 xmacro(_NET_WM_WINDOW_TYPE_UTILITY)
 xmacro(_NET_WM_WINDOW_TYPE_TOOLBAR)
 xmacro(_NET_WM_WINDOW_TYPE_SPLASH)
+xmacro(_NET_WM_WINDOW_TYPE_MENU)
+xmacro(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
+xmacro(_NET_WM_WINDOW_TYPE_POPUP_MENU)
+xmacro(_NET_WM_WINDOW_TYPE_TOOLTIP)
 xmacro(_NET_WM_DESKTOP)
 xmacro(_NET_WM_STRUT_PARTIAL)
 xmacro(_NET_CLIENT_LIST)
index 19345f8c82ad6c6144d8c7bbde53f849bceb86eb..75a719e9469e7d84d650440c6a2146d1a08b20a5 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * bindings.h: Functions for configuring, finding, and running bindings.
  *
@@ -24,7 +24,8 @@ const char *DEFAULT_BINDING_MODE;
  *
  */
 Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code,
-                           const char *release, const char *whole_window, const char *command, const char *mode);
+                           const char *release, const char *border, const char *whole_window,
+                           const char *command, const char *mode);
 
 /**
  * Grab the bound keys (tell X to send us keypress events for those keycodes)
index c63672a7cce954e461ab979350091d71fe62cef2..7ce80d97abcbf50f8edb998e31ce161943c38ed3 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * click.c: Button press (mouse click) events.
  *
index 263801f63d6f125de0d90c7713ec42865326e9d2..2ffa49b011e1b47043dd75bd03a8888f2e6c7e14 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * cmdparse.y: the parser for commands you send to i3 (or bind on keys)
  *
index 780a9e8eb781893dd3fb69c8642783b98c73e676..6d1046d4b74cece97c59ee2ee36e475dbb13eeda 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * commands.c: all command functions (see commands_parser.c)
  *
@@ -67,7 +67,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which);
 void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resize_ppt);
 
 /**
- * Implementation of 'border normal|none|1pixel|toggle'.
+ * Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
  *
  */
 void cmd_border(I3_CMD, char *border_style_str, char *border_width);
@@ -109,10 +109,10 @@ void cmd_workspace_back_and_forth(I3_CMD);
 void cmd_workspace_name(I3_CMD, char *name);
 
 /**
- * Implementation of 'mark <mark>'
+ * Implementation of 'mark [--toggle] <mark>'
  *
  */
-void cmd_mark(I3_CMD, char *mark);
+void cmd_mark(I3_CMD, char *mark, char *toggle);
 
 /**
  * Implementation of 'unmark [mark]'
@@ -132,6 +132,12 @@ void cmd_mode(I3_CMD, char *mode);
  */
 void cmd_move_con_to_output(I3_CMD, char *name);
 
+/**
+ * Implementation of 'move [window|container] [to] mark <str>'.
+ *
+ */
+void cmd_move_con_to_mark(I3_CMD, char *mark);
+
 /**
  * Implementation of 'floating enable|disable|toggle'
  *
@@ -252,6 +258,12 @@ void cmd_move_window_to_position(I3_CMD, char *method, char *x, char *y);
  */
 void cmd_move_window_to_center(I3_CMD, char *method);
 
+/**
+ * Implementation of 'move [window|container] [to] position mouse'
+ *
+ */
+void cmd_move_window_to_mouse(I3_CMD);
+
 /**
  * Implementation of 'move scratchpad'.
  *
@@ -264,6 +276,12 @@ void cmd_move_scratchpad(I3_CMD);
  */
 void cmd_scratchpad_show(I3_CMD);
 
+/**
+ * Implementation of 'title_format <format>'
+ *
+ */
+void cmd_title_format(I3_CMD, char *format);
+
 /**
  * Implementation of 'rename workspace <name> to <name>'
  *
index cfa44dd5a5a03621fd04fd9f081018c297ba0b6d..1acb7faebe791f999db86166b209dfe9b80636a5 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * commands.c: all command functions (see commands_parser.c)
  *
index b025adab16e0349eef40fca2e5972d033bef57c0..4813b77673a77c59a68034aff469f9b8db2ef45d 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * con.c: Functions which deal with containers directly (creating containers,
  *        searching containers, getting specific properties from containers,
@@ -42,12 +42,19 @@ bool con_is_leaf(Con *con);
  */
 bool con_has_managed_window(Con *con);
 
-/*
+/**
  * Returns true if a container should be considered split.
  *
  */
 bool con_is_split(Con *con);
 
+/**
+ * This will only return true for containers which have some parent with
+ * a tabbed / stacked parent of which they are not the currently focused child.
+ *
+ */
+bool con_is_hidden(Con *con);
+
 /**
  * Returns true if this node has regular or floating children.
  *
@@ -126,6 +133,13 @@ Con *con_by_window_id(xcb_window_t window);
  */
 Con *con_by_frame_id(xcb_window_t frame);
 
+/**
+ * Returns the container with the given mark or NULL if no such container
+ * exists.
+ *
+ */
+Con *con_by_mark(const char *mark);
+
 /**
  * Returns the first container below 'con' which wants to swallow this window
  * TODO: priority
@@ -203,6 +217,12 @@ void con_disable_fullscreen(Con *con);
  */
 void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp);
 
+/**
+ * Moves the given container to the given mark.
+ *
+ */
+bool con_move_to_mark(Con *con, const char *mark);
+
 /**
  * Returns the orientation of the given container (for stacked containers,
  * vertical orientation is used regardless of the actual orientation of the
index 9a0af0e6cd6b8119e1a5ae4a4bd5fcef915cb66e..ff360bb5d126e348fa393abf428d415d21a83b36 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * include/config.h: Contains all structs/variables for the configurable
  * part of i3 as well as functions handling the configuration file (calling
@@ -167,6 +167,26 @@ struct Config {
      * flag can be delayed using an urgency timer. */
     float workspace_urgency_timer;
 
+    /** Use a timer to delay exiting when no output is available.
+      * This can prevent i3 from exiting when all outputs disappear momentarily. */
+    float zero_disp_exit_timer_ms;
+
+    /** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
+    enum {
+        /* Focus if the target workspace is visible, set urgency hint otherwise. */
+        FOWA_SMART,
+        /* Always set the urgency hint. */
+        FOWA_URGENT,
+        /* Always focus the window. */
+        FOWA_FOCUS,
+        /* Ignore the request (no focus, no urgency hint). */
+        FOWA_NONE
+    } focus_on_window_activation;
+
+    /** Specifies whether or not marks should be displayed in the window
+     * decoration. Marks starting with a "_" will be ignored either way. */
+    bool show_marks;
+
     /** The default border style for new windows. */
     border_style_t default_border;
 
@@ -235,6 +255,9 @@ struct Barconfig {
      * disables the tray (it’s enabled by default). */
     char *tray_output;
 
+    /* Padding around the tray icons. */
+    int tray_padding;
+
     /** Path to the i3 IPC socket. This option is discouraged since programs
      * can find out the path by looking for the I3_SOCKET_PATH property on the
      * root window! */
@@ -261,13 +284,7 @@ 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;
+    TAILQ_HEAD(bar_bindings_head, Barbinding) bar_bindings;
 
     /** Bar position (bottom by default). */
     enum { P_BOTTOM = 0,
@@ -324,11 +341,30 @@ struct Barconfig {
         char *urgent_workspace_border;
         char *urgent_workspace_bg;
         char *urgent_workspace_text;
+
+        char *binding_mode_border;
+        char *binding_mode_bg;
+        char *binding_mode_text;
     } colors;
 
     TAILQ_ENTRY(Barconfig) configs;
 };
 
+/**
+ * Defines a mouse command to be executed instead of the default behavior when
+ * clicking on the non-statusline part of i3bar.
+ *
+ */
+struct Barbinding {
+    /** The button to be used (e.g., 1 for "button1"). */
+    int input_code;
+
+    /** The command which is to be executed for this button. */
+    char *command;
+
+    TAILQ_ENTRY(Barbinding) bindings;
+};
+
 /**
  * Finds the configuration file to use (either the one specified by
  * override_configpath), the user’s one or the system default) and calls
index 6c960b1f8c765771c2fd6d340872a76d7ea4fae6..fc34a85f751e3862796915e9fc5462a7b5e12078 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * config_directives.h: all config storing functions (see config_parser.c)
  *
@@ -51,8 +51,12 @@ CFGFUN(force_focus_wrapping, const char *value);
 CFGFUN(force_xinerama, const char *value);
 CFGFUN(fake_outputs, const char *outputs);
 CFGFUN(force_display_urgency_hint, const long duration_ms);
+CFGFUN(delay_exit_on_zero_displays, const long duration_ms);
+CFGFUN(focus_on_window_activation, const char *mode);
+CFGFUN(show_marks, const char *value);
 CFGFUN(hide_edge_borders, const char *borders);
 CFGFUN(assign, const char *workspace);
+CFGFUN(no_focus);
 CFGFUN(ipc_socket, const char *path);
 CFGFUN(restart_state, const char *path);
 CFGFUN(popup_during_fullscreen, const char *value);
@@ -61,10 +65,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 *whole_window, const char *command);
+CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, 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 *whole_window, const char *command);
+CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command);
 
 CFGFUN(bar_font, const char *font);
 CFGFUN(bar_separator_symbol, const char *separator);
@@ -76,14 +80,17 @@ 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_bindsym, const char *button, 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);
 CFGFUN(bar_socket_path, const char *socket_path);
 CFGFUN(bar_tray_output, const char *output);
+CFGFUN(bar_tray_padding, const long spacing_px);
 CFGFUN(bar_color_single, const char *colorclass, const char *color);
 CFGFUN(bar_status_command, const char *command);
 CFGFUN(bar_binding_mode_indicator, const char *value);
 CFGFUN(bar_workspace_buttons, const char *value);
 CFGFUN(bar_strip_workspace_numbers, const char *value);
+CFGFUN(bar_start);
 CFGFUN(bar_finish);
index 9fc3bf2ffb5ea63e2b3e22bef34a69f9472dd9d5..28c28e4882e7f2b6fa220cfe98ca4d4b391f1819 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * config_parser.h: config parser-related definitions
  *
index cec571e9df8eecf71f10c32ebb8c2098f8770d16..66000e45462fa03fda2b5f2b9548aa023b14272d 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * include/data.h: This file defines all data structures used by i3
  *
@@ -255,6 +255,10 @@ 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 the window border. */
+    bool border;
+
     /** 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). */
@@ -359,6 +363,8 @@ struct Window {
 
     /** The name of the window. */
     i3String *name;
+    /** The format with which the window's name should be displayed. */
+    char *title_format;
 
     /** The WM_WINDOW_ROLE of this window (for example, the pidgin buddy window
      * sets "buddy list"). Useful to match specific windows in assignments or
@@ -378,6 +384,9 @@ struct Window {
      * default will be 'accepts focus'. */
     bool doesnt_accept_focus;
 
+    /** The _NET_WM_WINDOW_TYPE for this window. */
+    xcb_atom_t window_type;
+
     /** Whether the window says it is a dock window */
     enum { W_NODOCK = 0,
            W_DOCK_TOP = 1,
@@ -408,6 +417,8 @@ struct Match {
     struct regex *instance;
     struct regex *mark;
     struct regex *window_role;
+    struct regex *workspace;
+    xcb_atom_t window_type;
     enum {
         U_DONTCHECK = -1,
         U_LATEST = 0,
@@ -460,6 +471,7 @@ struct Assignment {
      *
      * A_COMMAND = run the specified command for the matching window
      * A_TO_WORKSPACE = assign the matching window to the specified workspace
+     * A_NO_FOCUS = don't focus matched window when it is managed
      *
      * While the type is a bitmask, only one value can be set at a time. It is
      * a bitmask to allow filtering for multiple types, for example in the
@@ -469,7 +481,8 @@ struct Assignment {
     enum {
         A_ANY = 0,
         A_COMMAND = (1 << 0),
-        A_TO_WORKSPACE = (1 << 1)
+        A_TO_WORKSPACE = (1 << 1),
+        A_NO_FOCUS = (1 << 2)
     } type;
 
     /** the criteria to check if a window matches */
@@ -543,6 +556,8 @@ struct Con {
 
     /* user-definable mark to jump to this container later */
     char *mark;
+    /* cached to decide whether a redraw is needed */
+    bool mark_changed;
 
     double percent;
 
index 3e65c35e0eb01b2c08d67d6011256181ba2f3faf..3875ec6bbdc8d482d71cfad6944858655cae4559 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * debug.c: Debugging functions, especially FormatEvent, which prints unhandled
  *          events.  This code is from xcb-util.
index 6f88ae97f18cb2610590a6712ac5954e9ce8a9ed..b1a5a0e0f3176ccaed3ef222669717164744be57 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * display_version.c: displays the running i3 version, runs as part of
  *                    i3 --moreversion.
index 3b5806285b4f66badec03fd7d2bb513601cb997d..8fb7902a7d7adc911a19a0ddf4bc903f290de207 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * ewmh.c: Get/set certain EWMH properties easily.
  *
index 75ef77ba8e71f6ea3ecc01f2dacfce1b9e5c6313..32b07fa97e30be5bf3dcaeb819ec92417a8ce558 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * Faking outputs is useful in pathological situations (like network X servers
  * which don’t support multi-monitor in a useful way) and for our testsuite.
index bea5f7a2885c5474afa5f037e140a4f3c4d15804..78e75be8fd35d9589dea7ac873131a8fdaf4cac9 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * floating.c: Floating windows.
  *
@@ -64,6 +64,18 @@ void floating_raise_con(Con *con);
  */
 bool floating_maybe_reassign_ws(Con *con);
 
+/**
+ * Centers a floating con above the specified rect.
+ *
+ */
+void floating_center(Con *con, Rect rect);
+
+/**
+ * Moves the given floating con to the current pointer position.
+ *
+ */
+void floating_move_to_pointer(Con *con);
+
 #if 0
 /**
  * Removes the floating client from its workspace and attaches it to the new
index 82f6b982fc43474c5717e5c48748a04409e8102c..d80a24f8fdbbda1d1dc7fd98bee473cc562f3a90 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * handlers.c: Small handlers for various events (keypresses, focus changes,
  *             …).
index 5ca875417bb163c386e5da8d462ffcc6d1a2c486..f1912fd55eb633df71f999aa3ebe793932bee62a 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3.h: global variables that are used all over i3.
  *
@@ -24,6 +24,9 @@
 #include "data.h"
 #include "xcb.h"
 
+/** Git commit identifier, from version.c */
+extern const char *i3_version;
+
 /** The original value of RLIMIT_CORE when i3 was started. We need to restore
  * this before starting any other process, since we set RLIMIT_CORE to
  * RLIM_INFINITY for i3 debugging versions. */
index f1b50dec011b1e07548628e35b713013eb3d48dd..8912bf130a5daf877ccb2445821524599a581ad9 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * This public header defines the different constants and message types to use
  * for the IPC interface to i3 (see docs/ipc for more information).
index 4eed319a21e86c7a95765c96c03974e9d6c6b7c5..e1c18816715e1fccddc199a19f736ed184e1b6e2 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
  *
index 86cc6836f1300402ad29a26c3cddcc62968c4ff2..bbfec83b4808d725451820e492d1896807963324 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * key_press.c: key press handler
  *
index d6a2e98097c5baa7fd2dc6d337a91db06efbce61..69c4452862a026b979e04d0796cb1aa18a0fb295 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * libi3: contains functions which are used by i3 *and* accompanying tools such
  * as i3-msg, i3-config-wizard, …
@@ -127,6 +127,13 @@ void *srealloc(void *ptr, size_t size);
  */
 char *sstrdup(const char *str);
 
+/**
+ * Safe-wrapper around strndup which exits if strndup returns NULL (meaning that
+ * there is no more memory available)
+ *
+ */
+char *sstrndup(const char *str, size_t size);
+
 /**
  * Safe-wrapper around asprintf which exits if it returns -1 (meaning that
  * there is no more memory available)
@@ -236,6 +243,11 @@ bool i3string_is_markup(i3String *str);
  */
 void i3string_set_markup(i3String *str, bool is_markup);
 
+/**
+ * Escape pango markup characters in the given string.
+ */
+i3String *i3string_escape_markup(i3String *str);
+
 /**
  * Returns the number of glyphs in an i3String.
  *
@@ -374,6 +386,12 @@ xcb_char2b_t *convert_utf8_to_ucs2(char *input, size_t *real_strlen);
  */
 void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background);
 
+/**
+ * Returns true if and only if the current font is a pango font.
+ *
+ */
+bool font_is_pango(void);
+
 /**
  * Draws text onto the specified X drawable (normally a pixmap) at the
  * specified coordinates (from the top left corner of the leftmost, uppermost
@@ -435,6 +453,24 @@ char *get_exe_path(const char *argv0);
  */
 int logical_px(const int logical);
 
+/**
+ * This function resolves ~ in pathnames.
+ * It may resolve wildcards in the first part of the path, but if no match
+ * or multiple matches are found, it just returns a copy of path as given.
+ *
+ */
+char *resolve_tilde(const char *path);
+
+/**
+ * Get the path of the first configuration file found. If override_configpath
+ * is specified, that path is returned and saved for further calls. Otherwise,
+ * checks the home directory first, then the system directory first, always
+ * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
+ * $XDG_CONFIG_DIRS)
+ *
+ */
+char *get_config_path(const char *override_configpath, bool use_system_paths);
+
 /**
  * Emulates mkdir -p (creates any missing folders)
  *
index 8736a50cf46559dead850d0edb66a8e3717eab2a..f8b7da20a6f7ac58144f8cc53e0a891351a015bc 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * load_layout.c: Restore (parts of) the layout, for example after an inplace
  *                restart.
index a5086dbee5ae78fa3327017763882d3bbdac39e5..e7fc8acba1341c182078a83a9657f789d0354ef8 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * log.c: Logging functions.
  *
index 18c6e374472f6b199588b966b95580743894fa6a..1ce539540be033a0eadbf3c5fd1c00fa09311a79 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * main.c: Initialization, main loop
  *
index bd1a14f76ec4184ad559356bcf6ade3a113e80d3..b0c4238155969e206f9874c6dc41f410f04636a7 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * manage.c: Initially managing new windows (or existing ones on restart).
  *
index 09975cac20fe644162930e08ea25742e1d12644f..dbd9bb795b95975d173de9bfcc4f5745ec3f766b 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * A "match" is a data structure which acts like a mask or expression to match
  * certain windows or not. For example, when using commands, you can specify a
index 939665ec41f09b73cb2be7cce20dc598a5dfb5fe..7debcf28e397c8b06877853de7f9e9cff51ed9ba 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * move.c: Moving containers into some direction.
  *
index 6514c477fc7f5ca01b5e9e6daec629eb2877743d..e0125c06f74540d7c6ef96e4d655dd52a139ecc9 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * output.c: Output (monitor) related functions.
  *
index 43f7efe826ae26820842aa5a67fc10ea67a614cc..823ddea4e98f1be582ee2a7ca91cb9137ef762cf 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * For more information on RandR, please see the X.org RandR specification at
  * http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
index e11d377e453ae7d8ddb7db83ce383d62414de226..2e2f22fd45f142e87af6079f1d2d9afbb00cdaad 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * regex.c: Interface to libPCRE (perl compatible regular expressions).
  *
index 1794b513894ea0c8583ff15400e1a66f679f39db..717459e98268dd8d743134e11820f62b33c78d5e 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * render.c: Renders (determines position/sizes) the layout tree, updating the
  *           various rects. Needs to be pushed to X11 (see x.c) to be visible.
index 5c795046c5fdd27c3269c1eab3aa01555145a6d5..945678d98b6d518c31e2ab597d7e2bcc35255645 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * resize.c: Interactive resizing.
  *
index 3f0229d3d8eee7ed9216662d8c9ac5cfea335159..f952834c564939c557752e11f813effc5f32e5dc 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * restore_layout.c: Everything for restored containers that is not pure state
  *                   parsing (which can be found in load_layout.c).
index 1aca73bda56ce466932628b0a5466b363fa3a41d..b385550cdbaf144274d3e4557445e9eaa9a550e2 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * scratchpad.c: Scratchpad functions (TODO: more description)
  *
index 5af697e708ff90283585893f5a81df418666282d..231681cab591d012bef2ca9075eb206c7bb6f923 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * The format of the shmlog data structure which i3 development versions use by
  * default (ringbuffer for storing the debug log).
index 184db73bc7d7d2890668aed32eba46510f252d96..56680bc48a2aaa84e3b252517ff415c2eb7ad5ce 100644 (file)
@@ -2,8 +2,8 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
- * © 2009-2010 Jan-Erik Rediger
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Jan-Erik Rediger
  *
  * sighandler.c: Interactive crash dialog upon SIGSEGV/SIGABRT/SIGFPE (offers
  *               to restart inplace).
index cb784913b303efc859a6894750673b4a596288ee..7d5d2a39644abf350b8c3191b48a9490654bfe8c 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * startup.c: Startup notification code. Ensures a startup notification context
  *            is setup when launching applications. We store the current
index b3c2a515131eef95ef964ab94ffce7a3057ee803..af3309e9092295c4aec4d55d59308970c98d8e07 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * tree.c: Everything that primarily modifies the layout tree data structure.
  *
index dec681168b49ee851995dd3d88444405d598e257..01f732caed9aab3eae9571ecfc34fb58d8921ea8 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * util.c: Utility functions, which can be useful everywhere within i3 (see
  *         also libi3).
@@ -106,14 +106,6 @@ void exec_i3_utility(char *name, char *argv[]);
 void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie,
                  char *err_message);
 
-/**
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-char *resolve_tilde(const char *path);
-
 /**
  * Checks if the given path exists by calling stat().
  *
index 480cee18844362adb707ea3a447828bca216419a..7a248277abd09cf92fa564998f07602544d066f9 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * window.c: Updates window attributes (X11 hints/properties).
  *
@@ -56,6 +56,12 @@ void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop);
  */
 void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool before_mgmt);
 
+/**
+ * Updates the _NET_WM_WINDOW_TYPE property.
+ *
+ */
+void window_update_type(i3Window *window, xcb_get_property_reply_t *reply);
+
 /**
  * Updates the WM_HINTS (we only care about the input focus handling part).
  *
index d0f801e0039240195496c6d3245f0ff9540e43d9..82d18919c232ff4982cc4dfd78a4d370ee4ed71e 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * workspace.c: Modifying workspaces, accessing them, moving containers to
  *              workspaces.
index 07f8a725cfb3a8e3c52eefbfd94246b28951bd6d..b6855cb328ccc67a52265b7c6dfd95fa200d0466 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * x.c: Interface to X11, transfers our in-memory state to X11 (see also
  *      render.c). Basically a big state machine.
index 9f4ea91fd8f29733ff4549ad2d8fcaa9af386ece..7dab5d100c8a39014dcc5d9f11db6588d853d3ab 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * xcb.c: Helper functions for easier usage of XCB
  *
@@ -108,6 +108,16 @@ 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);
 
+/**
+ * Returns the first supported _NET_WM_WINDOW_TYPE atom.
+ *
+ */
+xcb_atom_t xcb_get_preferred_window_type(xcb_get_property_reply_t *reply);
+
+/**
+ * Returns true if the given reply contains the given data.
+ *
+ */
 bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom);
 
 /**
index 89c2c457887ed0d660cc6518f5a0efae405fbac9..066d834aa4b39e3cd9f78d14da385e698d19c09d 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * xcb_compat.h: uses #define to create aliases for xcb functions which got
  *               renamed. Makes the code work with >= 0.3.8 xcb-util and
index bb329e4c88ca06cfb90ed1da5c63a3d0a560fb52..e0ee98138cfb2c1855037ab03b155683f13a1368 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * xcursor.c: libXcursor support for themed cursors.
  *
index 46c2a63558ccc12715ead04d1e91d0732b94e9d6..936b9653b98ae6b3eb2b2220f24657de22505dad 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * This is LEGACY code (we support RandR, which can do much more than
  * Xinerama), but necessary for the poor users of the nVidia binary
index e8422aabd2d051294d3fedcd007b94658d0722b5..93c63d8f7b641a14da94d61eb10841454721a97a 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * yajl_utils.h
  *
index 6f58d57a409c8eaa87728513c77bbd6a745bf674..a347b08f089f47ca758d02b526fc8a2d5c88b905 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include "libi3.h"
index 2c0f5771c1a9db75b5eb40027929c68f47fe58d3..225577a44017157d001323b29d9fb77b0329f535 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <stdlib.h>
index 847bc61beb30c2d474ef4ee5c96639305f4e91e3..b502b52c6390c57dc5e9774ba269f33bce738716 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <assert.h>
@@ -340,6 +340,18 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background
     }
 }
 
+/*
+ * Returns true if and only if the current font is a pango font.
+ *
+ */
+bool font_is_pango(void) {
+#if PANGO_SUPPORT
+    return savedFont->type == FONT_TYPE_PANGO;
+#else
+    return false;
+#endif
+}
+
 static int predict_text_width_xcb(const xcb_char2b_t *text, size_t text_len);
 
 static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawable_t drawable,
index b093594e10484d74677752756798062b5178719b..44ad295d5fc48378a3651dba78bfaea3949f5780 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <stdlib.h>
diff --git a/libi3/get_config_path.c b/libi3/get_config_path.c
new file mode 100644 (file)
index 0000000..bad75c4
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include "libi3.h"
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/*
+ * Checks if the given path exists by calling stat().
+ *
+ */
+static bool path_exists(const char *path) {
+    struct stat buf;
+    return (stat(path, &buf) == 0);
+}
+
+/*
+ * Get the path of the first configuration file found. If override_configpath
+ * is specified, that path is returned and saved for further calls. Otherwise,
+ * checks the home directory first, then the system directory first, always
+ * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
+ * $XDG_CONFIG_DIRS)
+ *
+ */
+char *get_config_path(const char *override_configpath, bool use_system_paths) {
+    char *xdg_config_home, *xdg_config_dirs, *config_path;
+
+    static const char *saved_configpath = NULL;
+
+    if (override_configpath != NULL) {
+        saved_configpath = override_configpath;
+        return sstrdup(saved_configpath);
+    }
+
+    if (saved_configpath != NULL)
+        return sstrdup(saved_configpath);
+
+    /* 1: check the traditional path under the home directory */
+    config_path = resolve_tilde("~/.i3/config");
+    if (path_exists(config_path))
+        return config_path;
+    free(config_path);
+
+    /* 2: check for $XDG_CONFIG_HOME/i3/config */
+    if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
+        xdg_config_home = "~/.config";
+
+    xdg_config_home = resolve_tilde(xdg_config_home);
+    sasprintf(&config_path, "%s/i3/config", xdg_config_home);
+    free(xdg_config_home);
+
+    if (path_exists(config_path))
+        return config_path;
+    free(config_path);
+
+    /* The below paths are considered system-level, and can be skipped if the
+     * caller only wants user-level configs. */
+    if (!use_system_paths)
+        return NULL;
+
+    /* 3: check the traditional path under /etc */
+    config_path = SYSCONFDIR "/i3/config";
+    if (path_exists(config_path))
+        return sstrdup(config_path);
+
+    /* 4: check for $XDG_CONFIG_DIRS/i3/config */
+    if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
+        xdg_config_dirs = "/etc/xdg";
+
+    char *buf = sstrdup(xdg_config_dirs);
+    char *tok = strtok(buf, ":");
+    while (tok != NULL) {
+        tok = resolve_tilde(tok);
+        sasprintf(&config_path, "%s/i3/config", tok);
+        free(tok);
+        if (path_exists(config_path)) {
+            free(buf);
+            return config_path;
+        }
+        free(config_path);
+        tok = strtok(NULL, ":");
+    }
+    free(buf);
+
+    return NULL;
+}
index cf8cbe3863654c150a0b74800e3fcbd1f71e9d14..3b6976ad6b0d1669149fed67533aa53d1b1d4b7c 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <stdint.h>
index 941d4439e75177161b09d3fdca06597a6aa8c24c..a3dee9cd3ef84dddae60ac6d44cd2494ea3a4a93 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <assert.h>
index d11722f000ec54fbebc6d34b8a52d0d2c8ac98c6..ccf266db641aa673276e83b785b331d9c7f4525c 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include "libi3.h"
index 69629daa6a657b6d98abcf58824935d0dc1264fa..f493b4f11e6a6d118951afc058b9632c6621cd5b 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <sys/types.h>
index cb4edd6732b956f91e24c1738b796d5263a47c97..0ef4fced4f27b1fd1bf415727efb6f1bbdd14796 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <string.h>
index 80709ed3b255c638933e0644841accecc7eb4318..a9ba31653e07cacc1b86ed030075eccccc509a52 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <string.h>
diff --git a/libi3/resolve_tilde.c b/libi3/resolve_tilde.c
new file mode 100644 (file)
index 0000000..26cbabe
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+
+#include "libi3.h"
+#include <err.h>
+#include <glob.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This function resolves ~ in pathnames.
+ * It may resolve wildcards in the first part of the path, but if no match
+ * or multiple matches are found, it just returns a copy of path as given.
+ *
+ */
+char *resolve_tilde(const char *path) {
+    static glob_t globbuf;
+    char *head, *tail, *result;
+
+    tail = strchr(path, '/');
+    head = sstrndup(path, tail ? (size_t)(tail - path) : strlen(path));
+
+    int res = glob(head, GLOB_TILDE, NULL, &globbuf);
+    free(head);
+    /* no match, or many wildcard matches are bad */
+    if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
+        result = sstrdup(path);
+    else if (res != 0) {
+        err(EXIT_FAILURE, "glob() failed");
+    } else {
+        head = globbuf.gl_pathv[0];
+        result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1);
+        strncpy(result, head, strlen(head));
+        if (tail)
+            strncat(result, tail, strlen(tail));
+    }
+    globfree(&globbuf);
+
+    return result;
+}
index f70c31a78fd5c7fed10a3ff556e8dc1a6cc9b90e..df54ef093c5cd2c34ce417ee0ed7cfbb6f577d4d 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <stdio.h>
index db9b6b4a42efbf287e06466295ad0e2d2e30e2e5..f5973cab982185f869d0ed6b39eaf0ea80b67235 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <string.h>
@@ -48,6 +48,13 @@ char *sstrdup(const char *str) {
     return result;
 }
 
+char *sstrndup(const char *str, size_t size) {
+    char *result = strndup(str, size);
+    if (result == NULL)
+        err(EXIT_FAILURE, "strndup()");
+    return result;
+}
+
 int sasprintf(char **strp, const char *fmt, ...) {
     va_list args;
     int result;
index e6297f9eed89b0282a2b104fab360921eb723b07..70244743283f33e9f8034682e1f2a01b26ce42a8 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * string.c: Define an i3String type to automagically handle UTF-8/UCS-2
  *           conversions. Some font backends need UCS-2 (X core fonts),
 #include <stdlib.h>
 #include <string.h>
 
+#if PANGO_SUPPORT
+#include <glib.h>
+#endif
+
 #include "libi3.h"
 
 struct _i3String {
@@ -185,6 +189,18 @@ void i3string_set_markup(i3String *str, bool is_markup) {
     str->is_markup = is_markup;
 }
 
+/*
+ * Escape pango markup characters in the given string.
+ */
+i3String *i3string_escape_markup(i3String *str) {
+#if PANGO_SUPPORT
+    const char *text = i3string_as_utf8(str);
+    return i3string_from_utf8(g_markup_escape_text(text, -1));
+#else
+    return str;
+#endif
+}
+
 /*
  * Returns the number of glyphs in an i3String.
  *
index eec1a0edcfd9527bc39445e6a21c1acb8a8c1222..c4032dc5816130b01dd1689b819a7914219fc1fa 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <sys/types.h>
index 9a8c478f0c7213d3f1c19ba4c370623e5015e389..3a7d0194d45f5430ea4d783a897126f8e5f6f0eb 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  */
 #include <err.h>
index 315a9218dd5930695e540506ce11e0cd30cabbf4..94dc630a8c1e0ab6e7de6e54508dacfcdb4e1b2a 100644 (file)
@@ -1,7 +1,7 @@
 # vim:ts=2:sw=2:expandtab
 #
 # i3 - an improved dynamic tiling window manager
-# © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
 #
 # parser-specs/commands.spec: Specification file for generate-command-parser.pl
 # which will generate the appropriate header files for our C parser.
@@ -37,18 +37,21 @@ state INITIAL:
   'rename' -> RENAME
   'nop' -> NOP
   'scratchpad' -> SCRATCHPAD
+  'title_format' -> TITLE_FORMAT
   'mode' -> MODE
   'bar' -> BAR
 
 state CRITERIA:
-  ctype = 'class' -> CRITERION
-  ctype = 'instance' -> CRITERION
+  ctype = 'class'       -> CRITERION
+  ctype = 'instance'    -> CRITERION
   ctype = 'window_role' -> CRITERION
-  ctype = 'con_id' -> CRITERION
-  ctype = 'id' -> CRITERION
-  ctype = 'con_mark' -> CRITERION
-  ctype = 'title' -> CRITERION
-  ctype = 'urgent' -> CRITERION
+  ctype = 'con_id'      -> CRITERION
+  ctype = 'id'          -> CRITERION
+  ctype = 'window_type' -> CRITERION
+  ctype = 'con_mark'    -> CRITERION
+  ctype = 'title'       -> CRITERION
+  ctype = 'urgent'      -> CRITERION
+  ctype = 'workspace'   -> CRITERION
   ']' -> call cmd_criteria_match_windows(); INITIAL
 
 state CRITERION:
@@ -76,7 +79,8 @@ state DEBUGLOG:
   argument = 'toggle', 'on', 'off'
     -> call cmd_debuglog($argument)
 
-# border normal|none|1pixel|toggle|1pixel
+# border normal|pixel [<n>]
+# border none|1pixel|toggle
 state BORDER:
   border_style = 'normal', 'pixel'
     -> BORDER_WIDTH
@@ -189,10 +193,12 @@ state FLOATING:
   floating = 'enable', 'disable', 'toggle'
       -> call cmd_floating($floating)
 
-# mark <mark>
+# mark [--toggle] <mark>
 state MARK:
+  toggle = '--toggle'
+      ->
   mark = string
-      -> call cmd_mark($mark)
+      -> call cmd_mark($mark, $toggle)
 
 # unmark [mark]
 state UNMARK:
@@ -263,10 +269,12 @@ state RENAME_WORKSPACE_NEW_NAME:
 # move <direction> [<pixels> [px]]
 # move [window|container] [to] workspace [<str>|next|prev|next_on_output|prev_on_output|current]
 # move [window|container] [to] output <str>
+# move [window|container] [to] mark <str>
 # move [window|container] [to] scratchpad
 # move workspace to [output] <str>
 # move scratchpad
 # move [window|container] [to] [absolute] position [ [<pixels> [px] <pixels> [px]] | center ]
+# move [window|container] [to] position mouse|cursor|pointer
 state MOVE:
   'window'
       ->
@@ -278,6 +286,8 @@ state MOVE:
       -> MOVE_WORKSPACE
   'output'
       -> MOVE_TO_OUTPUT
+  'mark'
+      -> MOVE_TO_MARK
   'scratchpad'
       -> call cmd_move_scratchpad()
   direction = 'left', 'right', 'up', 'down'
@@ -319,6 +329,10 @@ state MOVE_TO_OUTPUT:
   output = string
       -> call cmd_move_con_to_output($output)
 
+state MOVE_TO_MARK:
+  mark = string
+      -> call cmd_move_con_to_mark($mark)
+
 state MOVE_WORKSPACE_TO_OUTPUT:
   'output'
       ->
@@ -332,6 +346,8 @@ state MOVE_TO_ABSOLUTE_POSITION:
 state MOVE_TO_POSITION:
   'center'
       -> call cmd_move_window_to_center($method)
+  'mouse', 'cursor', 'pointer'
+      -> call cmd_move_window_to_mouse()
   coord_x = word
       -> MOVE_TO_POSITION_X
 
@@ -360,6 +376,10 @@ state SCRATCHPAD:
   'show'
       -> call cmd_scratchpad_show()
 
+state TITLE_FORMAT:
+  format = string
+      -> call cmd_title_format($format)
+
 # bar (hidden_state hide|show|toggle)|(mode dock|hide|invisible|toggle) [<bar_id>]
 state BAR:
   bar_type = 'hidden_state'
index 25be5cf107e2bef2eebdba243980ee04085c9efa..3767acf8a4847ce21ea074b079c0e04e6dbaaa89 100644 (file)
@@ -1,7 +1,7 @@
 # vim:ts=2:sw=2:expandtab
 #
 # i3 - an improved dynamic tiling window manager
-# © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
 #
 # parser-specs/config.spec: Specification file for generate-command-parser.pl
 # which will generate the appropriate header files for our C parser.
@@ -31,6 +31,7 @@ state INITIAL:
   'hide_edge_borders'                      -> HIDE_EDGE_BORDERS
   'for_window'                             -> FOR_WINDOW
   'assign'                                 -> ASSIGN
+  'no_focus'                               -> NO_FOCUS
   'focus_follows_mouse'                    -> FOCUS_FOLLOWS_MOUSE
   'mouse_warping'                          -> MOUSE_WARPING
   'force_focus_wrapping'                   -> FORCE_FOCUS_WRAPPING
@@ -38,6 +39,9 @@ state INITIAL:
   'workspace_auto_back_and_forth'          -> WORKSPACE_BACK_AND_FORTH
   'fake_outputs', 'fake-outputs'           -> FAKE_OUTPUTS
   'force_display_urgency_hint'             -> FORCE_DISPLAY_URGENCY_HINT
+  'delay_exit_on_zero_displays'            -> DELAY_EXIT_ON_ZERO_DISPLAYS
+  'focus_on_window_activation'             -> FOCUS_ON_WINDOW_ACTIVATION
+  'show_marks'                             -> SHOW_MARKS
   'workspace'                              -> WORKSPACE
   'ipc_socket', 'ipc-socket'               -> IPC_SOCKET
   'restart_state'                          -> RESTART_STATE
@@ -100,8 +104,6 @@ state WORKSPACE_LAYOUT:
 
 # new_window <normal|1pixel|none>
 # new_float <normal|1pixel|none>
-# TODO: new_float is not in the userguide yet
-# TODO: pixel is not in the userguide yet
 state NEW_WINDOW:
   border = 'normal', 'pixel'
       -> NEW_WINDOW_PIXELS
@@ -148,6 +150,15 @@ state ASSIGN_WORKSPACE:
   workspace = string
       -> call cfg_assign($workspace)
 
+# no_focus <criteria>
+state NO_FOCUS:
+  '['
+      -> call cfg_criteria_init(NO_FOCUS_END); CRITERIA
+
+state NO_FOCUS_END:
+  end
+      -> call cfg_no_focus()
+
 # Criteria: Used by for_window and assign.
 state CRITERIA:
   ctype = 'class'       -> CRITERION
@@ -155,9 +166,11 @@ state CRITERIA:
   ctype = 'window_role' -> CRITERION
   ctype = 'con_id'      -> CRITERION
   ctype = 'id'          -> CRITERION
+  ctype = 'window_type' -> CRITERION
   ctype = 'con_mark'    -> CRITERION
   ctype = 'title'       -> CRITERION
   ctype = 'urgent'      -> CRITERION
+  ctype = 'workspace'   -> CRITERION
   ']'
       -> call cfg_criteria_pop_state()
 
@@ -204,12 +217,33 @@ state FORCE_DISPLAY_URGENCY_HINT:
   duration_ms = number
       -> FORCE_DISPLAY_URGENCY_HINT_MS
 
+# show_marks
+state SHOW_MARKS:
+  value = word
+      -> call cfg_show_marks($value)
+
 state FORCE_DISPLAY_URGENCY_HINT_MS:
   'ms'
       ->
   end
       -> call cfg_force_display_urgency_hint(&duration_ms)
 
+# delay_exit_on_zero_displays <delay> ms
+state DELAY_EXIT_ON_ZERO_DISPLAYS:
+  duration_ms = number
+      -> DELAY_EXIT_ON_ZERO_DISPLAYS_MS
+
+state DELAY_EXIT_ON_ZERO_DISPLAYS_MS:
+  'ms'
+      ->
+  end
+      -> call cfg_delay_exit_on_zero_displays(&duration_ms)
+
+# focus_on_window_activation <smart|urgent|focus|none>
+state FOCUS_ON_WINDOW_ACTIVATION:
+  mode = word
+      -> call cfg_focus_on_window_activation($mode)
+
 # workspace <workspace> output <output>
 state WORKSPACE:
   workspace = word
@@ -278,6 +312,8 @@ state FONT:
 state BINDING:
   release = '--release'
       ->
+  border = '--border'
+      ->
   whole_window = '--whole-window'
       ->
   modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
@@ -290,10 +326,12 @@ state BINDING:
 state BINDCOMMAND:
   release = '--release'
       ->
+  border = '--border'
+      ->
   whole_window = '--whole-window'
       ->
   command = string
-      -> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command)
+      -> call cfg_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $command)
 
 ################################################################################
 # Mode configuration
@@ -327,6 +365,10 @@ state MODE_IGNORE_LINE:
 state MODE_BINDING:
   release = '--release'
       ->
+  border = '--border'
+      ->
+  whole_window = '--whole-window'
+      ->
   modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
       ->
   '+'
@@ -337,10 +379,12 @@ state MODE_BINDING:
 state MODE_BINDCOMMAND:
   release = '--release'
       ->
+  border = '--border'
+      ->
   whole_window = '--whole-window'
       ->
   command = string
-      -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $whole_window, $command); MODE
+      -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $command); MODE
 
 ################################################################################
 # Bar configuration (i3bar)
@@ -350,7 +394,7 @@ state BARBRACE:
   end
       ->
   '{'
-      -> BAR
+      -> call cfg_bar_start(); BAR
 
 state BAR:
   end ->
@@ -366,9 +410,11 @@ state BAR:
   'modifier'               -> BAR_MODIFIER
   'wheel_up_cmd'           -> BAR_WHEEL_UP_CMD
   'wheel_down_cmd'         -> BAR_WHEEL_DOWN_CMD
+  'bindsym'                -> BAR_BINDSYM
   'position'               -> BAR_POSITION
   'output'                 -> BAR_OUTPUT
   'tray_output'            -> BAR_TRAY_OUTPUT
+  'tray_padding'           -> BAR_TRAY_PADDING
   'font'                   -> BAR_FONT
   'separator_symbol'       -> BAR_SEPARATOR_SYMBOL
   'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
@@ -420,6 +466,14 @@ state BAR_WHEEL_DOWN_CMD:
   command = string
       -> call cfg_bar_wheel_down_cmd($command); BAR
 
+state BAR_BINDSYM:
+  button = word
+      -> BAR_BINDSYM_COMMAND
+
+state BAR_BINDSYM_COMMAND:
+  command = string
+      -> call cfg_bar_bindsym($button, $command); BAR
+
 state BAR_POSITION:
   position = 'top', 'bottom'
       -> call cfg_bar_position($position); BAR
@@ -432,6 +486,16 @@ state BAR_TRAY_OUTPUT:
   output = word
       -> call cfg_bar_tray_output($output); BAR
 
+state BAR_TRAY_PADDING:
+  padding_px = number
+      -> BAR_TRAY_PADDING_PX
+
+state BAR_TRAY_PADDING_PX:
+  'px'
+      ->
+  end
+      -> call cfg_bar_tray_padding(&padding_px); BAR
+
 state BAR_FONT:
   font = string
       -> call cfg_bar_font($font); BAR
@@ -468,7 +532,7 @@ state BAR_COLORS:
   'set' -> BAR_COLORS_IGNORE_LINE
   colorclass = 'background', 'statusline', 'separator'
       -> BAR_COLORS_SINGLE
-  colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace'
+  colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace', 'binding_mode'
       -> BAR_COLORS_BORDER
   '}'
       -> BAR
index 646034a69e1b920256b7ea280282a3c05b08b516..1dab4ee14572c7cf656d5f1ce9322767901f2c8d 100755 (executable)
@@ -1,9 +1,9 @@
 #!/bin/zsh
 # This script is used to prepare a new release of i3.
 
-export RELEASE_VERSION="4.10.1"
-export PREVIOUS_VERSION="4.10"
-export RELEASE_BRANCH="next"
+export RELEASE_VERSION="4.10.3"
+export PREVIOUS_VERSION="4.10.2"
+export RELEASE_BRANCH="master"
 
 if [ ! -e "../i3.github.io" ]
 then
@@ -74,14 +74,20 @@ if [ "${RELEASE_BRANCH}" = "master" ]; then
        git checkout master
        git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
        git checkout next
-       git merge --no-ff master -m "Merge branch 'master' into next"
+       git merge --no-ff -X ours master -m "Merge branch 'master' into next"
 else
        git checkout next
        git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
        git checkout master
-       git merge --no-ff next -m "Merge branch 'next' into master"
+       git merge --no-ff -X theirs next -m "Merge branch 'next' into master"
 fi
 
+git remote remove origin
+git remote add origin git@github.com:i3/i3.git
+git config --add remote.origin.push "+refs/tags/*:refs/tags/*"
+git config --add remote.origin.push "+refs/heads/next:refs/heads/next"
+git config --add remote.origin.push "+refs/heads/master:refs/heads/master"
+
 ################################################################################
 # Section 2: Debian packaging
 ################################################################################
@@ -92,6 +98,7 @@ mkdir debian
 # Copy over the changelog because we expect it to be locally modified in the
 # start directory.
 cp "${STARTDIR}/debian/changelog" i3/debian/changelog
+(cd i3 && git add debian/changelog && git commit -m 'Update debian/changelog')
 
 cat > ${TMPDIR}/Dockerfile <<EOT
 FROM debian:sid
@@ -129,6 +136,10 @@ debsign -k4AC8EE1D ${TMPDIR}/debian/*.changes
 # Section 3: website
 ################################################################################
 
+# Ensure we are in the correct branch for copying the docs.
+cd ${TMPDIR}/i3
+git checkout ${RELEASE_BRANCH}
+
 cd ${TMPDIR}
 git clone --quiet ${STARTDIR}/../i3.github.io
 cd i3.github.io
@@ -151,21 +162,45 @@ git commit -a -m "save docs for ${PREVIOUS_VERSION}"
 for i in $(find _docs -maxdepth 1 -and -type f -and \! -regex ".*\.\(html\|man\)$" -and \! -name "Makefile")
 do
        base="$(basename $i)"
-       [ -e "${STARTDIR}/docs/${base}" ] && cp "${STARTDIR}/docs/${base}" "_docs/${base}"
+       [ -e "${TMPDIR}/i3/docs/${base}" ] && cp "${TMPDIR}/i3/docs/${base}" "_docs/${base}"
 done
 
+sed -i "s,Verify you are using i3 ≥ .*,Verify you are using i3 ≥ ${RELEASE_VERSION},g" _docs/debugging
+
 (cd _docs && make)
 
 for i in $(find _docs -maxdepth 1 -and -type f -and \! -regex ".*\.\(html\|man\)$" -and \! -name "Makefile")
 do
        base="$(basename $i)"
-       [ -e "${STARTDIR}/docs/${base}" ] && cp "_docs/${base}.html" docs/
+       [ -e "${TMPDIR}/i3/docs/${base}" ] && cp "_docs/${base}.html" docs/
 done
 
 git commit -a -m "update docs for ${RELEASE_VERSION}"
 
+git remote remove origin
+git remote add origin git@github.com:i3/i3.github.io.git
+git config --add remote.origin.push "+refs/heads/master:refs/heads/master"
+
 ################################################################################
-# Section 4: final push instructions
+# Section 4: prepare release announcement email
+################################################################################
+
+cd ${TMPDIR}
+cat >email.txt <<EOT
+From: Michael Stapelberg <michael@i3wm.org>
+To: i3-announce@i3.zekjur.net
+Subject: i3 v${RELEASE_VERSION} released
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 8bit
+
+Hi,
+
+I just released i3 v${RELEASE_VERSION}. Release notes follow:
+EOT
+cat ${TMPDIR}/i3/RELEASE-NOTES-${RELEASE_VERSION} >>email.txt
+
+################################################################################
+# Section 5: final push instructions
 ################################################################################
 
 echo "As a final sanity check, install the debian package and see whether i3 works."
@@ -174,18 +209,18 @@ echo "When satisfied, run:"
 echo "  cd ${TMPDIR}/i3"
 echo "  git checkout next"
 echo "  vi debian/changelog"
-# TODO: can we just set up the remote spec properly?
-echo "  git push git@github.com:i3/i3 next"
-echo "  git push git@github.com:i3/i3 master"
-echo "  git push git@github.com:i3/i3 --tags"
+echo "  git commit -a -m \"debian: update changelog\""
+echo "  git push"
 echo ""
 echo "  cd ${TMPDIR}/i3.github.io"
-# TODO: can we just set up the remote spec properly?
-echo "  git push git@github.com:i3/i3.github.io master"
+echo "  git push"
 echo ""
 echo "  cd ${TMPDIR}/debian"
 echo "  dput *.changes"
 echo ""
+echo "  cd ${TMPDIR}"
+echo "  sendmail -t < email.txt"
+echo ""
 echo "Announce on:"
 echo "  twitter"
 echo "  google+"
index 96834f648539d4bcb06c1778a4b6be65117845f4..babe890e9250c439419b45278405d9d6e0a8ed93 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * assignments.c: Assignments for specific windows (for_window).
  *
index 9a38ba9132ec4ce8fb47529e6b2d8f1d48688dc7..63ca0836a352a784f1c3de7da12bb71d61a8a85d 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * bindings.c: Functions for configuring, finding and, running bindings.
  */
@@ -49,10 +49,12 @@ 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 *whole_window, const char *command, const char *modename) {
+                           const char *release, const char *border, 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->border = (border != NULL);
     new_binding->whole_window = (whole_window != NULL);
     if (strcmp(bindtype, "bindsym") == 0) {
         new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0
@@ -61,10 +63,11 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch
 
         new_binding->symbol = sstrdup(input_code);
     } else {
-        // TODO: strtol with proper error handling
-        new_binding->keycode = atoi(input_code);
+        char *endptr;
+        long keycode = strtol(input_code, &endptr, 10);
+        new_binding->keycode = keycode;
         new_binding->input_type = B_KEYBOARD;
-        if (new_binding->keycode == 0) {
+        if (keycode == LONG_MAX || keycode == LONG_MIN || keycode < 0 || *endptr != '\0' || endptr == input_code) {
             ELOG("Could not parse \"%s\" as an input code, ignoring this binding.\n", input_code);
             FREE(new_binding);
             return NULL;
@@ -254,10 +257,11 @@ void translate_keysyms(void) {
 
     TAILQ_FOREACH(bind, bindings, bindings) {
         if (bind->input_type == B_MOUSE) {
-            int button = atoi(bind->symbol + (sizeof("button") - 1));
+            char *endptr;
+            long button = strtol(bind->symbol + (sizeof("button") - 1), &endptr, 10);
             bind->keycode = button;
 
-            if (button < 1)
+            if (button == LONG_MAX || button == LONG_MIN || button < 0 || *endptr != '\0' || endptr == bind->symbol)
                 ELOG("Could not translate string to button: \"%s\"\n", bind->symbol);
 
             continue;
index 10abc0570c3140201a1d46cae53421f3f243d16d..bd2dcb9fdac2bb5a93d8769e42b3749f93234fff 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * click.c: Button press (mouse click) events.
  *
@@ -180,14 +180,18 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     if (con->parent->type == CT_DOCKAREA)
         goto done;
 
+    const bool is_left_or_right_click = (event->detail == XCB_BUTTON_INDEX_1 ||
+                                         event->detail == XCB_BUTTON_INDEX_3);
+
     /* if the user has bound an action to this click, it should override the
      * default behavior. */
-    if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) {
+    if (dest == CLICK_DECORATION || dest == CLICK_INSIDE || dest == CLICK_BORDER) {
         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)) {
+        if (bind && ((dest == CLICK_DECORATION || bind->whole_window) ||
+                     (dest == CLICK_BORDER && bind->border))) {
             CommandResult *result = run_binding(bind, con);
 
             /* ASYNC_POINTER eats the event */
@@ -278,7 +282,8 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
             return 1;
         }
 
-        if (!in_stacked && dest == CLICK_DECORATION) {
+        if (!in_stacked && dest == CLICK_DECORATION &&
+            is_left_or_right_click) {
             /* try tiling resize, but continue if it doesn’t work */
             DLOG("tiling resize with fallback\n");
             if (tiling_resize(con, event, dest))
@@ -291,7 +296,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
             return 1;
         }
 
-        if (dest == CLICK_BORDER) {
+        if (dest == CLICK_BORDER && is_left_or_right_click) {
             DLOG("floating resize due to border click\n");
             floating_resize_window(floatingcon, proportional, event);
             return 1;
@@ -299,7 +304,8 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
 
         /* 6: dragging, if this was a click on a decoration (which did not lead
          * to a resize) */
-        if (!in_stacked && dest == CLICK_DECORATION) {
+        if (!in_stacked && dest == CLICK_DECORATION &&
+            (event->detail == XCB_BUTTON_INDEX_1)) {
             floating_drag_window(floatingcon, event);
             return 1;
         }
@@ -320,8 +326,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     }
     /* 8: otherwise, check for border/decoration clicks and resize */
     else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) &&
-             (event->detail == XCB_BUTTON_INDEX_1 ||
-              event->detail == XCB_BUTTON_INDEX_3)) {
+             is_left_or_right_click) {
         DLOG("Trying to resize (tiling)\n");
         tiling_resize(con, event, dest);
     }
@@ -344,8 +349,8 @@ done:
  */
 int handle_button_press(xcb_button_press_event_t *event) {
     Con *con;
-    DLOG("Button %d %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
-         event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"),
+    DLOG("Button %d (state %d) %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n",
+         event->detail, event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"),
          event->event, event->child, event->event_x, event->event_y, event->root_x,
          event->root_y);
 
index 9d5275dc3d57dadb4d70f7bc74b30052107a8151..62adcc6536ab87b36dd02c45ce50d555e1292200 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * commands.c: all command functions (see commands_parser.c)
  *
@@ -363,6 +363,31 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
         return;
     }
 
+    if (strcmp(ctype, "window_type") == 0) {
+        if (strcasecmp(cvalue, "normal") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_NORMAL;
+        else if (strcasecmp(cvalue, "dialog") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_DIALOG;
+        else if (strcasecmp(cvalue, "utility") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_UTILITY;
+        else if (strcasecmp(cvalue, "toolbar") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLBAR;
+        else if (strcasecmp(cvalue, "splash") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_SPLASH;
+        else if (strcasecmp(cvalue, "menu") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_MENU;
+        else if (strcasecmp(cvalue, "dropdown_menu") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
+        else if (strcasecmp(cvalue, "popup_menu") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_POPUP_MENU;
+        else if (strcasecmp(cvalue, "tooltip") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLTIP;
+        else
+            ELOG("unknown window_type value \"%s\"\n", cvalue);
+
+        return;
+    }
+
     if (strcmp(ctype, "con_mark") == 0) {
         current_match->mark = regex_new(cvalue);
         return;
@@ -386,6 +411,11 @@ void cmd_criteria_add(I3_CMD, char *ctype, char *cvalue) {
         return;
     }
 
+    if (strcmp(ctype, "workspace") == 0) {
+        current_match->workspace = regex_new(cvalue);
+        return;
+    }
+
     ELOG("Unknown criterion: %s\n", ctype);
 }
 
@@ -791,7 +821,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz
 }
 
 /*
- * Implementation of 'border normal|none|1pixel|toggle|pixel'.
+ * Implementation of 'border normal|pixel [<n>]', 'border none|1pixel|toggle'.
  *
  */
 void cmd_border(I3_CMD, char *border_style_str, char *border_width) {
@@ -1037,28 +1067,48 @@ void cmd_workspace_name(I3_CMD, char *name) {
 }
 
 /*
- * Implementation of 'mark <mark>'
+ * Implementation of 'mark [--toggle] <mark>'
  *
  */
-void cmd_mark(I3_CMD, char *mark) {
-    DLOG("Clearing all windows which have that mark first\n");
+void cmd_mark(I3_CMD, char *mark, char *toggle) {
+    HANDLE_EMPTY_MATCH;
 
-    Con *con;
-    TAILQ_FOREACH(con, &all_cons, all_cons) {
-        if (con->mark && strcmp(con->mark, mark) == 0)
-            FREE(con->mark);
+    owindow *current = TAILQ_FIRST(&owindows);
+    if (current == NULL) {
+        ysuccess(false);
+        return;
     }
 
-    DLOG("marking window with str %s\n", mark);
-    owindow *current;
-
-    HANDLE_EMPTY_MATCH;
+    /* Marks must be unique, i.e., no two windows must have the same mark. */
+    if (current != TAILQ_LAST(&owindows, owindows_head)) {
+        yerror("A mark must not be put onto more than one window");
+        return;
+    }
 
-    TAILQ_FOREACH(current, &owindows, owindows) {
-        DLOG("matching: %p / %s\n", current->con, current->con->name);
+    DLOG("matching: %p / %s\n", current->con, current->con->name);
+    current->con->mark_changed = true;
+    if (toggle != NULL && current->con->mark && strcmp(current->con->mark, mark) == 0) {
+        DLOG("removing window mark %s\n", mark);
+        FREE(current->con->mark);
+    } else {
+        DLOG("marking window with str %s\n", mark);
+        FREE(current->con->mark);
         current->con->mark = sstrdup(mark);
     }
 
+    DLOG("Clearing all non-matched windows with this mark\n");
+    Con *con;
+    TAILQ_FOREACH(con, &all_cons, all_cons) {
+        /* Skip matched window, we took care of it already. */
+        if (current->con == con)
+            continue;
+
+        if (con->mark && strcmp(con->mark, mark) == 0) {
+            FREE(con->mark);
+            con->mark_changed = true;
+        }
+    }
+
     cmd_output->needs_tree_render = true;
     // XXX: default reply for now, make this a better reply
     ysuccess(true);
@@ -1072,16 +1122,20 @@ void cmd_unmark(I3_CMD, char *mark) {
     if (mark == NULL) {
         Con *con;
         TAILQ_FOREACH(con, &all_cons, all_cons) {
+            if (con->mark == NULL)
+                continue;
+
             FREE(con->mark);
+            con->mark_changed = true;
         }
-        DLOG("removed all window marks");
+        DLOG("Removed all window marks.\n");
     } else {
-        Con *con;
-        TAILQ_FOREACH(con, &all_cons, all_cons) {
-            if (con->mark && strcmp(con->mark, mark) == 0)
-                FREE(con->mark);
+        Con *con = con_by_mark(mark);
+        if (con != NULL) {
+            FREE(con->mark);
+            con->mark_changed = true;
         }
-        DLOG("removed window mark %s\n", mark);
+        DLOG("Removed window mark \"%s\".\n", mark);
     }
 
     cmd_output->needs_tree_render = true;
@@ -1143,6 +1197,26 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
     ysuccess(true);
 }
 
+/*
+ * Implementation of 'move [container|window] [to] mark <str>'.
+ *
+ */
+void cmd_move_con_to_mark(I3_CMD, char *mark) {
+    DLOG("moving window to mark \"%s\"\n", mark);
+
+    HANDLE_EMPTY_MATCH;
+
+    bool result = true;
+    owindow *current;
+    TAILQ_FOREACH(current, &owindows, owindows) {
+        DLOG("moving matched window %p / %s to mark \"%s\"\n", current->con, current->con->name, mark);
+        result &= con_move_to_mark(current->con, mark);
+    }
+
+    cmd_output->needs_tree_render = true;
+    ysuccess(result);
+}
+
 /*
  * Implementation of 'floating enable|disable|toggle'
  *
@@ -1746,31 +1820,48 @@ void cmd_move_window_to_center(I3_CMD, char *method) {
     }
 
     if (strcmp(method, "absolute") == 0) {
-        Rect *rect = &focused->parent->rect;
-
         DLOG("moving to absolute center\n");
-        rect->x = croot->rect.width / 2 - rect->width / 2;
-        rect->y = croot->rect.height / 2 - rect->height / 2;
+        floating_center(focused->parent, croot->rect);
 
         floating_maybe_reassign_ws(focused->parent);
         cmd_output->needs_tree_render = true;
     }
 
     if (strcmp(method, "position") == 0) {
-        Rect *wsrect = &con_get_workspace(focused)->rect;
-        Rect newrect = focused->parent->rect;
-
         DLOG("moving to center\n");
-        newrect.x = wsrect->width / 2 - newrect.width / 2;
-        newrect.y = wsrect->height / 2 - newrect.height / 2;
+        floating_center(focused->parent, con_get_workspace(focused)->rect);
 
-        floating_reposition(focused->parent, newrect);
+        cmd_output->needs_tree_render = true;
     }
 
     // XXX: default reply for now, make this a better reply
     ysuccess(true);
 }
 
+/*
+ * Implementation of 'move [window|container] [to] position mouse'
+ *
+ */
+void cmd_move_window_to_mouse(I3_CMD) {
+    HANDLE_EMPTY_MATCH;
+
+    owindow *current;
+    TAILQ_FOREACH(current, &owindows, owindows) {
+        Con *floating_con = con_inside_floating(current->con);
+        if (floating_con == NULL) {
+            DLOG("con %p / %s is not floating, cannot move it to the mouse position.\n",
+                 current->con, current->con->name);
+            continue;
+        }
+
+        DLOG("moving floating container %p / %s to cursor position\n", floating_con, floating_con->name);
+        floating_move_to_pointer(floating_con);
+    }
+
+    cmd_output->needs_tree_render = true;
+    ysuccess(true);
+}
+
 /*
  * Implementation of 'move scratchpad'.
  *
@@ -1813,13 +1904,42 @@ void cmd_scratchpad_show(I3_CMD) {
     ysuccess(true);
 }
 
+/*
+ * Implementation of 'title_format <format>'
+ *
+ */
+void cmd_title_format(I3_CMD, char *format) {
+    DLOG("setting title_format to \"%s\"\n", format);
+    HANDLE_EMPTY_MATCH;
+
+    owindow *current;
+    TAILQ_FOREACH(current, &owindows, owindows) {
+        if (current->con->window == NULL)
+            continue;
+
+        DLOG("setting title_format for %p / %s\n", current->con, current->con->name);
+        FREE(current->con->window->title_format);
+
+        /* If we only display the title without anything else, we can skip the parsing step,
+         * so we remove the title format altogether. */
+        if (strcasecmp(format, "%title") != 0)
+            current->con->window->title_format = sstrdup(format);
+
+        /* Make sure the window title is redrawn immediately. */
+        current->con->window->name_x_changed = true;
+    }
+
+    cmd_output->needs_tree_render = true;
+    ysuccess(true);
+}
+
 /*
  * Implementation of 'rename workspace [<name>] to <name>'
  *
  */
 void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
     if (strncasecmp(new_name, "__", strlen("__")) == 0) {
-        LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.", new_name);
+        LOG("Cannot rename workspace to \"%s\": names starting with __ are i3-internal.\n", new_name);
         ysuccess(false);
         return;
     }
index fa4c2360101513e4796490c9720ff9b63533915f..9ae75abea21ff28a1fb53501ece991a2037e46d8 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * commands_parser.c: hand-written parser to parse commands (commands are what
  * you bind on keys and what you can send to i3 using the IPC interface, like
index b610b0c60c1c9cfb792d107a7ff6d05acdc27487..08c720015bd40b3099dc55fb55148fb4e7089214 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * con.c: Functions which deal with containers directly (creating containers,
  *        searching containers, getting specific properties from containers,
@@ -70,20 +70,10 @@ Con *con_new(Con *parent, i3Window *window) {
     return new;
 }
 
-/*
- * Attaches the given container to the given parent. This happens when moving
- * a container or when inserting a new container at a specific place in the
- * tree.
- *
- * ignore_focus is to just insert the Con at the end (useful when creating a
- * new split container *around* some containers, that is, detaching and
- * attaching them in order without wanting to mess with the focus in between).
- *
- */
-void con_attach(Con *con, Con *parent, bool ignore_focus) {
+static void _con_attach(Con *con, Con *parent, Con *previous, bool ignore_focus) {
     con->parent = parent;
     Con *loop;
-    Con *current = NULL;
+    Con *current = previous;
     struct nodes_head *nodes_head = &(parent->nodes_head);
     struct focus_head *focus_head = &(parent->focus_head);
 
@@ -155,8 +145,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) {
         /* Insert the container after the tiling container, if found.
          * When adding to a CT_OUTPUT, just append one after another. */
         if (current && parent->type != CT_OUTPUT) {
-            DLOG("Inserting con = %p after last focused tiling con %p\n",
-                 con, current);
+            DLOG("Inserting con = %p after con %p\n", con, current);
             TAILQ_INSERT_AFTER(nodes_head, current, con, nodes);
         } else
             TAILQ_INSERT_TAIL(nodes_head, con, nodes);
@@ -170,6 +159,20 @@ add_to_focus_head:
     con_force_split_parents_redraw(con);
 }
 
+/*
+ * Attaches the given container to the given parent. This happens when moving
+ * a container or when inserting a new container at a specific place in the
+ * tree.
+ *
+ * ignore_focus is to just insert the Con at the end (useful when creating a
+ * new split container *around* some containers, that is, detaching and
+ * attaching them in order without wanting to mess with the focus in between).
+ *
+ */
+void con_attach(Con *con, Con *parent, bool ignore_focus) {
+    _con_attach(con, parent, NULL, ignore_focus);
+}
+
 /*
  * Detaches the given container from its current parent
  *
@@ -257,6 +260,29 @@ bool con_is_split(Con *con) {
     }
 }
 
+/*
+ * This will only return true for containers which have some parent with
+ * a tabbed / stacked parent of which they are not the currently focused child.
+ *
+ */
+bool con_is_hidden(Con *con) {
+    Con *current = con;
+
+    /* ascend to the workspace level and memorize the highest-up container
+     * which is stacked or tabbed. */
+    while (current != NULL && current->type != CT_WORKSPACE) {
+        Con *parent = current->parent;
+        if (parent != NULL && (parent->layout == L_TABBED || parent->layout == L_STACKED)) {
+            if (TAILQ_FIRST(&(parent->focus_head)) != current)
+                return true;
+        }
+
+        current = parent;
+    }
+
+    return false;
+}
+
 /*
  * Returns true if this node accepts a window (if the node swallows windows,
  * it might already have swallowed enough and cannot hold any more).
@@ -460,6 +486,21 @@ Con *con_by_frame_id(xcb_window_t frame) {
     return NULL;
 }
 
+/*
+ * Returns the container with the given mark or NULL if no such container
+ * exists.
+ *
+ */
+Con *con_by_mark(const char *mark) {
+    Con *con;
+    TAILQ_FOREACH(con, &all_cons, all_cons) {
+        if (con->mark != NULL && strcmp(con->mark, mark) == 0)
+            return con;
+    }
+
+    return NULL;
+}
+
 /*
  * Returns the first container below 'con' which wants to swallow this window
  * TODO: priority
@@ -682,93 +723,75 @@ void con_disable_fullscreen(Con *con) {
     con_set_fullscreen_mode(con, CF_NONE);
 }
 
-/*
- * Moves the given container to the currently focused container on the given
- * workspace.
- *
- * The fix_coordinates flag will translate the current coordinates (offset from
- * the monitor position basically) to appropriate coordinates on the
- * destination workspace.
- * Not enabling this behaviour comes in handy when this function gets called by
- * floating_maybe_reassign_ws, which will only "move" a floating window when it
- * *already* changed its coordinates to a different output.
- *
- * The dont_warp flag disables pointer warping and will be set when this
- * function is called while dragging a floating window.
- *
- * TODO: is there a better place for this function?
- *
- */
-void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
+static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fix_coordinates, bool dont_warp) {
+    Con *orig_target = target;
+
     /* Prevent moving if this would violate the fullscreen focus restrictions. */
-    if (!con_fullscreen_permits_focusing(workspace)) {
-        LOG("Cannot move out of a fullscreen container");
-        return;
+    Con *target_ws = con_get_workspace(target);
+    if (!con_fullscreen_permits_focusing(target_ws)) {
+        LOG("Cannot move out of a fullscreen container.\n");
+        return false;
     }
 
     if (con_is_floating(con)) {
-        DLOG("Using FLOATINGCON instead\n");
+        DLOG("Container is floating, using parent instead.\n");
         con = con->parent;
     }
 
     Con *source_ws = con_get_workspace(con);
-    if (workspace == source_ws) {
-        DLOG("Not moving, already there\n");
-        return;
-    }
 
     if (con->type == CT_WORKSPACE) {
         /* Re-parent all of the old workspace's floating windows. */
         Con *child;
         while (!TAILQ_EMPTY(&(source_ws->floating_head))) {
             child = TAILQ_FIRST(&(source_ws->floating_head));
-            con_move_to_workspace(child, workspace, true, true);
+            con_move_to_workspace(child, target_ws, true, true);
         }
 
         /* If there are no non-floating children, ignore the workspace. */
         if (con_is_leaf(con))
-            return;
+            return false;
 
         con = workspace_encapsulate(con);
         if (con == NULL) {
             ELOG("Workspace failed to move its contents into a container!\n");
-            return;
+            return false;
         }
     }
 
+    /* Save the urgency state so that we can restore it. */
+    bool urgent = con->urgent;
+
     /* Save the current workspace. So we can call workspace_show() by the end
      * of this function. */
     Con *current_ws = con_get_workspace(focused);
 
     Con *source_output = con_get_output(con),
-        *dest_output = con_get_output(workspace);
+        *dest_output = con_get_output(target_ws);
 
     /* 1: save the container which is going to be focused after the current
      * container is moved away */
     Con *focus_next = con_next_focused(con);
 
-    /* 2: get the focused container of this workspace */
-    Con *next = con_descend_focused(workspace);
-
-    /* 3: we go up one level, but only when next is a normal container */
-    if (next->type != CT_WORKSPACE) {
-        DLOG("next originally = %p / %s / type %d\n", next, next->name, next->type);
-        next = next->parent;
+    /* 2: we go up one level, but only when target is a normal container */
+    if (target->type != CT_WORKSPACE) {
+        DLOG("target originally = %p / %s / type %d\n", target, target->name, target->type);
+        target = target->parent;
     }
 
-    /* 4: if the target container is floating, we get the workspace instead.
+    /* 3: if the target container is floating, we get the workspace instead.
      * Only tiling windows need to get inserted next to the current container.
      * */
-    Con *floatingcon = con_inside_floating(next);
+    Con *floatingcon = con_inside_floating(target);
     if (floatingcon != NULL) {
         DLOG("floatingcon, going up even further\n");
-        next = floatingcon->parent;
+        target = floatingcon->parent;
     }
 
     if (con->type == CT_FLOATING_CON) {
-        Con *ws = con_get_workspace(next);
+        Con *ws = con_get_workspace(target);
         DLOG("This is a floating window, using workspace %p / %s\n", ws, ws->name);
-        next = ws;
+        target = ws;
     }
 
     if (source_output != dest_output) {
@@ -782,8 +805,8 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         /* If moving to a visible workspace, call show so it can be considered
          * focused. Must do before attaching because workspace_show checks to see
          * if focused container is in its area. */
-        if (workspace_is_visible(workspace)) {
-            workspace_show(workspace);
+        if (workspace_is_visible(target_ws)) {
+            workspace_show(target_ws);
 
             /* Don’t warp if told so (when dragging floating windows with the
              * mouse for example) */
@@ -796,29 +819,29 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
 
     /* If moving a fullscreen container and the destination already has a
      * fullscreen window on it, un-fullscreen the target's fullscreen con. */
-    Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT);
+    Con *fullscreen = con_get_fullscreen_con(target_ws, CF_OUTPUT);
     if (con->fullscreen_mode != CF_NONE && fullscreen != NULL) {
         con_toggle_fullscreen(fullscreen, CF_OUTPUT);
         fullscreen = NULL;
     }
 
-    DLOG("Re-attaching container to %p / %s\n", next, next->name);
-    /* 5: re-attach the con to the parent of this focused container */
+    DLOG("Re-attaching container to %p / %s\n", target, target->name);
+    /* 4: re-attach the con to the parent of this focused container */
     Con *parent = con->parent;
     con_detach(con);
-    con_attach(con, next, false);
+    _con_attach(con, target, behind_focused ? NULL : orig_target, !behind_focused);
 
-    /* 6: fix the percentages */
+    /* 5: fix the percentages */
     con_fix_percent(parent);
     con->percent = 0.0;
-    con_fix_percent(next);
+    con_fix_percent(target);
 
-    /* 7: focus the con on the target workspace, but only within that
+    /* 6: focus the con on the target workspace, but only within that
      * workspace, that is, don’t move focus away if the target workspace is
      * invisible.
      * We don’t focus the con for i3 pseudo workspaces like __i3_scratch and
      * we don’t focus when there is a fullscreen con on that workspace. */
-    if (!con_is_internal(workspace) && !fullscreen) {
+    if (!con_is_internal(target_ws) && !fullscreen) {
         /* We need to save the focused workspace on the output in case the
          * new workspace is hidden and it's necessary to immediately switch
          * back to the originally-focused workspace. */
@@ -830,7 +853,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
             con_focus(old_focus);
     }
 
-    /* 8: when moving to another workspace, we leave the focus on the current
+    /* 7: when moving to another workspace, we leave the focus on the current
      * workspace. (see also #809) */
 
     /* Descend focus stack in case focus_next is a workspace which can
@@ -843,7 +866,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
     if (source_ws == current_ws)
         con_focus(con_descend_focused(focus_next));
 
-    /* If anything within the container is associated with a startup sequence,
+    /* 8. If anything within the container is associated with a startup sequence,
      * delete it so child windows won't be created on the old workspace. */
     struct Startup_Sequence *sequence;
     xcb_get_property_cookie_t cookie;
@@ -877,7 +900,78 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
 
     CALL(parent, on_remove_child);
 
+    /* 9. If the container was marked urgent, move the urgency hint. */
+    if (urgent) {
+        workspace_update_urgent_flag(source_ws);
+        con_set_urgency(con, true);
+    }
+
     ipc_send_window_event("move", con);
+    return true;
+}
+
+/*
+ * Moves the given container to the given mark.
+ *
+ */
+bool con_move_to_mark(Con *con, const char *mark) {
+    Con *target = con_by_mark(mark);
+    if (target == NULL) {
+        DLOG("found no container with mark \"%s\"\n", mark);
+        return false;
+    }
+
+    /* For floating target containers, we just send the window to the same workspace. */
+    if (con_is_floating(target)) {
+        DLOG("target container is floating, moving container to target's workspace.\n");
+        con_move_to_workspace(con, con_get_workspace(target), true, false);
+        return true;
+    }
+
+    /* For split containers, we use the currently focused container within it.
+     * This allows setting marks on, e.g., tabbed containers which will move
+     * con to a new tab behind the focused tab. */
+    if (con_is_split(target)) {
+        DLOG("target is a split container, descending to the currently focused child.\n");
+        target = TAILQ_FIRST(&(target->focus_head));
+    }
+
+    if (con == target) {
+        DLOG("cannot move the container to itself, aborting.\n");
+        return false;
+    }
+
+    return _con_move_to_con(con, target, false, true, false);
+}
+
+/*
+ * Moves the given container to the currently focused container on the given
+ * workspace.
+ *
+ * The fix_coordinates flag will translate the current coordinates (offset from
+ * the monitor position basically) to appropriate coordinates on the
+ * destination workspace.
+ * Not enabling this behaviour comes in handy when this function gets called by
+ * floating_maybe_reassign_ws, which will only "move" a floating window when it
+ * *already* changed its coordinates to a different output.
+ *
+ * The dont_warp flag disables pointer warping and will be set when this
+ * function is called while dragging a floating window.
+ *
+ * TODO: is there a better place for this function?
+ *
+ */
+void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
+    assert(workspace->type == CT_WORKSPACE);
+
+    Con *source_ws = con_get_workspace(con);
+    if (workspace == source_ws) {
+        DLOG("Not moving, already there\n");
+        return;
+    }
+
+    Con *target = con_descend_focused(workspace);
+    _con_move_to_con(con, target, true, fix_coordinates, dont_warp);
 }
 
 /*
@@ -1346,11 +1440,7 @@ void con_set_layout(Con *con, layout_t layout) {
          * with an orientation). Since we switched to splith/splitv layouts,
          * using the "default" layout (which "only" should happen when using
          * legacy configs) is using the last split layout (either splith or
-         * splitv) in order to still do the same thing.
-         *
-         * Starting from v4.6 though, we will nag users about using "layout
-         * default", and in v4.9 we will remove it entirely (with an
-         * appropriate i3-migrate-config mechanism). */
+         * splitv) in order to still do the same thing. */
         con->layout = con->last_split_layout;
         /* In case last_split_layout was not initialized… */
         if (con->layout == L_DEFAULT)
index 6f906b8ce02e7218828ae2f449bf3223a82dd205..a931ba1f2de513d3e9d44a6e313e883a149cbd3f 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * config.c: Configuration file (calling the parser (src/config_parser.c) with
  *           the correct path, switching key bindings mode).
@@ -39,73 +39,6 @@ void update_barconfig() {
     }
 }
 
-/*
- * Get the path of the first configuration file found. If override_configpath
- * is specified, that path is returned and saved for further calls. Otherwise,
- * checks the home directory first, then the system directory first, always
- * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
- * $XDG_CONFIG_DIRS)
- *
- */
-static char *get_config_path(const char *override_configpath) {
-    char *xdg_config_home, *xdg_config_dirs, *config_path;
-
-    static const char *saved_configpath = NULL;
-
-    if (override_configpath != NULL) {
-        saved_configpath = override_configpath;
-        return sstrdup(saved_configpath);
-    }
-
-    if (saved_configpath != NULL)
-        return sstrdup(saved_configpath);
-
-    /* 1: check the traditional path under the home directory */
-    config_path = resolve_tilde("~/.i3/config");
-    if (path_exists(config_path))
-        return config_path;
-    free(config_path);
-
-    /* 2: check for $XDG_CONFIG_HOME/i3/config */
-    if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
-        xdg_config_home = "~/.config";
-
-    xdg_config_home = resolve_tilde(xdg_config_home);
-    sasprintf(&config_path, "%s/i3/config", xdg_config_home);
-    free(xdg_config_home);
-
-    if (path_exists(config_path))
-        return config_path;
-    free(config_path);
-
-    /* 3: check the traditional path under /etc */
-    config_path = SYSCONFDIR "/i3/config";
-    if (path_exists(config_path))
-        return sstrdup(config_path);
-
-    /* 4: check for $XDG_CONFIG_DIRS/i3/config */
-    if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
-        xdg_config_dirs = "/etc/xdg";
-
-    char *buf = sstrdup(xdg_config_dirs);
-    char *tok = strtok(buf, ":");
-    while (tok != NULL) {
-        tok = resolve_tilde(tok);
-        sasprintf(&config_path, "%s/i3/config", tok);
-        free(tok);
-        if (path_exists(config_path)) {
-            free(buf);
-            return config_path;
-        }
-        free(config_path);
-        tok = strtok(NULL, ":");
-    }
-    free(buf);
-
-    die("Unable to find the configuration file (looked at "
-        "~/.i3/config, $XDG_CONFIG_HOME/i3/config, " SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
-}
-
 /*
  * Finds the configuration file to use (either the one specified by
  * override_configpath), the user’s one or the system default) and calls
@@ -113,7 +46,12 @@ static char *get_config_path(const char *override_configpath) {
  *
  */
 bool parse_configuration(const char *override_configpath, bool use_nagbar) {
-    char *path = get_config_path(override_configpath);
+    char *path = get_config_path(override_configpath, true);
+    if (path == NULL) {
+        die("Unable to find the configuration file (looked at "
+            "~/.i3/config, $XDG_CONFIG_HOME/i3/config, " SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
+    }
+
     LOG("Parsing configfile %s\n", path);
     FREE(current_configpath);
     current_configpath = path;
@@ -192,6 +130,9 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
             FREE(barconfig->colors.urgent_workspace_border);
             FREE(barconfig->colors.urgent_workspace_bg);
             FREE(barconfig->colors.urgent_workspace_text);
+            FREE(barconfig->colors.binding_mode_border);
+            FREE(barconfig->colors.binding_mode_bg);
+            FREE(barconfig->colors.binding_mode_text);
             TAILQ_REMOVE(&barconfigs, barconfig, configs);
             FREE(barconfig);
         }
@@ -252,6 +193,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000");
     INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000");
 
+    config.show_marks = true;
+
     config.default_border = BS_NORMAL;
     config.default_floating_border = BS_NORMAL;
     config.default_border_width = logical_px(2);
@@ -263,6 +206,10 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     if (config.workspace_urgency_timer == 0)
         config.workspace_urgency_timer = 0.5;
 
+    /* Set default zero displays exit delay to 500ms */
+    if (config.zero_disp_exit_timer_ms == 0)
+        config.zero_disp_exit_timer_ms = 500;
+
     parse_configuration(override_configpath, true);
 
     if (reload) {
index c8b25c7645f6cfc7e8c2d8ab210101138719957d..5b85012cbe6b8e7f0f7cf2ee849162f5080b45f5 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * config_directives.c: all config storing functions (see config_parser.c)
  *
@@ -89,6 +89,31 @@ CFGFUN(criteria_add, const char *ctype, const char *cvalue) {
         return;
     }
 
+    if (strcmp(ctype, "window_type") == 0) {
+        if (strcasecmp(cvalue, "normal") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_NORMAL;
+        else if (strcasecmp(cvalue, "dialog") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_DIALOG;
+        else if (strcasecmp(cvalue, "utility") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_UTILITY;
+        else if (strcasecmp(cvalue, "toolbar") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLBAR;
+        else if (strcasecmp(cvalue, "splash") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_SPLASH;
+        else if (strcasecmp(cvalue, "menu") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_MENU;
+        else if (strcasecmp(cvalue, "dropdown_menu") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
+        else if (strcasecmp(cvalue, "popup_menu") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_POPUP_MENU;
+        else if (strcasecmp(cvalue, "tooltip") == 0)
+            current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLTIP;
+        else
+            ELOG("unknown window_type value \"%s\"\n", cvalue);
+
+        return;
+    }
+
     if (strcmp(ctype, "con_mark") == 0) {
         current_match->mark = regex_new(cvalue);
         return;
@@ -112,6 +137,11 @@ CFGFUN(criteria_add, const char *ctype, const char *cvalue) {
         return;
     }
 
+    if (strcmp(ctype, "workspace") == 0) {
+        current_match->workspace = regex_new(cvalue);
+        return;
+    }
+
     ELOG("Unknown criterion: %s\n", ctype);
 }
 
@@ -171,8 +201,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 *whole_window, const char *command) {
-    configure_binding(bindtype, modifiers, key, release, whole_window, command, DEFAULT_BINDING_MODE);
+CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) {
+    configure_binding(bindtype, modifiers, key, release, border, whole_window, command, DEFAULT_BINDING_MODE);
 }
 
 /*******************************************************************************
@@ -181,8 +211,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 *whole_window, const char *command) {
-    configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode);
+CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) {
+    configure_binding(bindtype, modifiers, key, release, border, whole_window, command, current_mode);
 }
 
 CFGFUN(enter_mode, const char *modename) {
@@ -329,6 +359,31 @@ CFGFUN(force_display_urgency_hint, const long duration_ms) {
     config.workspace_urgency_timer = duration_ms / 1000.0;
 }
 
+CFGFUN(delay_exit_on_zero_displays, const long duration_ms) {
+    config.zero_disp_exit_timer_ms = duration_ms;
+}
+
+CFGFUN(focus_on_window_activation, const char *mode) {
+    if (strcmp(mode, "smart") == 0)
+        config.focus_on_window_activation = FOWA_SMART;
+    else if (strcmp(mode, "urgent") == 0)
+        config.focus_on_window_activation = FOWA_URGENT;
+    else if (strcmp(mode, "focus") == 0)
+        config.focus_on_window_activation = FOWA_FOCUS;
+    else if (strcmp(mode, "none") == 0)
+        config.focus_on_window_activation = FOWA_NONE;
+    else {
+        ELOG("Unknown focus_on_window_activation mode \"%s\", ignoring it.\n", mode);
+        return;
+    }
+
+    DLOG("Set new focus_on_window_activation mode = %i.\n", config.focus_on_window_activation);
+}
+
+CFGFUN(show_marks, const char *value) {
+    config.show_marks = eval_boolstr(value);
+}
+
 CFGFUN(workspace, const char *workspace, const char *output) {
     DLOG("Assigning workspace \"%s\" to output \"%s\"\n", workspace, output);
     /* Check for earlier assignments of the same workspace so that we
@@ -402,7 +457,7 @@ CFGFUN(assign, const char *workspace) {
         ELOG("Match is empty, ignoring this assignment\n");
         return;
     }
-    DLOG("new assignment, using above criteria, to workspace %s\n", workspace);
+    DLOG("New assignment, using above criteria, to workspace \"%s\".\n", workspace);
     Assignment *assignment = scalloc(sizeof(Assignment));
     match_copy(&(assignment->match), current_match);
     assignment->type = A_TO_WORKSPACE;
@@ -410,6 +465,19 @@ CFGFUN(assign, const char *workspace) {
     TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
 }
 
+CFGFUN(no_focus) {
+    if (match_is_empty(current_match)) {
+        ELOG("Match is empty, ignoring this assignment\n");
+        return;
+    }
+
+    DLOG("New assignment, using above criteria, to ignore focus on manage.\n");
+    Assignment *assignment = scalloc(sizeof(Assignment));
+    match_copy(&(assignment->match), current_match);
+    assignment->type = A_NO_FOCUS;
+    TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
+}
+
 /*******************************************************************************
  * Bar configuration (i3bar)
  ******************************************************************************/
@@ -467,14 +535,44 @@ CFGFUN(bar_modifier, const char *modifier) {
         current_bar.modifier = M_SHIFT;
 }
 
+static void bar_configure_binding(const char *button, const char *command) {
+    if (strncasecmp(button, "button", strlen("button")) != 0) {
+        ELOG("Bindings for a bar can only be mouse bindings, not \"%s\", ignoring.\n", button);
+        return;
+    }
+
+    int input_code = atoi(button + strlen("button"));
+    if (input_code < 1) {
+        ELOG("Button \"%s\" does not seem to be in format 'buttonX'.\n", button);
+        return;
+    }
+
+    struct Barbinding *current;
+    TAILQ_FOREACH(current, &(current_bar.bar_bindings), bindings) {
+        if (current->input_code == input_code) {
+            ELOG("command for button %s was already specified, ignoring.\n", button);
+            return;
+        }
+    }
+
+    struct Barbinding *new_binding = scalloc(sizeof(struct Barbinding));
+    new_binding->input_code = input_code;
+    new_binding->command = sstrdup(command);
+    TAILQ_INSERT_TAIL(&(current_bar.bar_bindings), new_binding, bindings);
+}
+
 CFGFUN(bar_wheel_up_cmd, const char *command) {
-    FREE(current_bar.wheel_up_cmd);
-    current_bar.wheel_up_cmd = sstrdup(command);
+    ELOG("'wheel_up_cmd' is deprecated. Please us 'bindsym button4 %s' instead.\n", command);
+    bar_configure_binding("button4", command);
 }
 
 CFGFUN(bar_wheel_down_cmd, const char *command) {
-    FREE(current_bar.wheel_down_cmd);
-    current_bar.wheel_down_cmd = sstrdup(command);
+    ELOG("'wheel_down_cmd' is deprecated. Please us 'bindsym button5 %s' instead.\n", command);
+    bar_configure_binding("button5", command);
+}
+
+CFGFUN(bar_bindsym, const char *button, const char *command) {
+    bar_configure_binding(button, command);
 }
 
 CFGFUN(bar_position, const char *position) {
@@ -507,6 +605,7 @@ CFGFUN(bar_color, const char *colorclass, const char *border, const char *backgr
     APPLY_COLORS(active_workspace);
     APPLY_COLORS(inactive_workspace);
     APPLY_COLORS(urgent_workspace);
+    APPLY_COLORS(binding_mode);
 
 #undef APPLY_COLORS
 }
@@ -521,6 +620,10 @@ CFGFUN(bar_tray_output, const char *output) {
     current_bar.tray_output = sstrdup(output);
 }
 
+CFGFUN(bar_tray_padding, const long padding_px) {
+    current_bar.tray_padding = padding_px;
+}
+
 CFGFUN(bar_color_single, const char *colorclass, const char *color) {
     if (strcmp(colorclass, "background") == 0)
         current_bar.colors.background = sstrdup(color);
@@ -547,6 +650,11 @@ CFGFUN(bar_strip_workspace_numbers, const char *value) {
     current_bar.strip_workspace_numbers = eval_boolstr(value);
 }
 
+CFGFUN(bar_start) {
+    TAILQ_INIT(&(current_bar.bar_bindings));
+    current_bar.tray_padding = 2;
+}
+
 CFGFUN(bar_finish) {
     DLOG("\t new bar configuration finished, saving.\n");
     /* Generate a unique ID for this bar if not already configured */
index eef03cae238e0cf7455fd64ca1b18658e47d977f..9a0d19254db4d2c7d349054bdf351c8a96d24b11 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * config_parser.c: hand-written parser to parse configuration directives.
  *
@@ -837,11 +837,11 @@ static char *migrate_config(char *input, off_t size) {
  */
 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;
+    int fd;
     struct stat stbuf;
     char *buf;
     FILE *fstr;
-    char buffer[1026], key[512], value[512];
+    char buffer[4096], key[512], value[512], *continuation = NULL;
 
     if ((fd = open(f, O_RDONLY)) == -1)
         die("Could not open configuration file: %s\n", strerror(errno));
@@ -850,27 +850,30 @@ bool parse_file(const char *f, bool use_nagbar) {
         die("Could not fstat file: %s\n", strerror(errno));
 
     buf = scalloc((stbuf.st_size + 1) * sizeof(char));
-    while (read_bytes < stbuf.st_size) {
-        if ((ret = read(fd, buf + read_bytes, (stbuf.st_size - read_bytes))) < 0)
-            die("Could not read(): %s\n", strerror(errno));
-        read_bytes += ret;
-    }
-
-    if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
-        die("Could not lseek: %s\n", strerror(errno));
 
     if ((fstr = fdopen(fd, "r")) == NULL)
         die("Could not fdopen: %s\n", strerror(errno));
 
     while (!feof(fstr)) {
-        if (fgets(buffer, 1024, fstr) == NULL) {
+        if (!continuation)
+            continuation = buffer;
+        if (fgets(continuation, sizeof(buffer) - (continuation - buffer), fstr) == NULL) {
             if (feof(fstr))
                 break;
             die("Could not read configuration file\n");
         }
+        if (buffer[strlen(buffer) - 1] != '\n') {
+            ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer));
+        }
+        continuation = strstr(buffer, "\\\n");
+        if (continuation) {
+            continue;
+        }
+
+        strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1);
 
         /* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */
-        if (sscanf(buffer, "%s %[^\n]", key, value) < 1 ||
+        if (sscanf(buffer, "%511s %511[^\n]", key, value) < 1 ||
             key[0] == '#' || strlen(key) < 3)
             continue;
 
@@ -996,7 +999,7 @@ bool parse_file(const char *f, bool use_nagbar) {
     check_for_duplicate_bindings(context);
 
     if (use_nagbar && (context->has_errors || context->has_warnings)) {
-        ELOG("FYI: You are using i3 version " I3_VERSION "\n");
+        ELOG("FYI: You are using i3 version %s\n", i3_version);
         if (version == 3)
             ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
 
index f3bc106de210681639a1125ba42634e52409e11a..bb880b2f52e1ff67b7fe6c4e82510c51afdb6c75 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * debug.c: Debugging functions, especially FormatEvent, which prints unhandled
  *          events.  This code is from xcb-util.
index a539dad366adf3b447ccb61f817cfdf08cf35abc..c82610ed38ff2856975a8ae37838b947a4f23e1b 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * display_version.c: displays the running i3 version, runs as part of
  *                    i3 --moreversion.
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <fcntl.h>
+#include <time.h>
 #include "all.h"
 
-static bool human_readable_key;
-static char *human_readable_version;
+static bool human_readable_key, loaded_config_file_name_key;
+static char *human_readable_version, *loaded_config_file_name;
 
 static int version_string(void *ctx, const unsigned char *val, size_t len) {
     if (human_readable_key)
         sasprintf(&human_readable_version, "%.*s", (int)len, val);
+    if (loaded_config_file_name_key)
+        sasprintf(&loaded_config_file_name, "%.*s", (int)len, val);
     return 1;
 }
 
 static int version_map_key(void *ctx, const unsigned char *stringval, size_t stringlen) {
     human_readable_key = (stringlen == strlen("human_readable") &&
                           strncmp((const char *)stringval, "human_readable", strlen("human_readable")) == 0);
+    loaded_config_file_name_key = (stringlen == strlen("loaded_config_file_name") &&
+                                   strncmp((const char *)stringval, "loaded_config_file_name", strlen("loaded_config_file_name")) == 0);
     return 1;
 }
 
@@ -104,6 +109,21 @@ void display_running_version(void) {
 
     printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom);
 
+    if (loaded_config_file_name) {
+        struct stat sb;
+        time_t now;
+        char mtime[64];
+        printf("Loaded i3 config: %s", loaded_config_file_name);
+        if (stat(loaded_config_file_name, &sb) == -1) {
+            printf("\n");
+            ELOG("Cannot stat config file \"%s\"\n", loaded_config_file_name);
+        } else {
+            strftime(mtime, sizeof(mtime), "%c", localtime(&(sb.st_mtime)));
+            time(&now);
+            printf(" (Last modified: %s, %.f seconds ago)\n", mtime, difftime(now, sb.st_mtime));
+        }
+    }
+
 #ifdef __linux__
     size_t destpath_size = 1024;
     ssize_t linksize;
index 844a0db921ada15d78cea84818ef82b282da19ac..a1d2489ee60d93aef759dcf00343c4cc4446ea31 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * ewmh.c: Get/set certain EWMH properties easily.
  *
@@ -234,6 +234,6 @@ 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");
 
-    /* only send the first 24 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 24, supported_atoms);
+    /* only send the first 30 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 30, supported_atoms);
 }
index 4f2740992b9540639b0fd5009a1a95382487129e..b4f92cd404f78f4048ee0ad789e4100c84983f30 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * Faking outputs is useful in pathological situations (like network X servers
  * which don’t support multi-monitor in a useful way) and for our testsuite.
index e6ca2d7188d6392c172aa09bf17d6eb0588f6544..a82e2525c2a1e31927f428b56a304838c1b7e6e8 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * floating.c: Floating windows.
  *
@@ -246,12 +246,10 @@ void floating_enable(Con *con, bool automatic) {
         if (con->window && con->window->leader != XCB_NONE &&
             (leader = con_by_window_id(con->window->leader)) != NULL) {
             DLOG("Centering above leader\n");
-            nc->rect.x = leader->rect.x + (leader->rect.width / 2) - (nc->rect.width / 2);
-            nc->rect.y = leader->rect.y + (leader->rect.height / 2) - (nc->rect.height / 2);
+            floating_center(nc, leader->rect);
         } else {
             /* center the window on workspace as fallback */
-            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);
+            floating_center(nc, ws->rect);
         }
     }
 
@@ -310,8 +308,7 @@ void floating_enable(Con *con, bool automatic) {
     }
 
     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);
+    floating_center(nc, ws->rect);
 
     ipc_send_window_event("floating", con);
 }
@@ -420,6 +417,51 @@ bool floating_maybe_reassign_ws(Con *con) {
     return true;
 }
 
+/*
+ * Centers a floating con above the specified rect.
+ *
+ */
+void floating_center(Con *con, Rect rect) {
+    con->rect.x = rect.x + (rect.width / 2) - (con->rect.width / 2);
+    con->rect.y = rect.y + (rect.height / 2) - (con->rect.height / 2);
+}
+
+/*
+ * Moves the given floating con to the current pointer position.
+ *
+ */
+void floating_move_to_pointer(Con *con) {
+    assert(con->type == CT_FLOATING_CON);
+
+    xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL);
+    if (reply == NULL) {
+        ELOG("could not query pointer position, not moving this container\n");
+        return;
+    }
+
+    Output *output = get_output_containing(reply->root_x, reply->root_y);
+    if (output == NULL) {
+        ELOG("The pointer is not on any output, cannot move the container here.\n");
+        return;
+    }
+
+    /* Determine where to put the window. */
+    int32_t x = reply->root_x - con->rect.width / 2;
+    int32_t y = reply->root_y - con->rect.height / 2;
+    FREE(reply);
+
+    /* Correct target coordinates to be in-bounds. */
+    x = MAX(x, (int32_t)output->rect.x);
+    y = MAX(y, (int32_t)output->rect.y);
+    if (x + con->rect.width > output->rect.x + output->rect.width)
+        x = output->rect.x + output->rect.width - con->rect.width;
+    if (y + con->rect.height > output->rect.y + output->rect.height)
+        y = output->rect.y + output->rect.height - con->rect.height;
+
+    /* Update container's coordinates to position it correctly. */
+    floating_reposition(con, (Rect){x, y, con->rect.width, con->rect.height});
+}
+
 DRAGGING_CB(drag_window_callback) {
     const struct xcb_button_press_event_t *event = extra;
 
@@ -621,7 +663,7 @@ static void xcb_drag_check_cb(EV_P_ ev_check *w, int revents) {
                 break;
 
             case XCB_KEY_PRESS:
-                DLOG("A key was pressed during drag, reverting changes.");
+                DLOG("A key was pressed during drag, reverting changes.\n");
                 dragloop->result = DRAG_REVERT;
                 handle_event(type, event);
                 break;
index 041f7e366f76a96da1620c1ab950ffacc084edc5..d7d48a1cfb5847c9cfcf5c1ce728717d495925d1 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * handlers.c: Small handlers for various events (keypresses, focus changes,
  *             …).
@@ -215,7 +215,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
 
     /* Skip events where the pointer was over a child window, we are only
      * interested in events on the root window. */
-    if (event->child != 0)
+    if (event->child != XCB_NONE)
         return;
 
     Con *con;
@@ -228,7 +228,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
     if (config.disable_focus_follows_mouse)
         return;
 
-    if (con->layout != L_DEFAULT)
+    if (con->layout != L_DEFAULT && con->layout != L_SPLITV && con->layout != L_SPLITH)
         return;
 
     /* see over which rect the user is */
@@ -245,8 +245,6 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) {
         x_push_changes(croot);
         return;
     }
-
-    return;
 }
 
 /*
@@ -743,16 +741,17 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             workspace_show(ws);
             con_focus(con);
         } else {
-            /* If the request is from an application, only focus if the
-             * workspace is visible. Otherwise set the urgency hint. */
-            if (workspace_is_visible(ws)) {
-                DLOG("Request to focus con on a visible workspace. Focusing con = %p\n", con);
+            /* Request is from an application. */
+
+            if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
+                DLOG("Focusing con = %p\n", con);
                 workspace_show(ws);
                 con_focus(con);
-            } else {
-                DLOG("Request to focus con on a hidden workspace. Setting urgent con = %p\n", con);
+            } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) {
+                DLOG("Marking con = %p urgent\n", con);
                 con_set_urgency(con, true);
-            }
+            } else
+                DLOG("Ignoring request for con = %p.\n", con);
         }
 
         tree_render();
@@ -879,7 +878,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
                 floating_drag_window(con->parent, &fake);
                 break;
             case _NET_WM_MOVERESIZE_SIZE_TOPLEFT... _NET_WM_MOVERESIZE_SIZE_LEFT:
-                floating_resize_window(con->parent, FALSE, &fake);
+                floating_resize_window(con->parent, false, &fake);
                 break;
             default:
                 DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
@@ -891,15 +890,15 @@ static void handle_client_message(xcb_client_message_event_t *event) {
     }
 }
 
-#if 0
-int handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
-                        xcb_atom_t atom, xcb_get_property_reply_t *property) {
-        /* TODO: Implement this one. To do this, implement a little test program which sleep(1)s
-         before changing this property. */
-        ELOG("_NET_WM_WINDOW_TYPE changed, this is not yet implemented.\n");
-        return 0;
+bool handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+                        xcb_atom_t atom, xcb_get_property_reply_t *reply) {
+    Con *con;
+    if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
+        return false;
+
+    window_update_type(con->window, reply);
+    return true;
 }
-#endif
 
 /*
  * Handles the size hints set by a window, but currently only the part necessary for displaying
@@ -1263,7 +1262,8 @@ static struct property_handler_t property_handlers[] = {
     {0, UINT_MAX, handle_transient_for},
     {0, 128, handle_windowrole_change},
     {0, 128, handle_class_change},
-    {0, UINT_MAX, handle_strut_partial_change}};
+    {0, UINT_MAX, handle_strut_partial_change},
+    {0, UINT_MAX, handle_window_type}};
 #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
 
 /*
@@ -1283,6 +1283,7 @@ void property_handlers_init(void) {
     property_handlers[6].atom = A_WM_WINDOW_ROLE;
     property_handlers[7].atom = XCB_ATOM_WM_CLASS;
     property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
+    property_handlers[9].atom = A__NET_WM_WINDOW_TYPE;
 }
 
 static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
index 8c7634643ab7cc10292abda8869e24dfd435556d..76c1da899ce7610489a28eefe98446010eb3cd1c 100644 (file)
--- a/src/i3.mk
+++ b/src/i3.mk
@@ -32,6 +32,10 @@ include/all.h.pch: $(i3_HEADERS)
        echo "[i3] PCH all.h"
        $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch
 
+src/version.o: src/version.c LAST_VERSION $(i3_HEADERS_DEP)
+       echo "[i3] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$<
+
 src/%.o: src/%.c $(i3_HEADERS_DEP)
        echo "[i3] CC $<"
        $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$<
@@ -70,26 +74,26 @@ i3: libi3.a $(i3_OBJECTS)
 
 install-i3: i3
        echo "[i3] Install"
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/bin
        $(INSTALL) -d -m 0755 $(DESTDIR)$(SYSCONFDIR)/i3
-       $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/include/i3
+       $(INSTALL) -d -m 0755 $(DESTDIR)$(EXEC_PREFIX)/include/i3
        $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/xsessions
        $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/share/applications
-       $(INSTALL) -m 0755 i3 $(DESTDIR)$(PREFIX)/bin/
-       $(LN) -sf i3 $(DESTDIR)$(PREFIX)/bin/i3-with-shmlog
-       $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-save-tree $(DESTDIR)$(PREFIX)/bin/
-       $(INSTALL) -m 0755 i3-dmenu-desktop $(DESTDIR)$(PREFIX)/bin/
+       $(INSTALL) -m 0755 i3 $(DESTDIR)$(EXEC_PREFIX)/bin/
+       $(LN) -sf i3 $(DESTDIR)$(EXEC_PREFIX)/bin/i3-with-shmlog
+       $(INSTALL) -m 0755 i3-migrate-config-to-v4 $(DESTDIR)$(EXEC_PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-sensible-editor $(DESTDIR)$(EXEC_PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-sensible-pager $(DESTDIR)$(EXEC_PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-sensible-terminal $(DESTDIR)$(EXEC_PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-save-tree $(DESTDIR)$(EXEC_PREFIX)/bin/
+       $(INSTALL) -m 0755 i3-dmenu-desktop $(DESTDIR)$(EXEC_PREFIX)/bin/
        test -e $(DESTDIR)$(SYSCONFDIR)/i3/config || $(INSTALL) -m 0644 i3.config $(DESTDIR)$(SYSCONFDIR)/i3/config
        test -e $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes || $(INSTALL) -m 0644 i3.config.keycodes $(DESTDIR)$(SYSCONFDIR)/i3/config.keycodes
        $(INSTALL) -m 0644 i3.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3.desktop
        $(INSTALL) -m 0644 i3-with-shmlog.xsession.desktop $(DESTDIR)$(PREFIX)/share/xsessions/i3-with-shmlog.desktop
        $(INSTALL) -m 0644 i3.applications.desktop $(DESTDIR)$(PREFIX)/share/applications/i3.desktop
-       $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(PREFIX)/include/i3/
+       $(INSTALL) -m 0644 include/i3/ipc.h $(DESTDIR)$(EXEC_PREFIX)/include/i3/
 
 clean-i3:
        echo "[i3] Clean"
-       rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3-config-parser.stamp i3 test.config_parser test.commands_parser src/*.gcno src/cfgparse.* src/cmdparse.*
+       rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3-config-parser.stamp i3 test.config_parser test.commands_parser src/*.gcno src/cfgparse.* src/cmdparse.* LAST_VERSION
index 8fed75f1b13dbf3993d7ac4c7dbbfa80bf4fbd58..51a3d22315e625945f0e2765a033cfb83ee6d20c 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
  *
@@ -469,6 +469,28 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     y(map_close);
 }
 
+static void dump_bar_bindings(yajl_gen gen, Barconfig *config) {
+    if (TAILQ_EMPTY(&(config->bar_bindings)))
+        return;
+
+    ystr("bindings");
+    y(array_open);
+
+    struct Barbinding *current;
+    TAILQ_FOREACH(current, &(config->bar_bindings), bindings) {
+        y(map_open);
+
+        ystr("input_code");
+        y(integer, current->input_code);
+        ystr("command");
+        ystr(current->command);
+
+        y(map_close);
+    }
+
+    y(array_close);
+}
+
 static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     y(map_open);
 
@@ -492,6 +514,10 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     } while (0)
 
     YSTR_IF_SET(tray_output);
+
+    ystr("tray_padding");
+    y(integer, config->tray_padding);
+
     YSTR_IF_SET(socket_path);
 
     ystr("mode");
@@ -549,15 +575,7 @@ 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);
-    }
+    dump_bar_bindings(gen, config);
 
     ystr("position");
     if (config->position == P_BOTTOM)
@@ -611,6 +629,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     YSTR_IF_SET(urgent_workspace_border);
     YSTR_IF_SET(urgent_workspace_bg);
     YSTR_IF_SET(urgent_workspace_text);
+    YSTR_IF_SET(binding_mode_border);
+    YSTR_IF_SET(binding_mode_bg);
+    YSTR_IF_SET(binding_mode_text);
     y(map_close);
 
     y(map_close);
@@ -791,7 +812,10 @@ IPC_HANDLER(get_version) {
     y(integer, PATCH_VERSION);
 
     ystr("human_readable");
-    ystr(I3_VERSION);
+    ystr(i3_version);
+
+    ystr("loaded_config_file_name");
+    ystr(current_configpath);
 
     y(map_close);
 
index 95e5079eef44101e8a5cf97c1b88a00a4b4d84fe..88d09a0ce510429db8a3713ed9c0b28954371821 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * key_press.c: key press handler
  *
index c4d39fcee57fa569be1ac07fced152b57b56b55f..e4c4531d3c82f7e4cbb5726b3cbebccfb92ad349 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * load_layout.c: Restore (parts of) the layout, for example after an inplace
  *                restart.
index 6f44fa96e37f79fcfc848e377ba657a8e0df73a8..7cf988438063ca5df2aaacce329e7d1e35224cb6 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * log.c: Logging functions.
  *
index 8b514178bbbf3283fc9391f11ef5c6f42643ba1a..4c5b5f18ced0aff76ae7b2838c89039dfe49673d 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * main.c: Initialization, main loop
  *
@@ -188,7 +188,7 @@ static void handle_signal(int sig, siginfo_t *info, void *data) {
 int main(int argc, char *argv[]) {
     /* Keep a symbol pointing to the I3_VERSION string constant so that we have
      * it in gdb backtraces. */
-    const char *i3_version __attribute__((unused)) = I3_VERSION;
+    const char *_i3_version __attribute__((unused)) = i3_version;
     char *override_configpath = NULL;
     bool autostart = true;
     char *layout_path = NULL;
@@ -261,11 +261,11 @@ int main(int argc, char *argv[]) {
                 only_check_config = true;
                 break;
             case 'v':
-                printf("i3 version " I3_VERSION " © 2009-2014 Michael Stapelberg and contributors\n");
+                printf("i3 version %s © 2009 Michael Stapelberg and contributors\n", i3_version);
                 exit(EXIT_SUCCESS);
                 break;
             case 'm':
-                printf("Binary i3 version:  " I3_VERSION " © 2009-2014 Michael Stapelberg and contributors\n");
+                printf("Binary i3 version:  %s © 2009 Michael Stapelberg and contributors\n", i3_version);
                 display_running_version();
                 exit(EXIT_SUCCESS);
                 break;
@@ -450,13 +450,13 @@ int main(int argc, char *argv[]) {
             memset(cwd, '\0', cwd_size);
             if (read(patternfd, cwd, cwd_size) > 0)
                 /* a trailing newline is included in cwd */
-                LOG("CORE DUMPS: Your core_pattern is: %s", cwd);
+                LOG("CORE DUMPS: Your core_pattern is: \"%s\".\n", cwd);
             close(patternfd);
         }
         free(cwd);
     }
 
-    LOG("i3 " I3_VERSION " starting\n");
+    LOG("i3 %s starting\n", i3_version);
 
     conn = xcb_connect(NULL, &conn_screen);
     if (xcb_connection_has_error(conn))
@@ -474,6 +474,12 @@ int main(int argc, char *argv[]) {
     root_screen = xcb_aux_get_screen(conn, conn_screen);
     root = root_screen->root;
 
+/* Place requests for the atoms we need as soon as possible */
+#define xmacro(atom) \
+    xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
+#include "atoms.xmacro"
+#undef xmacro
+
     /* By default, we use the same depth and visual as the root window, which
      * usually is TrueColor (24 bit depth) and the corresponding visual.
      * However, we also check if a 32 bit depth and visual are available (for
@@ -491,6 +497,20 @@ int main(int argc, char *argv[]) {
     xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root);
     xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
 
+/* Setup NetWM atoms */
+#define xmacro(name)                                                                       \
+    do {                                                                                   \
+        xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
+        if (!reply) {                                                                      \
+            ELOG("Could not get atom " #name "\n");                                        \
+            exit(-1);                                                                      \
+        }                                                                                  \
+        A_##name = reply->atom;                                                            \
+        free(reply);                                                                       \
+    } while (0);
+#include "atoms.xmacro"
+#undef xmacro
+
     load_configuration(conn, override_configpath, false);
 
     if (config.ipc_socket_path == NULL) {
@@ -512,12 +532,6 @@ int main(int argc, char *argv[]) {
     }
     DLOG("root geometry reply: (%d, %d) %d x %d\n", greply->x, greply->y, greply->width, greply->height);
 
-/* Place requests for the atoms we need as soon as possible */
-#define xmacro(atom) \
-    xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
-#include "atoms.xmacro"
-#undef xmacro
-
     xcursor_load_cursors();
 
     /* Set a cursor for the root window (otherwise the root window will show no
@@ -547,20 +561,6 @@ int main(int argc, char *argv[]) {
 
     restore_connect();
 
-/* Setup NetWM atoms */
-#define xmacro(name)                                                                       \
-    do {                                                                                   \
-        xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
-        if (!reply) {                                                                      \
-            ELOG("Could not get atom " #name "\n");                                        \
-            exit(-1);                                                                      \
-        }                                                                                  \
-        A_##name = reply->atom;                                                            \
-        free(reply);                                                                       \
-    } while (0);
-#include "atoms.xmacro"
-#undef xmacro
-
     property_handlers_init();
 
     ewmh_setup_hints();
@@ -574,7 +574,7 @@ int main(int argc, char *argv[]) {
 
     bool needs_tree_init = true;
     if (layout_path) {
-        LOG("Trying to restore the layout from %s...", layout_path);
+        LOG("Trying to restore the layout from \"%s\".\n", layout_path);
         needs_tree_init = !tree_restore(layout_path, greply);
         if (delete_layout_path) {
             unlink(layout_path);
@@ -621,6 +621,8 @@ int main(int argc, char *argv[]) {
             ELOG("ERROR: No screen at (%d, %d), starting on the first screen\n",
                  pointerreply->root_x, pointerreply->root_y);
             output = get_first_output();
+            if (!output)
+                die("No usable outputs available.\n");
         }
 
         con_focus(con_descend_focused(output_get_content(output->con)));
@@ -766,7 +768,7 @@ int main(int argc, char *argv[]) {
             sigaction(SIGABRT, &action, NULL) == -1 ||
             sigaction(SIGFPE, &action, NULL) == -1 ||
             sigaction(SIGSEGV, &action, NULL) == -1)
-            ELOG("Could not setup signal handler");
+            ELOG("Could not setup signal handler.\n");
     }
 
     /* Catch all signals with default action "Term", see signal(7) */
@@ -775,7 +777,7 @@ int main(int argc, char *argv[]) {
         sigaction(SIGALRM, &action, NULL) == -1 ||
         sigaction(SIGUSR1, &action, NULL) == -1 ||
         sigaction(SIGUSR2, &action, NULL) == -1)
-        ELOG("Could not setup signal handler");
+        ELOG("Could not setup signal handler.\n");
 
     /* Ignore SIGPIPE to survive errors when an IPC client disconnects
      * while we are sending them a message */
index 2b3c6743a20df8b8b1c4525c9719fa11cdf6f01a..8dcc17a27cec45defa4d49b149823afff4ab6a29 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * manage.c: Initially managing new windows (or existing ones on restart).
  *
@@ -168,20 +168,11 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     cwindow->id = window;
     cwindow->depth = get_visual_depth(attr->visual);
 
-    /* We need to grab the mouse buttons for click to focus */
+    /* We need to grab buttons 1-3 for click-to-focus and buttons 1-5
+     * to allow for mouse bindings using --whole-window to work correctly. */
     xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
                     XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
-                    1 /* left mouse button */,
-                    XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
-
-    xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
-                    XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
-                    2 /* middle mouse button */,
-                    XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
-
-    xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
-                    XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
-                    3 /* right mouse button */,
+                    XCB_BUTTON_INDEX_ANY,
                     XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
 
     /* update as much information as possible so far (some replies may be NULL) */
@@ -210,6 +201,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     /* check if the window needs WM_TAKE_FOCUS */
     cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS);
 
+    /* read the preferred _NET_WM_WINDOW_TYPE atom */
+    cwindow->window_type = xcb_get_preferred_window_type(type_reply);
+
     /* Where to start searching for a container that swallows the new one? */
     Con *search_at = croot;
 
@@ -512,8 +506,10 @@ 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->window->doesnt_accept_focus && nc->mapped) {
-        DLOG("Now setting focus.\n");
-        con_focus(nc);
+        if (assignment_for(cwindow, A_NO_FOCUS) == NULL) {
+            DLOG("Now setting focus.\n");
+            con_focus(nc);
+        }
     }
 
     tree_render();
index dc4d422f27550d9754ea791977d00c899148c0c6..b95cf2342bf61bbebdd90c7aebb5c4e9de89e39d 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * A "match" is a data structure which acts like a mask or expression to match
  * certain windows or not. For example, when using commands, you can specify a
  */
 void match_init(Match *match) {
     memset(match, 0, sizeof(Match));
-    match->dock = -1;
+    match->dock = M_DONTCHECK;
     match->urgent = U_DONTCHECK;
+    /* we use this as the placeholder value for "not set". */
+    match->window_type = UINT32_MAX;
 }
 
 /*
@@ -46,8 +48,10 @@ bool match_is_empty(Match *match) {
             match->class == NULL &&
             match->instance == NULL &&
             match->window_role == NULL &&
+            match->workspace == NULL &&
             match->urgent == U_DONTCHECK &&
             match->id == XCB_NONE &&
+            match->window_type == UINT32_MAX &&
             match->con_id == NULL &&
             match->dock == -1 &&
             match->floating == M_ANY);
@@ -75,6 +79,7 @@ void match_copy(Match *dest, Match *src) {
     DUPLICATE_REGEX(class);
     DUPLICATE_REGEX(instance);
     DUPLICATE_REGEX(window_role);
+    DUPLICATE_REGEX(workspace);
 }
 
 /*
@@ -129,6 +134,14 @@ bool match_matches_window(Match *match, i3Window *window) {
         }
     }
 
+    if (match->window_type != UINT32_MAX) {
+        if (window->window_type == match->window_type) {
+            LOG("window_type matches (%i)\n", match->window_type);
+        } else {
+            return false;
+        }
+    }
+
     Con *con = NULL;
     if (match->urgent == U_LATEST) {
         /* if the window isn't urgent, no sense in searching */
@@ -161,7 +174,22 @@ bool match_matches_window(Match *match, i3Window *window) {
         LOG("urgent matches oldest\n");
     }
 
-    if (match->dock != -1) {
+    if (match->workspace != NULL) {
+        if ((con = con_by_window_id(window->id)) == NULL)
+            return false;
+
+        Con *ws = con_get_workspace(con);
+        if (ws == NULL)
+            return false;
+
+        if (regex_matches(match->workspace, ws->name)) {
+            LOG("workspace matches (%s)\n", ws->name);
+        } else {
+            return false;
+        }
+    }
+
+    if (match->dock != M_DONTCHECK) {
         if ((window->dock == W_DOCK_TOP && match->dock == M_DOCK_TOP) ||
             (window->dock == W_DOCK_BOTTOM && match->dock == M_DOCK_BOTTOM) ||
             ((window->dock == W_DOCK_TOP || window->dock == W_DOCK_BOTTOM) &&
index 1999a1fe901853d06924df49f5c026cc4f7977e2..bd228a1cc19a19692f48e3f3dd7fb9b6c920f39a 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * move.c: Moving containers into some direction.
  *
@@ -249,6 +249,14 @@ void tree_move(Con *con, int direction) {
                         ? AFTER
                         : BEFORE);
         insert_con_into(con, target, position);
+    } else if (con->parent->parent->type == CT_WORKSPACE &&
+               con->parent->layout != L_DEFAULT &&
+               con_num_children(con->parent) == 1) {
+        /* Con is the lone child of a non-default layout container at the edge
+         * of the workspace. Treat it as though the workspace is its parent
+         * and move it to the next output. */
+        DLOG("Grandparent is workspace\n");
+        move_to_output_directed(con, direction);
     } else {
         DLOG("Moving into container above\n");
         position = (direction == D_UP || direction == D_LEFT ? BEFORE : AFTER);
index 822a0f8891004b5c8c8b14cc4c8f68deace51f3b..ec5d5f4791643af05a96df7238a819f72a3dc571 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * output.c: Output (monitor) related functions.
  *
index 9549c9d5f032bb1bffc7425016b4c7ced0b58945..1bd9993164d946b455db95ba3a5b0fd31f2ef503 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * For more information on RandR, please see the X.org RandR specification at
  * http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
@@ -69,7 +69,7 @@ Output *get_first_output(void) {
     if (output->active)
         return output;
 
-    die("No usable outputs available.\n");
+    return NULL;
 }
 
 /*
@@ -570,6 +570,8 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
     if (!new->active) {
         DLOG("width/height 0/0, disabling output\n");
         return;
+    } else {
+        new->to_be_disabled = false;
     }
 
     DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
@@ -591,11 +593,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id,
     new->changed = true;
 }
 
-/*
- * (Re-)queries the outputs via RandR and stores them in the list of outputs.
- *
- */
-void randr_query_outputs(void) {
+static bool __randr_query_outputs(void) {
     Output *output, *other, *first;
     xcb_randr_get_output_primary_cookie_t pcookie;
     xcb_randr_get_screen_resources_current_cookie_t rcookie;
@@ -609,7 +607,7 @@ void randr_query_outputs(void) {
     xcb_randr_output_t *randr_outputs;
 
     if (randr_disabled)
-        return;
+        return true;
 
     /* Get screen resources (primary output, crtcs, outputs, modes) */
     rcookie = xcb_randr_get_screen_resources_current(conn, root);
@@ -621,7 +619,7 @@ void randr_query_outputs(void) {
         DLOG("primary output is %08x\n", primary->output);
     if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
         disable_randr(conn);
-        return;
+        return true;
     }
     cts = res->config_timestamp;
 
@@ -703,6 +701,11 @@ void randr_query_outputs(void) {
             DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
 
             first = get_first_output();
+            if (!first) {
+                FREE(res);
+                FREE(primary);
+                return false;
+            }
 
             /* TODO: refactor the following code into a nice function. maybe
              * use an on_destroy callback which is implement differently for
@@ -818,6 +821,32 @@ void randr_query_outputs(void) {
 
     FREE(res);
     FREE(primary);
+
+    return true;
+}
+
+/*
+ * (Re-)queries the outputs via RandR and stores them in the list of outputs.
+ *
+ */
+void randr_query_outputs(void) {
+    static bool first_query = true;
+
+    if (first_query) {
+        /* find monitors at least once via RandR */
+        if (!__randr_query_outputs())
+            die("No usable outputs available.\n");
+        first_query = false;
+    } else {
+        /* requery */
+        if (!__randr_query_outputs()) {
+            DLOG("sleep %f ms due to zero displays\n", config.zero_disp_exit_timer_ms);
+            usleep(config.zero_disp_exit_timer_ms * 1000);
+
+            if (!__randr_query_outputs())
+                die("No usable outputs available.\n");
+        }
+    }
 }
 
 /*
index 60dee5cc92a24d57e42eddefb78a7436a3ed728b..9549a98b54ba36980fe956b8957e63d4e21f1c6b 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * regex.c: Interface to libPCRE (perl compatible regular expressions).
  *
index d7d576673e4b65bb13dcf9ddfbf1e5e20e2bf070..76dfa69c7d2f0d30e192b4caafe5d868cf457121 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * render.c: Renders (determines position/sizes) the layout tree, updating the
  *           various rects. Needs to be pushed to X11 (see x.c) to be visible.
index 4ed835d57ac37c681cdbe78c1d2b059c7a467be0..05fe50558dba737352efc15333b362cf4ce69e20 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * resize.c: Interactive resizing.
  *
@@ -88,7 +88,7 @@ bool resize_find_tiling_participants(Con **current, Con **other, direction_t dir
         }
     }
 
-    DLOG("Found participants: first=%p and second=%p.", first, second);
+    DLOG("Found participants: first=%p and second=%p.\n", first, second);
     *current = first;
     *other = second;
     if (first == NULL || second == NULL) {
index 53a70d69afe7345c8ed792951303e98aa7c4b18d..f56372a690ac5427e140a05e2747fccc813234d1 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * restore_layout.c: Everything for restored containers that is not pure state
  *                   parsing (which can be found in load_layout.c).
index 3b7c2a72b009131e616cd57cf0665af3499c46bc..06a7cc739452c2f04869ae49a8cdf38ce5521167 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * scratchpad.c: Moving windows to the scratchpad and making them visible again.
  *
@@ -198,10 +198,7 @@ void scratchpad_show(Con *con) {
         con->rect.width = output->rect.width * 0.5;
         con->rect.height = output->rect.height * 0.75;
         floating_check_size(con);
-        con->rect.x = output->rect.x +
-                      ((output->rect.width / 2.0) - (con->rect.width / 2.0));
-        con->rect.y = output->rect.y +
-                      ((output->rect.height / 2.0) - (con->rect.height / 2.0));
+        floating_center(con, con_get_workspace(con)->rect);
     }
 
     /* Activate active workspace if window is from another workspace to ensure
index e971f6bdd18c8f8f3b2e38b0aeb165d50a6bb574..ceaa4842286ec14f27567fdf56c29188853719f9 100644 (file)
@@ -4,8 +4,8 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
- * © 2009-2010 Jan-Erik Rediger
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Jan-Erik Rediger
  *
  * sighandler.c: Interactive crash dialog upon SIGSEGV/SIGABRT/SIGFPE (offers
  *               to restart inplace).
@@ -327,5 +327,5 @@ void setup_signal_handler(void) {
         sigaction(SIGABRT, &action, NULL) == -1 ||
         sigaction(SIGFPE, &action, NULL) == -1 ||
         sigaction(SIGSEGV, &action, NULL) == -1)
-        ELOG("Could not setup signal handler");
+        ELOG("Could not setup signal handler.\n");
 }
index aa347bd75fe86096096b7bc0b6148f7859fe8a91..b062ea8ba0cb3af34d32d5a047dc5d84cdfeeb8f 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * startup.c: Startup notification code. Ensures a startup notification context
  *            is setup when launching applications. We store the current
@@ -131,7 +131,7 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) {
  *
  */
 void start_application(const char *command, bool no_startup_id) {
-    SnLauncherContext *context;
+    SnLauncherContext *context = NULL;
 
     if (!no_startup_id) {
         /* Create a startup notification context to monitor the progress of this
index 753b02fcd35734073f7afa4e241045323bae054f..92b56e6c310909afe95ea828a796f1b7d9e36259 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * tree.c: Everything that primarily modifies the layout tree data structure.
  *
index 5760ae72588f52afc5cd6caf617a4b184e344ab9..0a8ba6e2785194b0453d27923d6833bd3387de46 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * util.c: Utility functions, which can be useful everywhere within i3 (see
  *         also libi3).
@@ -159,38 +159,6 @@ void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, char *err_mes
     }
 }
 
-/*
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-char *resolve_tilde(const char *path) {
-    static glob_t globbuf;
-    char *head, *tail, *result;
-
-    tail = strchr(path, '/');
-    head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
-
-    int res = glob(head, GLOB_TILDE, NULL, &globbuf);
-    free(head);
-    /* no match, or many wildcard matches are bad */
-    if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
-        result = sstrdup(path);
-    else if (res != 0) {
-        die("glob() failed");
-    } else {
-        head = globbuf.gl_pathv[0];
-        result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1);
-        strncpy(result, head, strlen(head));
-        if (tail)
-            strncat(result, tail, strlen(tail));
-    }
-    globfree(&globbuf);
-
-    return result;
-}
-
 /*
  * Checks if the given path exists by calling stat().
  *
diff --git a/src/version.c b/src/version.c
new file mode 100644 (file)
index 0000000..6e385ad
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * Stores the latest Git commit identifier so that it can be linked into i3
+ * and used dynamically without recompiling every object file.
+ *
+ */
+const char *i3_version = I3_VERSION;
index 36737943fe92cc9a0b2c564c4f4300cb22d7c32b..dd04b1b9e822c851e56948775ef6e45364773de9 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * window.c: Updates window attributes (X11 hints/properties).
  *
@@ -27,30 +27,27 @@ void window_update_class(i3Window *win, xcb_get_property_reply_t *prop, bool bef
      * null-terminated strings (for compatibility reasons). Instead, we
      * use strdup() on both strings */
     const size_t prop_length = xcb_get_property_value_length(prop);
-    char *new_class = smalloc(prop_length + 1);
-    memcpy(new_class, xcb_get_property_value(prop), prop_length);
-    new_class[prop_length] = '\0';
+    char *new_class = xcb_get_property_value(prop);
+    const size_t class_class_index = strnlen(new_class, prop_length) + 1;
 
     FREE(win->class_instance);
     FREE(win->class_class);
 
-    win->class_instance = sstrdup(new_class);
-    if ((strlen(new_class) + 1) < prop_length)
-        win->class_class = sstrdup(new_class + strlen(new_class) + 1);
+    win->class_instance = sstrndup(new_class, prop_length);
+    if (class_class_index < prop_length)
+        win->class_class = sstrndup(new_class + class_class_index, prop_length - class_class_index);
     else
         win->class_class = NULL;
     LOG("WM_CLASS changed to %s (instance), %s (class)\n",
         win->class_instance, win->class_class);
 
     if (before_mgmt) {
-        free(new_class);
         free(prop);
         return;
     }
 
     run_assignments(win);
 
-    free(new_class);
     free(prop);
 }
 
@@ -232,6 +229,23 @@ void window_update_role(i3Window *win, xcb_get_property_reply_t *prop, bool befo
     free(prop);
 }
 
+/*
+ * Updates the _NET_WM_WINDOW_TYPE property.
+ *
+ */
+void window_update_type(i3Window *window, xcb_get_property_reply_t *reply) {
+    xcb_atom_t new_type = xcb_get_preferred_window_type(reply);
+    if (new_type == XCB_NONE) {
+        DLOG("cannot read _NET_WM_WINDOW_TYPE from window.\n");
+        return;
+    }
+
+    window->window_type = new_type;
+    LOG("_NET_WM_WINDOW_TYPE changed to %i.\n", window->window_type);
+
+    run_assignments(window);
+}
+
 /*
  * Updates the WM_HINTS (we only care about the input focus handling part).
  *
index 8bd4b5ea2fa466078e83e3d0062656edfd2de96b..59d6d77c86f7cff8cb2c41f744ddf8d6c16d362a 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * workspace.c: Modifying workspaces, accessing them, moving containers to
  *              workspaces.
@@ -129,7 +129,7 @@ Con *create_workspace_on_output(Output *output, Con *content) {
             continue;
         DLOG("relevant command = %s\n", bind->command);
         const char *target = bind->command + strlen("workspace ");
-        while ((*target == ' ' || *target == '\t') && target != '\0')
+        while (*target == ' ' || *target == '\t')
             target++;
         /* We check if this is the workspace
          * next/prev/next_on_output/prev_on_output/back_and_forth/number command.
@@ -729,7 +729,7 @@ workspace_prev_on_output_end:
  */
 void workspace_back_and_forth(void) {
     if (!previous_workspace_name) {
-        DLOG("No previous workspace name set. Not switching.");
+        DLOG("No previous workspace name set. Not switching.\n");
         return;
     }
 
@@ -742,7 +742,7 @@ void workspace_back_and_forth(void) {
  */
 Con *workspace_back_and_forth_get(void) {
     if (!previous_workspace_name) {
-        DLOG("no previous workspace name set.");
+        DLOG("No previous workspace name set.\n");
         return NULL;
     }
 
@@ -847,6 +847,9 @@ Con *workspace_attach_to(Con *ws) {
     DLOG("Attaching new split %p to workspace %p\n", new, ws);
     con_attach(new, ws, false);
 
+    /* 5: fix the percentages */
+    con_fix_percent(ws);
+
     return new;
 }
 
diff --git a/src/x.c b/src/x.c
index 9dd09117cf72eaeb234b2f1c10b7c49a9e89b17b..ef6c734162c0431bc6dad0ee7bb56d17e9c78788 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * x.c: Interface to X11, transfers our in-memory state to X11 (see also
  *      render.c). Basically a big state machine.
@@ -36,6 +36,7 @@ typedef struct con_state {
     bool mapped;
     bool unmap_now;
     bool child_mapped;
+    bool is_hidden;
 
     /** The con for which this state is. */
     Con *con;
@@ -301,6 +302,45 @@ void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
     free(event);
 }
 
+static i3String *parse_title_format(char *format, i3String *_title) {
+    /* We need to ensure that we only escape the window title if pango
+     * is used by the current font. */
+    const bool is_markup = font_is_pango();
+
+    i3String *title = is_markup ? i3string_escape_markup(_title) : _title;
+    const char *escaped_title = i3string_as_utf8(title);
+
+    /* We have to first iterate over the string to see how much buffer space
+     * we need to allocate. */
+    int buffer_len = strlen(format) + 1;
+    for (char *walk = format; *walk != '\0'; walk++) {
+        if (STARTS_WITH(walk, "%title")) {
+            buffer_len = buffer_len - strlen("%title") + strlen(escaped_title);
+            walk += strlen("%title") - 1;
+        }
+    }
+
+    /* Now we can parse the format string. */
+    char buffer[buffer_len];
+    char *outwalk = buffer;
+    for (char *walk = format; *walk != '\0'; walk++) {
+        if (*walk != '%') {
+            *(outwalk++) = *walk;
+            continue;
+        }
+
+        if (STARTS_WITH(walk + 1, "title")) {
+            outwalk += sprintf(outwalk, "%s", escaped_title);
+            walk += strlen("title");
+        }
+    }
+    *outwalk = '\0';
+
+    i3String *formatted = i3string_from_utf8(buffer);
+    i3string_set_markup(formatted, is_markup);
+    return formatted;
+}
+
 /*
  * Draws the decoration of the given container onto its parent.
  *
@@ -363,6 +403,7 @@ void x_draw_decoration(Con *con) {
         (con->window == NULL || !con->window->name_x_changed) &&
         !parent->pixmap_recreated &&
         !con->pixmap_recreated &&
+        !con->mark_changed &&
         memcmp(p, con->deco_render_params, sizeof(struct deco_render_params)) == 0) {
         free(p);
         goto copy_pixmaps;
@@ -381,6 +422,7 @@ void x_draw_decoration(Con *con) {
 
     parent->pixmap_recreated = false;
     con->pixmap_recreated = false;
+    con->mark_changed = false;
 
     /* 2: draw the client.background, but only for the parts around the client_rect */
     if (con->window != NULL) {
@@ -531,10 +573,28 @@ void x_draw_decoration(Con *con) {
     //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult);
     int indent_px = (indent_level * 5) * indent_mult;
 
-    draw_text(win->name,
+    int mark_width = 0;
+    if (config.show_marks && con->mark != NULL && (con->mark)[0] != '_') {
+        char *formatted_mark;
+        sasprintf(&formatted_mark, "[%s]", con->mark);
+        i3String *mark = i3string_from_utf8(formatted_mark);
+        FREE(formatted_mark);
+        mark_width = predict_text_width(mark);
+
+        draw_text(mark, parent->pixmap, parent->pm_gc,
+                  con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2),
+                  con->deco_rect.y + text_offset_y, mark_width);
+
+        I3STRING_FREE(mark);
+    }
+
+    i3String *title = win->title_format == NULL ? win->name : parse_title_format(win->title_format, win->name);
+    draw_text(title,
               parent->pixmap, parent->pm_gc,
-              con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y,
-              con->deco_rect.width - 2 - indent_px);
+              con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y,
+              con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2));
+    if (win->title_format != NULL)
+        I3STRING_FREE(title);
 
 after_title:
     /* Since we don’t clip the text at all, it might in some cases be painted
@@ -594,6 +654,33 @@ void x_deco_recurse(Con *con) {
         x_draw_decoration(con);
 }
 
+/*
+ * Sets or removes the _NET_WM_STATE_HIDDEN property on con if necessary.
+ *
+ */
+static void set_hidden_state(Con *con) {
+    if (con->window == NULL) {
+        return;
+    }
+
+    con_state *state = state_for_frame(con->frame);
+    bool should_be_hidden = con_is_hidden(con);
+    if (should_be_hidden == state->is_hidden)
+        return;
+
+    unsigned int num = 0;
+    uint32_t values[1];
+    if (should_be_hidden) {
+        DLOG("setting _NET_WM_STATE_HIDDEN for con = %p\n", con);
+        values[num++] = A__NET_WM_STATE_HIDDEN;
+    } else {
+        DLOG("removing _NET_WM_STATE_HIDDEN for con = %p\n", con);
+    }
+
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, con->window->id, A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values);
+    state->is_hidden = should_be_hidden;
+}
+
 /*
  * This function pushes the properties of each node of the layout tree to
  * X11 if they have changed (like the map state, position of the window, …).
@@ -797,6 +884,8 @@ void x_push_node(Con *con) {
         fake_absolute_configure_notify(con);
     }
 
+    set_hidden_state(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). */
index 5dda5ccee1bc6d940842af8100fb04209923ff03..303d6af1cf2c9b583fff8653bd69fe230e00e457 100644 (file)
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * xcb.c: Helper functions for easier usage of XCB
  *
@@ -154,6 +154,35 @@ void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r) {
     add_ignore_event(cookie.sequence, -1);
 }
 
+/*
+ * Returns the first supported _NET_WM_WINDOW_TYPE atom.
+ *
+ */
+xcb_atom_t xcb_get_preferred_window_type(xcb_get_property_reply_t *reply) {
+    if (reply == NULL || xcb_get_property_value_length(reply) == 0)
+        return XCB_NONE;
+
+    xcb_atom_t *atoms;
+    if ((atoms = xcb_get_property_value(reply)) == NULL)
+        return XCB_NONE;
+
+    for (int i = 0; i < xcb_get_property_value_length(reply) / (reply->format / 8); i++) {
+        if (atoms[i] == A__NET_WM_WINDOW_TYPE_NORMAL ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_DIALOG ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_UTILITY ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_TOOLBAR ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_SPLASH ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_MENU ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_POPUP_MENU ||
+            atoms[i] == A__NET_WM_WINDOW_TYPE_TOOLTIP) {
+            return atoms[i];
+        }
+    }
+
+    return XCB_NONE;
+}
+
 /*
  * Returns true if the given reply contains the given atom.
  *
index 0a8da42545c5384f405b7de67a60675e7b419ccc..1e1e23b0dedadd2d6ae342e71a8edded2d541088 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * xcursor.c: xcursor support for themed cursors.
  *
index 9e412e0330db13a788068c1bcb3c34534047fb40..ec030230760b39f927e7a7fd3048c26d7bce6f3f 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * This is LEGACY code (we support RandR, which can do much more than
  * Xinerama), but necessary for the poor users of the nVidia binary
index f2c15013f5246b7e9346819d60365818d4fd68dd..3c2a26f97255c09161e13c03f48ee4658aaa8b0e 100755 (executable)
@@ -8,8 +8,8 @@ WriteMakefile(
     MIN_PERL_VERSION => '5.010000', # 5.10.0
     PREREQ_PM => {
         'AnyEvent'     => 0,
-        'AnyEvent::I3' => '0.15',
-        'X11::XCB'     => '0.09',
+        'AnyEvent::I3' => '0.16',
+        'X11::XCB'     => '0.12',
         'Inline'       => 0,
         'Inline::C'    => 0,
         'ExtUtils::PkgConfig' => 0,
index eaf57bde1620495c3ef168018a76189c39c32bf5..c1244e08745bfff08211a741d0a833d0acbc79e3 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env perl
 # vim:ts=4:sw=4:expandtab
-# © 2010-2012 Michael Stapelberg and contributors
+# © 2010 Michael Stapelberg and contributors
 package complete_run;
 use strict;
 use warnings;
@@ -8,7 +8,6 @@ use v5.10;
 use utf8;
 # the following are modules which ship with Perl (>= 5.10):
 use Pod::Usage;
-use Cwd qw(abs_path);
 use File::Temp qw(tempfile tempdir);
 use Getopt::Long;
 use POSIX ();
@@ -17,8 +16,20 @@ use TAP::Parser;
 use TAP::Parser::Aggregator;
 use Time::HiRes qw(time);
 use IO::Handle;
+
+my $dirname;
+
+BEGIN {
+    use File::Basename;
+    use Cwd qw(abs_path);
+
+    # fileparse()[1] contains the directory portion of the specified path.
+    # See File::Basename(3p) for more details.
+    $dirname = (fileparse(abs_path($0)))[1];
+}
+
 # these are shipped with the testsuite
-use lib qw(lib);
+use lib $dirname . 'lib';
 use StartXServer;
 use StatusLine;
 use TestWorker;
@@ -70,6 +81,8 @@ my $result = GetOptions(
 
 pod2usage(-verbose => 2, -exitcode => 0) if $help;
 
+chdir $dirname or die "Could not chdir into $dirname";
+
 # Check for missing executables
 my @binaries = qw(
                    ../i3
@@ -86,6 +99,16 @@ foreach my $binary (@binaries) {
     die "$binary is not an executable" unless -x $binary;
 }
 
+if ($options{coverage}) {
+    qx(command -v lcov &> /dev/null);
+    die "Cannot find lcov needed for coverage testing." if $?;
+    qx(command -v genhtml &> /dev/null);
+    die "Cannot find genhtml needed for coverage testing." if $?;
+
+    # clean out the counters that may be left over from previous tests.
+    qx(lcov -d ../ --zerocounters &> /dev/null);
+}
+
 qx(Xephyr -help 2>&1);
 die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?;
 
@@ -227,6 +250,17 @@ if ($numtests == 1) {
 
 END { cleanup() }
 
+if ($options{coverage}) {
+    print("\nGenerating test coverage report...\n");
+    qx(lcov -d ../ -b ../ --capture -o latest/i3-coverage.info);
+    qx(genhtml -o latest/i3-coverage latest/i3-coverage.info);
+    if ($?) {
+        print("Could not generate test coverage html. Did you compile i3 with test coverage support?\n");
+    } else {
+        print("Test coverage report generated in latest/i3-coverage\n");
+    }
+}
+
 exit ($aggregator->failed > 0);
 
 #
@@ -391,12 +425,15 @@ available in C<latest/xtrace-for-$test.log>.
 
 =item B<--coverage-testing>
 
-Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.
+Generates a test coverage report at C<latest/i3-coverage>. Exits i3 cleanly
+during tests (instead of kill -9) to make coverage testing work properly.
 
 =item B<--parallel>
 
-Number of Xephyr instances to start (if you dont 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 Xephyr instance
   ./complete-run.pl -p 1
+
+=back
index c149cbd1204a0ddf63bfeff9eff6c710c0dde4c7..ac1a26cadbd4d691350f3a53e02e102c2d84f917 100644 (file)
@@ -606,7 +606,7 @@ sub get_dock_clients {
 
 =head2 cmd($command)
 
-Sends the specified command to i3.
+Sends the specified command to i3 and returns the output.
 
   my $ws = unused_workspace;
   cmd "workspace $ws";
index bb913819b108526371dbc1430813197e8d7dd80a..e88c37a283e366ca03bf0e02aff4b257746edf26 100644 (file)
@@ -53,10 +53,10 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 force_display_urgency_hint 0ms
 EOT
 
-my $type;
+my ($type, $tmp, $w1, $w2);
 for ($type = 1; $type <= 2; $type++) {
     my $pid = launch_with_config($config);
-    my $tmp = fresh_workspace;
+    $tmp = fresh_workspace;
 
 #####################################################################
 # Create two windows and put them in stacking mode
@@ -269,8 +269,8 @@ for ($type = 1; $type <= 2; $type++) {
     my $ws1 = fresh_workspace;
     my $ws2 = fresh_workspace;
     cmd "workspace $ws1";
-    my $w1 = open_window;
-    my $w2 = open_window;
+    $w1 = open_window;
+    $w2 = open_window;
     cmd "workspace $ws2";
     sync_with_i3;
     set_urgency($w1, 1, $type);
@@ -284,10 +284,10 @@ for ($type = 1; $type <= 2; $type++) {
 ##############################################################################
 # Check if urgent flag can be unset if we move the window out of the container
 ##############################################################################
-    my $tmp = fresh_workspace;
+    $tmp = fresh_workspace;
     cmd 'layout tabbed';
-    my $w1 = open_window;
-    my $w2 = open_window;
+    $w1 = open_window;
+    $w2 = open_window;
     sync_with_i3;
     cmd '[id="' . $w2->id . '"] focus';
     sync_with_i3;
@@ -307,6 +307,33 @@ for ($type = 1; $type <= 2; $type++) {
     my $ws = get_ws($tmp);
     ok(!$ws->{urgent}, 'urgent flag not set on workspace');
 
+##############################################################################
+# Regression test for #1187: Urgency hint moves to new workspace when moving
+# a container to another workspace.
+##############################################################################
+
+    my $tmp_source = fresh_workspace;
+    my $tmp_target = fresh_workspace;
+    cmd 'workspace ' . $tmp_source;
+    sync_with_i3;
+    $w1 = open_window;
+    $w2 = open_window;
+    sync_with_i3;
+    cmd '[id="' . $w1->id . '"] focus';
+    sync_with_i3;
+    cmd 'mark urgent_con';
+    cmd '[id="' . $w2->id . '"] focus';
+    set_urgency($w1, 1, $type);
+    sync_with_i3;
+    cmd '[con_mark="urgent_con"] move container to workspace ' . $tmp_target;
+    sync_with_i3;
+    my $source_ws = get_ws($tmp_source);
+    my $target_ws = get_ws($tmp_target);
+    ok(!$source_ws->{urgent}, 'Source workspace is no longer marked urgent');
+    is($target_ws->{urgent}, 1, 'Target workspace is now marked urgent');
+
+##############################################################################
+
     exit_gracefully($pid);
 }
 
index 97315c3deb2f5876f404f41058bf42af54db9db9..c5e61a328935404d4a3438ec6e275dca3efebc77 100644 (file)
@@ -17,6 +17,7 @@
 # Tests resizing tiling containers
 use i3test;
 
+my ($left, $right);
 my $tmp = fresh_workspace;
 
 cmd 'split v';
@@ -84,8 +85,8 @@ cmp_float($nodes->[1]->{percent}, 0.75, 'bottom window got 75%');
 
 $tmp = fresh_workspace;
 
-my $left = open_window;
-my $right = open_window;
+$left = open_window;
+$right = open_window;
 
 cmd 'split v';
 
@@ -261,8 +262,8 @@ cmp_ok($content[0]->{rect}->{width}, '==', $oldrect->{width}, 'width the same as
 
 $tmp = fresh_workspace;
 
-my $left = open_floating_window;
-my $right = open_floating_window;
+$left = open_floating_window;
+$right = open_floating_window;
 
 sub get_floating_rect {
     my ($window_id) = @_;
index ada7c5c5bd67c98f79d2be3d088027d54f1c3016..985a7bfd687ec5e8d44e99c81d6ef64a1bafaeca 100644 (file)
@@ -17,6 +17,8 @@
 use i3test i3_autostart => 0;
 use X11::XCB qw(PROP_MODE_REPLACE);
 
+my (@nodes);
+
 ##############################################################
 # 1: test the following directive:
 #    for_window [class="borderless"] border none
@@ -287,9 +289,6 @@ exit_gracefully($pid);
 # 8: check that the role criterion works properly
 ##############################################################
 
-# this configuration is broken because "asdf" is not a valid integer
-# the for_window should therefore recognize this error and don’t add the
-# assignment
 $config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
@@ -329,9 +328,6 @@ exit_gracefully($pid);
 #    *after* the window has been mapped
 ##############################################################
 
-# this configuration is broken because "asdf" is not a valid integer
-# the for_window should therefore recognize this error and don’t add the
-# assignment
 $config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
@@ -370,5 +366,98 @@ is($content[0]->{border}, 'none', 'no border (window_role 2)');
 
 exit_gracefully($pid);
 
+##############################################################
+# 10: check that the criterion 'window_type' works
+##############################################################
+
+# test all window types
+my %window_types = (
+    'normal'        => '_NET_WM_WINDOW_TYPE_NORMAL',
+    'dialog'        => '_NET_WM_WINDOW_TYPE_DIALOG',
+    'utility'       => '_NET_WM_WINDOW_TYPE_UTILITY',
+    'toolbar'       => '_NET_WM_WINDOW_TYPE_TOOLBAR',
+    'splash'        => '_NET_WM_WINDOW_TYPE_SPLASH',
+    'menu'          => '_NET_WM_WINDOW_TYPE_MENU',
+    'dropdown_menu' => '_NET_WM_WINDOW_TYPE_DROPDOWN_MENU',
+    'popup_menu'    => '_NET_WM_WINDOW_TYPE_POPUP_MENU',
+    'tooltip'       => '_NET_WM_WINDOW_TYPE_TOOLTIP'
+);
+
+while (my ($window_type, $atom) = each %window_types) {
+
+    $config = <<"EOT";
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [window_type="$window_type"] floating enable, mark branded
+EOT
+
+    $pid = launch_with_config($config);
+    $tmp = fresh_workspace;
+
+    $window = open_window(window_type => $x->atom(name => $atom));
+
+    my @nodes = @{get_ws($tmp)->{floating_nodes}};
+    cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
+    is($nodes[0]->{nodes}[0]->{mark}, 'branded', "mark set (window_type = $atom)");
+
+    exit_gracefully($pid);
+
+}
+
+##############################################################
+# 11: check that the criterion 'window_type' works if the
+#     _NET_WM_WINDOW_TYPE is changed after managing.
+##############################################################
+
+while (my ($window_type, $atom) = each %window_types) {
+
+    $config = <<"EOT";
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [window_type="$window_type"] floating enable, mark branded
+EOT
+
+    $pid = launch_with_config($config);
+    $tmp = fresh_workspace;
+
+    $window = open_window();
+
+    my $atomname = $x->atom(name => '_NET_WM_WINDOW_TYPE');
+    my $atomtype = $x->atom(name => 'ATOM');
+    $x->change_property(PROP_MODE_REPLACE, $window->id, $atomname->id, $atomtype->id,
+      32, 1, pack('L1', $x->atom(name => $atom)->id));
+    $x->flush;
+    sync_with_i3;
+
+    my @nodes = @{get_ws($tmp)->{floating_nodes}};
+    cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
+    is($nodes[0]->{nodes}[0]->{mark}, 'branded', "mark set (window_type = $atom)");
+
+    exit_gracefully($pid);
+
+}
+
+##############################################################
+# 12: check that the criterion 'workspace' works
+##############################################################
+
+$config = <<"EOT";
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+for_window [workspace="trigger"] floating enable, mark triggered
+EOT
+
+$pid = launch_with_config($config);
+
+cmd 'workspace trigger';
+$window = open_window;
+
+@nodes = @{get_ws('trigger')->{floating_nodes}};
+cmp_ok(@nodes, '==', 1, 'one floating container on this workspace');
+is($nodes[0]->{nodes}[0]->{mark}, 'triggered', "mark set for workspace criterion");
+
+exit_gracefully($pid);
+
+##############################################################
 
 done_testing;
index a0363ef3df4017238a801433c5e67e7fb935ee69..d8f9d2890c4d542d9780935e77937cdb5eb2bc0d 100644 (file)
@@ -147,8 +147,6 @@ $input = <<EOT;
     bindsym Mod1+s restart
     bindsym Mod1+s reload
     bindsym Mod1+s exit
-    bindsym Mod1+s stack-limit cols 2
-    bindsym Mod1+s stack-limit rows 3
     bind Mod1+c exec /usr/bin/urxvt
     mode "asdf" {
         bind 36 mode default
@@ -161,8 +159,6 @@ ok(line_exists($output, qr|^bindsym Mod1\+s mark foo$|), 'mark unchanged');
 ok(line_exists($output, qr|^bindsym Mod1\+s restart$|), 'restart unchanged');
 ok(line_exists($output, qr|^bindsym Mod1\+s reload$|), 'reload unchanged');
 ok(line_exists($output, qr|^bindsym Mod1\+s exit$|), 'exit unchanged');
-ok(line_exists($output, qr|^bindsym Mod1\+s stack-limit cols 2$|), 'stack-limit unchanged');
-ok(line_exists($output, qr|^bindsym Mod1\+s stack-limit rows 3$|), 'stack-limit unchanged');
 ok(line_exists($output, qr|^bindcode Mod1\+c exec /usr/bin/urxvt$|), 'bind changed to bindcode');
 ok(line_exists($output, qr|^mode "asdf" {$|), 'mode asdf unchanged');
 ok(line_exists($output, qr|^bindcode 36 mode \"default\"$|), 'mode default unchanged');
index 4ca41799ea5a06f1754c5b4f6333dcca930465d3..b27bed53ed0648c05a742919d26ab1fc98aa3c08 100644 (file)
@@ -155,6 +155,11 @@ cmd "rename workspace temp to $first_ws";
 
 is_num_children($first_ws, 3, 'three containers on the first workspace');
 
+# empty 'from' workspaces should not crash the renaming of startup sequences
+cmd "workspace $first_ws";
+cmd "rename workspace to temp";
+cmd "rename workspace to $first_ws";
+
 # Switch to the first workspace and move the focused window to the
 # second workspace.
 cmd "workspace $first_ws";
index 8675dd7183c003c335753308d140eef89b050867..cc4826c15766be171d652b4a102e813001d52da6 100644 (file)
@@ -66,6 +66,7 @@ ok($bar_config->{workspace_buttons}, 'workspace buttons enabled per default');
 ok($bar_config->{binding_mode_indicator}, 'mode indicator enabled per default');
 is($bar_config->{mode}, 'dock', 'dock mode by default');
 is($bar_config->{position}, 'bottom', 'position bottom by default');
+is($bar_config->{tray_padding}, 2, 'tray_padding ok');
 
 #####################################################################
 # ensure that reloading cleans up the old bar configs
@@ -96,6 +97,7 @@ bar {
 
     tray_output LVDS1
     tray_output HDMI2
+    tray_padding 0
     position top
     mode dock
     font Terminus
@@ -112,6 +114,7 @@ bar {
         active_workspace    #333333 #222222 #888888
         inactive_workspace  #333333 #222222 #888888
         urgent_workspace    #2f343a #900000 #ffffff
+        binding_mode        #abc123 #123abc #ababab
     }
 }
 EOT
@@ -133,6 +136,7 @@ is($bar_config->{mode}, 'dock', 'dock mode');
 is($bar_config->{position}, 'top', 'position top');
 is_deeply($bar_config->{outputs}, [ 'HDMI1', 'HDMI2' ], 'outputs ok');
 is($bar_config->{tray_output}, 'HDMI2', 'tray_output ok');
+is($bar_config->{tray_padding}, 0, 'tray_padding ok');
 is($bar_config->{font}, 'Terminus', 'font ok');
 is($bar_config->{socket_path}, '/tmp/foobar', 'socket_path ok');
 is_deeply($bar_config->{colors},
@@ -151,6 +155,9 @@ is_deeply($bar_config->{colors},
         urgent_workspace_border => '#2f343a',
         urgent_workspace_text => '#ffffff',
         urgent_workspace_bg => '#900000',
+        binding_mode_border => '#abc123',
+        binding_mode_text => '#ababab',
+        binding_mode_bg => '#123abc',
     }, 'colors ok');
 
 exit_gracefully($pid);
index edfc46e0374158eab1eba96380de55f2da92bc3f..eeaee449f96108420082087243d16d8a1a4b2fe2 100644 (file)
@@ -377,7 +377,7 @@ sub verify_scratchpad_move_with_visible_scratch_con {
     # this should bring up window 1
     cmd 'scratchpad show';
 
-    my $ws = get_ws($first);
+    $ws = get_ws($first);
     is(scalar @{$ws->{floating_nodes}}, 1, 'one floating node on ws1');
     is($x->input_focus, $window1->id, "showed the correct scratchpad window1");
 
index 6d67731a1e71a9914485962ebc3fc73215d1b042..fc7fa882797f861e2d4ddefd1f6ad133fb1ae5cc 100644 (file)
@@ -144,13 +144,13 @@ is(parser_calls("\nworkspace test"),
 ################################################################################
 
 is(parser_calls('unknown_literal'),
-   "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'shmlog', 'debuglog', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'unmark', 'resize', 'rename', 'nop', 'scratchpad', 'mode', 'bar'\n" .
+   "ERROR: Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'shmlog', 'debuglog', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'unmark', 'resize', 'rename', 'nop', 'scratchpad', 'title_format', 'mode', 'bar'\n" .
    "ERROR: Your command: unknown_literal\n" .
    "ERROR:               ^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
 
 is(parser_calls('move something to somewhere'),
-   "ERROR: Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" .
+   "ERROR: Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'mark', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" .
    "ERROR: Your command: move something to somewhere\n" .
    "ERROR:                    ^^^^^^^^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
index 1e91d47f9cf5cdcb71f9ccf350aefd1f61f2e364..fbcc586a5d0952cde9020d899b9c56d01e137909 100644 (file)
@@ -45,19 +45,27 @@ mode "meh" {
     bindsym Mod1 + Shift +   x resize grow
     bindcode Mod1+44 resize shrink
     bindsym --release Mod1+x exec foo
+    bindsym --whole-window button3 nop
+    bindsym --release --whole-window button3 nop
+    bindsym --border button3 nop
+    bindsym --release --border button3 nop
 }
 EOT
 
 my $expected = <<'EOT';
 cfg_enter_mode(meh)
-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)
+cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), (null), resize grow)
+cfg_mode_binding(bindcode, Mod1, 44, (null), (null), (null), resize shrink)
+cfg_mode_binding(bindsym, Mod1, x, --release, (null), (null), exec foo)
+cfg_mode_binding(bindsym, (null), button3, (null), (null), --whole-window, nop)
+cfg_mode_binding(bindsym, (null), button3, --release, (null), --whole-window, nop)
+cfg_mode_binding(bindsym, (null), button3, (null), --border, (null), nop)
+cfg_mode_binding(bindsym, (null), button3, --release, --border, (null), nop)
 EOT
 
 is(parser_calls($config),
    $expected,
-   'single number (move workspace 3) ok');
+   'mode bindings ok');
 
 ################################################################################
 # exec and exec_always
@@ -361,6 +369,40 @@ is(parser_calls($config),
    $expected,
    'force_display_urgency_hint ok');
 
+################################################################################
+# delay_exit_on_zero_displays
+################################################################################
+
+is(parser_calls('delay_exit_on_zero_displays 300'),
+   "cfg_delay_exit_on_zero_displays(300)\n",
+   'delay_exit_on_zero_displays ok');
+
+is(parser_calls('delay_exit_on_zero_displays 500 ms'),
+   "cfg_delay_exit_on_zero_displays(500)\n",
+   'delay_exit_on_zero_displays ok');
+
+is(parser_calls('delay_exit_on_zero_displays 700ms'),
+   "cfg_delay_exit_on_zero_displays(700)\n",
+   'delay_exit_on_zero_displays ok');
+
+$config = <<'EOT';
+delay_exit_on_zero_displays 300
+delay_exit_on_zero_displays 500 ms
+delay_exit_on_zero_displays 700ms
+delay_exit_on_zero_displays 700
+EOT
+
+$expected = <<'EOT';
+cfg_delay_exit_on_zero_displays(300)
+cfg_delay_exit_on_zero_displays(500)
+cfg_delay_exit_on_zero_displays(700)
+cfg_delay_exit_on_zero_displays(700)
+EOT
+
+is(parser_calls($config),
+   $expected,
+   'delay_exit_on_zero_displays ok');
+
 ################################################################################
 # workspace
 ################################################################################
@@ -433,7 +475,7 @@ client.focused          #4c7899 #285577 #ffffff #2e9ef4
 EOT
 
 my $expected_all_tokens = <<'EOT';
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'delay_exit_on_zero_displays', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
 EOT
 
 my $expected_end = <<'EOT';
@@ -620,7 +662,7 @@ EOT
 
 $expected = <<'EOT';
 cfg_enter_mode(yo)
-cfg_mode_binding(bindsym, (null), x, (null), (null), resize shrink left)
+cfg_mode_binding(bindsym, (null), x, (null), (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" {
@@ -646,8 +688,9 @@ bar {
 EOT
 
 $expected = <<'EOT';
+cfg_bar_start()
 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', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'separator_symbol', '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', 'bindsym', 'position', 'output', 'tray_output', 'tray_padding', 'font', 'separator_symbol', '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 f285338b8208efc2b1e1985d86069405bd0e1b3f..93b26d94294d7eb3bb5f88685ee3460879bbd446 100644 (file)
 #
 # checks if mark and unmark work correctly
 use i3test;
+use List::Util qw(first);
+
+my ($con, $first, $second);
 
 sub get_marks {
     return i3(get_socket_path())->get_marks->recv;
 }
 
+sub get_mark_for_window_on_workspace {
+    my ($ws, $con) = @_;
+
+    my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
+    return $current->{mark};
+}
+
 ##############################################################
 # 1: check that there are no marks set yet
 ##############################################################
@@ -76,4 +86,78 @@ cmd 'unmark';
 
 is_deeply(get_marks(), [], 'all marks removed');
 
+##############################################################
+# 4: mark a con, use same mark to mark another con,
+#    check that only the latter is marked
+##############################################################
+
+$first = open_window;
+$second = open_window;
+
+cmd 'mark important';
+cmd 'focus left';
+cmd 'mark important';
+
+is(get_mark_for_window_on_workspace($tmp, $first), 'important', 'first container now has the mark');
+ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the mark');
+
+##############################################################
+# 5: mark a con, toggle the mark, check that the mark is gone
+##############################################################
+
+$con = open_window;
+cmd 'mark important';
+cmd 'mark --toggle important';
+ok(!get_mark_for_window_on_workspace($tmp, $con), 'container no longer has the mark');
+
+##############################################################
+# 6: toggle a mark on an unmarked con, check it is marked
+##############################################################
+
+$con = open_window;
+cmd 'mark --toggle important';
+is(get_mark_for_window_on_workspace($tmp, $con), 'important', 'container now has the mark');
+
+##############################################################
+# 7: mark a con, toggle a different mark, check it is marked
+#    with the new mark
+##############################################################
+
+$con = open_window;
+cmd 'mark boring';
+cmd 'mark --toggle important';
+is(get_mark_for_window_on_workspace($tmp, $con), 'important', 'container has the most recent mark');
+
+##############################################################
+# 8: mark a con, toggle the mark on another con,
+#    check only the latter has the mark
+##############################################################
+
+$first = open_window;
+$second = open_window;
+
+cmd 'mark important';
+cmd 'focus left';
+cmd 'mark --toggle important';
+
+is(get_mark_for_window_on_workspace($tmp, $first), 'important', 'left container has the mark now');
+ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark');
+
+##############################################################
+# 9: try to mark two cons with the same mark and check that
+#    it fails
+##############################################################
+
+$first = open_window(wm_class => 'iamnotunique');
+$second = open_window(wm_class => 'iamnotunique');
+
+my $result = cmd "[instance=iamnotunique] mark important";
+
+is($result->[0]->{success}, 0, 'command was unsuccessful');
+is($result->[0]->{error}, 'A mark must not be put onto more than one window', 'correct error is returned');
+ok(!get_mark_for_window_on_workspace($tmp, $first), 'first container is not marked');
+ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr is not marked');
+
+##############################################################
+
 done_testing;
index c2de64e4da9a53a0ef6b9c02ef71d7ed008c4942..4c172ff578a2de8014acb9b8802b2f585bcabd54 100644 (file)
@@ -50,7 +50,7 @@ is($e->{container}->{floating}, 'user_on', 'the container should be floating');
 
 $cv = AnyEvent->condvar;
 cmd '[id="' . $win->{id} . '"] floating disable';
-my $e = $cv->recv;
+$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');
index 22a2eb4e2504c2e531ecb3ec76877be48930178d..c023bca479dd15e19e5247c56ccf39ae63a9f84c 100644 (file)
 # Bug still in: 4.8-16-g6888a1f
 use i3test;
 
-my $ws = fresh_workspace;
+my ($ws, $win1, $win2, $win3, $ws_con);
 
-my $win1 = open_window;
-my $win2 = open_window;
-my $win3 = open_window;
+###############################################################################
+# Tets moving with 'id' criterion.
+###############################################################################
+
+$ws = fresh_workspace;
+
+$win1 = open_window;
+$win2 = open_window;
+$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);
+$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');
 
+###############################################################################
+# Tets moving with 'window_type' criterion.
+###############################################################################
+
+# test all window types
+my %window_types = ( 
+    'normal'        => '_NET_WM_WINDOW_TYPE_NORMAL',
+    'dialog'        => '_NET_WM_WINDOW_TYPE_DIALOG',
+    'utility'       => '_NET_WM_WINDOW_TYPE_UTILITY',
+    'toolbar'       => '_NET_WM_WINDOW_TYPE_TOOLBAR',
+    'splash'        => '_NET_WM_WINDOW_TYPE_SPLASH',
+    'menu'          => '_NET_WM_WINDOW_TYPE_MENU',
+    'dropdown_menu' => '_NET_WM_WINDOW_TYPE_DROPDOWN_MENU',
+    'popup_menu'    => '_NET_WM_WINDOW_TYPE_POPUP_MENU',
+    'tooltip'       => '_NET_WM_WINDOW_TYPE_TOOLTIP'
+);
+
+while (my ($window_type, $atom) = each %window_types) {
+
+    $ws = fresh_workspace;
+
+    $win1 = open_window(window_type => $x->atom(name => $atom));
+    $win2 = open_window;
+    $win3 = open_window;
+
+    cmd '[window_type="' . $window_type . '"] move right';
+
+    $ws_con = get_ws($ws);
+    is($ws_con->{nodes}[0]->{window}, $win2->{id}, 'the `move [direction]` command should work with window_type = ' . $window_type);
+    is($x->input_focus, $win3->{id}, 'it should not disturb focus');
+
+}
+
+###############################################################################
+
 done_testing;
index 2ac9ecbbcc92a82d08620cf03868b5d15ee2d5ad..09226ff1f1ebe650259a86a068f300dedd69ea79 100644 (file)
@@ -59,7 +59,7 @@ is($event->{container}->{urgent}, 1, 'the container should be urgent');
 
 $cv = AnyEvent->condvar;
 $win->delete_hint('urgency');
-my $event = $cv->recv;
+$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');
index 614d6b3a8896a4a8b1bcc30101ccfa89ab4a19b3..ec17353e177c56e9d64043e2c82aea44a28908f1 100644 (file)
@@ -20,6 +20,8 @@
 use i3test i3_autostart => 0;
 use File::Temp qw(tempfile);
 
+my ($cfg, $ret, $out);
+
 sub check_config {
     my ($config) = @_;
     my ($fh, $tmpfile) = tempfile(UNLINK => 1);
@@ -35,12 +37,12 @@ sub check_config {
 # 1: test with a bogus configuration file
 ################################################################################
 
-my $cfg = <<EOT;
+$cfg = <<EOT;
 # i3 config file (v4)
 i_am_an_unknown_config option
 EOT
 
-my ($ret, $out) = check_config($cfg);
+($ret, $out) = check_config($cfg);
 is($ret, 1, "exit code == 1");
 like($out, qr/ERROR: *CONFIG: *[Ee]xpected.*tokens/, 'bogus config file');
 
@@ -48,12 +50,12 @@ like($out, qr/ERROR: *CONFIG: *[Ee]xpected.*tokens/, 'bogus config file');
 # 2: test with a valid configuration file
 ################################################################################
 
-my $cfg = <<EOT;
+$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);
+($ret, $out) = check_config($cfg);
 is($ret, 0, "exit code == 0");
 is($out, "", 'valid config file');
 
index e6cacdedfeaf3ee153409bb2406020e8c94cb94f..3685b30ce52ad7205d8382a602b1dfa4a778938e 100644 (file)
@@ -31,16 +31,17 @@ EOT
 my $pid = launch_with_config($config);
 
 sub change_window_class {
-    my ($window, $class) = @_;
+    my ($window, $class, $length) = @_;
     my $atomname = $x->atom(name => 'WM_CLASS');
     my $atomtype = $x->atom(name => 'STRING');
+    $length ||= length($class) + 1;
     $x->change_property(
         PROP_MODE_REPLACE,
         $window->id,
         $atomname->id,
         $atomtype->id,
         8,
-        length($class) + 1,
+        $length,
         $class
     );
     sync_with_i3;
@@ -65,6 +66,13 @@ is($con->{window_properties}->{instance}, 'special',
 is($con->{mark}, 'special_class_mark',
     'A `for_window` assignment should run for a match when the window changes class');
 
+change_window_class($win, "abcdefghijklmnopqrstuv\0abcd", 24);
+
+$con = @{get_ws_content($ws)}[0];
+
+is($con->{window_properties}->{class}, 'a',
+    'Non-null-terminated strings should be handled correctly');
+
 exit_gracefully($pid);
 
 done_testing;
diff --git a/testcases/t/240-focus-on-window-activation.t b/testcases/t/240-focus-on-window-activation.t
new file mode 100644 (file)
index 0000000..b2f8cac
--- /dev/null
@@ -0,0 +1,208 @@
+#!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 for the focus_on_window_activation directive
+# Ticket: #1426
+use i3test i3_autostart => 0;
+use List::Util qw(first);
+
+my ($config, $pid, $first, $second, $ws1, $ws2);
+
+sub send_net_active_window {
+    my ($id) = @_; 
+
+    my $msg = pack "CCSLLLLLLL",
+        X11::XCB::CLIENT_MESSAGE, # response_type
+        32, # format
+        0, # sequence
+        $id, # destination window
+        $x->atom(name => '_NET_ACTIVE_WINDOW')->id,
+        0, # source
+        0, 0, 0, 0;
+
+    $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
+}
+
+sub get_urgency_for_window_on_workspace {
+    my ($ws, $con) = @_;
+
+    my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
+    return $current->{urgent};
+}
+
+#####################################################################
+# 1: If mode is set to 'urgent' and the target workspace is visible,
+#    check that the urgent flag is set and focus is not lost.
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation urgent
+EOT
+
+$pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+$first = open_window;
+$second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is($x->input_focus, $second->id, 'second window is still focused');
+is(get_urgency_for_window_on_workspace($ws, $first), 1, 'first window is marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 2: If mode is set to 'urgent' and the target workspace is not
+#    visible, check that the urgent flag is set and focus is not lost.
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation urgent
+EOT
+
+$pid = launch_with_config($config);
+
+$ws1 = fresh_workspace;
+$first = open_window;
+$ws2 = fresh_workspace;
+$second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is(focused_ws(), $ws2, 'second workspace is still focused');
+is($x->input_focus, $second->id, 'second window is still focused');
+is(get_urgency_for_window_on_workspace($ws1, $first), 1, 'first window is marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 3: If mode is set to 'focus' and the target workspace is visible,
+#    check that the focus is switched.
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation focus
+EOT
+
+$pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+$first = open_window;
+$second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is($x->input_focus, $first->id, 'first window is now focused');
+ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 4: If mode is set to 'focus' and the target workspace is not
+#    visible, check that the focus switched.
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation focus
+EOT
+
+$pid = launch_with_config($config);
+
+$ws1 = fresh_workspace;
+$first = open_window;
+$ws2 = fresh_workspace;
+$second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is(focused_ws(), $ws1, 'first workspace is now focused');
+is($x->input_focus, $first->id, 'first window is now focused');
+ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 5: If mode is set to 'none' and the target workspace is visible,
+#    check that nothing happens.
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation none
+EOT
+
+$pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+$first = open_window;
+$second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is($x->input_focus, $second->id, 'second window is still focused');
+ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 6: If mode is set to 'none' and the target workspace is not
+#    visible, check that nothing happens.
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation none
+EOT
+
+$pid = launch_with_config($config);
+
+$ws1 = fresh_workspace;
+$first = open_window;
+$ws2 = fresh_workspace;
+$second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is(focused_ws(), $ws2, 'second workspace is still focused');
+is($x->input_focus, $second->id, 'second window is still focused');
+ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/240-tabbed-floating-disable-crash.t b/testcases/t/240-tabbed-floating-disable-crash.t
new file mode 100644 (file)
index 0000000..7947158
--- /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)
+#
+# Verifies that i3 does not crash when floating and then unfloating an
+# unfocused window within a tabbed container.
+# Ticket: #1484
+# Bug still in: 4.9.1-124-g856e1f9
+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_layout tabbed
+EOT
+
+my $pid = launch_with_config($config);
+
+open_window;
+open_window;
+
+# Mark the second window, then focus the workspace.
+cmd 'mark foo, focus parent, focus parent';
+
+# Float and unfloat the marked window (without it being focused).
+cmd '[con_mark=foo] floating enable, floating disable';
+
+does_i3_live;
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/241-consistent-center.t b/testcases/t/241-consistent-center.t
new file mode 100644 (file)
index 0000000..283d173
--- /dev/null
@@ -0,0 +1,106 @@
+#!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)
+#
+# Verifies that most of i3's centering methods produce consistent results.
+# Decorations are disabled to avoid floating_enable's logic which shifts
+# windows upwards dependent on their decoration height.
+#
+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 none
+new_float none
+EOT
+
+my $pid = launch_with_config($config);
+
+#####################################################################
+# Open a floating window, verifying that its initial position is
+# centered, and also verify that both centering methods leave it in
+# its original spot.
+#####################################################################
+
+my $first = open_floating_window;
+
+my $initial = $first->rect;
+is(int($initial->{x} + $initial->{width} / 2), int($x->root->rect->width / 2),
+   'x coordinates match');
+is(int($initial->{y} + $initial->{height} / 2), int($x->root->rect->height / 2),
+   'y coordinates match');
+
+cmd 'move position center';
+
+my $new = $first->rect;
+is($initial->{x}, $new->{x}, 'x coordinates match');
+is($initial->{y}, $new->{y}, 'y coordinates match');
+
+cmd 'move absolute position center';
+
+$new = $first->rect;
+is($initial->{x}, $new->{x}, 'x coordinates match');
+is($initial->{y}, $new->{y}, 'y coordinates match');
+
+#####################################################################
+# Create a second window and move it into and out of the scratchpad.
+# Because it hasn't been moved or resized, it should be floated in
+# the center of the screen when pulled out of the scratchpad.
+#####################################################################
+
+my $second = open_window;
+
+cmd 'move scratchpad, scratchpad show';
+
+$new = $second->rect;
+my $mid_init = $initial->{x} + int($initial->{width} / 2);
+my $mid_new = $new->{x} + int($new->{width} / 2);
+is($mid_init, $mid_new, 'x midpoint is ws center');
+
+$mid_init = $initial->{y} + int($initial->{height} / 2);
+$mid_new = $new->{y} + int($new->{height} / 2);
+is($mid_init, $mid_new, 'y midpoint is ws center');
+
+#####################################################################
+# Verify that manually floating a tiled window results in proper
+# centering.
+#####################################################################
+
+my $third = open_window;
+
+cmd 'floating enable';
+
+$new = $third->rect;
+is($initial->{x}, $new->{x}, 'x coordinates match');
+is($initial->{y}, $new->{y}, 'y coordinates match');
+
+#####################################################################
+# Create a child window of the previous window, which should result
+# in the new window being centered over the last one.
+#####################################################################
+
+my $fourth = open_window( dont_map => 1, client_leader => $third );
+$fourth->map;
+sync_with_i3;
+
+my $child = $fourth->rect;
+is($new->{x}, $child->{x}, 'x coordinates match');
+is($new->{y}, $child->{y}, 'y coordinates match');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/242-no-focus.t b/testcases/t/242-no-focus.t
new file mode 100644 (file)
index 0000000..143ae5c
--- /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 the 'no_focus' directive.
+# Ticket: #1416
+use i3test i3_autostart => 0;
+
+my ($config, $pid, $ws, $first, $second, $focused);
+
+#####################################################################
+# 1: open a window and check that it takes focus
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+EOT
+
+$pid = launch_with_config($config);
+$ws = fresh_workspace;
+$first = open_window;
+$focused = get_focused($ws);
+$second = open_window;
+
+isnt(get_focused($ws), $focused, 'focus has changed');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 2: open a window matched by a no_focus directive and check that
+#    it doesn't take focus
+#####################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+no_focus [instance=notme]
+EOT
+
+$pid = launch_with_config($config);
+$ws = fresh_workspace;
+$first = open_window;
+$focused = get_focused($ws);
+$second = open_window(wm_class => 'notme');
+
+is(get_focused($ws), $focused, 'focus has not changed');
+
+exit_gracefully($pid);
+
+#####################################################################
+
+done_testing;
diff --git a/testcases/t/243-move-to-mark.t b/testcases/t/243-move-to-mark.t
new file mode 100644 (file)
index 0000000..58105d7
--- /dev/null
@@ -0,0 +1,341 @@
+#!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 for the 'move [window|container] to mark' command
+# Ticket: #1643
+use i3test;
+
+# In the following tests descriptions, we will always use the following names:
+#  * 'S' for the source container which is going to be moved,
+#  * 'M' for the marked target container to which 'S' will be moved.
+
+my ($A, $B, $S, $M, $F, $source_ws, $target_ws, $ws);
+my ($nodes, $focus);
+my $cmd_result;
+
+my $_NET_WM_STATE_REMOVE = 0;
+my $_NET_WM_STATE_ADD = 1;
+my $_NET_WM_STATE_TOGGLE = 2;
+
+sub set_urgency {
+    my ($win, $urgent_flag) = @_; 
+    my $msg = pack "CCSLLLLLL",
+        X11::XCB::CLIENT_MESSAGE, # response_type
+        32, # format
+        0, # sequence
+        $win->id, # window
+        $x->atom(name => '_NET_WM_STATE')->id, # message type
+        ($urgent_flag ? $_NET_WM_STATE_ADD : $_NET_WM_STATE_REMOVE), # data32[0]
+        $x->atom(name => '_NET_WM_STATE_DEMANDS_ATTENTION')->id, # 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);
+}
+
+###############################################################################
+# Given 'M' and 'S' in a horizontal split, when 'S' is moved to 'M', then
+# verify that nothing changed.
+###############################################################################
+
+$ws = fresh_workspace;
+$M = open_window;
+cmd 'mark target';
+$S = open_window;
+
+cmd 'move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($ws);
+is(@{$nodes}, 2, 'there are two containers');
+is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
+is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
+
+###############################################################################
+# Given 'S' and 'M' in a horizontal split, when 'S' is moved to 'M', then
+# both containers switch places.
+###############################################################################
+
+$ws = fresh_workspace;
+$S = open_window;
+$M = open_window;
+cmd 'mark target';
+cmd 'focus left';
+
+cmd 'move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($ws);
+is(@{$nodes}, 2, 'there are two containers');
+is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
+is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
+
+###############################################################################
+# Given 'S' and no container 'M' exists, when 'S' is moved to 'M', then
+# the command is unsuccessful.
+###############################################################################
+
+$ws = fresh_workspace;
+$S = open_window;
+
+$cmd_result = cmd 'move container to mark absent';
+
+is($cmd_result->[0]->{success}, 0, 'command was unsuccessful');
+
+###############################################################################
+# Given 'S' and 'M' on different workspaces, when 'S' is moved to 'M', then
+# 'S' ends up on the same workspace as 'M'.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+$target_ws = fresh_workspace;
+$M = open_window;
+cmd 'mark target';
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($source_ws);
+is(@{$nodes}, 0, 'source workspace is empty');
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 2, 'both containers are on the target workspace');
+is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
+is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
+
+###############################################################################
+# Given 'S' and 'M' on different workspaces and 'S' is urgent, when 'S' is 
+# moved to 'M', then the urgency flag is transferred to the target workspace.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+$F = open_window;
+$target_ws = fresh_workspace;
+$M = open_window;
+cmd 'mark target';
+cmd 'workspace ' . $source_ws;
+set_urgency($S, 1);
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+$source_ws = get_ws($source_ws);
+$target_ws = get_ws($target_ws);
+ok(!$source_ws->{urgent}, 'source workspace is no longer urgent');
+ok($target_ws->{urgent}, 'target workspace is urgent');
+
+###############################################################################
+# Given 'S' and 'M' where 'M' is inside a tabbed container, when 'S' is moved
+# to 'M', then 'S' ends up as a new tab.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+
+# open tabbed container ['A' 'M' 'B']
+$target_ws = fresh_workspace;
+$A = open_window;
+cmd 'layout tabbed';
+$M = open_window;
+cmd 'mark target';
+$B = open_window;
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 1, 'there is a tabbed container');
+
+$nodes = $nodes->[0]->{nodes};
+is(@{$nodes}, 4, 'all four containers are on the target workspace');
+is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
+is($nodes->[1]->{window}, $M->{id}, 'M is the second tab');
+is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
+is($nodes->[3]->{window}, $B->{id}, 'B is the fourth tab');
+
+###############################################################################
+# Given 'S' and 'M' where 'M' is a tabbed container where the currently focused
+# tab is a nested layout, when 'S' is moved to 'M', then 'S' is a new tab
+# within 'M'.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+
+$target_ws = fresh_workspace;
+$A = open_window;
+cmd 'layout tabbed';
+cmd 'focus parent';
+cmd 'mark target';
+cmd 'focus child';
+$B = open_window;
+cmd 'split h';
+$F = open_window;
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 1, 'there is a tabbed container');
+
+$nodes = $nodes->[0]->{nodes};
+is(@{$nodes}, 3, 'there are three tabs');
+
+is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
+is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
+
+###############################################################################
+# Given 'S' and 'M' where 'M' is inside a split container inside a tabbed
+# container, when 'S' is moved to 'M', then 'S' ends up as a container
+# within the same tab as 'M'.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+
+# open tabbed container ['A'['B' 'M']]
+$target_ws = fresh_workspace;
+$A = open_window;
+cmd 'layout tabbed';
+$B = open_window;
+cmd 'split h';
+$M = open_window;
+cmd 'mark target';
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 1, 'there is a tabbed container');
+
+$nodes = $nodes->[0]->{nodes};
+is(@{$nodes}, 2, 'there are two tabs');
+
+$nodes = $nodes->[1]->{nodes};
+is(@{$nodes}, 3, 'the tab with the marked children has three children');
+is($nodes->[0]->{window}, $B->{id}, 'B is the first tab');
+is($nodes->[1]->{window}, $M->{id}, 'M is the second tab');
+is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
+
+###############################################################################
+# Given 'S', 'A' and 'B' where 'A' and 'B' are inside the tabbed container 'M',
+# when 'S' is moved to 'M', then 'S' ends up as a new tab in 'M'.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+$target_ws = fresh_workspace;
+$A = open_window;
+cmd 'layout tabbed';
+$B = open_window;
+cmd 'focus parent';
+cmd 'mark target';
+cmd 'focus child';
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 1, 'there is a tabbed container');
+
+$nodes = $nodes->[0]->{nodes};
+is(@{$nodes}, 3, 'there are three tabs');
+
+is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
+is($nodes->[1]->{window}, $B->{id}, 'B is the second tab');
+is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
+
+###############################################################################
+# Given 'S', 'A', 'F' and 'M', where 'M' is a workspace containing a tabbed
+# container, when 'S' is moved to 'M', then 'S' does not end up as a tab, but
+# rather as a new window next to the tabbed container.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+$target_ws = fresh_workspace;
+$A = open_window;
+cmd 'layout tabbed';
+$F = open_window;
+$M = $target_ws;
+cmd 'focus parent';
+cmd 'focus parent';
+cmd 'mark target';
+cmd 'focus ' . $source_ws;
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 2, 'there is a tabbed container and a window');
+is($nodes->[1]->{window}, $S->{id}, 'S is the second window');
+
+###############################################################################
+# Given 'S' and 'M' where 'S' is floating and 'M' on a different workspace,
+# when 'S' is moved to 'M', then 'S' is a floating container on the same
+# workspaces as 'M'.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_floating_window;
+$target_ws = fresh_workspace;
+$M = open_window;
+cmd 'mark target';
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+is(@{get_ws($target_ws)->{floating_nodes}}, 1, 'target workspace has the container now');
+
+###############################################################################
+# Given 'S' and 'M' where 'M' is floating and on a different workspace,
+# when 'S' is moved to 'M', then 'S' ends up as a tiling container on the
+# same workspace as 'M'.
+###############################################################################
+
+$source_ws = fresh_workspace;
+$S = open_window;
+$target_ws = fresh_workspace;
+$M = open_floating_window;
+cmd 'mark target';
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+($nodes, $focus) = get_ws_content($target_ws);
+is(@{$nodes}, 1, 'tiling container moved to the target workspace');
+
+###############################################################################
+# Given 'S' and 'M' are the same container, when 'S' is moved to 'M', then
+# the command is ignored.
+###############################################################################
+
+$ws = fresh_workspace;
+$S = open_window;
+$M = $S;
+cmd 'mark target';
+
+cmd '[id="' . $S->{id} . '"] move container to mark target';
+sync_with_i3;
+
+does_i3_live;
+
+###############################################################################
+
+done_testing;
diff --git a/testcases/t/243-net-wm-state-hidden.t b/testcases/t/243-net-wm-state-hidden.t
new file mode 100644 (file)
index 0000000..3f2301c
--- /dev/null
@@ -0,0 +1,201 @@
+#!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 for setting and removing the _NET_WM_STATE_HIDDEN atom properly.
+# Ticket: #1648
+use i3test;
+use X11::XCB qw(:all);
+
+sub is_hidden {
+    sync_with_i3;
+    my $atom = $x->atom(name => '_NET_WM_STATE_HIDDEN');
+
+    my ($con) = @_;
+    my $cookie = $x->get_property(
+        0,
+        $con->{id},
+        $x->atom(name => '_NET_WM_STATE')->id,
+        GET_PROPERTY_TYPE_ANY,
+        0,
+        4096
+    );
+
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    my $len = $reply->{length};
+    return 0 if $len == 0;
+
+    my @atoms = unpack("L$len", $reply->{value});
+    for (my $i = 0; $i < $len; $i++) {
+        return 1 if $atoms[$i] == $atom->id;
+    }
+
+    return 0;
+}
+
+my ($tabA, $tabB, $tabC, $subtabA, $subtabB, $windowA, $windowB);
+
+###############################################################################
+# Given two containers next to each other, when focusing one, then the other
+# one does not have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$windowA = open_window;
+$windowB = open_window;
+
+ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given two containers on different workspaces, when one is focused, then
+# the other one does not have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$windowA = open_window;
+fresh_workspace;
+$windowB = open_window;
+
+ok(!is_hidden($windowA), 'left window does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($windowB), 'right window does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given two containers in the same tabbed container, when one is focused, then
+# (only) the other one has _NET_WM_STATE_HIDDEN set.
+# Given the other tab is focused, then the atom is transferred.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+
+cmd 'focus left';
+
+ok(!is_hidden($tabA), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given three containers in the same stacked container, when the focused tab
+# is moved to another workspace, then the now focused tab does not have
+# _NET_WM_STATE_HIDDEN set anymore.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout stacked';
+$tabB = open_window;
+$tabC = open_window;
+cmd 'move window to workspace unused';
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabC), 'moved window does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given three containers in the same stacked container, when a not focused
+# tab is moved to another workspace, then it does not have _NET_WM_STATE_HIDDEN
+# set anymore.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout stacked';
+$tabB = open_window;
+cmd 'mark moveme';
+$tabC = open_window;
+cmd '[con_mark="moveme"] move window to workspace unused';
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'moved window does not have _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given a tabbed container and some other container, when the latter is moved
+# into the tabbed container, then all other tabs have _NET_WM_STATE_HIDDEN
+# set.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+cmd 'focus parent';
+cmd 'split h';
+$tabC = open_window;
+cmd 'move left';
+
+ok(is_hidden($tabA), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($tabB), 'unfocused tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabC), 'focused tab does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given a stacked container nested inside another tabbed container with the
+# inner one being in the currently focused tab, then the focused tab of the
+# inner container does not have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+cmd 'split h';
+open_window;
+cmd 'split v';
+cmd 'layout stacked';
+$subtabA = open_window;
+$subtabB = open_window;
+
+ok(is_hidden($tabA), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($tabB), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
+ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
+
+cmd 'focus left';
+
+ok(!is_hidden($subtabB), 'focused inner tab does not have _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+# Given a stacked container nested inside another tabbed container with the
+# inner one being in a currently not focused tab, then all tabs of the inner
+# container have _NET_WM_STATE_HIDDEN set.
+###############################################################################
+
+fresh_workspace;
+$tabA = open_window;
+cmd 'layout tabbed';
+$tabB = open_window;
+cmd 'split h';
+open_window;
+cmd 'split v';
+cmd 'layout stacked';
+$subtabA = open_window;
+$subtabB = open_window;
+cmd 'focus parent';
+cmd 'focus parent';
+cmd 'focus left';
+
+ok(!is_hidden($tabA), 'focused outer tab does not have _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($tabB), 'unfocused outer tab has _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($subtabA), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
+ok(is_hidden($subtabB), 'unfocused inner tab has _NET_WM_STATE_HIDDEN set');
+
+###############################################################################
+
+done_testing;
diff --git a/testcases/t/245-move-position-mouse.t b/testcases/t/245-move-position-mouse.t
new file mode 100644 (file)
index 0000000..ae04927
--- /dev/null
@@ -0,0 +1,142 @@
+#!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 the 'move [window|container] [to] position mouse|cursor|pointer command.
+# Ticket: #1696
+use i3test i3_autostart => 0;
+use POSIX qw(floor);
+
+sub warp_pointer {
+    my ($pos_x, $pos_y) = @_;
+    $x->root->warp_pointer($pos_x, $pos_y);
+    sync_with_i3;
+}
+
+my ($pid, $config, $ws, $rect, @cons);
+my $root_rect = $x->root->rect;
+
+##########################################################################
+
+##########################################################################
+# Given a floating container and the cursor far from any edges, when
+# moving the container to the mouse, then the container is moved such
+# that the cursor is centered inside it.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer(100, 100);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is(floor($rect->{x} + $rect->{width} / 2), 100, "con is centered around cursor horizontally");
+is(floor($rect->{y} + $rect->{height} / 2), 100, "con is centered around cursor vertically");
+
+exit_gracefully($pid);
+
+##########################################################################
+# Given a floating container and the cursor is in the left upper edge
+# of the output, when moving the container to the mouse, then the 
+# container is moved but adjusted to stay in-bounds.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer(5, 5);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is($rect->{x}, 0, "con is on the left edge");
+is($rect->{y}, 0, "con is on the upper edge");
+
+exit_gracefully($pid);
+
+##########################################################################
+# Given a floating container and the cursor is in the left right lower
+# edge of the output, when moving the container to the mouse, then the 
+# container is moved but adjusted to stay in-bounds.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer($root_rect->width - 5, $root_rect->height - 5);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is($rect->{x} + $rect->{width}, $root_rect->width, "con is on the right edge");
+is($rect->{y} + $rect->{height}, $root_rect->height, "con is on the lower edge");
+
+exit_gracefully($pid);
+
+##########################################################################
+# Given a floating container and the cursor being close to the corner of
+# an output, when the container is moved to the mouse, then the container
+# is moved but adjusted to the output boundaries.
+# This test verifies that boundaries of individual outputs are respected
+# and not just boundaries of the entire X root screen.
+##########################################################################
+
+$config = <<EOT;
+# i3 config file (v4)
+font pango:monospace 8
+fake-outputs 500x500+0+0,500x500+500+0,500x500+0+500,500x500+500+500
+EOT
+$pid = launch_with_config($config);
+
+$ws = fresh_workspace;
+open_floating_window;
+warp_pointer(495, 495);
+
+cmd 'move position mouse';
+
+@cons = @{get_ws($ws)->{floating_nodes}};
+$rect = $cons[0]->{rect};
+
+is($rect->{x} + $rect->{width}, 500, "con is on the right edge");
+is($rect->{y} + $rect->{height}, 500, "con is on the lower edge");
+
+exit_gracefully($pid);
+
+##########################################################################
+
+done_testing;
diff --git a/testcases/t/246-window-decoration-focus.t b/testcases/t/246-window-decoration-focus.t
new file mode 100644 (file)
index 0000000..06dfad8
--- /dev/null
@@ -0,0 +1,148 @@
+#!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 hovering over the window decoration of a window causes it to focus
+# correctly.
+# Ticket: #1056
+# Bug still in: 4.10.2-174-g8029ff0
+use i3test;
+
+my ($ws, $A, $B, $C, $target, $y);
+my @cons;
+
+# ==================================================================================
+# Given the following layout (= denotes the window decoration),
+# when moving the mouse from 1 to 2,
+# then the C should be focused.
+#
+# This should especially be the case if B is the focus head of its vertically split parent container.
+#
+# +===A===+===B===+
+# |       |       |
+# |     1 +=2=C===+
+# |       |       |
+# +-------+-------+
+#
+# ==================================================================================
+
+$ws = fresh_workspace;
+
+open_window;
+$B = open_window;
+cmd 'split v';
+open_window;
+$target = get_focused($ws);
+
+@cons = @{get_ws($ws)->{nodes}};
+$A = $cons[0];
+$C = $cons[1]->{nodes}[1];
+
+$y = $C->{rect}->{y} - 0.5 * $C->{deco_rect}->{height};
+
+# make sure that B is the focus head of its parent
+cmd '[id="' . $B->{id} . '"] focus';
+
+# move pointer to position 1
+$x->root->warp_pointer($C->{rect}->{x} - 0.5 * $A->{rect}->{width}, $y);
+sync_with_i3;
+
+# move pointer to position 2
+$x->root->warp_pointer($C->{rect}->{x} + 0.5 * $C->{rect}->{width}, $y);
+sync_with_i3;
+
+is(get_focused($ws), $target, 'focus switched to container C');
+
+# ==================================================================================
+# Given a tabbed container when the mouse is moved onto the window decoration
+# then the focus head of the tabbed container is focused regardless of which particular
+# tab's decoration the mouse is on.
+#
+# +=========+=========+
+# |         |         |
+# |       1 +=2==|****| <- tab to the right is focus head of tabbed container
+# |         |         |
+# +---------+---------+
+#
+# ==================================================================================
+
+$ws = fresh_workspace;
+
+open_window;
+open_window;
+cmd 'split v';
+open_window;
+cmd 'split h';
+open_window;
+$target = get_focused($ws);
+cmd 'layout tabbed';
+
+@cons = @{get_ws($ws)->{nodes}};
+$A = $cons[0];
+$B = $cons[1]->{nodes}[1]->{nodes}[1];
+
+$y = $B->{rect}->{y} - 0.5 * $B->{deco_rect}->{height};
+
+$x->root->warp_pointer($B->{rect}->{x} - 0.5 * $A->{rect}->{width}, $y);
+sync_with_i3;
+
+$x->root->warp_pointer($B->{rect}->{x} + 0.2 * $B->{rect}->{width}, $y);
+sync_with_i3;
+
+is(get_focused($ws), $target, 'focus switched to the focus head of the tabbed container');
+
+# ==================================================================================
+# Given a stacked container when the mouse is moved onto the window decoration
+# then the focus head of the stacked container is focused regardless of which particular
+# tab's decoration the mouse is on.
+#
+# +=========+=========+
+# |         |         |
+# |       1 +=2=======+
+# |         +*********+ <- the lower tab is the focus head of the stacked container
+# |         |         |
+# +---------+---------+
+#
+# ==================================================================================
+
+$ws = fresh_workspace;
+
+open_window;
+open_window;
+cmd 'split v';
+open_window;
+cmd 'split h';
+open_window;
+$target = get_focused($ws);
+cmd 'layout stacked';
+
+@cons = @{get_ws($ws)->{nodes}};
+$A = $cons[0];
+$B = $cons[1]->{nodes}[1]->{nodes}[0];
+$C = $cons[1]->{nodes}[1]->{nodes}[1];
+
+$y = $B->{rect}->{y} - 1.5 * $B->{deco_rect}->{height};
+
+$x->root->warp_pointer($B->{rect}->{x} - 0.5 * $A->{rect}->{width}, $y);
+sync_with_i3;
+
+$x->root->warp_pointer($B->{rect}->{x} + 0.5 * $B->{rect}->{width}, $y);
+sync_with_i3;
+
+is(get_focused($ws), $target, 'focus switched to the focus head of the stacked container');
+
+# ==================================================================================
+
+done_testing;
diff --git a/testcases/t/247-config-line-continuation.t b/testcases/t/247-config-line-continuation.t
new file mode 100644 (file)
index 0000000..0396d87
--- /dev/null
@@ -0,0 +1,201 @@
+#!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)
+#
+# Checks that the line continuation are parsed correctly
+#
+
+use i3test i3_autostart => 0;
+
+# starts i3 with the given config, opens a window, returns its border style
+sub launch_get_border {
+    my ($config) = @_;
+
+    my $pid = launch_with_config($config);
+
+    my $i3 = i3(get_socket_path(0));
+    my $tmp = fresh_workspace;
+
+    my $window = open_window(name => '"special title"');
+
+    my @content = @{get_ws_content($tmp)};
+    cmp_ok(@content, '==', 1, 'one node on this workspace now');
+    my $border = $content[0]->{border};
+
+    exit_gracefully($pid);
+
+    return $border;
+}
+
+#####################################################################
+# test string escaping
+#####################################################################
+
+my $config = <<'EOT';
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+set $vartest \"special title\"
+for_window [title="$vartest"] border none
+EOT
+
+is(launch_get_border($config), 'none', 'no border');
+
+#####################################################################
+# test the line continuation
+#####################################################################
+
+$config = <<'EOT';
+# i3 config file (v4)
+font \
+-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+# Use line contiuation with too many lines (>4096 characters).
+# This config is invalid. Use it to ensure no buffer overflow.
+bindsym Mod1+b \
+0001-This is a very very very very very very very very very very very very very very very very very long cmd \
+0002-This is a very very very very very very very very very very very very very very very very very long cmd \
+0003-This is a very very very very very very very very very very very very very very very very very long cmd \
+0004-This is a very very very very very very very very very very very very very very very very very long cmd \
+0005-This is a very very very very very very very very very very very very very very very very very long cmd \
+0006-This is a very very very very very very very very very very very very very very very very very long cmd \
+0007-This is a very very very very very very very very very very very very very very very very very long cmd \
+0008-This is a very very very very very very very very very very very very very very very very very long cmd \
+0009-This is a very very very very very very very very very very very very very very very very very long cmd \
+0010-This is a very very very very very very very very very very very very very very very very very long cmd \
+0011-This is a very very very very very very very very very very very very very very very very very long cmd \
+0012-This is a very very very very very very very very very very very very very very very very very long cmd \
+0013-This is a very very very very very very very very very very very very very very very very very long cmd \
+0014-This is a very very very very very very very very very very very very very very very very very long cmd \
+0015-This is a very very very very very very very very very very very very very very very very very long cmd \
+0016-This is a very very very very very very very very very very very very very very very very very long cmd \
+0017-This is a very very very very very very very very very very very very very very very very very long cmd \
+0018-This is a very very very very very very very very very very very very very very very very very long cmd \
+0019-This is a very very very very very very very very very very very very very very very very very long cmd \
+0020-This is a very very very very very very very very very very very very very very very very very long cmd \
+0021-This is a very very very very very very very very very very very very very very very very very long cmd \
+0022-This is a very very very very very very very very very very very very very very very very very long cmd \
+0023-This is a very very very very very very very very very very very very very very very very very long cmd \
+0024-This is a very very very very very very very very very very very very very very very very very long cmd \
+0025-This is a very very very very very very very very very very very very very very very very very long cmd \
+0026-This is a very very very very very very very very very very very very very very very very very long cmd \
+0027-This is a very very very very very very very very very very very very very very very very very long cmd \
+0028-This is a very very very very very very very very very very very very very very very very very long cmd \
+0029-This is a very very very very very very very very very very very very very very very very very long cmd \
+0030-This is a very very very very very very very very very very very very very very very very very long cmd \
+0031-This is a very very very very very very very very very very very very very very very very very long cmd \
+0032-This is a very very very very very very very very very very very very very very very very very long cmd \
+0033-This is a very very very very very very very very very very very very very very very very very long cmd \
+0034-This is a very very very very very very very very very very very very very very very very very long cmd \
+0035-This is a very very very very very very very very very very very very very very very very very long cmd \
+0036-This is a very very very very very very very very very very very very very very very very very long cmd \
+0037-This is a very very very very very very very very very very very very very very very very very long cmd \
+0038-This is a very very very very very very very very very very very very very very very very very long cmd \
+0039-This is a very very very very very very very very very very very very very very very very very long cmd \
+0040-This is a very very very very very very very very very very very very very very very very very long cmd \
+0041-This is a very very very very very very very very very very very very very very very very very long cmd \
+0042-This is a very very very very very very very very very very very very very very very very very long cmd \
+0043-This is a very very very very very very very very very very very very very very very very very long cmd \
+0044-This is a very very very very very very very very very very very very very very very very very long cmd \
+0045-This is a very very very very very very very very very very very very very very very very very long cmd \
+0046-This is a very very very very very very very very very very very very very very very very very long cmd \
+0047-This is a very very very very very very very very very very very very very very very very very long cmd \
+0048-This is a very very very very very very very very very very very very very very very very very long cmd \
+0049-This is a very very very very very very very very very very very very very very very very very long cmd \
+0050-This is a very very very very very very very very very very very very very very very very very long cmd \
+0051-This is a very very very very very very very very very very very very very very very very very long cmd \
+0052-This is a very very very very very very very very very very very very very very very very very long cmd \
+0053-This is a very very very very very very very very very very very very very very very very very long cmd \
+0054-This is a very very very very very very very very very very very very very very very very very long cmd \
+0055-This is a very very very very very very very very very very very very very very very very very long cmd \
+0056-This is a very very very very very very very very very very very very very very very very very long cmd \
+0057-This is a very very very very very very very very very very very very very very very very very long cmd \
+0058-This is a very very very very very very very very very very very very very very very very very long cmd \
+0059-This is a very very very very very very very very very very very very very very very very very long cmd \
+0060-This is a very very very very very very very very very very very very very very very very very long cmd \
+0061-This is a very very very very very very very very very very very very very very very very very long cmd \
+0062-This is a very very very very very very very very very very very very very very very very very long cmd \
+0063-This is a very very very very very very very very very very very very very very very very very long cmd \
+0064-This is a very very very very very very very very very very very very very very very very very long cmd \
+0065-This is a very very very very very very very very very very very very very very very very very long cmd \
+0066-This is a very very very very very very very very very very very very very very very very very long cmd \
+0067-This is a very very very very very very very very very very very very very very very very very long cmd \
+0068-This is a very very very very very very very very very very very very very very very very very long cmd \
+0069-This is a very very very very very very very very very very very very very very very very very long cmd \
+0070-This is a very very very very very very very very very very very very very very very very very long cmd \
+0071-This is a very very very very very very very very very very very very very very very very very long cmd \
+0072-This is a very very very very very very very very very very very very very very very very very long cmd \
+0073-This is a very very very very very very very very very very very very very very very very very long cmd \
+0074-This is a very very very very very very very very very very very very very very very very very long cmd \
+0075-This is a very very very very very very very very very very very very very very very very very long cmd \
+0076-This is a very very very very very very very very very very very very very very very very very long cmd \
+0077-This is a very very very very very very very very very very very very very very very very very long cmd \
+0078-This is a very very very very very very very very very very very very very very very very very long cmd \
+0079-This is a very very very very very very very very very very very very very very very very very long cmd \
+0080-This is a very very very very very very very very very very very very very very very very very long cmd \
+0081-This is a very very very very very very very very very very very very very very very very very long cmd \
+0082-This is a very very very very very very very very very very very very very very very very very long cmd \
+0083-This is a very very very very very very very very very very very very very very very very very long cmd \
+0084-This is a very very very very very very very very very very very very very very very very very long cmd \
+0085-This is a very very very very very very very very very very very very very very very very very long cmd \
+0086-This is a very very very very very very very very very very very very very very very very very long cmd \
+0087-This is a very very very very very very very very very very very very very very very very very long cmd \
+0088-This is a very very very very very very very very very very very very very very very very very long cmd \
+0089-This is a very very very very very very very very very very very very very very very very very long cmd \
+0090-This is a very very very very very very very very very very very very very very very very very long cmd \
+0091-This is a very very very very very very very very very very very very very very very very very long cmd \
+0092-This is a very very very very very very very very very very very very very very very very very long cmd \
+0093-This is a very very very very very very very very very very very very very very very very very long cmd \
+0094-This is a very very very very very very very very very very very very very very very very very long cmd \
+0095-This is a very very very very very very very very very very very very very very very very very long cmd \
+0096-This is a very very very very very very very very very very very very very very very very very long cmd \
+0097-This is a very very very very very very very very very very very very very very very very very long cmd \
+0098-This is a very very very very very very very very very very very very very very very very very long cmd \
+0099-This is a very very very very very very very very very very very very very very very very very long cmd
+
+# Use line continuation for variables
+set \
+$vartest \
+\"special title\"
+
+# Use line continuation for commands
+for_window \
+[title="$vartest"] \
+border \
+none
+
+EOT
+
+is(launch_get_border($config), 'none', 'no border');
+
+#####################################################################
+# test the line continuation within a string
+#####################################################################
+
+$config = <<'EOT';
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+set \
+$vartest \
+\"special \
+title\"
+for_window [title="$vartest"] border none
+EOT
+
+is(launch_get_border($config), 'none', 'no border');
+
+
+done_testing;
diff --git a/testcases/t/523-move-position-center.t b/testcases/t/523-move-position-center.t
new file mode 100644 (file)
index 0000000..6b58424
--- /dev/null
@@ -0,0 +1,63 @@
+#!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)
+#
+# Verifies that 'move position center' moves floating cons to the center of
+# the appropriate output.
+# Ticket: #1211
+# Bug still in: 4.9.1-108-g037cb31
+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
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+
+workspace left output fake-0
+workspace right output fake-1
+EOT
+
+my $pid = launch_with_config($config);
+
+#####################################################################
+# Verify that 'move position center' on a floating window does not
+# move it to another output.
+#####################################################################
+
+cmd 'workspace left';
+
+# Sync in case focus switched outputs due to the workspace change.
+sync_with_i3;
+
+my $floating = open_floating_window;
+
+# Center the window on the left workspace
+cmd 'move position center';
+sync_with_i3;
+
+is(scalar @{get_ws('left')->{floating_nodes}}, 1, 'one floating node on left ws');
+is(scalar @{get_ws('right')->{floating_nodes}}, 0, 'no floating nodes on right ws');
+
+# Center the window on the right workspace
+cmd 'move workspace right; workspace right; move position center';
+sync_with_i3;
+
+is(scalar @{get_ws('left')->{floating_nodes}}, 0, 'no floating nodes on left ws');
+is(scalar @{get_ws('right')->{floating_nodes}}, 1, 'one floating node on right ws');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/524-move.t b/testcases/t/524-move.t
new file mode 100644 (file)
index 0000000..473bf23
--- /dev/null
@@ -0,0 +1,99 @@
+#!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 the behaviour of 'move <direction>' when moving containers across
+# outputs on workspaces that have non-default layouts.
+# Ticket: #1603
+# Bug still in: 4.10.1-40-g0ad097e
+use List::Util qw(first);
+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
+
+fake-outputs 1024x768+0+0,1024x768+1024+0,1024x768+1024+768,1024x768+0+768
+
+workspace left-top output fake-0
+workspace right-top output fake-1
+workspace right-bottom output fake-2
+workspace left-bottom output fake-3
+
+workspace_layout stacked
+EOT
+
+my $pid = launch_with_config($config);
+
+#####################################################################
+# Create two windows in the upper left workspace and move them
+# clockwise around the workspaces until the end up where they began.
+#####################################################################
+
+cmd 'workspace left-top';
+
+my $first = open_window(wm_class => 'first');
+my $second = open_window(wm_class => 'second');
+
+is_num_children('left-top', 1, 'one child on left-top');
+is_num_children('right-top', 0, 'no children on right-top');
+
+# Move the second window into its own stacked container.
+cmd 'move right';
+is_num_children('left-top', 2, 'two children on left-top');
+
+# Move the second window onto the upper right workspace.
+cmd 'move right';
+is_num_children('left-top', 1, 'one child on left-top');
+is_num_children('right-top', 1, 'one child on right-top');
+
+# Move the first window onto the upper right workspace.
+cmd '[class="first"] move right';
+is_num_children('left-top', 0, 'no children on left-top');
+is_num_children('right-top', 2, 'two children on right-top');
+
+# Move the second window onto the lower right workspace.
+cmd '[class="second"] move down, move down';
+is_num_children('right-top', 1, 'one child on right-top');
+is_num_children('right-bottom', 1, 'two children on right-bottom');
+
+# Move the first window onto the lower right workspace.
+cmd '[class="first"] move down';
+is_num_children('right-top', 0, 'no children on right-top');
+is_num_children('right-bottom', 2, 'two children on right-bottom');
+
+# Move the second windo onto the lower left workspace.
+cmd '[class="second"] move left, move left';
+is_num_children('right-bottom', 1, 'one child on right-bottom');
+is_num_children('left-bottom', 1, 'one on left-bottom');
+
+# Move the first window onto the lower left workspace.
+cmd '[class="first"] move left';
+is_num_children('right-bottom', 0, 'no children on right-bottom');
+is_num_children('left-bottom', 2, 'two children on left-bottom');
+
+# Move the second window onto the upper left workspace.
+cmd '[class="second"] move up, move up';
+is_num_children('left-bottom', 1, 'one child on left-bottom');
+is_num_children('left-top', 1, 'one child on left-top');
+
+# Move the first window onto the upper left workspace.
+cmd '[class="first"] move up';
+is_num_children('left-bottom', 0, 'no children on left-bottom');
+is_num_children('left-top', 2, 'two children on left-top');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/525-i3bar-mouse-bindings.t b/testcases/t/525-i3bar-mouse-bindings.t
new file mode 100644 (file)
index 0000000..eabcad7
--- /dev/null
@@ -0,0 +1,104 @@
+#!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)
+#
+# Ensures that mouse bindings on the i3bar work correctly.
+# Ticket: #1695
+use i3test i3_autostart => 0;
+
+my ($cv, $timer);
+sub reset_test {
+    $cv = AE::cv;
+    $timer = AE::timer(1, 0, sub { $cv->send(0); });
+}
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+focus_follows_mouse no
+
+bar {
+    font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+    position top
+
+    bindsym button1 focus left
+    bindsym button2 focus right
+    bindsym button3 focus left
+    bindsym button4 focus right
+    bindsym button5 focus left
+}
+EOT
+
+SKIP: {
+    qx(command -v xdotool 2> /dev/null);
+    skip 'xdotool is required for this test', 1 if $?;
+
+    my $pid = launch_with_config($config);
+    my $i3 = i3(get_socket_path());
+    $i3->connect()->recv;
+    my $ws = fresh_workspace;
+
+    reset_test;
+    $i3->subscribe({
+        window => sub {
+            my ($event) = @_;
+            if ($event->{change} eq 'focus') {
+                $cv->send($event->{container});
+            }
+        },
+    })->recv;
+
+    my $left = open_window;
+    my $right = open_window;
+    sync_with_i3;
+    my $con = $cv->recv;
+    is($con->{window}, $right->{id}, 'focus is initially on the right container');
+    reset_test;
+
+    qx(xdotool mousemove 3 3 click 1);
+    sync_with_i3;
+    $con = $cv->recv;
+    is($con->{window}, $left->{id}, 'button 1 moves focus left');
+    reset_test;
+
+    qx(xdotool mousemove 3 3 click 2);
+    sync_with_i3;
+    $con = $cv->recv;
+    is($con->{window}, $right->{id}, 'button 2 moves focus right');
+    reset_test;
+
+    qx(xdotool mousemove 3 3 click 3);
+    sync_with_i3;
+    $con = $cv->recv;
+    is($con->{window}, $left->{id}, 'button 3 moves focus left');
+    reset_test;
+
+    qx(xdotool mousemove 3 3 click 4);
+    sync_with_i3;
+    $con = $cv->recv;
+    is($con->{window}, $right->{id}, 'button 4 moves focus right');
+    reset_test;
+
+    qx(xdotool mousemove 3 3 click 5);
+    sync_with_i3;
+    $con = $cv->recv;
+    is($con->{window}, $left->{id}, 'button 5 moves focus left');
+    reset_test;
+
+    exit_gracefully($pid);
+
+}
+
+done_testing;
diff --git a/tests/queue.h b/tests/queue.h
deleted file mode 100644 (file)
index 0b3a9c0..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-/*     $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $        */
-/*     $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $       */
-
-/*
- * Copyright (c) 1991, 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *     @(#)queue.h     8.5 (Berkeley) 8/20/94
- */
-
-#pragma once
-
-/*
- * This file defines five types of data structures: singly-linked lists,
- * lists, simple queues, tail queues, and circular queues.
- *
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction.  Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may only be traversed in the forward direction.
- *
- * A simple queue is headed by a pair of pointers, one the head of the
- * list and the other to the tail of the list. The elements are singly
- * linked to save space, so elements can only be removed from the
- * head of the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the
- * list. A simple queue may only be traversed in the forward direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * A circle queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the list.
- * A circle queue may be traversed in either direction, but has a more
- * complex end of list detection.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- */
-
-#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
-#define _Q_INVALIDATE(a) (a) = ((void *)-1)
-#else
-#define _Q_INVALIDATE(a)
-#endif
-
-/*
- * Singly-linked List definitions.
- */
-#define SLIST_HEAD(name, type)                      \
-    struct name {                                   \
-        struct type *slh_first; /* first element */ \
-    }
-
-#define SLIST_HEAD_INITIALIZER(head) \
-    { NULL }
-
-#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_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))
-
-/*
- * 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)
-
-/*
- * List definitions.
- */
-#define LIST_HEAD(name, type)                      \
-    struct name {                                  \
-        struct type *lh_first; /* first element */ \
-    }
-
-#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 */ \
-    }
-
-/*
- * 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_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)
-
-/*
- * 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_INITIALIZER(head) \
-    { NULL, &(head).sqh_first }
-
-#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_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)
-
-/*
- * 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_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 */ \
-    }
-
-/*
- * 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))
-/* 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_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))
-
-/*
- * 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)
-
-/*
- * 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_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 */ \
-    }
-
-/*
- * 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))
-
-/*
- * 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)
diff --git a/tests/swap.c b/tests/swap.c
deleted file mode 100644 (file)
index abc3b3b..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * vim:ts=4:sw=4:expandtab
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "queue.h"
-
-struct obj {
-    int abc;
-    TAILQ_ENTRY(obj) entry;
-};
-
-TAILQ_HEAD(objhead, obj) head;
-
-void dump() {
-    struct obj *e;
-    printf("dump:\n");
-    e = TAILQ_FIRST(&head);
-    printf("first: %d\n", e->abc);
-    e = TAILQ_LAST(&head, objhead);
-    printf("last: %d\n", e->abc);
-    TAILQ_FOREACH(e, &head, entry) {
-        printf("  %d\n", e->abc);
-    }
-    printf("again, but reverse:\n");
-    TAILQ_FOREACH_REVERSE(e, &head, objhead, entry) {
-        printf("  %d\n", e->abc);
-    }
-    printf("done\n\n");
-}
-
-#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)
-
-void _TAILQ_SWAP(struct obj *first, struct obj *second, struct objhead *head) {
-    struct obj **tqe_prev = first->entry.tqe_prev;
-    *tqe_prev = second;
-
-    second->entry.tqe_prev = first->entry.tqe_prev;
-
-    first->entry.tqe_prev = &(second->entry.tqe_next);
-
-    first->entry.tqe_next = second->entry.tqe_next;
-
-    if (second->entry.tqe_next) {
-        struct obj *tqe_next = second->entry.tqe_next;
-        tqe_next->entry.tqe_prev = &(first->entry.tqe_next);
-    }
-
-    second->entry.tqe_next = first;
-
-    if (head->tqh_last == &(second->entry.tqe_next))
-        head->tqh_last = &(first->entry.tqe_next);
-}
-
-int main() {
-    printf("hello\n");
-
-    TAILQ_INIT(&head);
-
-    struct obj first;
-    first.abc = 123;
-
-    struct obj second;
-    second.abc = 456;
-
-    struct obj third;
-    third.abc = 789;
-
-    struct obj fourth;
-    fourth.abc = 999;
-
-    struct obj fifth;
-    fifth.abc = 5555;
-
-    /*
-     * ************************************************
-     */
-    printf("swapping first two elements:\n");
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-
-    dump();
-
-    TAILQ_SWAP(&first, &second, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("swapping last two elements:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&first, &second, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list 2:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list, swap, then insert:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    TAILQ_INSERT_AFTER(&head, &third, &fifth, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list, swap, then append:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    TAILQ_INSERT_TAIL(&head, &fifth, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list, swap, then remove:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    TAILQ_REMOVE(&head, &second, entry);
-
-    dump();
-}
diff --git a/tests/test_table.c b/tests/test_table.c
deleted file mode 100644 (file)
index 554be3e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <stdio.h>
-#include "table.h"
-
-void print_table() {
-    int r, c;
-    printf("printing table...\n");
-    for (c = 0; c < table_dims.x; c++)
-        for (r = 0; r < table_dims.y; r++)
-            printf("table[%d][%d] = %p\n", c, r, table[c][r]);
-    printf("done\n");
-}
-
-int main() {
-    printf("table.c tests\n");
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    init_table();
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    print_table();
-
-    printf("expand_table_cols()\n");
-    expand_table_cols();
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    print_table();
-
-    printf("expand_table_rows()\n");
-    expand_table_rows();
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    print_table();
-}