[Adopted] Use WM_SIZE_HINTS when available to set the geometry of floating windows
- 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:
-# i3status/i3lock bugreports/feature requests
+# Contributing
-Note that i3status and i3lock related bugreports and feature requests should be
-filed in the corresponding repositories, i.e. https://github.com/i3/i3status
-and https://github.com/i3/i3lock
+## i3status/i3lock bug reports and feature requests
-# i3 bugreports/feature requests
+Note that bug reports and feature requests for related projects should be filed in the corresponding repositories for [i3status](https://github.com/i3/i3status) and [i3lock](https://github.com/i3/i3lock).
-1. Read http://i3wm.org/docs/debugging.html
+## i3 bug reports and feature requests
+
+1. Read the [debugging instructions](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
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
+## Pull requests
* Before sending a pull request for new features, please check with us that the
feature is something we want to see in i3 by opening an issue which has
- â\80\9cfeature requestâ\80\9d or â\80\9cenhancement” in its title.
+ â\80\9dfeature requestâ\80\9d or â\80\9denhancement” in its title.
* 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
+* Run the [testsuite](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.
+++ /dev/null
-
- ┌──────────────────────────────┐
- │ Release notes for i3 v4.10.2 │
- └──────────────────────────────┘
-
-This is i3 v4.10.2. This version is considered stable. All users of i3 are
-strongly encouraged to upgrade.
-
- ┌────────────────────────────┐
- │ Bugfixes │
- └────────────────────────────┘
-
- • Cope with non-null-terminated x class properties.
- • Get workspace name when renaming current workspace (fixes crash).
- • Use a reasonable default sep_block_width if a separator_symbol is given.
- • Remove windows from the save set when unmapping.
-
- ┌────────────────────────────┐
- │ Thanks! │
- └────────────────────────────┘
-
-Thanks for testing, bugfixes, discussions and everything I forgot go out to:
-
- Ingo Bürk, Michael Hofmann,
-
--- Michael Stapelberg, 2015-04-16
--- /dev/null
+
+ ┌──────────────────────────────┐
+ │ Release notes for i3 v4.10.3 │
+ └──────────────────────────────┘
+
+This is i3 v4.10.3. This version is considered stable. All users of i3 are
+strongly encouraged to upgrade.
+
+ ┌────────────────────────────┐
+ │ Bugfixes │
+ └────────────────────────────┘
+
+ • serialize con_id with %p in run_binding() (For FreeBSD)
+ • ignore InputHint when not in WM_HINTS (fixes e.g. mupdf focus)
+ • disable physically disconnect RandR outputs
+ • initialize workspace rect to the output's upon creation
+ • userguide: quoted strings need to be used, escaping isn’t possible
+ • mkdirp: do not throw an error if directory exists (fixes layout loss for
+ in-place restarts)
+ • i3bar: fix freeing static strings
+
+ ┌────────────────────────────┐
+ │ Thanks! │
+ └────────────────────────────┘
+
+Thanks for testing, bugfixes, discussions and everything I forgot go out to:
+
+ Tony Crisci, Deiz, Theo Buehler, shdown
+
+-- Michael Stapelberg, 2015-07-30
-i3-wm (4.10.3-1) experimental; urgency=medium
+i3-wm (4.10.4-1) unstable; urgency=medium
* NOT YET RELEASED.
- -- Michael Stapelberg <stapelberg@debian.org> Thu, 16 Apr 2015 09:08:30 +0200
+ -- Michael Stapelberg <stapelberg@debian.org> Thu, 30 Jul 2015 22:30:45 +0200
+
+i3-wm (4.10.3-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org> Thu, 30 Jul 2015 21:51:27 +0200
+
+i3-wm (4.10.2-2) unstable; urgency=medium
+
+ * New upstream release.
+ * experimental to unstable because i3-wm 4.10.2-1 was only in experimental
+ due to the freeze.
+
+ -- Michael Stapelberg <stapelberg@debian.org> Fri, 01 May 2015 20:21:18 +0200
i3-wm (4.10.2-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
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 option determines in which mode new containers on workspace level will
start.
-///////////////////////////////
-See also <<stack-limit>>.
-//////////////////////////////
*Syntax*:
---------------------------------------------
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_window normal|none|pixel
new_window normal|pixel <px>
-new_float normal|1pixel|none|pixel
+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
=== 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.
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
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
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*:
------------------------
-------------------------------------------------
bar {
status_command i3status --config ~/.i3status.conf
+
+ # For dash(1) users who want signal handling to work:
+ status_command exec ~/.bin/my_status_command
}
-------------------------------------------------
=== 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
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>>.
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*:
----------------------------------------
active_workspace #333333 #5f676a #ffffff
inactive_workspace #333333 #222222 #888888
urgent_workspace #2f343a #900000 #ffffff
+ binding_mode #2f343a #900000 #ffffff
}
}
--------------------------------------
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]]
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"
+
+# print all window titles bold
+for_window [class=".*"] title_format "<b>%title</b>"
-[[stack-limit]]
+# print window titles of firefox windows red
+for_window [class="(?i)firefox"] title_format "<span foreground='red'>%title</span>"
+-------------------------------------------------------------------------------------
-///////////////////////////////////////////////////////////////////////////////
-TODO: not yet implemented
-=== Changing the stack-limit of a container
+=== Changing border style
-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.
+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.
-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.
+There is also +border toggle+ which will toggle the different border styles.
*Syntax*:
------------------------------
-stack-limit cols|rows <value>
------------------------------
-
-*Examples*:
--------------------
-# I always want to have two window titles in one line
-stack-limit cols 2
+-----------------------------------------------
+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]]
goto free_resources;
}
- DLOG("Determined coordinates of window with input focus at x = %i / y = %i", coordinates->dst_x, coordinates->dst_y);
+ 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;
# 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+(.*)/);
if (crtc == NULL)
goto free_resources;
- DLOG("Found primary output on position x = %i / y = %i / w = %i / h = %i",
+ 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");
# 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
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;
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;
struct status_block *err_block = scalloc(sizeof(struct status_block));
err_block->full_text = i3string_from_utf8("Error: ");
- err_block->name = "error";
- err_block->color = "red";
+ err_block->name = sstrdup("error");
+ err_block->color = sstrdup("red");
err_block->no_separator = true;
struct status_block *message_block = scalloc(sizeof(struct status_block));
message_block->full_text = i3string_from_utf8(message);
- message_block->name = "error_message";
- message_block->color = "red";
+ message_block->name = sstrdup("error_message");
+ message_block->color = sstrdup("red");
message_block->no_separator = true;
TAILQ_INSERT_HEAD(&statusline_head, err_block, blocks);
#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
}
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);
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;
/* Additional offset between the tray and the statusline, if the tray is not empty */
static const int tray_loff_px = 2;
-/* Padding around the tray icons */
-static const int tray_spacing_px = 2;
-
/* Vertical offset between the bar and a separator */
static const int sep_voff_px = 4;
TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
if (!trayclient->mapped)
continue;
- tray_width += icon_size + logical_px(tray_spacing_px);
+ 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);
}
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 * (icon_size + logical_px(tray_spacing_px))));
- uint32_t x = output->rect.w - (clients * (icon_size + logical_px(tray_spacing_px)));
+ 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 - icon_size - logical_px(tray_spacing_px),
- logical_px(tray_spacing_px));
+ 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
continue;
xcb_rectangle_t rect;
- rect.x = output->rect.w - (clients * (icon_size + logical_px(tray_spacing_px)));
- rect.y = logical_px(tray_spacing_px);
+ 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;
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(tray_spacing_px);
+ icon_size = bar_height - 2 * logical_px(config.tray_padding);
if (config.separator_symbol)
separator_symbol_width = predict_text_width(config.separator_symbol);
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,
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_scratchpad_show(I3_CMD);
+/**
+ * Implementation of 'title_format <format>'
+ *
+ */
+void cmd_title_format(I3_CMD, char *format);
+
/**
* Implementation of 'rename workspace <name> to <name>'
*
* 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
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);
/** 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
struct regex *instance;
struct regex *mark;
struct regex *window_role;
+ struct regex *workspace;
xcb_atom_t window_type;
enum {
U_DONTCHECK = -1,
*/
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
}
}
+/*
+ * 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,
bool mkdirp(const char *path) {
if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
return true;
- if (errno != ENOENT) {
+ if (errno == EEXIST) {
+ struct stat st;
+ /* Check that the named file actually is a directory. */
+ if (stat(path, &st)) {
+ ELOG("stat(%s) failed: %s\n", path, strerror(errno));
+ return false;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ ELOG("mkdir(%s) failed: %s\n", path, strerror(ENOTDIR));
+ return false;
+ }
+ return true;
+ } else if (errno != ENOENT) {
ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
return false;
}
#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.
*
<refentrytitle>{mantitle}</refentrytitle>
<manvolnum>{manvolnum}</manvolnum>
<refmiscinfo class="source">i3</refmiscinfo>
-<refmiscinfo class="version">4.10.2</refmiscinfo>
+<refmiscinfo class="version">4.10.3</refmiscinfo>
<refmiscinfo class="manual">i3 Manual</refmiscinfo>
</refmeta>
<refnamediv>
'rename' -> RENAME
'nop' -> NOP
'scratchpad' -> SCRATCHPAD
+ 'title_format' -> TITLE_FORMAT
'mode' -> MODE
'bar' -> BAR
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
'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'
ctype = 'con_mark' -> CRITERION
ctype = 'title' -> CRITERION
ctype = 'urgent' -> CRITERION
+ ctype = 'workspace' -> CRITERION
']'
-> call cfg_criteria_pop_state()
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.2"
-export PREVIOUS_VERSION="4.10.1"
+export RELEASE_VERSION="4.10.3"
+export PREVIOUS_VERSION="4.10.2"
export RELEASE_BRANCH="master"
if [ ! -e "../i3.github.io" ]
exit 1
fi
+if ! (cd ../i3.github.io && git pull)
+then
+ echo "Could not update ../i3.github.io repository"
+ exit 1
+fi
+
if [ ! -e "RELEASE-NOTES-${RELEASE_VERSION}" ]
then
echo "RELEASE-NOTES-${RELEASE_VERSION} not found."
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
# 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
cp ${TMPDIR}/i3/RELEASE-NOTES-${RELEASE_VERSION} downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt
git add downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt
sed -i "s,<h2>Documentation for i3 v[^<]*</h2>,<h2>Documentation for i3 v${RELEASE_VERSION}</h2>,g" docs/index.html
-sed -i "s,Verify you are using i3 ≥ .*,Verify you are using i3 ≥ ${RELEASE_VERSION},g" docs/debugging.html
sed -i "s,<span style=\"margin-left: 2em; color: #c0c0c0\">[^<]*</span>,<span style=\"margin-left: 2em; color: #c0c0c0\">${RELEASE_VERSION}</span>,g" index.html
sed -i "s,The current stable version is .*$,The current stable version is ${RELEASE_VERSION}.,g" downloads/index.html
sed -i "s,<tbody>,<tbody>\n <tr>\n <td>${RELEASE_VERSION}</td>\n <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.bz2\">i3-${RELEASE_VERSION}.tar.bz2</a></td>\n <td>$(ls -lh ../i3/i3-${RELEASE_VERSION}.tar.bz2 | awk -F " " {'print $5'} | sed 's/K$/ KiB/g')</td>\n <td><a href=\"/downloads/i3-${RELEASE_VERSION}.tar.bz2.asc\">signature</a></td>\n <td>$(date +'%Y-%m-%d')</td>\n <td><a href=\"/downloads/RELEASE-NOTES-${RELEASE_VERSION}.txt\">release notes</a></td>\n </tr>\n,g" downloads/index.html
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}"
echo "Announce on:"
echo " twitter"
echo " google+"
-echo " mailing list"
echo " #i3 topic"
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;
*/
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);
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) {
FREE(con->mark);
con->mark_changed = true;
}
- DLOG("removed all window marks");
+ DLOG("Removed all window marks.\n");
} else {
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 '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;
}
* checks before resetting the urgency.
*/
if (con->urgent && con_is_leaf(con)) {
- con->urgent = false;
+ con_set_urgency(con, false);
con_update_parents_urgency(con);
workspace_update_urgent_flag(con_get_workspace(con));
ipc_send_window_event("urgent", con);
/* Prevent moving if this would violate the fullscreen focus restrictions. */
Con *target_ws = con_get_workspace(target);
if (!con_fullscreen_permits_focusing(target_ws)) {
- LOG("Cannot move out of a fullscreen container");
+ 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;
}
*
*/
void con_set_urgency(Con *con, bool urgent) {
- if (focused == con) {
+ if (urgent && focused == con) {
DLOG("Ignoring urgency flag for current client\n");
- con->window->urgent.tv_sec = 0;
- con->window->urgent.tv_usec = 0;
return;
}
+ const bool old_urgent = con->urgent;
+
if (con->urgency_timer == NULL) {
con->urgent = urgent;
} else
if ((ws = con_get_workspace(con)) != NULL)
workspace_update_urgent_flag(ws);
- if (con->urgent == urgent) {
+ if (con->urgent != old_urgent) {
LOG("Urgency flag changed to %d\n", con->urgent);
ipc_send_window_event("urgent", con);
}
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);
}
return;
}
+ if (strcmp(ctype, "workspace") == 0) {
+ current_match->workspace = regex_new(cvalue);
+ return;
+ }
+
ELOG("Unknown criterion: %s\n", ctype);
}
return;
}
- DLOG("Set new focus_on_window_activation mode = %i", config.focus_on_window_activation);
+ DLOG("Set new focus_on_window_activation mode = %i.\n", config.focus_on_window_activation);
}
CFGFUN(show_marks, const char *value) {
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;
return;
}
- DLOG("new assignment, using above criteria, to ignore focus on manage");
+ 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;
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 */
*/
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;
#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;
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.");
+ ELOG("The pointer is not on any output, cannot move the container here.\n");
return;
}
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;
/* 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;
}
/*
DLOG("Marking con = %p urgent\n", con);
con_set_urgency(con, true);
} else
- DLOG("Ignoring request for con = %p", con);
+ DLOG("Ignoring request for con = %p.\n", con);
}
tree_render();
*
*/
void handle_event(int type, xcb_generic_event_t *event) {
- DLOG("event type %d, xkb_base %d\n", type, xkb_base);
+ if (type != XCB_MOTION_NOTIFY)
+ DLOG("event type %d, xkb_base %d\n", type, xkb_base);
+
if (randr_base > -1 &&
type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
handle_screen_change(event);
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);
ystr("human_readable");
ystr(i3_version);
+ ystr("loaded_config_file_name");
+ ystr(current_configpath);
+
y(map_close);
const unsigned char *payload;
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);
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);
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 */
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) */
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 &&
DUPLICATE_REGEX(class);
DUPLICATE_REGEX(instance);
DUPLICATE_REGEX(window_role);
+ DUPLICATE_REGEX(workspace);
}
/*
LOG("urgent matches oldest\n");
}
+ 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) ||
}
}
- 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) {
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");
}
*
*/
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
/* remove the urgency hint of the workspace (if set) */
if (con->urgent) {
- con->urgent = false;
+ con_set_urgency(con, false);
con_update_parents_urgency(con);
workspace_update_urgent_flag(con_get_workspace(con));
}
}
window->window_type = new_type;
- LOG("_NET_WM_WINDOW_TYPE changed to %i", window->window_type);
+ LOG("_NET_WM_WINDOW_TYPE changed to %i.\n", window->window_type);
run_assignments(window);
}
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.
static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) {
Con *con = w->data;
+ ev_timer_stop(main_loop, con->urgency_timer);
+ FREE(con->urgency_timer);
+
if (con->urgent) {
DLOG("Resetting urgency flag of con %p by timer\n", con);
- con->urgent = false;
+ con_set_urgency(con, false);
con_update_parents_urgency(con);
workspace_update_urgent_flag(con_get_workspace(con));
ipc_send_window_event("urgent", con);
tree_render();
}
-
- ev_timer_stop(main_loop, con->urgency_timer);
- FREE(con->urgency_timer);
}
static void _workspace_show(Con *workspace) {
*/
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;
}
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.
*
I3STRING_FREE(mark);
}
- draw_text(win->name,
+ 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 + 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
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 $tmp_target = fresh_workspace;
cmd 'workspace ' . $tmp_source;
sync_with_i3;
- my $w1 = open_window;
- my $w2 = open_window;
+ $w1 = open_window;
+ $w2 = open_window;
sync_with_i3;
cmd '[id="' . $w1->id . '"] focus';
sync_with_i3;
# 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
}
+##############################################################
+# 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');
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');
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
use i3test;
use List::Util qw(first);
+my ($con, $first, $second);
+
sub get_marks {
return i3(get_socket_path())->get_marks->recv;
}
# check that only the latter is marked
##############################################################
-my $first = open_window;
-my $second = open_window;
+$first = open_window;
+$second = open_window;
cmd 'mark important';
cmd 'focus left';
# 5: mark a con, toggle the mark, check that the mark is gone
##############################################################
-my $con = open_window;
+$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
##############################################################
-my $con = open_window;
+$con = open_window;
cmd 'mark --toggle important';
is(get_mark_for_window_on_workspace($tmp, $con), 'important', 'container now has the mark');
# with the new mark
##############################################################
-my $con = open_window;
+$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');
# check only the latter has the mark
##############################################################
-my $first = open_window;
-my $second = open_window;
+$first = open_window;
+$second = open_window;
cmd 'mark important';
cmd 'focus left';
# it fails
##############################################################
-my $first = open_window(wm_class => 'iamnotunique');
-my $second = open_window(wm_class => 'iamnotunique');
+$first = open_window(wm_class => 'iamnotunique');
+$second = open_window(wm_class => 'iamnotunique');
my $result = cmd "[instance=iamnotunique] mark important";
$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');
$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');
use i3test i3_autostart => 0;
use List::Util qw(first);
+my ($config, $pid, $first, $second, $ws1, $ws2);
+
sub send_net_active_window {
my ($id) = @_;
# check that the urgent flag is set and focus is not lost.
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
my $ws = fresh_workspace;
-my $first = open_window;
-my $second = open_window;
+$first = open_window;
+$second = open_window;
send_net_active_window($first->id);
sync_with_i3;
# visible, check that the urgent flag is set and focus is not lost.
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
-my $ws1 = fresh_workspace;
-my $first = open_window;
-my $ws2 = fresh_workspace;
-my $second = open_window;
+$ws1 = fresh_workspace;
+$first = open_window;
+$ws2 = fresh_workspace;
+$second = open_window;
send_net_active_window($first->id);
sync_with_i3;
# check that the focus is switched.
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
my $ws = fresh_workspace;
-my $first = open_window;
-my $second = open_window;
+$first = open_window;
+$second = open_window;
send_net_active_window($first->id);
sync_with_i3;
# visible, check that the focus switched.
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
-my $ws1 = fresh_workspace;
-my $first = open_window;
-my $ws2 = fresh_workspace;
-my $second = open_window;
+$ws1 = fresh_workspace;
+$first = open_window;
+$ws2 = fresh_workspace;
+$second = open_window;
send_net_active_window($first->id);
sync_with_i3;
# check that nothing happens.
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
my $ws = fresh_workspace;
-my $first = open_window;
-my $second = open_window;
+$first = open_window;
+$second = open_window;
send_net_active_window($first->id);
sync_with_i3;
# visible, check that nothing happens.
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
-my $ws1 = fresh_workspace;
-my $first = open_window;
-my $ws2 = fresh_workspace;
-my $second = open_window;
+$ws1 = fresh_workspace;
+$first = open_window;
+$ws2 = fresh_workspace;
+$second = open_window;
send_net_active_window($first->id);
sync_with_i3;
# Ticket: #1416
use i3test i3_autostart => 0;
+my ($config, $pid, $ws, $first, $second, $focused);
+
#####################################################################
# 1: open a window and check that it takes focus
#####################################################################
-my $config = <<EOT;
+$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
EOT
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
-my $ws = fresh_workspace;
-my $first = open_window;
-my $focused = get_focused($ws);
-my $second = open_window;
+$ws = fresh_workspace;
+$first = open_window;
+$focused = get_focused($ws);
+$second = open_window;
isnt(get_focused($ws), $focused, 'focus has changed');
# it doesn't take focus
#####################################################################
-my $config = <<EOT;
+$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
-my $pid = launch_with_config($config);
+$pid = launch_with_config($config);
-my $ws = fresh_workspace;
-my $first = open_window;
-my $focused = get_focused($ws);
-my $second = open_window(wm_class => 'notme');
+$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');
--- /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)
+#
+# Ensures the urgency hint is cleared properly in the case where i3 set it (due
+# to focus_on_window_activation=urgent), hence the application not clearing it.
+# Ticket: #1825
+# Bug still in: 4.10.3-253-g03799dd
+use i3test i3_autostart => 0;
+
+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);
+}
+
+my $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
+
+my $pid = launch_with_config($config);
+my $i3 = i3(get_socket_path(0));
+my $ws = fresh_workspace;
+my $first = open_window;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+is($x->input_focus, $second->id, 'second window still focused');
+
+cmd '[urgent=latest] focus';
+sync_with_i3;
+is($x->input_focus, $first->id, 'first window focused');
+
+cmd 'focus right';
+sync_with_i3;
+is($x->input_focus, $second->id, 'second window focused again');
+
+cmd '[urgent=latest] focus';
+sync_with_i3;
+is($x->input_focus, $second->id, 'second window still focused');
+
+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;