i3-command-parser.stamp
i3-config-parser.stamp
.clang_complete
+LAST_VERSION
- 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:
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
* 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.
┌──────────────┬────────┬────────┬────────────────────────────────────────┐
│ 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.
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)
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
## 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
#
# 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)
# 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)
-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
docs/two_columns.png
docs/two_terminals.png
docs/modes.png
-docs/stacklimit.png
docs/ipc.html
docs/multi-monitor.html
docs/wsbar.html
</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
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
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.
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.
== _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
+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
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).
# 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)
----------------------------------------------------------------
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
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?
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:*
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
* 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
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)
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
*Syntax*:
------------------------------
font <X core font description>
-font pango:[family list] [style options] [size]
+font pango:<family list> [<style options>] <size>
------------------------------
*Examples*:
*Syntax*:
----------------------------------
-bindsym [--release] [Modifiers+]keysym command
-bindcode [--release] [Modifiers+]keycode command
+bindsym [--release] [<Modifiers>+]<keysym> command
+bindcode [--release] [<Modifiers>+]<keycode> command
----------------------------------
*Examples*:
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*:
--------------------------------
*Syntax*:
--------------------------------
-floating_modifier <Modifiers>
+floating_modifier <Modifier>
--------------------------------
*Example*:
behavior.
*Syntax*:
-----------------------------------------------
-default_orientation <horizontal|vertical|auto>
-----------------------------------------------
+--------------------------------------------
+default_orientation horizontal|vertical|auto
+--------------------------------------------
*Example*:
----------------------------
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*:
---------------------
=== 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
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*:
----------------------
=== 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*:
------------------------------------------------
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
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
variables can be handy.
*Syntax*:
---------------
-set $name value
---------------
+-------------------
+set $<name> <value>
+-------------------
*Example*:
------------------------
*Syntax*:
------------------------------------------------------------
-assign <criteria> [→] workspace
+assign <criteria> [→] [workspace] <workspace>
------------------------------------------------------------
*Examples*:
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*:
--------------------------------
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
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:
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.
links in your browser window).
*Syntax*:
-----------------------------
-focus_follows_mouse <yes|no>
-----------------------------
+--------------------------
+focus_follows_mouse yes|no
+--------------------------
*Example*:
----------------------
behavior described above.
*Syntax*:
----------------------------
-mouse_warping <output|none>
----------------------------
+-------------------------
+mouse_warping output|none
+-------------------------
*Example*:
------------------
3. Leave fullscreen mode.
*Syntax*:
--------------------------------------------------
-popup_during_fullscreen <smart|ignore|leave_fullscreen>
--------------------------------------------------
+-----------------------------------------------------
+popup_during_fullscreen smart|ignore|leave_fullscreen
+-----------------------------------------------------
*Example*:
------------------------------
will always wrap.
*Syntax*:
------------------------------
-force_focus_wrapping <yes|no>
------------------------------
+---------------------------
+force_focus_wrapping yes|no
+---------------------------
*Example*:
------------------------
+--force-xinerama+ commandline parameter, a configuration option is provided:
*Syntax*:
------------------------
-force_xinerama <yes|no>
------------------------
+---------------------
+force_xinerama yes|no
+---------------------
*Example*:
------------------
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*:
---------------------------------
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
have to have correct quoting etc.
*Syntax*:
-----------------------
-i3bar_command command
-----------------------
+-----------------------
+i3bar_command <command>
+-----------------------
*Example*:
-------------------------------------------------
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
}
-------------------------------------------------
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*:
----------------
=== 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
The default is bottom.
*Syntax*:
----------------------
-position <top|bottom>
----------------------
+-------------------
+position top|bottom
+-------------------
*Example*:
---------------------
you can turn off the functionality entirely.
*Syntax*:
--------------------------
-tray_output <none|primary|output>
--------------------------
+---------------------------------
+tray_output none|primary|<output>
+---------------------------------
*Example*:
-------------------------
}
# show tray icons on the primary monitor
-tray_output primary
+bar {
+ tray_output primary
+}
# show tray icons on the big monitor
bar {
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>>.
The default is to show workspace buttons.
*Syntax*:
---------------------------
-workspace_buttons <yes|no>
---------------------------
+------------------------
+workspace_buttons yes|no
+------------------------
*Example*:
------------------------
The default is to display the full name within the workspace button.
*Syntax*:
-----------------------------------
-strip_workspace_numbers <yes|no>
-----------------------------------
+------------------------------
+strip_workspace_numbers yes|no
+------------------------------
*Example*:
----------------------------
The default is to show the mode indicator.
*Syntax*:
--------------------------------
-binding_mode_indicator <yes|no>
--------------------------------
+-----------------------------
+binding_mode_indicator yes|no
+-----------------------------
*Example*:
-----------------------------
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*:
----------------------------------------
statusline <color>
separator <color>
- colorclass <border> <background> <text>
+ <colorclass> <border> <background> <text>
}
----------------------------------------
active_workspace #333333 #5f676a #ffffff
inactive_workspace #333333 #222222 #888888
urgent_workspace #2f343a #900000 #ffffff
+ binding_mode #2f343a #900000 #ffffff
}
}
--------------------------------------
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:
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::
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]]
strings if they appear in your command.
*Syntax*:
-------------------------------
-exec [--no-startup-id] command
-------------------------------
+--------------------------------
+exec [--no-startup-id] <command>
+--------------------------------
*Example*:
------------------------------
to splith or vice-versa.
*Syntax*:
----------------------------
-split <vertical|horizontal>
----------------------------
+-------------------------
+split vertical|horizontal
+-------------------------
*Example*:
------------------------------
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*:
--------------
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.
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
# 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
# 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
*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*:
+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*:
--------------------------------------------------------
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
+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
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)*:
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]]
*Syntax*:
------------------------------
shmlog <size_in_bytes>
-shmlog <on|off|toggle>
+shmlog on|off|toggle
------------------------------
*Examples*:
likely useful in combination with the above-described <<shmlog>> command.
*Syntax*:
-------------------------
-debuglog <on|off|toggle>
-------------------------
+----------------------
+debuglog on|off|toggle
+----------------------
*Examples*:
------------------------
# 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.
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"
* 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.
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.
*
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) {
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 */
}
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";
}
}
- /* 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) {
#!/usr/bin/env perl
# vim:ts=4:sw=4:expandtab
#
-# © 2012-2014 Michael Stapelberg
+# © 2012 Michael Stapelberg
#
# No dependencies except for perl ≥ v5.10
'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 {
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"
* 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.
*
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"
* 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.
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");
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,
# 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;
}
restart
reload
exit
- stack-limit
);
my ($statement, $key, $command) = ($line =~ /([a-zA-Z_-]+)\s+([^\s]+)\s+(.*)/);
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"
* 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.
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)
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"
* 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.
#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"
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
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,
#!/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
# 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
# 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
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"
* 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
*
* 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
* 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).
*
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;
char *fontname;
i3String *separator_symbol;
char *tray_output;
+ int tray_padding;
int num_outputs;
char **outputs;
* 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
*
* 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
*
* 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
*
* 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.
* 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
* 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
* 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
*
* 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
*
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;
* 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
*
* 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).
*
#include "common.h"
static char *cur_key;
+static bool parsing_bindings;
/*
* Parse a key.
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;
}
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
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;
}
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);
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,
};
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 */
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
}
* 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
*
* 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>
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':
* 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
*
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);
* 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
*
* 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.
* 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
*
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 */
* 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
*
/* 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;
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;
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);
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);
}
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;
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;
* 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;
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,
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,
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);
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);
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);
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,
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
* 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
* 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).
*
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)
* 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.
*
*
*/
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)
* 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.
*
* 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)
*
* 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)
*
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);
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]'
*/
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'
*
*/
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'.
*
*/
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>'
*
* 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)
*
* 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,
*/
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.
*
*/
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
*/
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
* 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
* 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;
* 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! */
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,
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
* 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)
*
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);
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);
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);
* 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
*
* 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
*
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). */
/** 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
* 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,
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,
*
* 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
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 */
/* user-definable mark to jump to this container later */
char *mark;
+ /* cached to decide whether a redraw is needed */
+ bool mark_changed;
double percent;
* 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.
* 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.
* 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.
*
* 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.
* 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.
*
*/
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
* 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,
* …).
* 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.
*
#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. */
* 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).
* 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).
*
* 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
*
* 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, …
*/
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)
*/
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.
*
*/
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
*/
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)
*
* 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.
* 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.
*
* 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
*
* 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).
*
* 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
* 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.
*
* 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.
*
* 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
* 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).
*
* 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.
* 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.
*
* 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).
* 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)
*
* 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).
* 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).
* 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
* 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.
*
* 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).
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().
*
* 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).
*
*/
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).
*
* 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.
* 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.
* 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
*
*/
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);
/**
* 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
* 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.
*
* 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
* 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
*
* 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"
* 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>
* 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>
}
}
+/*
+ * 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,
* 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>
--- /dev/null
+/*
+ * 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;
+}
* 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>
* 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>
* 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"
* 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>
* 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>
* 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>
--- /dev/null
+/*
+ * 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;
+}
* 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>
* 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>
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;
* 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 {
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.
*
* 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>
* 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>
# 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.
'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:
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
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:
# 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'
->
-> MOVE_WORKSPACE
'output'
-> MOVE_TO_OUTPUT
+ 'mark'
+ -> MOVE_TO_MARK
'scratchpad'
-> call cmd_move_scratchpad()
direction = 'left', 'right', 'up', 'down'
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'
->
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
'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'
# 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.
'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
'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
# 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
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
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()
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
state BINDING:
release = '--release'
->
+ border = '--border'
+ ->
whole_window = '--whole-window'
->
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
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
state MODE_BINDING:
release = '--release'
->
+ border = '--border'
+ ->
+ whole_window = '--whole-window'
+ ->
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
->
'+'
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)
end
->
'{'
- -> BAR
+ -> call cfg_bar_start(); BAR
state BAR:
end ->
'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
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
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
'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
#!/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
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
################################################################################
# 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
# 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
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."
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+"
* 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).
*
* 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.
*/
*
*/
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
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;
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;
* 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.
*
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 */
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))
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;
/* 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;
}
}
/* 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);
}
*/
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);
* 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)
*
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;
return;
}
+ if (strcmp(ctype, "workspace") == 0) {
+ current_match->workspace = regex_new(cvalue);
+ return;
+ }
+
ELOG("Unknown criterion: %s\n", ctype);
}
}
/*
- * 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) {
}
/*
- * 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);
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;
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'
*
}
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'.
*
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;
}
* 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
* 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,
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);
/* 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);
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
*
}
}
+/*
+ * 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).
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
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) {
/* 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) */
/* 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. */
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
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;
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);
}
/*
* 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)
* 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).
}
}
-/*
- * 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
*
*/
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;
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);
}
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);
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) {
* 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)
*
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;
return;
}
+ if (strcmp(ctype, "workspace") == 0) {
+ current_match->workspace = regex_new(cvalue);
+ return;
+ }
+
ELOG("Unknown criterion: %s\n", ctype);
}
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);
}
/*******************************************************************************
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) {
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
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;
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)
******************************************************************************/
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) {
APPLY_COLORS(active_workspace);
APPLY_COLORS(inactive_workspace);
APPLY_COLORS(urgent_workspace);
+ APPLY_COLORS(binding_mode);
#undef APPLY_COLORS
}
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);
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 */
* 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.
*
*/
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));
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;
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");
* 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.
* 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;
}
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;
* 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.
*
/* 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);
}
* 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.
* 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.
*
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);
}
}
}
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);
}
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;
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;
* 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,
* …).
/* 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;
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 */
x_push_changes(croot);
return;
}
-
- return;
}
/*
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();
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);
}
}
-#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
{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))
/*
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) {
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}/$<
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
* 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).
*
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);
} while (0)
YSTR_IF_SET(tray_output);
+
+ ystr("tray_padding");
+ y(integer, config->tray_padding);
+
YSTR_IF_SET(socket_path);
ystr("mode");
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)
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);
y(integer, PATCH_VERSION);
ystr("human_readable");
- ystr(I3_VERSION);
+ ystr(i3_version);
+
+ ystr("loaded_config_file_name");
+ ystr(current_configpath);
y(map_close);
* 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
*
* 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.
* 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.
*
* 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
*
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;
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;
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))
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
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) {
}
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
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();
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);
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)));
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) */
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 */
* 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).
*
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) */
/* 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;
/* 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();
* 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;
}
/*
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);
DUPLICATE_REGEX(class);
DUPLICATE_REGEX(instance);
DUPLICATE_REGEX(window_role);
+ DUPLICATE_REGEX(workspace);
}
/*
}
}
+ 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 */
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) &&
* 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.
*
? 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);
* 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.
*
* 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
if (output->active)
return output;
- die("No usable outputs available.\n");
+ return NULL;
}
/*
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,
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;
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);
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;
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
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");
+ }
+ }
}
/*
* 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).
*
* 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.
* 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.
*
}
}
- 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) {
* 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).
* 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.
*
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
* 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).
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");
}
* 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
*
*/
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
* 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.
*
* 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).
}
}
-/*
- * 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().
*
--- /dev/null
+/*
+ * 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;
* 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).
*
* 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);
}
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).
*
* 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.
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.
*/
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;
}
*/
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;
}
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;
}
* 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.
bool mapped;
bool unmap_now;
bool child_mapped;
+ bool is_hidden;
/** The con for which this state is. */
Con *con;
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.
*
(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;
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) {
//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
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, …).
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). */
* 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
*
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.
*
* 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.
*
* 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
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,
#!/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;
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 ();
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;
pod2usage(-verbose => 2, -exitcode => 0) if $help;
+chdir $dirname or die "Could not chdir into $dirname";
+
# Check for missing executables
my @binaries = qw(
../i3
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 $?;
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);
#
=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 don’t want to start num_cores * 2
+Number of Xephyr instances to start (if you don't want to start num_cores * 2
instances for some reason).
# Run all tests on a single Xephyr instance
./complete-run.pl -p 1
+
+=back
=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";
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
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);
##############################################################################
# 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;
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);
}
# Tests resizing tiling containers
use i3test;
+my ($left, $right);
my $tmp = fresh_workspace;
cmd 'split v';
$tmp = fresh_workspace;
-my $left = open_window;
-my $right = open_window;
+$left = open_window;
+$right = open_window;
cmd 'split v';
$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) = @_;
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
# 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
# *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
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;
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
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');
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";
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
tray_output LVDS1
tray_output HDMI2
+ tray_padding 0
position top
mode dock
font Terminus
active_workspace #333333 #222222 #888888
inactive_workspace #333333 #222222 #888888
urgent_workspace #2f343a #900000 #ffffff
+ binding_mode #abc123 #123abc #ababab
}
}
EOT
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},
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);
# 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");
################################################################################
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');
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
$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
################################################################################
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';
$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" {
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
#
# 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
##############################################################
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;
$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');
# 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;
$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');
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);
# 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');
# 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');
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;
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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Test 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Ensure that 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# Tests 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;
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+# (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+# (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+# (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+# (unless you are already familiar with Perl)
+#
+# 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;
+++ /dev/null
-/* $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)
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-#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();
-}