]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 29 Mar 2015 15:46:37 +0000 (17:46 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 29 Mar 2015 15:46:37 +0000 (17:46 +0200)
89 files changed:
.travis.yml [new file with mode: 0644]
debian/changelog
debian/control
docs/hacking-howto
docs/i3bar-protocol
docs/layout-saving
docs/userguide
i3-config-wizard/main.c
i3-dmenu-desktop
i3-dump-log/main.c
i3-input/main.c
i3-migrate-config-to-v4
i3-nagbar/main.c
i3-save-tree
i3-sensible-terminal
i3.config
i3.config.keycodes
i3bar/include/child.h
i3bar/include/common.h
i3bar/include/config.h
i3bar/include/ipc.h
i3bar/include/mode.h
i3bar/include/outputs.h
i3bar/include/util.h
i3bar/include/workspaces.h
i3bar/include/xcb.h
i3bar/src/child.c
i3bar/src/config.c
i3bar/src/ipc.c
i3bar/src/main.c
i3bar/src/mode.c
i3bar/src/outputs.c
i3bar/src/workspaces.c
i3bar/src/xcb.c
include/config.h
include/config_directives.h
include/data.h
include/key_press.h
include/libi3.h
include/output.h
include/startup.h
include/workspace.h
libi3/dpi.c
libi3/font.c
libi3/ipc_send_message.c
libi3/safewrappers.c
libi3/string.c
man/i3-sensible-editor.man
man/i3-sensible-pager.man
man/i3-sensible-terminal.man
parser-specs/commands.spec
parser-specs/config.spec
release.sh [new file with mode: 0755]
src/click.c
src/commands.c
src/commands_parser.c
src/con.c
src/config.c
src/config_directives.c
src/config_parser.c
src/floating.c
src/handlers.c
src/ipc.c
src/load_layout.c
src/main.c
src/manage.c
src/output.c
src/render.c
src/restore_layout.c
src/sighandler.c
src/startup.c
src/tree.c
src/util.c
src/window.c
src/workspace.c
src/x.c
src/xcb.c
src/xcursor.c
testcases/complete-run.pl
testcases/lib/SocketActivation.pm
testcases/lib/StatusLine.pm
testcases/lib/i3test.pm
testcases/t/102-dock.t
testcases/t/175-startup-notification.t
testcases/t/187-commands-parser.t
testcases/t/200-urgency-timer.t
testcases/t/201-config-parser.t
testcases/t/232-cmd-workspace-number-selection.t [deleted file]
testcases/t/522-rename-assigned-workspace.t [new file with mode: 0644]

diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..733daea
--- /dev/null
@@ -0,0 +1,47 @@
+language: c
+compiler:
+  - gcc
+  - clang
+before_install:
+  # The travis VMs run on Ubuntu 12.04 which is very old and a huge pain to get
+  # into a state where we can build a recent version of i3 :(.
+  - "echo 'deb http://archive.ubuntu.com/ubuntu/ trusty main universe' | sudo tee /etc/apt/sources.list.d/trusty.list"
+  - "echo 'APT::Default-Release \"precise\";' | sudo tee /etc/apt/apt.conf.d/default-release"
+  - "echo 'deb http://archive.ubuntu.com/ubuntu/ utopic main universe' | sudo tee /etc/apt/sources.list.d/utopic.list"
+
+  - "echo 'Package: libc6' > /tmp/pin"
+  - "echo 'Pin: release n=trusty' >> /tmp/pin"
+  - "echo 'Pin-Priority: 999' >> /tmp/pin"
+  - "echo '' >> /tmp/pin"
+
+  - "echo 'Package: libxkbcommon*' >> /tmp/pin"
+  - "echo 'Pin: release n=trusty' >> /tmp/pin"
+  - "echo 'Pin-Priority: 999' >> /tmp/pin"
+  - "echo '' >> /tmp/pin"
+
+  - "echo 'Package: libyajl*' >> /tmp/pin"
+  - "echo 'Pin: release n=trusty' >> /tmp/pin"
+  - "echo 'Pin-Priority: 999' >> /tmp/pin"
+  - "echo '' >> /tmp/pin"
+
+  - "echo 'Package: libxcb-image*' >> /tmp/pin"
+  - "echo 'Pin: release n=trusty' >> /tmp/pin"
+  - "echo 'Pin-Priority: 999' >> /tmp/pin"
+  - "echo '' >> /tmp/pin"
+
+  - 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 -t utopic clang-format-3.5
+  - clang-format-3.5 --version
+install:
+  - sudo mk-build-deps --install --remove --tool 'apt-get --no-install-recommends' debian/control
+  # Install as many dependencies as possible via apt because cpanm is not very reliable/easy to debug.
+  - sudo apt-get install --no-install-recommends libanyevent-perl libanyevent-i3-perl libextutils-pkgconfig-perl xcb-proto cpanminus xvfb xserver-xephyr xauth libinline-perl libxml-simple-perl libmouse-perl libmousex-nativetraits-perl libextutils-depends-perl perl-modules libtest-deep-perl libtest-exception-perl libxml-parser-perl libtest-simple-perl libtest-fatal-perl libdata-dump-perl libtest-differences-perl libxml-tokeparser-perl libtest-use-ok-perl libipc-run-perl
+  - sudo /bin/sh -c 'cpanm -n -v X11::XCB || true'
+  - sudo /bin/sh -c 'cpanm -n -v AnyEvent::I3 || true'
+script:
+  - CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror" make -j
+  - (cd testcases && xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false))
+  - clang-format-3.5 -i $(find . -name "*.[ch]" | tr '\n' ' ') && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
index 3b5cdb212f12a2f1b77d077f53db3ca480630d61..1dcfc9f9fc6b45a458ded6042da814f30149c967 100644 (file)
@@ -1,8 +1,14 @@
-i3-wm (4.9.1-1) experimental; urgency=medium
+i3-wm (4.9.2-1) experimental; urgency=medium
 
   * NOT YET RELEASED.
 
- -- Michael Stapelberg <stapelberg@debian.org>  Sat, 28 Feb 2015 15:04:25 +0100
+ -- Michael Stapelberg <stapelberg@debian.org>  Sat, 07 Mar 2015 20:31:31 +0100
+
+i3-wm (4.9.1-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Sat, 07 Mar 2015 20:01:46 +0100
 
 i3-wm (4.9-1) experimental; urgency=medium
 
index cc9660f7260bccd8472ef07b81310c13efbc37f0..52fb1538130dae9aa9bbc45868cf68001fb02fa7 100644 (file)
@@ -40,8 +40,7 @@ Package: i3-wm
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
 Provides: x-window-manager
-Suggests: rxvt-unicode | x-terminal-emulator
-Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl
+Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl, rxvt-unicode | x-terminal-emulator
 Description: improved dynamic tiling window manager
  Key features of i3 are good documentation, reasonable defaults (changeable in
  a simple configuration file) and good multi-monitor support. The user
index a591047ede3986f6235d1e9ba301292902c9932a..12d6b14ee1b2da3406d4b78e7960c66288207749 100644 (file)
@@ -1012,7 +1012,7 @@ gets started in any way) and the window(s) which appear.
 
 Imagine for example using dmenu: The user starts dmenu by pressing Mod+d, dmenu
 gets started with PID 3390. The user then decides to launch Firefox, which
-takes a long time. So he enters firefox into dmenu and presses enter. Firefox
+takes a long time. So they enter firefox into dmenu and press enter. Firefox
 gets started with PID 4001. When it finally finishes loading, it creates an X11
 window and uses MapWindow to make it visible. This is the first time i3
 actually gets in touch with Firefox. It decides to map the window, but it has
index 8fd51ae9359cfe92ae1fd6f45a2057bf0a591b1a..6cb04bf67bda4563917f05b504b7893f083318f8 100644 (file)
@@ -117,10 +117,8 @@ click_events::
 === Blocks in detail
 
 full_text::
-       The most simple block you can think of is one which just includes the
-       only required key, the +full_text+ key. i3bar will display the string
-       value parsed as
-       https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
+       The +full_text+ will be displayed by i3bar on the status line. This is the
+       only required key.
 short_text::
        Where appropriate, the +short_text+ (string) entry should also be
        provided. It will be used in case the status line needs to be shortened
@@ -172,6 +170,10 @@ separator_block_width::
        this gap, a separator line will be drawn unless +separator+ is
        disabled. Normally, you want to set this to an odd value (the default
        is 9 pixels), since the separator line is drawn in the middle.
+markup::
+       A string that indicates how the text of the block should be parsed. Set to
+       +"pango"+ to use https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup].
+       Set to +"none"+ to not use any markup (default).
 
 If you want to put in your own entries into a block, prefix the key with an
 underscore (_). i3bar will ignore all keys it doesn’t understand, and prefixing
index 2b798df5ac645dc25cda0f282ff2c000ccb54a06..5897036e5143f310e2b665e68db6174f7318198b 100644 (file)
@@ -231,3 +231,31 @@ layouts, you can either use a JSON parser that supports these deviations (for
 example libyajl), transform the layout file to a JSON-conforming file, or
 link:http://cr.i3wm.org/[submit a patch] to make +i3-save-tree(1)+ optionally
 output standard-conforming JSON.
+
+== Troubleshooting
+
+=== Restoring a vertically split workspace
+
+When using +i3-save-tree+ with the +--workspace+ switch, only the *contents* of
+the workspace will be dumped. This means that properties of the workspace
+itself will be lost.
+
+This is relevant for, e.g., a vertically split container as the base container of
+a workspace. Since the split mode is a property of the workspace, it will not be
+stored. In this case, you will have to manually wrap your layout in such a
+container:
+
+--------------------------------------------------------------------------------
+// vim:ts=4:sw=4:et
+{
+    // this is a manually added container to restore the vertical split
+    "layout": "splitv",
+    "percent": 0.5,
+    "type": "con",
+    "nodes": [
+
+        // the dumped workspace layout goes here
+
+    ]
+}
+--------------------------------------------------------------------------------
index 45b05a062fcbaf020521fee55a0362cca997bc5f..3d935e40aac7c2fbf8f5d5f6c2b45e47db4dec8b 100644 (file)
@@ -1249,6 +1249,24 @@ bar {
 }
 --------------------------------------------------------------
 
+=== Custom separator symbol
+
+Specifies a custom symbol to be used for the separator as opposed to the vertical,
+one pixel thick separator. Note that you may have to adjust the +sep_block_width+ 
+property.
+
+*Syntax*:
+-------------------------
+separator_symbol <symbol>
+-------------------------
+
+*Example*:
+------------------------
+bar {
+    separator_symbol ":|:"
+}
+------------------------
+
 === Workspace buttons
 
 Specifies whether workspace buttons should be shown or not. This is useful if
@@ -2011,6 +2029,27 @@ bindsym $mod+minus scratchpad show
 bindsym mod4+s [title="^Sup ::"] scratchpad show
 ------------------------------------------------
 
+=== Nop
+
+There is a no operation command +nop+ which allows you to override default
+behavior. This can be useful for, e.g., disabling a focus change on clicks with
+the middle mouse button.
+
+The optional +comment+ argument is ignored, but will be printed to the log file
+for debugging purposes.
+
+*Syntax*:
+---------------
+nop [<comment>]
+---------------
+
+*Example*:
+----------------------------------------------
+# Disable focus change for clicks on titlebars
+# with the middle mouse button
+bindsym button2 nop
+----------------------------------------------
+
 === i3bar control
 
 There are two options in the configuration of each i3bar instance that can be
index c09d459d8d67cf0495e64955bec2c0d3e11ddafd..b3404424f317f4e580102f0351bfc7e5ad5d8f43 100644 (file)
@@ -493,70 +493,70 @@ static char *resolve_tilde(const char *path) {
  */
 static int handle_expose() {
     /* re-draw the background */
-    xcb_rectangle_t border = {0, 0, 300, (15 * font.height) + 8};
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
+    xcb_rectangle_t border = {0, 0, logical_px(300), (logical_px(15) * font.height) + logical_px(8)};
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
 
     set_font(&font);
 
 #define txt(x, row, text)                    \
     draw_text_ascii(text, pixmap, pixmap_gc, \
-                    x, (row - 1) * font.height + 4, 300 - x * 2)
+                    x, (row - 1) * font.height + logical_px(4), logical_px(500) - x * 2)
 
     if (current_step == STEP_WELCOME) {
         /* restore font color */
         set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
 
-        txt(10, 2, "You have not configured i3 yet.");
-        txt(10, 3, "Do you want me to generate ~/.i3/config?");
-        txt(85, 5, "Yes, generate ~/.i3/config");
-        txt(85, 7, "No, I will use the defaults");
+        txt(logical_px(10), 2, "You have not configured i3 yet.");
+        txt(logical_px(10), 3, "Do you want me to generate ~/.i3/config?");
+        txt(logical_px(85), 5, "Yes, generate ~/.i3/config");
+        txt(logical_px(85), 7, "No, I will use the defaults");
 
         /* green */
         set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
-        txt(25, 5, "<Enter>");
+        txt(logical_px(25), 5, "<Enter>");
 
         /* red */
         set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
-        txt(31, 7, "<ESC>");
+        txt(logical_px(31), 7, "<ESC>");
     }
 
     if (current_step == STEP_GENERATE) {
         set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
 
-        txt(10, 2, "Please choose either:");
-        txt(85, 4, "Win as default modifier");
-        txt(85, 5, "Alt as default modifier");
-        txt(10, 7, "Afterwards, press");
-        txt(85, 9, "to write ~/.i3/config");
-        txt(85, 10, "to abort");
+        txt(logical_px(10), 2, "Please choose either:");
+        txt(logical_px(85), 4, "Win as default modifier");
+        txt(logical_px(85), 5, "Alt as default modifier");
+        txt(logical_px(10), 7, "Afterwards, press");
+        txt(logical_px(85), 9, "to write ~/.i3/config");
+        txt(logical_px(85), 10, "to abort");
 
         /* the not-selected modifier */
         if (modifier == MOD_Mod4)
-            txt(31, 5, "<Alt>");
+            txt(logical_px(31), 5, "<Alt>");
         else
-            txt(31, 4, "<Win>");
+            txt(logical_px(31), 4, "<Win>");
 
         /* the selected modifier */
         set_font(&bold_font);
         set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
         if (modifier == MOD_Mod4)
-            txt(10, 4, "-> <Win>");
+            txt(logical_px(10), 4, "-> <Win>");
         else
-            txt(10, 5, "-> <Alt>");
+            txt(logical_px(10), 5, "-> <Alt>");
 
         /* green */
         set_font(&font);
         set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
-        txt(25, 9, "<Enter>");
+        txt(logical_px(25), 9, "<Enter>");
 
         /* red */
         set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
-        txt(31, 10, "<ESC>");
+        txt(logical_px(31), 10, "<ESC>");
     }
 
     /* Copy the contents of the pixmap to the real window */
-    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, 500);
+    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), logical_px(500));
     xcb_flush(conn);
 
     return 1;
@@ -592,6 +592,12 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
             finish();
     }
 
+    /* Swap between modifiers when up or down is pressed. */
+    if (sym == XK_Up || sym == XK_Down) {
+        modifier = (modifier == MOD_Mod1) ? MOD_Mod4 : MOD_Mod1;
+        handle_expose();
+    }
+
     /* cancel any time */
     if (sym == XK_Escape)
         exit(0);
@@ -637,14 +643,14 @@ static void handle_button_press(xcb_button_press_event_t *event) {
     if (current_step != STEP_GENERATE)
         return;
 
-    if (event->event_x >= 32 && event->event_x <= 68 &&
-        event->event_y >= 45 && event->event_y <= 54) {
+    if (event->event_x >= logical_px(32) && event->event_x <= logical_px(68) &&
+        event->event_y >= logical_px(45) && event->event_y <= logical_px(54)) {
         modifier = MOD_Mod4;
         handle_expose();
     }
 
-    if (event->event_x >= 32 && event->event_x <= 68 &&
-        event->event_y >= 56 && event->event_y <= 70) {
+    if (event->event_x >= logical_px(32) && event->event_x <= logical_px(68) &&
+        event->event_y >= logical_px(56) && event->event_y <= logical_px(70)) {
         modifier = MOD_Mod1;
         handle_expose();
     }
@@ -760,8 +766,8 @@ static void finish() {
 int main(int argc, char *argv[]) {
     config_path = resolve_tilde("~/.i3/config");
     socket_path = getenv("I3SOCK");
-    char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
-    char *patternbold = "-misc-fixed-bold-r-normal--13-120-75-75-C-70-iso10646-1";
+    char *pattern = "pango:monospace 8";
+    char *patternbold = "pango:monospace bold 8";
     int o, option_index = 0;
 
     static struct option long_options[] = {
@@ -863,14 +869,14 @@ int main(int argc, char *argv[]) {
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                /* the window id */
-        root,               /* parent == root */
-        490, 297, 300, 205, /* dimensions */
-        0,                  /* X11 border = 0, we draw our own */
+        win,                                                                /* the window id */
+        root,                                                               /* parent == root */
+        logical_px(490), logical_px(297), logical_px(300), logical_px(205), /* dimensions */
+        0,                                                                  /* X11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
-        (uint32_t[]) {
+        (uint32_t[]){
             0, /* back pixel: black */
             XCB_EVENT_MASK_EXPOSURE |
                 XCB_EVENT_MASK_BUTTON_PRESS});
@@ -914,7 +920,7 @@ int main(int argc, char *argv[]) {
     /* Create pixmap */
     pixmap = xcb_generate_id(conn);
     pixmap_gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, 500);
+    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), logical_px(500));
     xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
 
     /* Grab the keyboard to get all input */
index 2c54233d02501a57bb776cf7dad0260aea777a4f..cc72f1016429780c2e13a2ef8378c119701cb9e0 100755 (executable)
@@ -306,7 +306,7 @@ for my $app (keys %apps) {
 #   };
 
 # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-# ┃ Run dmenu to ask the user for her choice                                  ┃
+# ┃ Run dmenu to ask the user for their choice                                ┃
 # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 
 # open2 will just make dmenu’s STDERR go to our own STDERR.
index 86b39338eae682f927bf93236658719bb03899cc..1b0d593c5e1c7322c11c210e739adfade5f50030 100644 (file)
@@ -43,8 +43,7 @@ static int check_for_wrap(void) {
      * of the log. */
     wrap_count = header->wrap_count;
     const int len = (logbuffer + header->offset_last_wrap) - walk;
-    if (write(STDOUT_FILENO, walk, len) != len)
-        err(EXIT_FAILURE, "write()");
+    swrite(STDOUT_FILENO, walk, len);
     walk = logbuffer + sizeof(i3_shmlog_header);
     return 1;
 }
@@ -52,12 +51,8 @@ static int check_for_wrap(void) {
 static void print_till_end(void) {
     check_for_wrap();
     const int len = (logbuffer + header->offset_next_write) - walk;
-    const int n = write(STDOUT_FILENO, walk, len);
-    if (len != n)
-        err(EXIT_FAILURE, "write()");
-    if (n > 0) {
-        walk += n;
-    }
+    swrite(STDOUT_FILENO, walk, len);
+    walk += len;
 }
 
 int main(int argc, char *argv[]) {
index 9f3ff174a786928f66b9eef2eaccbd904dfe10d9..996fc0ea9f08958a3b563a359e85ad6c7388b798 100644 (file)
@@ -129,10 +129,11 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
     printf("expose!\n");
 
     /* re-draw the background */
-    xcb_rectangle_t border = {0, 0, 500, font.height + 8}, inner = {2, 2, 496, font.height + 8 - 4};
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#FF0000")});
+    xcb_rectangle_t border = {0, 0, logical_px(500), font.height + logical_px(8)},
+                    inner = {logical_px(2), logical_px(2), logical_px(496), font.height + logical_px(8) - logical_px(4)};
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")});
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
 
     /* restore font color */
@@ -140,17 +141,17 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t
 
     /* draw the prompt … */
     if (prompt != NULL) {
-        draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492);
+        draw_text(prompt, pixmap, pixmap_gc, logical_px(4), logical_px(4), logical_px(492));
     }
     /* … and the text */
     if (input_position > 0) {
         i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
-        draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492);
+        draw_text(input, pixmap, pixmap_gc, prompt_offset + logical_px(4), logical_px(4), logical_px(492));
         i3string_free(input);
     }
 
     /* Copy the contents of the pixmap to the real window */
-    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
+    xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), font.height + logical_px(8));
     xcb_flush(conn);
 
     return 1;
@@ -234,6 +235,9 @@ static void finish_input() {
 static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
     printf("Keypress %d, state raw = %d\n", event->detail, event->state);
 
+    // TODO: port the input handling code from i3lock once libxkbcommon ≥ 0.5.0
+    // is available in distros.
+
     /* See the documentation of xcb_key_symbols_get_keysym for this one.
      * Basically: We get either col 0 or col 1, depending on whether shift is
      * pressed. */
@@ -313,7 +317,7 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
 int main(int argc, char *argv[]) {
     format = strdup("%s");
     socket_path = getenv("I3SOCK");
-    char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
+    char *pattern = sstrdup("pango:monospace 8");
     int o, option_index = 0;
 
     static struct option long_options[] = {
@@ -403,14 +407,14 @@ int main(int argc, char *argv[]) {
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                          /* the window id */
-        root,                         /* parent == root */
-        50, 50, 500, font.height + 8, /* dimensions */
-        0,                            /* X11 border = 0, we draw our own */
+        win,                                                                          /* the window id */
+        root,                                                                         /* parent == root */
+        logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8), /* dimensions */
+        0,                                                                            /* X11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
-        (uint32_t[]) {
+        (uint32_t[]){
             0, /* back pixel: black */
             1, /* override redirect: don’t manage this window */
             XCB_EVENT_MASK_EXPOSURE});
@@ -421,7 +425,7 @@ int main(int argc, char *argv[]) {
     /* Create pixmap */
     pixmap = xcb_generate_id(conn);
     pixmap_gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
+    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), font.height + logical_px(8));
     xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
 
     /* Set input focus (we have override_redirect=1, so the wm will not do
index ae5bf4deb02811e715885dc622c741dcf114a6cd..b78378be1c65a68225a38d8ebace7b5be0845a3d 100755 (executable)
@@ -341,7 +341,7 @@ sub convert_command {
             # NOTE: This is not 100% accurate, as it only works for one level
             # of nested containers. As this is a common use case, we use 'focus
             # parent; $command' nevertheless. For advanced use cases, the user
-            # has to modify his config.
+            # has to modify their config.
             print "$statement $key focus parent; $command\n";
         }
         return;
index 8dbc4a761fa851f70ad29f8c1d94721efa1256e4..bf6d1cca41f29a6ba39bec468c677ef3571b2734 100644 (file)
@@ -5,7 +5,7 @@
  * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * i3-nagbar is a utility which displays a nag message, for example in the case
- * when the user has an error in his configuration file.
+ * when the user has an error in their configuration file.
  *
  */
 #include <stdio.h>
@@ -131,7 +131,7 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
     printf("button released on x = %d, y = %d\n",
            event->event_x, event->event_y);
     /* If the user hits the close button, we exit(0) */
-    if (event->event_x >= (rect.width - 32))
+    if (event->event_x >= (rect.width - logical_px(32)))
         exit(0);
     button_t *button = get_button_at(event->event_x, event->event_y);
     if (!button)
@@ -164,7 +164,9 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
     char *link_path;
     char *exe_path = get_exe_path(argv0);
     sasprintf(&link_path, "%s.nagbar_cmd", script_path);
-    symlink(exe_path, link_path);
+    if (symlink(exe_path, link_path) == -1) {
+        err(EXIT_FAILURE, "Failed to symlink %s to %s", link_path, exe_path);
+    }
 
     char *terminal_cmd;
     sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
@@ -188,21 +190,23 @@ static void handle_button_release(xcb_connection_t *conn, xcb_button_release_eve
  */
 static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
     /* re-draw the background */
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_background});
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_background});
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &rect);
 
     /* restore font color */
     set_font_colors(pixmap_gc, color_text, color_background);
     draw_text(prompt, pixmap, pixmap_gc,
-              4 + 4, 4 + 4, rect.width - 4 - 4);
+              logical_px(4) + logical_px(4),
+              logical_px(4) + logical_px(4),
+              rect.width - logical_px(4) - logical_px(4));
 
     /* render close button */
     const char *close_button_label = "X";
-    int line_width = 4;
+    int line_width = logical_px(4);
     /* set width to the width of the label */
     int w = predict_text_width(i3string_from_utf8(close_button_label));
     /* account for left/right padding, which seems to be set to 8px (total) below */
-    w += 8;
+    w += logical_px(8);
     int y = rect.width;
     uint32_t values[3];
     values[0] = color_button_background;
@@ -212,23 +216,25 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
     xcb_rectangle_t close = {y - w - (2 * line_width), 0, w + (2 * line_width), rect.height};
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
 
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_border});
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border});
     xcb_point_t points[] = {
         {y - w - (2 * line_width), line_width / 2},
         {y - (line_width / 2), line_width / 2},
-        {y - (line_width / 2), (rect.height - (line_width / 2)) - 2},
-        {y - w - (2 * line_width), (rect.height - (line_width / 2)) - 2},
+        {y - (line_width / 2), (rect.height - (line_width / 2)) - logical_px(2)},
+        {y - w - (2 * line_width), (rect.height - (line_width / 2)) - logical_px(2)},
         {y - w - (2 * line_width), line_width / 2}};
     xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points);
 
     values[0] = 1;
     set_font_colors(pixmap_gc, color_text, color_button_background);
     /* the x term here seems to set left/right padding */
-    draw_text_ascii(close_button_label, pixmap, pixmap_gc, y - w - line_width + w / 2 - 4,
-                    4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4);
+    draw_text_ascii(close_button_label, pixmap, pixmap_gc,
+                    y - w - line_width + w / 2 - logical_px(4),
+                    logical_px(4) + logical_px(3),
+                    rect.width - y + w + line_width - w / 2 + logical_px(4));
     y -= w;
 
-    y -= 20;
+    y -= logical_px(20);
 
     /* render custom buttons */
     line_width = 1;
@@ -236,21 +242,21 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
         /* set w to the width of the label */
         w = predict_text_width(buttons[c].label);
         /* account for left/right padding, which seems to be set to 12px (total) below */
-        w += 12;
-        y -= 30;
-        xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_button_background});
-        close = (xcb_rectangle_t) {y - w - (2 * line_width), 2, w + (2 * line_width), rect.height - 6};
+        w += logical_px(12);
+        y -= logical_px(30);
+        xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_button_background});
+        close = (xcb_rectangle_t){y - w - (2 * line_width), logical_px(2), w + (2 * line_width), rect.height - logical_px(6)};
         xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
 
-        xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {color_border});
+        xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border});
         buttons[c].x = y - w - (2 * line_width);
         buttons[c].width = w;
         xcb_point_t points2[] = {
-            {y - w - (2 * line_width), (line_width / 2) + 2},
-            {y - (line_width / 2), (line_width / 2) + 2},
-            {y - (line_width / 2), (rect.height - 4 - (line_width / 2))},
-            {y - w - (2 * line_width), (rect.height - 4 - (line_width / 2))},
-            {y - w - (2 * line_width), (line_width / 2) + 2}};
+            {y - w - (2 * line_width), (line_width / 2) + logical_px(2)},
+            {y - (line_width / 2), (line_width / 2) + logical_px(2)},
+            {y - (line_width / 2), (rect.height - logical_px(4) - (line_width / 2))},
+            {y - w - (2 * line_width), (rect.height - logical_px(4) - (line_width / 2))},
+            {y - w - (2 * line_width), (line_width / 2) + logical_px(2)}};
         xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
 
         values[0] = color_text;
@@ -258,13 +264,15 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
         set_font_colors(pixmap_gc, color_text, color_button_background);
         /* the x term seems to set left/right padding */
         draw_text(buttons[c].label, pixmap, pixmap_gc,
-                  y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6);
+                  y - w - line_width + logical_px(6),
+                  logical_px(4) + logical_px(3),
+                  rect.width - y + w + line_width - logical_px(6));
 
         y -= w;
     }
 
     /* border line at the bottom */
-    line_width = 2;
+    line_width = logical_px(2);
     values[0] = color_border_bottom;
     values[1] = line_width;
     xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
@@ -316,7 +324,7 @@ int main(int argc, char *argv[]) {
 
     argv0 = argv[0];
 
-    char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
+    char *pattern = sstrdup("pango:monospace 8");
     int o, option_index = 0;
     enum { TYPE_ERROR = 0,
            TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
@@ -408,14 +416,14 @@ int main(int argc, char *argv[]) {
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                                                 /* the window id */
-        root,                                                /* parent == root */
-        50, 50, 500, font.height + 8 + 8 /* 8 px padding */, /* dimensions */
-        0,                                                   /* x11 border = 0, we draw our own */
+        win,                                                                         /* the window id */
+        root,                                                                        /* parent == root */
+        50, 50, 500, font.height + logical_px(8) + logical_px(8) /* 8 px padding */, /* dimensions */
+        0,                                                                           /* x11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
-        (uint32_t[]) {
+        (uint32_t[]){
             0, /* back pixel: black */
             XCB_EVENT_MASK_EXPOSURE |
                 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
@@ -465,7 +473,7 @@ int main(int argc, char *argv[]) {
     } __attribute__((__packed__)) strut_partial;
     memset(&strut_partial, 0, sizeof(strut_partial));
 
-    strut_partial.top = font.height + 6;
+    strut_partial.top = font.height + logical_px(6);
     strut_partial.top_start_x = 0;
     strut_partial.top_end_x = 800;
 
@@ -481,7 +489,7 @@ int main(int argc, char *argv[]) {
     /* Create pixmap */
     pixmap = xcb_generate_id(conn);
     pixmap_gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
+    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + logical_px(8));
     xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
 
     /* Grab the keyboard to get all input */
@@ -512,7 +520,7 @@ int main(int argc, char *argv[]) {
 
             case XCB_CONFIGURE_NOTIFY: {
                 xcb_configure_notify_event_t *configure_notify = (xcb_configure_notify_event_t *)event;
-                rect = (xcb_rectangle_t) {
+                rect = (xcb_rectangle_t){
                     configure_notify->x,
                     configure_notify->y,
                     configure_notify->width,
index c64fc72a09ae119b6cd3bc8615781c3595be1551..289fd8c6980d4e8aa25cc64d9390947459846ee3 100755 (executable)
@@ -13,6 +13,7 @@ use POSIX qw(locale_h);
 use File::Find;
 use File::Basename qw(basename);
 use File::Temp qw(tempfile);
+use List::Util qw(first);
 use Getopt::Long;
 use Pod::Usage;
 use AnyEvent::I3;
@@ -41,11 +42,7 @@ my $result = GetOptions(
 
 die "Could not parse command line options" unless $result;
 
-if (!defined($workspace) && !defined($output)) {
-    die "One of --workspace or --output need to be specified";
-}
-
-unless (defined($workspace) ^ defined($output)) {
+if (defined($workspace) && defined($output)) {
     die "Only one of --workspace or --output can be specified";
 }
 
@@ -57,6 +54,15 @@ if (!$i3->connect->recv) {
     die "Could not connect to i3";
 }
 
+sub get_current_workspace {
+    my $current = first { $_->{focused} } @{$i3->get_workspaces->recv};
+    return $current->{name};
+}
+
+if (!defined($workspace) && !defined($output)) {
+    $workspace = get_current_workspace();
+}
+
 sub filter_containers {
     my ($tree, $pred) = @_;
 
@@ -216,7 +222,7 @@ my $tree = $i3->get_tree->recv;
 my $dump;
 if (defined($workspace)) {
     $dump = filter_containers($tree, sub {
-        $_->{type} eq 'workspace' && $_->{name} eq $workspace
+        $_->{type} eq 'workspace' && ($_->{name} eq $workspace || ($workspace =~ /^\d+$/ && $_->{num} eq $workspace))
     });
 } else {
     $dump = filter_containers($tree, sub {
@@ -246,7 +252,7 @@ for my $key (qw(nodes floating_nodes)) {
 
 =head1 SYNOPSIS
 
-    i3-save-tree [--workspace=name] [--output=name]
+    i3-save-tree [--workspace=name|number] [--output=name]
 
 =head1 DESCRIPTION
 
@@ -259,19 +265,20 @@ specification. When a window is mapped (made visible on the screen) that
 matches the specification, i3 will put it into that place and kill the
 placeholder.
 
+If neither argument is specified, the currently focused workspace will be used.
+
 =head1 OPTIONS
 
 =over
 
-=item B<--workspace=name>
+=item B<--workspace=name|number>
 
-Specifies the workspace that should be dumped, e.g. 1. Either this or --output
-need to be specified.
+Specifies the workspace that should be dumped, e.g. 1. This can either be a
+name or the number of a workspace.
 
 =item B<--output=name>
 
-Specifies the output that should be dumped, e.g. LVDS-1. Either this or
---workspace need to be specified.
+Specifies the output that should be dumped, e.g. LVDS-1.
 
 =back
 
index 747a9280aa6e307f77d155aeaab74717e5f3f6a0..dd4b075ac981740144011de201ebcb5acba232c3 100755 (executable)
@@ -13,3 +13,5 @@ for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm
         exec $terminal "$@"
     fi
 done
+
+i3-nagbar -m 'i3-sensible-terminal could not find a terminal emulator. Please install one.'
index d7e96fe9c01d185da6d007d02d0aff5be9b42ffc..f7722d361957c5d48b8aedc42c3d2033bba89d36 100644 (file)
--- a/i3.config
+++ b/i3.config
 
 # Font for window titles. Will also be used by the bar unless a different font
 # is used in the bar {} block below.
+font pango:monospace 8
+
 # This font is widely installed, provides lots of unicode glyphs, right-to-left
 # text rendering and scalability on retina/hidpi displays (thanks to pango).
-font pango:DejaVu Sans Mono 8
+#font pango:DejaVu Sans Mono 8
+
 # Before i3 v4.8, we used to recommend this one as the default:
 # font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 # The font above is very space-efficient, that is, it looks good, sharp and
@@ -166,7 +169,7 @@ bar {
 
 #######################################################################
 # automatically start i3-config-wizard to offer the user to create a
-# keysym-based config which used his favorite modifier (alt or windows)
+# keysym-based config which used their favorite modifier (alt or windows)
 #
 # i3-config-wizard will not launch if there already is a config file
 # in ~/.i3/config.
index 93528a54eeafd50f31983a9d98aebc2141fb9f90..0c978d0b7c15928009cc75a00f4d79b63d865728 100644 (file)
@@ -12,9 +12,12 @@ set $mod Mod1
 
 # Font for window titles. Will also be used by the bar unless a different font
 # is used in the bar {} block below.
+font pango:monospace 8
+
 # This font is widely installed, provides lots of unicode glyphs, right-to-left
 # text rendering and scalability on retina/hidpi displays (thanks to pango).
-font pango:DejaVu Sans Mono 8
+#font pango:DejaVu Sans Mono 8
+
 # Before i3 v4.8, we used to recommend this one as the default:
 # font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 # The font above is very space-efficient, that is, it looks good, sharp and
index 72272b24c71440907d0e02da40dba4cb2934e6bf..b87cfaacacffd8d78ff8679cf71dd8fe7e32c998 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * child.c: Getting Input for the statusline
+ * child.c: Getting input for the statusline
  *
  */
 #pragma once
@@ -41,7 +41,7 @@ typedef struct {
 } i3bar_child;
 
 /*
- * Start a child-process with the specified command and reroute stdin.
+ * Start a child process with the specified command and reroute stdin.
  * We actually start a $SHELL to execute the command so we don't have to care
  * about arguments and such
  *
@@ -49,26 +49,26 @@ typedef struct {
 void start_child(char *command);
 
 /*
- * kill()s the child-process (if any). Called when exit()ing.
+ * kill()s the child process (if any). Called when exit()ing.
  *
  */
 void kill_child_at_exit(void);
 
 /*
- * kill()s the child-process (if any) and closes and
- * free()s the stdin- and sigchild-watchers
+ * kill()s the child process (if any) and closes and
+ * free()s the stdin- and SIGCHLD-watchers
  *
  */
 void kill_child(void);
 
 /*
- * Sends a SIGSTOP to the child-process (if existent)
+ * Sends a SIGSTOP to the child process (if existent)
  *
  */
 void stop_child(void);
 
 /*
- * Sends a SIGCONT to the child-process (if existent)
+ * Sends a SIGCONT to the child process (if existent)
  *
  */
 void cont_child(void);
index e8b6be0a57ec7e2788a9a68a9eb4ed161cfceb8a..22b9a28ac81d39e9d7a3a24cf40f2ba900839fe7 100644 (file)
@@ -16,8 +16,6 @@
 typedef struct rect_t rect;
 
 struct ev_loop *main_loop;
-char *statusline;
-char *statusline_buffer;
 
 struct rect_t {
     int x;
@@ -37,13 +35,21 @@ typedef enum {
  * up one status line. */
 struct status_block {
     i3String *full_text;
+    i3String *short_text;
 
     char *color;
+
+    /* min_width can be specified either as a numeric value (in pixels) or as a
+     * string. For strings, we set min_width to the measured text width of
+     * min_width_str. */
     uint32_t min_width;
+    char *min_width_str;
+
     blockalign_t align;
 
     bool urgent;
     bool no_separator;
+    bool is_markup;
 
     /* The amount of pixels necessary to render a separater after the block. */
     uint32_t sep_block_width;
index 2c39930531aea2752df810a84d2ade9d1de3f640..fdc37445ab4944549742ca7c580fa4f488e51de0 100644 (file)
@@ -35,6 +35,7 @@ typedef struct config_t {
     char *bar_id;
     char *command;
     char *fontname;
+    i3String *separator_symbol;
     char *tray_output;
     int num_outputs;
     char **outputs;
@@ -49,7 +50,7 @@ typedef struct config_t {
 config_t config;
 
 /**
- * Start parsing the received bar configuration json-string
+ * Start parsing the received bar configuration JSON string
  *
  */
 void parse_config_json(char *json);
index c357be89e8efc7087a57a8a9a1cbc7e25b495607..a60a1ebae1603e44ae0f93847e58ebc6d4fc9f00 100644 (file)
@@ -13,7 +13,7 @@
 
 /*
  * Initiate a connection to i3.
- * socket-path must be a valid path to the ipc_socket of i3
+ * socket_path must be a valid path to the ipc_socket of i3
  *
  */
 int init_connection(const char *socket_path);
@@ -25,7 +25,7 @@ int init_connection(const char *socket_path);
 void destroy_connection(void);
 
 /*
- * Sends a Message to i3.
+ * Sends a message to i3.
  * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
  *
  */
index 6c3833f4ccfd05a4ee8267b413f350d65e5c2e57..37e8e01793d75fea581bd564f58a6155770ddaf0 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * mode.c: Handle mode-event and show current binding mode in the bar
+ * mode.c: Handle "mode" event and show current binding mode in the bar
  *
  */
 #pragma once
@@ -22,7 +22,7 @@ struct mode {
 typedef struct mode mode;
 
 /*
- * Start parsing the received json-string
+ * Start parsing the received JSON string
  *
  */
 void parse_mode_json(char *json);
index bd41e7767d247d3b75f1f014788f195008fdb4cd..73fbf33f32caf5a9ad674a0f18ebd3e903850797 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * outputs.c: Maintaining the output-list
+ * outputs.c: Maintaining the outputlist
  *
  */
 #pragma once
@@ -19,13 +19,13 @@ SLIST_HEAD(outputs_head, i3_output);
 struct outputs_head* outputs;
 
 /*
- * Start parsing the received json-string
+ * Start parsing the received JSON string
  *
  */
 void parse_outputs_json(char* json);
 
 /*
- * Initiate the output-list
+ * Initiate the outputlist
  *
  */
 void init_outputs(void);
@@ -42,7 +42,7 @@ struct i3_output {
     bool primary; /* If it is the primary output */
     bool visible; /* If the bar is visible on this output */
     int ws;       /* The number of the currently visible ws */
-    rect rect;    /* The rect (relative to the root-win) */
+    rect rect;    /* The rect (relative to the root window) */
 
     xcb_window_t bar;     /* The id of the bar of the output */
     xcb_pixmap_t buffer;  /* An extra pixmap for double-buffering */
index cb0903c1e48fe5ef27b34310fa7478e8907730c4..ac13799851c097d08a4c638cf53eb8dda8a44933 100644 (file)
@@ -26,7 +26,7 @@
         }                \
     } while (0)
 
-/* Securely fee single-linked list */
+/* Securely free single-linked list */
 #define FREE_SLIST(l, type)              \
     do {                                 \
         type *walk = SLIST_FIRST(l);     \
@@ -37,7 +37,7 @@
         }                                \
     } while (0)
 
-/* Securely fee tail-queues */
+/* Securely free tail queue */
 #define FREE_TAILQ(l, type)                         \
     do {                                            \
         type *walk = TAILQ_FIRST(l);                \
@@ -51,7 +51,7 @@
 #if defined(DLOG)
 #undef DLOG
 #endif
-/* Use cool logging-macros */
+/* Use cool logging macros */
 #define DLOG(fmt, ...)                                                 \
     do {                                                               \
         if (config.verbose) {                                          \
index b9cefa8405bff15ac3a82ccb01d67f53c10c2eac..1b8c6c729bd813ba6afb4c26f3e6af1ff5adcea2 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * workspaces.c: Maintaining the workspace-lists
+ * workspaces.c: Maintaining the workspace lists
  *
  */
 #pragma once
@@ -18,13 +18,13 @@ typedef struct i3_ws i3_ws;
 TAILQ_HEAD(ws_head, i3_ws);
 
 /*
- * Start parsing the received json-string
+ * Start parsing the received JSON string
  *
  */
 void parse_workspaces_json(char *json);
 
 /*
- * free() all workspace data-structures
+ * free() all workspace data structures
  *
  */
 void free_workspaces(void);
@@ -36,7 +36,7 @@ struct i3_ws {
     int name_width;           /* The rendered width of the name */
     bool visible;             /* If the ws is currently visible on an output */
     bool focused;             /* If the ws is currently focused */
-    bool urgent;              /* If the urgent-hint of the ws is set */
+    bool urgent;              /* If the urgent hint of the ws is set */
     rect rect;                /* The rect of the ws (not used (yet)) */
     struct i3_output *output; /* The current output of the ws */
 
index 32b52f4d1f5a3670bedd7197b446faf0b29cffb7..8e48c0c138e0734ea34ea77eae1ceddd689107c8 100644 (file)
@@ -65,14 +65,14 @@ void init_xcb_late(char *fontname);
 void init_colors(const struct xcb_color_strings_t *colors);
 
 /*
- * Cleanup the xcb-stuff.
+ * Cleanup the xcb stuff.
  * Called once, before the program terminates.
  *
  */
 void clean_xcb(void);
 
 /*
- * Get the earlier requested atoms and save them in the prepared data-structure
+ * Get the earlier requested atoms and save them in the prepared data structure
  *
  */
 void get_atoms(void);
@@ -90,7 +90,7 @@ void kick_tray_clients(i3_output *output);
 
 /*
  * We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
- * to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
+ * to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
  * they assume a light background.
  * See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
  *
@@ -104,7 +104,7 @@ void init_tray_colors(void);
 void destroy_window(i3_output *output);
 
 /*
- * Reallocate the statusline-buffer
+ * Reallocate the statusline buffer
  *
  */
 void realloc_sl_buffer(void);
index 2f7dd76e0cca07699b56038052efb712ebda8cbd..9cc50f2a25f7b5e3a3d9bb0981ecc4ea93ee8be3 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * child.c: Getting Input for the statusline
+ * child.c: Getting input for the statusline
  *
  */
 #include <stdlib.h>
@@ -30,7 +30,7 @@
 /* Global variables for child_*() */
 i3bar_child child;
 
-/* stdin- and sigchild-watchers */
+/* stdin- and SIGCHLD-watchers */
 ev_io *stdin_io;
 ev_child *child_sig;
 
@@ -54,26 +54,43 @@ typedef struct parser_ctx {
 
 parser_ctx parser_context;
 
-/* The buffer statusline points to */
 struct statusline_head statusline_head = TAILQ_HEAD_INITIALIZER(statusline_head);
-char *statusline_buffer = NULL;
+/* Used temporarily while reading a statusline */
+struct statusline_head statusline_buffer = TAILQ_HEAD_INITIALIZER(statusline_buffer);
 
 int child_stdin;
 
 /*
- * Clears all blocks from the statusline structure in memory and frees their
- * associated resources.
+ * Remove all blocks from the given statusline.
+ * If free_resources is set, the fields of each status block will be free'd.
  */
-static void clear_status_blocks() {
+static void clear_statusline(struct statusline_head *head, bool free_resources) {
     struct status_block *first;
-    while (!TAILQ_EMPTY(&statusline_head)) {
-        first = TAILQ_FIRST(&statusline_head);
-        I3STRING_FREE(first->full_text);
-        TAILQ_REMOVE(&statusline_head, first, blocks);
+    while (!TAILQ_EMPTY(head)) {
+        first = TAILQ_FIRST(head);
+        if (free_resources) {
+            I3STRING_FREE(first->full_text);
+            I3STRING_FREE(first->short_text);
+            FREE(first->color);
+            FREE(first->name);
+            FREE(first->instance);
+            FREE(first->min_width_str);
+        }
+
+        TAILQ_REMOVE(head, first, blocks);
         free(first);
     }
 }
 
+static void copy_statusline(struct statusline_head *from, struct statusline_head *to) {
+    struct status_block *current;
+    TAILQ_FOREACH(current, from, blocks) {
+        struct status_block *new_block = smalloc(sizeof(struct status_block));
+        memcpy(new_block, current, sizeof(struct status_block));
+        TAILQ_INSERT_TAIL(to, new_block, blocks);
+    }
+}
+
 /*
  * Replaces the statusline in memory with an error message. Pass a format
  * string and format parameters as you would in `printf'. The next time
@@ -81,12 +98,12 @@ static void clear_status_blocks() {
  * the space allocated for the statusline.
  */
 __attribute__((format(printf, 1, 2))) static void set_statusline_error(const char *format, ...) {
-    clear_status_blocks();
+    clear_statusline(&statusline_head, true);
 
     char *message;
     va_list args;
     va_start(args, format);
-    vasprintf(&message, format, args);
+    (void)vasprintf(&message, format, args);
 
     struct status_block *err_block = scalloc(sizeof(struct status_block));
     err_block->full_text = i3string_from_utf8("Error: ");
@@ -108,16 +125,13 @@ __attribute__((format(printf, 1, 2))) static void set_statusline_error(const cha
 }
 
 /*
- * Stop and free() the stdin- and sigchild-watchers
+ * Stop and free() the stdin- and SIGCHLD-watchers
  *
  */
 void cleanup(void) {
     if (stdin_io != NULL) {
         ev_io_stop(main_loop, stdin_io);
         FREE(stdin_io);
-        FREE(statusline_buffer);
-        /* statusline pointed to memory within statusline_buffer */
-        statusline = NULL;
     }
 
     if (child_sig != NULL) {
@@ -130,20 +144,12 @@ void cleanup(void) {
 
 /*
  * The start of a new array is the start of a new status line, so we clear all
- * previous entries.
- *
+ * previous entries from the buffer.
  */
 static int stdin_start_array(void *context) {
-    struct status_block *first;
-    while (!TAILQ_EMPTY(&statusline_head)) {
-        first = TAILQ_FIRST(&statusline_head);
-        I3STRING_FREE(first->full_text);
-        FREE(first->color);
-        FREE(first->name);
-        FREE(first->instance);
-        TAILQ_REMOVE(&statusline_head, first, blocks);
-        free(first);
-    }
+    // the blocks are still used by statusline_head, so we won't free the
+    // resources here.
+    clear_statusline(&statusline_buffer, false);
     return 1;
 }
 
@@ -172,10 +178,13 @@ static int stdin_boolean(void *context, int val) {
     parser_ctx *ctx = context;
     if (strcasecmp(ctx->last_map_key, "urgent") == 0) {
         ctx->block.urgent = val;
+        return 1;
     }
     if (strcasecmp(ctx->last_map_key, "separator") == 0) {
         ctx->block.no_separator = !val;
+        return 1;
     }
+
     return 1;
 }
 
@@ -183,9 +192,19 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
     parser_ctx *ctx = context;
     if (strcasecmp(ctx->last_map_key, "full_text") == 0) {
         ctx->block.full_text = i3string_from_markup_with_length((const char *)val, len);
+        return 1;
+    }
+    if (strcasecmp(ctx->last_map_key, "short_text") == 0) {
+        ctx->block.short_text = i3string_from_markup_with_length((const char *)val, len);
+        return 1;
     }
     if (strcasecmp(ctx->last_map_key, "color") == 0) {
         sasprintf(&(ctx->block.color), "%.*s", len, val);
+        return 1;
+    }
+    if (strcasecmp(ctx->last_map_key, "markup") == 0) {
+        ctx->block.is_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango")));
+        return 1;
     }
     if (strcasecmp(ctx->last_map_key, "align") == 0) {
         if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) {
@@ -195,23 +214,30 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) {
         } else {
             ctx->block.align = ALIGN_LEFT;
         }
-    } else if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
-        i3String *text = i3string_from_markup_with_length((const char *)val, len);
-        ctx->block.min_width = (uint32_t)predict_text_width(text);
-        i3string_free(text);
+        return 1;
+    }
+    if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
+        char *copy = (char *)malloc(len + 1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.min_width_str = copy;
+        return 1;
     }
     if (strcasecmp(ctx->last_map_key, "name") == 0) {
         char *copy = (char *)malloc(len + 1);
         strncpy(copy, (const char *)val, len);
         copy[len] = 0;
         ctx->block.name = copy;
+        return 1;
     }
     if (strcasecmp(ctx->last_map_key, "instance") == 0) {
         char *copy = (char *)malloc(len + 1);
         strncpy(copy, (const char *)val, len);
         copy[len] = 0;
         ctx->block.instance = copy;
+        return 1;
     }
+
     return 1;
 }
 
@@ -219,13 +245,20 @@ static int stdin_integer(void *context, long long val) {
     parser_ctx *ctx = context;
     if (strcasecmp(ctx->last_map_key, "min_width") == 0) {
         ctx->block.min_width = (uint32_t)val;
+        return 1;
     }
     if (strcasecmp(ctx->last_map_key, "separator_block_width") == 0) {
         ctx->block.sep_block_width = (uint32_t)val;
+        return 1;
     }
+
     return 1;
 }
 
+/*
+ * When a map is finished, we have an entire status block.
+ * Move it from the parser's context to the statusline buffer.
+ */
 static int stdin_end_map(void *context) {
     parser_ctx *ctx = context;
     struct status_block *new_block = smalloc(sizeof(struct status_block));
@@ -236,15 +269,37 @@ static int stdin_end_map(void *context) {
         new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!");
     if (new_block->urgent)
         ctx->has_urgent = true;
-    TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks);
+
+    if (new_block->min_width_str) {
+        i3String *text = i3string_from_utf8(new_block->min_width_str);
+        i3string_set_markup(text, new_block->is_markup);
+        new_block->min_width = (uint32_t)predict_text_width(text);
+        i3string_free(text);
+    }
+
+    i3string_set_markup(new_block->full_text, new_block->is_markup);
+
+    if (new_block->short_text != NULL)
+        i3string_set_markup(new_block->short_text, new_block->is_markup);
+
+    TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks);
     return 1;
 }
 
+/*
+ * When an array is finished, we have an entire statusline.
+ * Copy it from the buffer to the actual statusline.
+ */
 static int stdin_end_array(void *context) {
+    DLOG("copying statusline_buffer to statusline_head\n");
+    clear_statusline(&statusline_head, true);
+    copy_statusline(&statusline_buffer, &statusline_head);
+
     DLOG("dumping statusline:\n");
     struct status_block *current;
     TAILQ_FOREACH(current, &statusline_head, blocks) {
         DLOG("full_text = %s\n", i3string_as_utf8(current->full_text));
+        DLOG("short_text = %s\n", (current->short_text == NULL ? NULL : i3string_as_utf8(current->short_text)));
         DLOG("color = %s\n", current->color);
     }
     DLOG("end of dump\n");
@@ -386,8 +441,8 @@ void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
 }
 
 /*
- * We received a sigchild, meaning, that the child-process terminated.
- * We simply free the respective data-structures and don't care for input
+ * We received a SIGCHLD, meaning, that the child process terminated.
+ * We simply free the respective data structures and don't care for input
  * anymore
  *
  */
@@ -415,16 +470,27 @@ void child_write_output(void) {
     if (child.click_events) {
         const unsigned char *output;
         size_t size;
+        ssize_t n;
 
         yajl_gen_get_buf(gen, &output, &size);
-        write(child_stdin, output, size);
-        write(child_stdin, "\n", 1);
+
+        n = writeall(child_stdin, output, size);
+        if (n != -1)
+            n = writeall(child_stdin, "\n", 1);
+
         yajl_gen_clear(gen);
+
+        if (n == -1) {
+            child.click_events = false;
+            kill_child();
+            set_statusline_error("child_write_output failed");
+            draw_bars(false);
+        }
     }
 }
 
 /*
- * Start a child-process with the specified command and reroute stdin.
+ * Start a child process with the specified command and reroute stdin.
  * We actually start a $SHELL to execute the command so we don't have to care
  * about arguments and such.
  *
@@ -552,7 +618,7 @@ void send_block_clicked(int button, const char *name, const char *instance, int
 }
 
 /*
- * kill()s the child-process (if any). Called when exit()ing.
+ * kill()s the child process (if any). Called when exit()ing.
  *
  */
 void kill_child_at_exit(void) {
@@ -564,8 +630,8 @@ void kill_child_at_exit(void) {
 }
 
 /*
- * kill()s the child-process (if existent) and closes and
- * free()s the stdin- and sigchild-watchers
+ * kill()s the child process (if existent) and closes and
+ * free()s the stdin- and SIGCHLD-watchers
  *
  */
 void kill_child(void) {
@@ -580,7 +646,7 @@ void kill_child(void) {
 }
 
 /*
- * Sends a SIGSTOP to the child-process (if existent)
+ * Sends a SIGSTOP to the child process (if existent)
  *
  */
 void stop_child(void) {
@@ -591,7 +657,7 @@ void stop_child(void) {
 }
 
 /*
- * Sends a SIGCONT to the child-process (if existent)
+ * Sends a SIGCONT to the child process (if existent)
  *
  */
 void cont_child(void) {
index bb322619b427a3b33c1db31d22442ead6e145521..f7abaee7dff9f9aa65abab5af1fc36a3c4c65cfb 100644 (file)
@@ -38,7 +38,7 @@ static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t
 }
 
 /*
- * Parse a null-value (current_workspace)
+ * Parse a null value (current_workspace)
  *
  */
 static int config_null_cb(void *params_) {
@@ -144,6 +144,13 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len
         return 1;
     }
 
+    if (!strcmp(cur_key, "separator_symbol")) {
+        DLOG("separator = %.*s\n", len, val);
+        I3STRING_FREE(config.separator_symbol);
+        config.separator_symbol = i3string_from_utf8_with_length((const char *)val, len);
+        return 1;
+    }
+
     if (!strcmp(cur_key, "outputs")) {
         DLOG("+output %.*s\n", len, val);
         int new_num_outputs = config.num_outputs + 1;
@@ -231,7 +238,7 @@ static yajl_callbacks outputs_callbacks = {
 };
 
 /*
- * Start parsing the received bar configuration json-string
+ * Start parsing the received bar configuration JSON string
  *
  */
 void parse_config_json(char *json) {
@@ -241,13 +248,13 @@ void parse_config_json(char *json) {
 
     state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
 
-    /* FIXME: Proper errorhandling for JSON-parsing */
+    /* FIXME: Proper error handling for JSON parsing */
     switch (state) {
         case yajl_status_ok:
             break;
         case yajl_status_client_canceled:
         case yajl_status_error:
-            ELOG("Could not parse config-reply!\n");
+            ELOG("Could not parse config reply!\n");
             exit(EXIT_FAILURE);
             break;
     }
index e1b343e5d699a9216f561a2dfc8b38cf43561945..15a26d7b4eb42b502fa3ca1a45d01cee90bc42d3 100644 (file)
@@ -32,15 +32,15 @@ typedef void (*handler_t)(char *);
  *
  */
 void got_command_reply(char *reply) {
-    /* TODO: Error handling for command-replies */
+    /* TODO: Error handling for command replies */
 }
 
 /*
- * Called, when we get a reply with workspaces-data
+ * Called, when we get a reply with workspaces data
  *
  */
 void got_workspace_reply(char *reply) {
-    DLOG("Got Workspace-Data!\n");
+    DLOG("Got workspace data!\n");
     parse_workspaces_json(reply);
     draw_bars(false);
 }
@@ -51,18 +51,18 @@ void got_workspace_reply(char *reply) {
  *
  */
 void got_subscribe_reply(char *reply) {
-    DLOG("Got Subscribe Reply: %s\n", reply);
-    /* TODO: Error handling for subscribe-commands */
+    DLOG("Got subscribe reply: %s\n", reply);
+    /* TODO: Error handling for subscribe commands */
 }
 
 /*
- * Called, when we get a reply with outputs-data
+ * Called, when we get a reply with outputs data
  *
  */
 void got_output_reply(char *reply) {
-    DLOG("Parsing Outputs-JSON...\n");
+    DLOG("Parsing outputs JSON...\n");
     parse_outputs_json(reply);
-    DLOG("Reconfiguring Windows...\n");
+    DLOG("Reconfiguring windows...\n");
     realloc_sl_buffer();
     reconfig_windows(false);
 
@@ -80,7 +80,7 @@ void got_output_reply(char *reply) {
  */
 void got_bar_config(char *reply) {
     DLOG("Received bar config \"%s\"\n", reply);
-    /* We initiate the main-function by requesting infos about the outputs and
+    /* We initiate the main function by requesting infos about the outputs and
      * workspaces. Everything else (creating the bars, showing the right workspace-
      * buttons and more) is taken care of by the event-drivenness of the code */
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
@@ -104,7 +104,7 @@ void got_bar_config(char *reply) {
     FREE(config.command);
 }
 
-/* Data-structure to easily call the reply-handlers later */
+/* Data structure to easily call the reply handlers later */
 handler_t reply_handlers[] = {
     &got_command_reply,
     &got_workspace_reply,
@@ -116,20 +116,20 @@ handler_t reply_handlers[] = {
 };
 
 /*
- * Called, when a workspace-event arrives (i.e. the user changed the workspace)
+ * Called, when a workspace event arrives (i.e. the user changed the workspace)
  *
  */
 void got_workspace_event(char *event) {
-    DLOG("Got Workspace Event!\n");
+    DLOG("Got workspace event!\n");
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
 }
 
 /*
- * Called, when an output-event arrives (i.e. the screen-configuration changed)
+ * Called, when an output event arrives (i.e. the screen configuration changed)
  *
  */
 void got_output_event(char *event) {
-    DLOG("Got Output Event!\n");
+    DLOG("Got output event!\n");
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
     if (!config.disable_ws) {
         i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
@@ -137,11 +137,11 @@ void got_output_event(char *event) {
 }
 
 /*
- * Called, when a mode-event arrives (i3 changed binding mode).
+ * Called, when a mode event arrives (i3 changed binding mode).
  *
  */
 void got_mode_event(char *event) {
-    DLOG("Got Mode Event!\n");
+    DLOG("Got mode event!\n");
     parse_mode_json(event);
     draw_bars(false);
 }
@@ -180,7 +180,7 @@ void got_bar_config_update(char *event) {
     draw_bars(false);
 }
 
-/* Data-structure to easily call the event-handlers later */
+/* Data structure to easily call the event handlers later */
 handler_t event_handlers[] = {
     &got_workspace_event,
     &got_output_event,
@@ -201,7 +201,7 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
     uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) * 2;
     char *header = smalloc(header_len);
 
-    /* We first parse the fixed-length IPC-header, to know, how much data
+    /* We first parse the fixed-length IPC header, to know, how much data
      * we have to expect */
     uint32_t rec = 0;
     while (rec < header_len) {
@@ -268,7 +268,7 @@ void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
 }
 
 /*
- * Sends a Message to i3.
+ * Sends a message to i3.
  * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
  *
  */
@@ -296,18 +296,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
     if (payload != NULL)
         strncpy(walk, payload, len);
 
-    uint32_t written = 0;
-
-    while (to_write > 0) {
-        int n = write(i3_connection->fd, buffer + written, to_write);
-        if (n == -1) {
-            ELOG("write() failed: %s\n", strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-
-        to_write -= n;
-        written += n;
-    }
+    swrite(i3_connection->fd, buffer, to_write);
 
     FREE(buffer);
 
@@ -316,7 +305,7 @@ int i3_send_msg(uint32_t type, const char *payload) {
 
 /*
  * Initiate a connection to i3.
- * socket-path must be a valid path to the ipc_socket of i3
+ * socket_path must be a valid path to the ipc_socket of i3
  *
  */
 int init_connection(const char *socket_path) {
index 371aeec9be4e4457d372d6464f408843b3280ef6..a9a972dbe6c12a37a5893b830bf7244b5ef34bca 100644 (file)
@@ -60,7 +60,7 @@ void print_usage(char *elf_name) {
     printf("\n");
     printf("-b, --bar_id  <bar_id>\tBar ID for which to get the configuration\n");
     printf("-s, --socket  <sock_path>\tConnect to i3 via <sock_path>\n");
-    printf("-h, --help    Display this help-message and exit\n");
+    printf("-h, --help    Display this help message and exit\n");
     printf("-v, --version Display version number and exit\n");
     printf("\n");
     printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
@@ -71,7 +71,7 @@ void print_usage(char *elf_name) {
 
 /*
  * We watch various signals, that are there to make our application stop.
- * If we get one of those, we ev_unloop() and invoke the cleanup-routines
+ * If we get one of those, we ev_unloop() and invoke the cleanup routines
  * in main() with that
  *
  */
@@ -140,7 +140,7 @@ int main(int argc, char **argv) {
     }
 
     if (socket_path == NULL) {
-        ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
+        ELOG("No socket path specified, default to %s\n", i3_default_sock_path);
         socket_path = expand_path(i3_default_sock_path);
     }
 
@@ -150,7 +150,7 @@ int main(int argc, char **argv) {
         i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG, config.bar_id);
     }
 
-    /* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main-loop.
+    /* We listen to SIGTERM/QUIT/INT and try to exit cleanly, by stopping the main loop.
      * We only need those watchers on the stack, so putting them on the stack saves us
      * some calls to free() */
     ev_signal *sig_term = smalloc(sizeof(ev_signal));
@@ -166,13 +166,11 @@ int main(int argc, char **argv) {
     ev_signal_start(main_loop, sig_hup);
 
     /* From here on everything should run smooth for itself, just start listening for
-     * events. We stop simply stop the event-loop, when we are finished */
+     * events. We stop simply stop the event loop, when we are finished */
     ev_loop(main_loop, 0);
 
     kill_child();
 
-    FREE(statusline_buffer);
-
     clean_xcb();
     ev_default_destroy();
 
index 813d4aabae5b0541f1b9df348eefc0642f393339..0ff8ba4f0239fd3fa70d6706e108d40a8144349d 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * mode.c: Handle mode-event and show current binding mode in the bar
+ * mode.c: Handle mode event and show current binding mode in the bar
  *
  */
 #include <string.h>
@@ -48,7 +48,7 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) {
 /*
  * Parse a key.
  *
- * Essentially we just save it in the parsing-state
+ * Essentially we just save it in the parsing state
  *
  */
 static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
@@ -69,11 +69,11 @@ static yajl_callbacks mode_callbacks = {
 };
 
 /*
- * Start parsing the received json-string
+ * Start parsing the received JSON string
  *
  */
 void parse_mode_json(char *json) {
-    /* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
+    /* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
      * JSON in chunks */
     struct mode_json_params params;
 
@@ -90,13 +90,13 @@ void parse_mode_json(char *json) {
 
     state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
 
-    /* FIXME: Propper errorhandling for JSON-parsing */
+    /* FIXME: Propper error handling for JSON parsing */
     switch (state) {
         case yajl_status_ok:
             break;
         case yajl_status_client_canceled:
         case yajl_status_error:
-            ELOG("Could not parse mode-event!\n");
+            ELOG("Could not parse mode event!\n");
             exit(EXIT_FAILURE);
             break;
     }
index 02b718d3ae42418ddbcd8899f8aa312cfec5e278..8f5c95dc2f16d7e8ffb93b763099a8cc0b65b03e 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * outputs.c: Maintaining the output-list
+ * outputs.c: Maintaining the outputlist
  *
  */
 #include <string.h>
@@ -27,7 +27,7 @@ struct outputs_json_params {
 };
 
 /*
- * Parse a null-value (current_workspace)
+ * Parse a null value (current_workspace)
  *
  */
 static int outputs_null_cb(void *params_) {
@@ -139,7 +139,7 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len
 }
 
 /*
- * We hit the start of a json-map (rect or a new output)
+ * We hit the start of a JSON map (rect or a new output)
  *
  */
 static int outputs_start_map_cb(void *params_) {
@@ -221,7 +221,7 @@ static int outputs_end_map_cb(void *params_) {
 /*
  * Parse a key.
  *
- * Essentially we just save it in the parsing-state
+ * Essentially we just save it in the parsing state
  *
  */
 static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
@@ -247,7 +247,7 @@ static yajl_callbacks outputs_callbacks = {
 };
 
 /*
- * Initiate the output-list
+ * Initiate the outputlist
  *
  */
 void init_outputs(void) {
@@ -256,7 +256,7 @@ void init_outputs(void) {
 }
 
 /*
- * Start parsing the received json-string
+ * Start parsing the received JSON string
  *
  */
 void parse_outputs_json(char *json) {
@@ -279,7 +279,7 @@ void parse_outputs_json(char *json) {
             break;
         case yajl_status_client_canceled:
         case yajl_status_error:
-            ELOG("Could not parse outputs-reply!\n");
+            ELOG("Could not parse outputs reply!\n");
             exit(EXIT_FAILURE);
             break;
     }
index 45b511ffbb66fb69ce63a0861e5cf445826e5322..e30325265bb36835b2b5bdac5f8a7a76b54d2386 100644 (file)
@@ -4,7 +4,7 @@
  * i3bar - an xcb-based status- and ws-bar for i3
  * © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
  *
- * workspaces.c: Maintaining the workspace-lists
+ * workspaces.c: Maintaining the workspace lists
  *
  */
 #include <string.h>
@@ -135,7 +135,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
         params->workspaces_walk->name_width =
             predict_text_width(params->workspaces_walk->name);
 
-        DLOG("Got Workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
+        DLOG("Got workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
              params->workspaces_walk->canonical_name,
              i3string_as_utf8(params->workspaces_walk->name),
              params->workspaces_walk->name_width,
@@ -167,7 +167,7 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t
 }
 
 /*
- * We hit the start of a json-map (rect or a new output)
+ * We hit the start of a JSON map (rect or a new output)
  *
  */
 static int workspaces_start_map_cb(void *params_) {
@@ -195,7 +195,7 @@ static int workspaces_start_map_cb(void *params_) {
 /*
  * Parse a key.
  *
- * Essentially we just save it in the parsing-state
+ * Essentially we just save it in the parsing state
  *
  */
 static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
@@ -219,11 +219,11 @@ static yajl_callbacks workspaces_callbacks = {
 };
 
 /*
- * Start parsing the received json-string
+ * Start parsing the received JSON string
  *
  */
 void parse_workspaces_json(char *json) {
-    /* FIXME: Fasciliate stream-processing, i.e. allow starting to interpret
+    /* FIXME: Fasciliate stream processing, i.e. allow starting to interpret
      * JSON in chunks */
     struct workspaces_json_params params;
 
@@ -239,13 +239,13 @@ void parse_workspaces_json(char *json) {
 
     state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
 
-    /* FIXME: Propper errorhandling for JSON-parsing */
+    /* FIXME: Propper error handling for JSON parsing */
     switch (state) {
         case yajl_status_ok:
             break;
         case yajl_status_client_canceled:
         case yajl_status_error:
-            ELOG("Could not parse workspaces-reply!\n");
+            ELOG("Could not parse workspaces reply!\n");
             exit(EXIT_FAILURE);
             break;
     }
@@ -256,7 +256,7 @@ void parse_workspaces_json(char *json) {
 }
 
 /*
- * free() all workspace data-structures. Does not free() the heads of the tailqueues.
+ * free() all workspace data structures. Does not free() the heads of the tailqueues.
  *
  */
 void free_workspaces(void) {
index 3a1b8cb2277807a766e8f094a5194a3ef77dd1a0..e53b92262df976bd99d3bdccdfded1e04915c98a 100644 (file)
@@ -34,7 +34,7 @@
 #include "common.h"
 #include "libi3.h"
 
-/* We save the Atoms in an easy to access array, indexed by an enum */
+/* We save the atoms in an easy to access array, indexed by an enum */
 enum {
 #define ATOM_DO(name) name,
 #include "xcb_atoms.def"
@@ -63,6 +63,9 @@ static i3Font font;
 /* Overall height of the bar (based on font size) */
 int bar_height;
 
+/* Cached width of the custom separator if one was set */
+int separator_symbol_width;
+
 /* These are only relevant for XKB, which we only need for grabbing modifiers */
 int xkb_base;
 int mod_pressed = 0;
@@ -74,7 +77,7 @@ xcb_gcontext_t statusline_clear;
 xcb_pixmap_t statusline_pm;
 uint32_t statusline_width;
 
-/* Event-Watchers, to interact with the user */
+/* Event watchers, to interact with the user */
 ev_prepare *xcb_prep;
 ev_check *xcb_chk;
 ev_io *xcb_io;
@@ -106,7 +109,26 @@ struct xcb_colors_t {
 };
 struct xcb_colors_t colors;
 
-/* We define xcb_request_failed as a macro to include the relevant line-number */
+/* Horizontal offset between a workspace label and button borders */
+static const int ws_hoff_px = 4;
+
+/* Vertical offset between a workspace label and button borders */
+static const int ws_voff_px = 3;
+
+/* Offset between two workspace buttons */
+static const int ws_spacing_px = 1;
+
+/* Offset between the statusline and 1) workspace buttons on the left
+ *                                   2) the tray or screen edge on the right */
+static const int sb_hoff_px = 4;
+
+/* Additional offset between the tray and the statusline, if the tray is not empty */
+static const int tray_loff_px = 2;
+
+/* Vertical offset between the bar and a separator */
+static const int sep_voff_px = 4;
+
+/* We define xcb_request_failed as a macro to include the relevant line number */
 #define xcb_request_failed(cookie, err_msg) _xcb_request_failed(cookie, err_msg, __LINE__)
 int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
     xcb_generic_error_t *err;
@@ -123,11 +145,51 @@ uint32_t get_sep_offset(struct status_block *block) {
     return 0;
 }
 
+int get_tray_width(struct tc_head *trayclients) {
+    trayclient *trayclient;
+    int tray_width = 0;
+    TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
+        if (!trayclient->mapped)
+            continue;
+        tray_width += font.height + logical_px(2);
+    }
+    if (tray_width > 0)
+        tray_width += logical_px(tray_loff_px);
+    return tray_width;
+}
+
+/*
+ * Draws a separator for the given block if necessary.
+ *
+ */
+static void draw_separator(uint32_t x, struct status_block *block) {
+    uint32_t sep_offset = get_sep_offset(block);
+    if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0)
+        return;
+
+    uint32_t center_x = x - sep_offset;
+    if (config.separator_symbol == NULL) {
+        /* Draw a classic one pixel, vertical separator. */
+        uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
+        uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
+        xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
+        xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm, statusline_ctx, 2,
+                      (xcb_point_t[]){{center_x, logical_px(sep_voff_px)},
+                                      {center_x, bar_height - logical_px(sep_voff_px)}});
+    } else {
+        /* Draw a custom separator. */
+        uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2);
+        set_font_colors(statusline_ctx, colors.sep_fg, colors.bar_bg);
+        draw_text(config.separator_symbol, statusline_pm, statusline_ctx,
+                  separator_x, logical_px(ws_voff_px), x - separator_x);
+    }
+}
+
 /*
  * Redraws the statusline to the buffer
  *
  */
-void refresh_statusline(void) {
+void refresh_statusline(bool use_short_text) {
     struct status_block *block;
 
     uint32_t old_statusline_width = statusline_width;
@@ -135,6 +197,12 @@ void refresh_statusline(void) {
 
     /* Predict the text width of all blocks (in pixels). */
     TAILQ_FOREACH(block, &statusline_head, blocks) {
+        /* Try to use the shorter text if necessary and possible. */
+        if (use_short_text && block->short_text != NULL) {
+            I3STRING_FREE(block->full_text);
+            block->full_text = i3string_copy(block->short_text);
+        }
+
         if (i3string_get_num_bytes(block->full_text) == 0)
             continue;
 
@@ -174,7 +242,7 @@ void refresh_statusline(void) {
         realloc_sl_buffer();
 
     /* Clear the statusline pixmap. */
-    xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height};
+    xcb_rectangle_t rect = {0, 0, MAX(root_screen->width_in_pixels, statusline_width), bar_height};
     xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect);
 
     /* Draw the text of each block. */
@@ -192,32 +260,23 @@ void refresh_statusline(void) {
 
             /* Draw the background */
             uint32_t bg_color = colors.urgent_ws_bg;
-            uint32_t bg_values[] = { bg_color, bg_color };
+            uint32_t bg_values[] = {bg_color, bg_color};
             xcb_change_gc(xcb_connection, statusline_ctx, mask, bg_values);
 
             /* The urgent background “overshoots” by 2 px so that the text that
              * is printed onto it will not be look so cut off. */
-            xcb_rectangle_t bg_rect = { x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2) };
+            xcb_rectangle_t bg_rect = {x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2)};
             xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect);
         } else {
             fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg);
         }
 
         set_font_colors(statusline_ctx, fg_color, colors.bar_bg);
-        draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width);
+        draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, logical_px(ws_voff_px), block->width);
         x += block->width + block->sep_block_width + block->x_offset + block->x_append;
 
-        uint32_t sep_offset = get_sep_offset(block);
-        if (TAILQ_NEXT(block, blocks) != NULL && sep_offset > 0) {
-            /* This is not the last block, draw a separator. */
-            uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
-            uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
-            xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
-            xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
-                          statusline_ctx, 2,
-                          (xcb_point_t[]) { { x - sep_offset, logical_px(4) },
-                                            { x - sep_offset, bar_height - logical_px(4) } });
-        }
+        /* If this is not the last block, draw a separator. */
+        draw_separator(x, block);
     }
 }
 
@@ -273,7 +332,7 @@ void unhide_bars(void) {
         values[2] = walk->rect.w;
         values[3] = bar_height;
         values[4] = XCB_STACK_MODE_ABOVE;
-        DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
+        DLOG("Reconfiguring window for output %s to %d,%d\n", walk->name, values[0], values[1]);
         cookie = xcb_configure_window_checked(xcb_connection,
                                               walk->bar,
                                               mask,
@@ -317,14 +376,12 @@ void init_colors(const struct xcb_color_strings_t *new_colors) {
 }
 
 /*
- * Handle a button-press-event (i.e. a mouse click on one of our bars).
- * We determine, whether the click occured on a ws-button or if the scroll-
+ * Handle a button press event (i.e. a mouse click on one of our bars).
+ * We determine, whether the click occured on a workspace button or if the scroll-
  * wheel was used and change the workspace appropriately
  *
  */
 void handle_button(xcb_button_press_event_t *event) {
-    i3_ws *cur_ws;
-
     /* Determine, which bar was clicked */
     i3_output *walk;
     xcb_window_t bar = event->event;
@@ -335,45 +392,47 @@ void handle_button(xcb_button_press_event_t *event) {
     }
 
     if (walk == NULL) {
-        DLOG("Unknown Bar klicked!\n");
+        DLOG("Unknown bar clicked!\n");
         return;
     }
 
     int32_t x = event->event_x >= 0 ? event->event_x : 0;
     int32_t original_x = x;
 
-    DLOG("Got Button %d\n", event->detail);
+    DLOG("Got button %d\n", event->detail);
 
-    if (child_want_click_events()) {
-        /* If the child asked for click events,
-         * check if a status block has been clicked. */
+    int workspace_width = 0;
+    i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk;
 
-        /* First calculate width of tray area */
-        trayclient *trayclient;
-        int tray_width = 0;
-        TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
-            if (!trayclient->mapped)
-                continue;
-            tray_width += (font.height + logical_px(2));
-        }
-        if (tray_width > 0)
-            tray_width += logical_px(2);
+    TAILQ_FOREACH(ws_walk, walk->workspaces, tailq) {
+        int w = 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
+        if (x >= workspace_width && x <= workspace_width + w)
+            clicked_ws = ws_walk;
+        if (ws_walk->visible)
+            cur_ws = ws_walk;
+        workspace_width += w;
+        if (TAILQ_NEXT(ws_walk, tailq) != NULL)
+            workspace_width += logical_px(ws_spacing_px);
+    }
 
+    if (x > workspace_width && child_want_click_events()) {
+        /* If the child asked for click events,
+         * check if a status block has been clicked. */
+        int tray_width = get_tray_width(walk->trayclients);
         int block_x = 0, last_block_x;
-        int offset = walk->rect.w - statusline_width - tray_width - logical_px(4);
+        int offset = walk->rect.w - statusline_width - tray_width - logical_px(sb_hoff_px);
 
         x = original_x - offset;
         if (x >= 0) {
             struct status_block *block;
             int sep_offset_remainder = 0;
 
-            TAILQ_FOREACH (block, &statusline_head, blocks) {
+            TAILQ_FOREACH(block, &statusline_head, blocks) {
                 if (i3string_get_num_bytes(block->full_text) == 0)
                     continue;
 
                 last_block_x = block_x;
-                block_x += block->width + block->x_offset + block->x_append
-                           + get_sep_offset(block) + sep_offset_remainder;
+                block_x += block->width + block->x_offset + block->x_append + get_sep_offset(block) + sep_offset_remainder;
 
                 if (x <= block_x && x >= last_block_x) {
                     send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
@@ -386,15 +445,8 @@ void handle_button(xcb_button_press_event_t *event) {
         x = original_x;
     }
 
-    /* TODO: Move this to extern get_ws_for_output() */
-    TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) {
-        if (cur_ws->visible) {
-            break;
-        }
-    }
-
     if (cur_ws == NULL) {
-        DLOG("No Workspace active?\n");
+        DLOG("No workspace active?\n");
         return;
     }
 
@@ -436,17 +488,10 @@ void handle_button(xcb_button_press_event_t *event) {
             cur_ws = TAILQ_NEXT(cur_ws, tailq);
             break;
         case 1:
-            /* Check if this event regards a workspace button */
-            TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
-                DLOG("x = %d\n", x);
-                if (x >= 0 && x < cur_ws->name_width + logical_px(10)) {
-                    break;
-                }
-                x -= cur_ws->name_width + logical_px(11);
-            }
+            cur_ws = clicked_ws;
 
-            /* Otherwise, focus our currently visible workspace if it is not
-             * already focused */
+            /* if no workspace was clicked, focus our currently visible
+             * workspace if it is not already focused */
             if (cur_ws == NULL) {
                 TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
                     if (cur_ws->visible && !cur_ws->focused)
@@ -471,7 +516,7 @@ void handle_button(xcb_button_press_event_t *event) {
     size_t namelen = 0;
     const char *utf8_name = cur_ws->canonical_name;
     for (const char *walk = utf8_name; *walk != '\0'; walk++) {
-        if (*walk == '"')
+        if (*walk == '"' || *walk == '\\')
             num_quotes++;
         /* While we’re looping through the name anyway, we can save one
          * strlen(). */
@@ -485,7 +530,7 @@ void handle_button(xcb_button_press_event_t *event) {
     for (inpos = 0, outpos = strlen("workspace \"");
          inpos < namelen;
          inpos++, outpos++) {
-        if (utf8_name[inpos] == '"') {
+        if (utf8_name[inpos] == '"' || utf8_name[inpos] == '\\') {
             buffer[outpos] = '\\';
             outpos++;
         }
@@ -498,7 +543,7 @@ void handle_button(xcb_button_press_event_t *event) {
 
 /*
  * Handle visibility notifications: when none of the bars are visible, e.g.
- * if windows are in full-screen on each output, suspend the child process.
+ * if windows are in fullscreen on each output, suspend the child process.
  *
  */
 static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
@@ -506,7 +551,7 @@ static void handle_visibility_notify(xcb_visibility_notify_event_t *event) {
     int num_visible = 0;
     i3_output *output;
 
-    SLIST_FOREACH (output, outputs, slist) {
+    SLIST_FOREACH(output, outputs, slist) {
         if (!output->active) {
             continue;
         }
@@ -863,7 +908,7 @@ static void handle_property_notify(xcb_property_notify_event_t *event) {
         DLOG("xembed version = %d\n", xembed[0]);
         DLOG("xembed flags = %d\n", xembed[1]);
         bool map_it = ((xembed[1] & XEMBED_MAPPED) == XEMBED_MAPPED);
-        DLOG("map-state now %d\n", map_it);
+        DLOG("map state now %d\n", map_it);
         if (trayclient->mapped && !map_it) {
             /* need to unmap the window */
             xcb_unmap_window(xcb_connection, trayclient->win);
@@ -1002,7 +1047,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
                 redraw_bars();
                 break;
             case XCB_BUTTON_PRESS:
-                /* Button-press-events are mouse-buttons clicked on one of our bars */
+                /* Button press events are mouse buttons clicked on one of our bars */
                 handle_button((xcb_button_press_event_t *)event);
                 break;
             case XCB_CLIENT_MESSAGE:
@@ -1035,7 +1080,7 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) {
 }
 
 /*
- * Dummy Callback. We only need this, so that the Prepare- and Check-Watchers
+ * Dummy callback. We only need this, so that the prepare and check watchers
  * are triggered
  *
  */
@@ -1048,7 +1093,7 @@ void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
  *
  */
 char *init_xcb_early() {
-    /* FIXME: xcb_connect leaks Memory */
+    /* FIXME: xcb_connect leaks memory */
     xcb_connection = xcb_connect(NULL, &screen);
     if (xcb_connection_has_error(xcb_connection)) {
         ELOG("Cannot open display\n");
@@ -1091,7 +1136,7 @@ char *init_xcb_early() {
                                                                root_screen->width_in_pixels,
                                                                root_screen->height_in_pixels);
 
-    /* The various Watchers to communicate with xcb */
+    /* The various watchers to communicate with xcb */
     xcb_io = smalloc(sizeof(ev_io));
     xcb_prep = smalloc(sizeof(ev_prepare));
     xcb_chk = smalloc(sizeof(ev_check));
@@ -1109,9 +1154,9 @@ char *init_xcb_early() {
 
     char *path = root_atom_contents("I3_SOCKET_PATH", xcb_connection, screen);
 
-    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
-        xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
-        xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
+    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer") ||
+        xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline buffer clearcontext") ||
+        xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline buffer context")) {
         exit(EXIT_FAILURE);
     }
 
@@ -1119,7 +1164,7 @@ char *init_xcb_early() {
 }
 
 /*
- * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key-events
+ * Register for xkb keyevents. To grab modifiers without blocking other applications from receiving key events
  * involving that modifier, we sadly have to use xkb which is not yet fully supported
  * in xcb.
  *
@@ -1171,8 +1216,11 @@ void init_xcb_late(char *fontname) {
     /* Load the font */
     font = load_font(fontname, true);
     set_font(&font);
-    DLOG("Calculated Font-height: %d\n", font.height);
-    bar_height = font.height + logical_px(6);
+    DLOG("Calculated font height: %d\n", font.height);
+    bar_height = font.height + 2 * logical_px(ws_voff_px);
+
+    if (config.separator_symbol)
+        separator_symbol_width = predict_text_width(config.separator_symbol);
 
     xcb_flush(xcb_connection);
 
@@ -1285,7 +1333,7 @@ void init_tray(void) {
 
 /*
  * We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
- * to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
+ * to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
  * they assume a light background.
  * See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
  *
@@ -1325,7 +1373,7 @@ void init_tray_colors(void) {
 }
 
 /*
- * Cleanup the xcb-stuff.
+ * Cleanup the xcb stuff.
  * Called once, before the program terminates.
  *
  */
@@ -1370,7 +1418,7 @@ void get_atoms(void) {
     free(reply);
 
 #include "xcb_atoms.def"
-    DLOG("Got Atoms\n");
+    DLOG("Got atoms\n");
 }
 
 /*
@@ -1435,11 +1483,11 @@ void destroy_window(i3_output *output) {
 }
 
 /*
- * Reallocate the statusline-buffer
+ * Reallocate the statusline buffer
  *
  */
 void realloc_sl_buffer(void) {
-    DLOG("Re-allocating statusline-buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
+    DLOG("Re-allocating statusline buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n",
          statusline_width, root_screen->width_in_pixels);
     xcb_free_pixmap(xcb_connection, statusline_pm);
     statusline_pm = xcb_generate_id(xcb_connection);
@@ -1470,13 +1518,57 @@ void realloc_sl_buffer(void) {
                                                             mask,
                                                             vals);
 
-    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline-buffer") ||
-        xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline-buffer-clearcontext") ||
-        xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline-buffer-context")) {
+    if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer") ||
+        xcb_request_failed(clear_ctx_cookie, "Could not allocate statusline buffer clearcontext") ||
+        xcb_request_failed(sl_ctx_cookie, "Could not allocate statusline buffer context")) {
         exit(EXIT_FAILURE);
     }
 }
 
+/* Strut partial tells i3 where to reserve space for i3bar. This is determined
+ * by the `position` bar config directive. */
+xcb_void_cookie_t config_strut_partial(i3_output *output) {
+    /* A local struct to save the strut_partial property */
+    struct {
+        uint32_t left;
+        uint32_t right;
+        uint32_t top;
+        uint32_t bottom;
+        uint32_t left_start_y;
+        uint32_t left_end_y;
+        uint32_t right_start_y;
+        uint32_t right_end_y;
+        uint32_t top_start_x;
+        uint32_t top_end_x;
+        uint32_t bottom_start_x;
+        uint32_t bottom_end_x;
+    } __attribute__((__packed__)) strut_partial;
+    memset(&strut_partial, 0, sizeof(strut_partial));
+
+    switch (config.position) {
+        case POS_NONE:
+            break;
+        case POS_TOP:
+            strut_partial.top = bar_height;
+            strut_partial.top_start_x = output->rect.x;
+            strut_partial.top_end_x = output->rect.x + output->rect.w;
+            break;
+        case POS_BOT:
+            strut_partial.bottom = bar_height;
+            strut_partial.bottom_start_x = output->rect.x;
+            strut_partial.bottom_end_x = output->rect.x + output->rect.w;
+            break;
+    }
+    return xcb_change_property(xcb_connection,
+                               XCB_PROP_MODE_REPLACE,
+                               output->bar,
+                               atoms[_NET_WM_STRUT_PARTIAL],
+                               XCB_ATOM_CARDINAL,
+                               32,
+                               12,
+                               &strut_partial);
+}
+
 /*
  * Reconfigure all bars and create new bars for recently activated outputs
  *
@@ -1496,14 +1588,14 @@ void reconfig_windows(bool redraw_bars) {
             continue;
         }
         if (walk->bar == XCB_NONE) {
-            DLOG("Creating Window for output %s\n", walk->name);
+            DLOG("Creating window for output %s\n", walk->name);
 
             walk->bar = xcb_generate_id(xcb_connection);
             walk->buffer = xcb_generate_id(xcb_connection);
             mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
             /* Black background */
             values[0] = colors.bar_bg;
-            /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar-windows */
+            /* If hide_on_modifier is set to hide or invisible mode, i3 is not supposed to manage our bar windows */
             values[1] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
             /* We enable the following EventMask fields:
              * EXPOSURE, to get expose events (we have to re-draw then)
@@ -1565,7 +1657,7 @@ void reconfig_windows(bool redraw_bars) {
                                               name);
             free(name);
 
-            /* We want dock-windows (for now). When override_redirect is set, i3 is ignoring
+            /* We want dock windows (for now). When override_redirect is set, i3 is ignoring
              * this one */
             xcb_void_cookie_t dock_cookie = xcb_change_property(xcb_connection,
                                                                 XCB_PROP_MODE_REPLACE,
@@ -1576,51 +1668,9 @@ void reconfig_windows(bool redraw_bars) {
                                                                 1,
                                                                 (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
 
-            /* We need to tell i3, where to reserve space for i3bar */
-            /* left, right, top, bottom, left_start_y, left_end_y,
-             * right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
-             * bottom_end_x */
-            /* A local struct to save the strut_partial property */
-            struct {
-                uint32_t left;
-                uint32_t right;
-                uint32_t top;
-                uint32_t bottom;
-                uint32_t left_start_y;
-                uint32_t left_end_y;
-                uint32_t right_start_y;
-                uint32_t right_end_y;
-                uint32_t top_start_x;
-                uint32_t top_end_x;
-                uint32_t bottom_start_x;
-                uint32_t bottom_end_x;
-            } __attribute__((__packed__)) strut_partial;
-            memset(&strut_partial, 0, sizeof(strut_partial));
-
-            switch (config.position) {
-                case POS_NONE:
-                    break;
-                case POS_TOP:
-                    strut_partial.top = bar_height;
-                    strut_partial.top_start_x = walk->rect.x;
-                    strut_partial.top_end_x = walk->rect.x + walk->rect.w;
-                    break;
-                case POS_BOT:
-                    strut_partial.bottom = bar_height;
-                    strut_partial.bottom_start_x = walk->rect.x;
-                    strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
-                    break;
-            }
-            xcb_void_cookie_t strut_cookie = xcb_change_property(xcb_connection,
-                                                                 XCB_PROP_MODE_REPLACE,
-                                                                 walk->bar,
-                                                                 atoms[_NET_WM_STRUT_PARTIAL],
-                                                                 XCB_ATOM_CARDINAL,
-                                                                 32,
-                                                                 12,
-                                                                 &strut_partial);
-
-            /* We also want a graphics-context for the bars (it defines the properties
+            xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
+
+            /* We also want a graphics context for the bars (it defines the properties
              * with which we draw to them) */
             walk->bargc = xcb_generate_id(xcb_connection);
             xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection,
@@ -1670,15 +1720,21 @@ void reconfig_windows(bool redraw_bars) {
                    XCB_CONFIG_WINDOW_HEIGHT |
                    XCB_CONFIG_WINDOW_STACK_MODE;
             values[0] = walk->rect.x;
-            values[1] = walk->rect.y + walk->rect.h - bar_height;
+            if (config.position == POS_TOP)
+                values[1] = walk->rect.y;
+            else
+                values[1] = walk->rect.y + walk->rect.h - bar_height;
             values[2] = walk->rect.w;
             values[3] = bar_height;
             values[4] = XCB_STACK_MODE_ABOVE;
 
+            DLOG("Reconfiguring strut partial property for output %s\n", walk->name);
+            xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
+
             DLOG("Destroying buffer for output %s\n", walk->name);
             xcb_free_pixmap(xcb_connection, walk->buffer);
 
-            DLOG("Reconfiguring Window for output %s to %d,%d\n", walk->name, values[0], values[1]);
+            DLOG("Reconfiguring window for output %s to %d,%d\n", walk->name, values[0], values[1]);
             xcb_void_cookie_t cfg_cookie = xcb_configure_window_checked(xcb_connection,
                                                                         walk->bar,
                                                                         mask,
@@ -1686,7 +1742,7 @@ void reconfig_windows(bool redraw_bars) {
 
             mask = XCB_CW_OVERRIDE_REDIRECT;
             values[0] = (config.hide_on_modifier == M_DOCK ? 0 : 1);
-            DLOG("Changing Window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
+            DLOG("Changing window attribute override_redirect for output %s to %d\n", walk->name, values[0]);
             xcb_void_cookie_t chg_cookie = xcb_change_window_attributes(xcb_connection,
                                                                         walk->bar,
                                                                         mask,
@@ -1723,6 +1779,7 @@ void reconfig_windows(bool redraw_bars) {
             if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
                 xcb_request_failed(chg_cookie, "Could not change window") ||
                 xcb_request_failed(pm_cookie, "Could not create pixmap") ||
+                xcb_request_failed(strut_cookie, "Could not set strut") ||
                 (redraw_bars && (xcb_request_failed(umap_cookie, "Could not unmap window") ||
                                  (config.hide_on_modifier == M_DOCK && xcb_request_failed(map_cookie, "Could not map window"))))) {
                 exit(EXIT_FAILURE);
@@ -1736,10 +1793,10 @@ void reconfig_windows(bool redraw_bars) {
  *
  */
 void draw_bars(bool unhide) {
-    DLOG("Drawing Bars...\n");
-    int i = 0;
+    DLOG("Drawing bars...\n");
+    int workspace_width = 0;
 
-    refresh_statusline();
+    refresh_statusline(false);
 
     i3_output *outputs_walk;
     SLIST_FOREACH(outputs_walk, outputs, slist) {
@@ -1764,39 +1821,11 @@ void draw_bars(bool unhide) {
                                 1,
                                 &rect);
 
-        if (!TAILQ_EMPTY(&statusline_head)) {
-            DLOG("Printing statusline!\n");
-
-            /* Luckily we already prepared a seperate pixmap containing the rendered
-             * statusline, we just have to copy the relevant parts to the relevant
-             * position */
-            trayclient *trayclient;
-            int traypx = 0;
-            TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) {
-                if (!trayclient->mapped)
-                    continue;
-                /* We assume the tray icons are quadratic (we use the font
-                 * *height* as *width* of the icons) because we configured them
-                 * like this. */
-                traypx += font.height + logical_px(2);
-            }
-            /* Add 2px of padding if there are any tray icons */
-            if (traypx > 0)
-                traypx += logical_px(2);
-            xcb_copy_area(xcb_connection,
-                          statusline_pm,
-                          outputs_walk->buffer,
-                          outputs_walk->bargc,
-                          MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + logical_px(4))), 0,
-                          MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0,
-                          MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height);
-        }
-
         if (!config.disable_ws) {
             i3_ws *ws_walk;
             TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) {
-                DLOG("Drawing Button for WS %s at x = %d, len = %d\n",
-                     i3string_as_utf8(ws_walk->name), i, ws_walk->name_width);
+                DLOG("Drawing button for WS %s at x = %d, len = %d\n",
+                     i3string_as_utf8(ws_walk->name), workspace_width, ws_walk->name_width);
                 uint32_t fg_color = colors.inactive_ws_fg;
                 uint32_t bg_color = colors.inactive_ws_bg;
                 uint32_t border_color = colors.inactive_ws_border;
@@ -1824,10 +1853,10 @@ void draw_bars(bool unhide) {
                               outputs_walk->bargc,
                               mask,
                               vals_border);
-                xcb_rectangle_t rect_border = {i,
+                xcb_rectangle_t rect_border = {workspace_width,
                                                logical_px(1),
-                                               ws_walk->name_width + logical_px(10),
-                                               font.height + logical_px(4)};
+                                               ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
+                                               font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)};
                 xcb_poly_fill_rectangle(xcb_connection,
                                         outputs_walk->buffer,
                                         outputs_walk->bargc,
@@ -1838,10 +1867,10 @@ void draw_bars(bool unhide) {
                               outputs_walk->bargc,
                               mask,
                               vals);
-                xcb_rectangle_t rect = {i + logical_px(1),
+                xcb_rectangle_t rect = {workspace_width + logical_px(1),
                                         2 * logical_px(1),
-                                        ws_walk->name_width + logical_px(8),
-                                        font.height + logical_px(2)};
+                                        ws_walk->name_width + 2 * logical_px(ws_hoff_px),
+                                        font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)};
                 xcb_poly_fill_rectangle(xcb_connection,
                                         outputs_walk->buffer,
                                         outputs_walk->bargc,
@@ -1849,12 +1878,19 @@ void draw_bars(bool unhide) {
                                         &rect);
                 set_font_colors(outputs_walk->bargc, fg_color, bg_color);
                 draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc,
-                          i + logical_px(5), 3 * logical_px(1), ws_walk->name_width);
-                i += logical_px(10) + ws_walk->name_width + logical_px(1);
+                          workspace_width + logical_px(ws_hoff_px) + logical_px(1),
+                          logical_px(ws_voff_px),
+                          ws_walk->name_width);
+
+                workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
+                if (TAILQ_NEXT(ws_walk, tailq) != NULL)
+                    workspace_width += logical_px(ws_spacing_px);
             }
         }
 
         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 mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND;
@@ -1864,7 +1900,10 @@ void draw_bars(bool unhide) {
                           outputs_walk->bargc,
                           mask,
                           vals_border);
-            xcb_rectangle_t rect_border = {i, 1, binding.width + 10, font.height + 4};
+            xcb_rectangle_t rect_border = {workspace_width,
+                                           logical_px(1),
+                                           binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
+                                           font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)};
             xcb_poly_fill_rectangle(xcb_connection,
                                     outputs_walk->buffer,
                                     outputs_walk->bargc,
@@ -1876,7 +1915,10 @@ void draw_bars(bool unhide) {
                           outputs_walk->bargc,
                           mask,
                           vals);
-            xcb_rectangle_t rect = {i + 1, 2, binding.width + 8, font.height + 2};
+            xcb_rectangle_t rect = {workspace_width + logical_px(1),
+                                    2 * logical_px(1),
+                                    binding.width + 2 * logical_px(ws_hoff_px),
+                                    font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)};
             xcb_poly_fill_rectangle(xcb_connection,
                                     outputs_walk->buffer,
                                     outputs_walk->bargc,
@@ -1884,12 +1926,41 @@ void draw_bars(bool unhide) {
                                     &rect);
 
             set_font_colors(outputs_walk->bargc, fg_color, bg_color);
-            draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, binding.width);
+            draw_text(binding.name,
+                      outputs_walk->buffer,
+                      outputs_walk->bargc,
+                      workspace_width + logical_px(ws_hoff_px) + logical_px(1),
+                      logical_px(ws_voff_px),
+                      binding.width);
 
             unhide = true;
+            workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width;
+        }
+
+        if (!TAILQ_EMPTY(&statusline_head)) {
+            DLOG("Printing statusline!\n");
+
+            int tray_width = get_tray_width(outputs_walk->trayclients);
+            uint32_t max_statusline_width = outputs_walk->rect.w - workspace_width - tray_width - 2 * logical_px(sb_hoff_px);
+
+            /* If the statusline is too long, try to use short texts. */
+            if (statusline_width > max_statusline_width)
+                refresh_statusline(true);
+
+            /* Luckily we already prepared a seperate pixmap containing the rendered
+             * statusline, we just have to copy the relevant parts to the relevant
+             * position */
+            int visible_statusline_width = MIN(statusline_width, max_statusline_width);
+            xcb_copy_area(xcb_connection,
+                          statusline_pm,
+                          outputs_walk->buffer,
+                          outputs_walk->bargc,
+                          (int16_t)(statusline_width - visible_statusline_width), 0,
+                          (int16_t)(outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width), 0,
+                          (int16_t)visible_statusline_width, (int16_t)bar_height);
         }
 
-        i = 0;
+        workspace_width = 0;
     }
 
     /* Assure the bar is hidden/unhidden according to the specified hidden_state and mode */
index dea26d96344548a5573ebdb6de2813b4a1ccc32e..9a0af0e6cd6b8119e1a5ae4a4bd5fcef915cb66e 100644 (file)
@@ -105,7 +105,7 @@ struct Config {
 
     /** By default, focus follows mouse. If the user explicitly wants to
      * turn this off (and instead rely only on the keyboard for changing
-     * focus), we allow him to do this with this relatively special option.
+     * focus), we allow them to do this with this relatively special option.
      * It is not planned to add any different focus models. */
     bool disable_focus_follows_mouse;
 
@@ -285,6 +285,9 @@ struct Barconfig {
     /** Font specification for all text rendered on the bar. */
     char *font;
 
+    /** A custom separator to use instead of a vertical line. */
+    char *separator_symbol;
+
     /** Hide workspace buttons? Configuration option is 'workspace_buttons no'
      * but we invert the bool to get the correct default when initializing with
      * zero. */
index 0f1a6620ef1200f47b2f8b0738a7ab4589a4b24e..6c960b1f8c765771c2fd6d340872a76d7ea4fae6 100644 (file)
@@ -67,6 +67,7 @@ CFGFUN(enter_mode, const char *mode);
 CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
 
 CFGFUN(bar_font, const char *font);
+CFGFUN(bar_separator_symbol, const char *separator);
 CFGFUN(bar_mode, const char *mode);
 CFGFUN(bar_hidden_state, const char *hidden_state);
 CFGFUN(bar_id, const char *bar_id);
index 8f2c197df3e11e4a9234323a5fd8b3fd6049b289..cec571e9df8eecf71f10c32ebb8c2098f8770d16 100644 (file)
@@ -450,7 +450,7 @@ struct Match {
 /**
  * An Assignment makes specific windows go to a specific workspace/output or
  * run a command for that window. With this mechanism, the user can -- for
- * example -- assign his browser to workspace "www". Checking if a window is
+ * example -- assign their browser to workspace "www". Checking if a window is
  * assigned works by comparing the Match data structure with the window (see
  * match_matches_window()).
  *
@@ -460,7 +460,6 @@ struct Assignment {
      *
      * A_COMMAND = run the specified command for the matching window
      * A_TO_WORKSPACE = assign the matching window to the specified workspace
-     * A_TO_OUTPUT = assign the matching window to the specified output
      *
      * While the type is a bitmask, only one value can be set at a time. It is
      * a bitmask to allow filtering for multiple types, for example in the
@@ -470,18 +469,16 @@ struct Assignment {
     enum {
         A_ANY = 0,
         A_COMMAND = (1 << 0),
-        A_TO_WORKSPACE = (1 << 1),
-        A_TO_OUTPUT = (1 << 2)
+        A_TO_WORKSPACE = (1 << 1)
     } type;
 
     /** the criteria to check if a window matches */
     Match match;
 
-    /** destination workspace/output/command, depending on the type */
+    /** destination workspace/command, depending on the type */
     union {
         char *command;
         char *workspace;
-        char *output;
     } dest;
 
     TAILQ_ENTRY(Assignment) assignments;
index 9712e8b9eda608542ff331ca39bd148726568f89..86cc6836f1300402ad29a26c3cddcc62968c4ff2 100644 (file)
@@ -19,7 +19,7 @@ void handle_key_press(xcb_key_press_event_t *event);
 /**
  * Kills the commanderror i3-nagbar process, if any.
  *
- * Called when reloading/restarting, since the user probably fixed his wrong
+ * Called when reloading/restarting, since the user probably fixed their wrong
  * keybindings.
  *
  * If wait_for_it is set (restarting), this function will waitpid(), otherwise,
index c1a11dfc33ec7cef39b00310cbde3e07b99ff9f7..3a1258273b912847ccc429adafbaba28b86cbd82 100644 (file)
@@ -134,6 +134,20 @@ char *sstrdup(const char *str);
  */
 int sasprintf(char **strp, const char *fmt, ...);
 
+/**
+ * Wrapper around correct write which returns -1 (meaning that
+ * write failed) or count (meaning that all bytes were written)
+ *
+ */
+ssize_t writeall(int fd, const void *buf, size_t count);
+
+/**
+ * Safe-wrapper around writeall which exits if it returns -1 (meaning that
+ * write failed)
+ *
+ */
+ssize_t swrite(int fd, const void *buf, size_t count);
+
 /**
  * Build an i3String from an UTF-8 encoded string.
  * Returns the newly-allocated i3String.
@@ -169,6 +183,12 @@ i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_b
  */
 i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs);
 
+/**
+ * Copies the given i3string.
+ * Note that this will not free the source string.
+ */
+i3String *i3string_copy(i3String *str);
+
 /**
  * Free an i3String.
  *
@@ -211,6 +231,11 @@ size_t i3string_get_num_bytes(i3String *str);
  */
 bool i3string_is_markup(i3String *str);
 
+/**
+ * Set whether the i3String should use Pango markup.
+ */
+void i3string_set_markup(i3String *str, bool is_markup);
+
 /**
  * Returns the number of glyphs in an i3String.
  *
index 10ee7d1740ac4d4f96ee7dca366063713ab06511..6514c477fc7f5ca01b5e9e6daec629eb2877743d 100644 (file)
  *
  */
 Con *output_get_content(Con *output);
+
+/**
+ * Returns an 'output' corresponding to one of left/right/down/up or a specific
+ * output name.
+ *
+ */
+Output *get_output_from_string(Output *current_output, const char *output_str);
index 2f28baa772f9000918c9a6e341579d0f6a94c056..cb784913b303efc859a6894750673b4a596288ee 100644 (file)
@@ -44,6 +44,12 @@ void startup_sequence_delete(struct Startup_Sequence *sequence);
  */
 void startup_monitor_event(SnMonitorEvent *event, void *userdata);
 
+/**
+ * Renames workspaces that are mentioned in the startup sequences.
+ *
+ */
+void startup_sequence_rename_workspace(char *old_name, char *new_name);
+
 /**
  * Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
  *
index 9ee6f156674976275f37220d46e4aa594b8459b8..d0f801e0039240195496c6d3245f0ff9540e43d9 100644 (file)
@@ -179,3 +179,10 @@ Con *workspace_attach_to(Con *ws);
  * The container inherits the layout from the workspace.
  */
 Con *workspace_encapsulate(Con *ws);
+
+/**
+ * Move the given workspace to the specified output.
+ * This returns true if and only if moving the workspace was successful.
+ *
+ */
+bool workspace_move_to_output(Con *ws, char *output);
index 37d5b21502e5882df374933070497d5e73ae0484..6f58d57a409c8eaa87728513c77bbd6a745bf674 100644 (file)
@@ -17,6 +17,12 @@ extern xcb_screen_t *root_screen;
  *
  */
 int logical_px(const int logical) {
+    if (root_screen == NULL) {
+        /* Dpi info may not be available when parsing a config without an X
+         * server, such as for config file validation. */
+        return logical;
+    }
+
     const int dpi = (double)root_screen->height_in_pixels * 25.4 /
                     (double)root_screen->height_in_millimeters;
     /* There are many misconfigurations out there, i.e. systems with screens
index df478fb4fc87f69e6a7d659b4bec7ab8cee4a10d..847bc61beb30c2d474ef4ee5c96639305f4e91e3 100644 (file)
@@ -126,7 +126,10 @@ static void draw_text_pango(const char *text, size_t text_len,
     cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue);
     pango_cairo_update_layout(cr, layout);
     pango_layout_get_pixel_size(layout, NULL, &height);
-    cairo_move_to(cr, x, y - 0.5 * (height - savedFont->height));
+    /* Center the piece of text vertically if its height is smaller than the
+     * cached font height, and just let "high" symbols fall out otherwise. */
+    int yoffset = (height < savedFont->height ? 0.5 : 1) * (height - savedFont->height);
+    cairo_move_to(cr, x, y - yoffset);
     pango_cairo_show_layout(cr, layout);
 
     /* Free resources */
@@ -455,7 +458,7 @@ static int xcb_query_text_width(const xcb_char2b_t *text, size_t text_len) {
                                                                          cookie, &error);
     if (reply == NULL) {
         /* We return a safe estimate because a rendering error is better than
-         * a crash. Plus, the user will see the error in his log. */
+         * a crash. Plus, the user will see the error in their log. */
         fprintf(stderr, "Could not get text extents (X error code %d)\n",
                 error->error_code);
         return savedFont->specific.xcb.info->max_bounds.character_width * text_len;
index 28cb8359207e128b648a664824b6dcc59950e07e..80709ed3b255c638933e0644841accecc7eb4318 100644 (file)
@@ -32,33 +32,11 @@ int ipc_send_message(int sockfd, const uint32_t message_size,
         .size = message_size,
         .type = message_type};
 
-    size_t sent_bytes = 0;
-    int n = 0;
+    if (writeall(sockfd, ((void *)&header), sizeof(i3_ipc_header_t)) == -1)
+        return -1;
 
-    /* This first loop is basically unnecessary. No operating system has
-     * buffers which cannot fit 14 bytes into them, so the write() will only be
-     * called once. */
-    while (sent_bytes < sizeof(i3_ipc_header_t)) {
-        if ((n = write(sockfd, ((void *)&header) + sent_bytes, sizeof(i3_ipc_header_t) - sent_bytes)) == -1) {
-            if (errno == EAGAIN)
-                continue;
-            return -1;
-        }
-
-        sent_bytes += n;
-    }
-
-    sent_bytes = 0;
-
-    while (sent_bytes < message_size) {
-        if ((n = write(sockfd, payload + sent_bytes, message_size - sent_bytes)) == -1) {
-            if (errno == EAGAIN)
-                continue;
-            return -1;
-        }
-
-        sent_bytes += n;
-    }
+    if (writeall(sockfd, payload, message_size) == -1)
+        return -1;
 
     return 0;
 }
index cf634ad43b8d0f9248f60f351f506908b3507d59..db9b6b4a42efbf287e06466295ad0e2d2e30e2e5 100644 (file)
@@ -8,8 +8,10 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <unistd.h>
 #include <stdio.h>
 #include <err.h>
+#include <errno.h>
 
 #include "libi3.h"
 
@@ -56,3 +58,30 @@ int sasprintf(char **strp, const char *fmt, ...) {
     va_end(args);
     return result;
 }
+
+ssize_t writeall(int fd, const void *buf, size_t count) {
+    size_t written = 0;
+    ssize_t n = 0;
+
+    while (written < count) {
+        n = write(fd, buf + written, count - written);
+        if (n == -1) {
+            if (errno == EINTR || errno == EAGAIN)
+                continue;
+            return n;
+        }
+        written += (size_t)n;
+    }
+
+    return written;
+}
+
+ssize_t swrite(int fd, const void *buf, size_t count) {
+    ssize_t n;
+
+    n = writeall(fd, buf, count);
+    if (n == -1)
+        err(EXIT_FAILURE, "Failed to write %d", fd);
+    else
+        return n;
+}
index afeca9741c3fd4479278ce44dbad59180955d7a1..e6297f9eed89b0282a2b104fab360921eb723b07 100644 (file)
@@ -109,6 +109,16 @@ i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) {
     return str;
 }
 
+/**
+ * Copies the given i3string.
+ * Note that this will not free the source string.
+ */
+i3String *i3string_copy(i3String *str) {
+    i3String *copy = i3string_from_utf8(i3string_as_utf8(str));
+    copy->is_markup = str->is_markup;
+    return copy;
+}
+
 /*
  * Free an i3String.
  *
@@ -168,6 +178,13 @@ bool i3string_is_markup(i3String *str) {
     return str->is_markup;
 }
 
+/*
+ * Set whether the i3String should use Pango markup.
+ */
+void i3string_set_markup(i3String *str, bool is_markup) {
+    str->is_markup = is_markup;
+}
+
 /*
  * Returns the number of glyphs in an i3String.
  *
index 2a0448813ecb48ecb97624cc4717b5df8f7ab540..19b0f3a59267378ec30c54e146d19da93a3f8c07 100644 (file)
@@ -30,7 +30,7 @@ It tries to start one of the following (in that order):
 * gedit
 * mc-edit
 
-Please don’t complain about the order: If the user has any preference, he will
+Please don’t complain about the order: If the user has any preference, they will
 have $VISUAL or $EDITOR set.
 
 == SEE ALSO
index 6b04c4316fda10d0f8fe7a1876ad356df1db5984..22754c0bd96686e74e5cb372384b9381db9b8ef4 100644 (file)
@@ -23,7 +23,7 @@ It tries to start one of the following (in that order):
 * w3m
 * i3-sensible-editor(1)
 
-Please don’t complain about the order: If the user has any preference, he will
+Please don’t complain about the order: If the user has any preference, they will
 have $PAGER set.
 
 == SEE ALSO
index d1ad5198edaa8bd9fa30f63f3056d5f93a34149e..6fa91ac893900c61d077fba66a60a453fed3a5b6 100644 (file)
@@ -33,8 +33,8 @@ It tries to start one of the following (in that order):
 * roxterm
 * xfce4-terminal
 
-Please don’t complain about the order: If the user has any preference, she will
-have $TERMINAL set or modified her i3 configuration file.
+Please don’t complain about the order: If the user has any preference, they will
+have $TERMINAL set or modified their i3 configuration file.
 
 == SEE ALSO
 
index 82348df79c9c47d6b5dc9f352eceaaabb4b5100f..315a9218dd5930695e540506ce11e0cd30cabbf4 100644 (file)
@@ -353,6 +353,8 @@ state MODE:
 state NOP:
   comment = string
       -> call cmd_nop($comment)
+  end
+      -> call cmd_nop(NULL)
 
 state SCRATCHPAD:
   'show'
index dbdf83c2808aa076fce039a0a27313c8cb7c6953..25be5cf107e2bef2eebdba243980ee04085c9efa 100644 (file)
@@ -370,6 +370,7 @@ state BAR:
   'output'                 -> BAR_OUTPUT
   'tray_output'            -> BAR_TRAY_OUTPUT
   'font'                   -> BAR_FONT
+  'separator_symbol'       -> BAR_SEPARATOR_SYMBOL
   'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
   'workspace_buttons'      -> BAR_WORKSPACE_BUTTONS
   'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS
@@ -435,6 +436,10 @@ state BAR_FONT:
   font = string
       -> call cfg_bar_font($font); BAR
 
+state BAR_SEPARATOR_SYMBOL:
+  separator = string
+      -> call cfg_bar_separator_symbol($separator); BAR
+
 state BAR_BINDING_MODE_INDICATOR:
   value = word
       -> call cfg_bar_binding_mode_indicator($value); BAR
diff --git a/release.sh b/release.sh
new file mode 100755 (executable)
index 0000000..06f6dfc
--- /dev/null
@@ -0,0 +1,193 @@
+#!/bin/zsh
+# This script is used to prepare a new release of i3.
+
+export RELEASE_VERSION="4.10"
+export PREVIOUS_VERSION="4.9.1"
+export RELEASE_BRANCH="master"
+
+if [ ! -e "../i3.github.io" ]
+then
+       echo "../i3.github.io does not exist."
+       echo "Use git clone git://github.com/i3/i3.github.io"
+       exit 1
+fi
+
+if [ ! -e "RELEASE-NOTES-${RELEASE_VERSION}" ]
+then
+       echo "RELEASE-NOTES-${RELEASE_VERSION} not found."
+       exit 1
+fi
+
+if git diff-files --quiet --exit-code debian/changelog
+then
+       echo "Expected debian/changelog to be changed (containing the changelog for ${RELEASE_VERSION})."
+       exit 1
+fi
+
+eval $(gpg-agent --daemon)
+export GPG_AGENT_INFO
+
+################################################################################
+# Section 1: update git and build the release tarball
+################################################################################
+
+STARTDIR=$PWD
+
+TMPDIR=$(mktemp -d)
+cd $TMPDIR
+if ! wget http://i3wm.org/downloads/i3-${PREVIOUS_VERSION}.tar.bz2; then
+       echo "Could not download i3-${PREVIOUS_VERSION}.tar.bz2 (required for comparing files)."
+       exit 1
+fi
+git clone --quiet --branch "${RELEASE_BRANCH}" file://${STARTDIR}
+cd i3
+if [ ! -e "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" ]; then
+       echo "Required file RELEASE-NOTES-${RELEASE_VERSION} not found."
+       exit 1
+fi
+git checkout -b release-${RELEASE_VERSION}
+cp "${STARTDIR}/RELEASE-NOTES-${RELEASE_VERSION}" "RELEASE-NOTES-${RELEASE_VERSION}"
+git add RELEASE-NOTES-${RELEASE_VERSION}
+git rm RELEASE-NOTES-${PREVIOUS_VERSION}
+sed -i "s,<refmiscinfo class=\"version\">[^<]*</refmiscinfo>,<refmiscinfo class=\"version\">${RELEASE_VERSION}</refmiscinfo>,g" man/asciidoc.conf
+git commit -a -m "release i3 ${RELEASE_VERSION}"
+git tag "${RELEASE_VERSION}" -m "release i3 ${RELEASE_VERSION}" --sign --local-user=0x4AC8EE1D
+
+make dist
+
+echo "Differences in the release tarball file lists:"
+
+diff -u \
+       <(tar tf ../i3-${PREVIOUS_VERSION}.tar.bz2 | sed "s,i3-${PREVIOUS_VERSION}/,,g" | sort) \
+       <(tar tf    i3-${RELEASE_VERSION}.tar.bz2  | sed "s,i3-${RELEASE_VERSION}/,,g"  | sort) \
+       | colordiff
+
+if ! tar xf i3-${RELEASE_VERSION}.tar.bz2 --to-stdout --strip-components=1 i3-${RELEASE_VERSION}/I3_VERSION | grep -q "^${RELEASE_VERSION} "
+then
+       echo "I3_VERSION file does not start with ${RELEASE_VERSION}"
+       exit 1
+fi
+
+gpg --armor -b i3-${RELEASE_VERSION}.tar.bz2
+
+if [ "${RELEASE_BRANCH}" = "master" ]; then
+       git checkout master
+       git merge --no-ff release-${RELEASE_VERSION} -m "Merge branch 'release-${RELEASE_VERSION}'"
+       git checkout next
+       git merge --no-ff master -m "Merge branch 'master' into next"
+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"
+fi
+
+################################################################################
+# Section 2: Debian packaging
+################################################################################
+
+cd "${TMPDIR}"
+mkdir debian
+
+# Copy over the changelog because we expect it to be locally modified in the
+# start directory.
+cp "${STARTDIR}/debian/changelog" i3/debian/changelog
+
+cat > ${TMPDIR}/Dockerfile <<EOT
+FROM debian:sid
+RUN sed -i 's,^deb \(.*\),deb \1\ndeb-src \1,g' /etc/apt/sources.list
+RUN apt-get update && apt-get install -y dpkg-dev devscripts
+COPY i3/i3-${RELEASE_VERSION}.tar.bz2 /usr/src/i3-wm_${RELEASE_VERSION}.orig.tar.bz2
+WORKDIR /usr/src/
+RUN tar xf i3-wm_${RELEASE_VERSION}.orig.tar.bz2
+WORKDIR /usr/src/i3-${RELEASE_VERSION}
+COPY i3/debian /usr/src/i3-${RELEASE_VERSION}/debian/
+RUN mkdir debian/source
+RUN echo '3.0 (quilt)' > debian/source/format
+WORKDIR /usr/src
+RUN mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' i3-${RELEASE_VERSION}/debian/control
+WORKDIR /usr/src/i3-${RELEASE_VERSION}
+RUN dpkg-buildpackage -sa -j8
+EOT
+
+CONTAINER_NAME=$(echo "i3-${TMPDIR}" | sed 's,/,,g')
+docker build -t i3 .
+for file in $(docker run --name "${CONTAINER_NAME}" i3 /bin/sh -c "ls /usr/src/i3*_${RELEASE_VERSION}*")
+do
+       docker cp "${CONTAINER_NAME}:${file}" ${TMPDIR}/debian/
+done
+
+echo "Content of resulting package’s .changes file:"
+cat ${TMPDIR}/debian/*.changes
+
+# debsign is in devscripts, which is available in fedora and debian
+debsign -k4AC8EE1D ${TMPDIR}/debian/*.changes
+
+# TODO: docker cleanup
+
+################################################################################
+# Section 3: website
+################################################################################
+
+cd ${TMPDIR}
+git clone --quiet ${STARTDIR}/../i3.github.io
+cd i3.github.io
+cp ${TMPDIR}/i3/i3-${RELEASE_VERSION}.tar.bz2* downloads/
+git add downloads/i3-${RELEASE_VERSION}.tar.bz2*
+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,<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
+
+git commit -a -m "add ${RELEASE_VERSION} release"
+
+mkdir docs/${PREVIOUS_VERSION}
+tar cf - '--exclude=[0-9]\.[0-9e]*' docs | tar xf - --strip-components=1 -C docs/${PREVIOUS_VERSION}
+git add docs/${PREVIOUS_VERSION}
+git commit -a -m "save docs for ${PREVIOUS_VERSION}"
+
+for i in $(find _docs -maxdepth 1 -and -type f -and \! -regex ".*\.\(html\|man\)$" -and \! -name "Makefile")
+do
+       base="$(basename $i)"
+       [ -e "${STARTDIR}/docs/${base}" ] && cp "${STARTDIR}/docs/${base}" "_docs/${base}"
+done
+
+(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/
+done
+
+git commit -a -m "update docs for ${RELEASE_VERSION}"
+
+################################################################################
+# Section 4: final push instructions
+################################################################################
+
+echo "As a final sanity check, install the debian package and see whether i3 works."
+
+echo "When satisfied, run:"
+echo "  cd ${TMPDIR}/i3"
+echo "  git checkout next"
+echo "  vi debian/changelog"
+# TODO: can we just set up the remote spec properly?
+echo "  git push git@github.com:i3/i3 next"
+echo "  git push git@github.com:i3/i3 master"
+echo "  git push git@github.com:i3/i3 --tags"
+echo ""
+echo "  cd ${TMPDIR}/i3.github.io"
+# TODO: can we just set up the remote spec properly?
+echo "  git push git@github.com:i3/i3.github.io master"
+echo ""
+echo "  cd ${TMPDIR}/debian"
+echo "  dput *.changes"
+echo ""
+echo "Announce on:"
+echo "  twitter"
+echo "  google+"
+echo "  mailing list"
+echo "  #i3 topic"
index 51ffcf3ab3c4361452897713bf399bdf70c87302..10abc0570c3140201a1d46cae53421f3f243d16d 100644 (file)
@@ -46,6 +46,9 @@ static bool tiling_resize_for_border(Con *con, border_t border, xcb_button_press
         case BORDER_BOTTOM:
             search_direction = D_DOWN;
             break;
+        default:
+            assert(false);
+            break;
     }
 
     bool res = resize_find_tiling_participants(&first, &second, search_direction);
@@ -380,11 +383,6 @@ int handle_button_press(xcb_button_press_event_t *event) {
         return 0;
     }
 
-    if (event->child != XCB_NONE) {
-        DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n");
-        return route_click(con, event, mod_pressed, CLICK_INSIDE);
-    }
-
     /* Check if the click was on the decoration of a child */
     Con *child;
     TAILQ_FOREACH(child, &(con->nodes_head), nodes) {
@@ -394,5 +392,10 @@ int handle_button_press(xcb_button_press_event_t *event) {
         return route_click(child, event, mod_pressed, CLICK_DECORATION);
     }
 
+    if (event->child != XCB_NONE) {
+        DLOG("event->child not XCB_NONE, so this is an event which originated from a click into the application, but the application did not handle it.\n");
+        return route_click(con, event, mod_pressed, CLICK_INSIDE);
+    }
+
     return route_click(con, event, mod_pressed, CLICK_BORDER);
 }
index 92203e3925ba7ab730d510537238abc84941df4e..9b51b3ecc7cba9d7561819d77a42519fd757d142 100644 (file)
@@ -65,28 +65,6 @@ static bool definitelyGreaterThan(float a, float b, float epsilon) {
     return (a - b) > ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
 }
 
-/*
- * Returns an 'output' corresponding to one of left/right/down/up or a specific
- * output name.
- *
- */
-static Output *get_output_from_string(Output *current_output, const char *output_str) {
-    Output *output;
-
-    if (strcasecmp(output_str, "left") == 0)
-        output = get_output_next_wrap(D_LEFT, current_output);
-    else if (strcasecmp(output_str, "right") == 0)
-        output = get_output_next_wrap(D_RIGHT, current_output);
-    else if (strcasecmp(output_str, "up") == 0)
-        output = get_output_next_wrap(D_UP, current_output);
-    else if (strcasecmp(output_str, "down") == 0)
-        output = get_output_next_wrap(D_DOWN, current_output);
-    else
-        output = get_output_by_name(output_str);
-
-    return output;
-}
-
 /*
  * Returns the output containing the given container.
  */
@@ -516,27 +494,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 
     LOG("should move window to workspace %s\n", name);
     /* get the workspace */
-    Con *ws = NULL;
-    Con *output = NULL;
-
-    /* first look for a workspace with this name */
-    TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
-        GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
-    }
-
-    /* if the name is plain digits, we interpret this as a "workspace number"
-     * command */
-    if (!ws && name_is_digits(name)) {
-        long parsed_num = ws_name_to_number(name);
-        TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
-            GREP_FIRST(ws, output_get_content(output),
-                       child->num == parsed_num);
-        }
-    }
-
-    /* if no workspace was found, make a new one */
-    if (!ws)
-        ws = workspace_get(name, NULL);
+    Con *ws = workspace_get(name, NULL);
 
     ws = maybe_auto_back_and_forth_workspace(ws);
 
@@ -1071,30 +1029,7 @@ void cmd_workspace_name(I3_CMD, char *name) {
     DLOG("should switch to workspace %s\n", name);
     if (maybe_back_and_forth(cmd_output, name))
         return;
-
-    Con *ws = NULL;
-    Con *output = NULL;
-
-    /* first look for a workspace with this name */
-    TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
-        GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name));
-    }
-
-    /* if the name is only digits, we interpret this as a "workspace number"
-     * command */
-    if (!ws && name_is_digits(name)) {
-        long parsed_num = ws_name_to_number(name);
-        TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
-            GREP_FIRST(ws, output_get_content(output),
-                       child->num == parsed_num);
-        }
-    }
-
-    /* if no workspace was found, make a new one */
-    if (!ws)
-        ws = workspace_get(name, NULL);
-
-    workspace_show(ws);
+    workspace_show_by_name(name);
 
     cmd_output->needs_tree_render = true;
     // XXX: default reply for now, make this a better reply
@@ -1177,28 +1112,13 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
 
     HANDLE_EMPTY_MATCH;
 
-    /* get the output */
     Output *current_output = NULL;
-    Output *output;
-
     // TODO: fix the handling of criteria
     TAILQ_FOREACH(current, &owindows, owindows)
     current_output = get_output_of_con(current->con);
-
     assert(current_output != NULL);
 
-    // TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
-    if (strcasecmp(name, "up") == 0)
-        output = get_output_next_wrap(D_UP, current_output);
-    else if (strcasecmp(name, "down") == 0)
-        output = get_output_next_wrap(D_DOWN, current_output);
-    else if (strcasecmp(name, "left") == 0)
-        output = get_output_next_wrap(D_LEFT, current_output);
-    else if (strcasecmp(name, "right") == 0)
-        output = get_output_next_wrap(D_RIGHT, current_output);
-    else
-        output = get_output_by_name(name);
-
+    Output *output = get_output_from_string(current_output, name);
     if (!output) {
         LOG("No such output found.\n");
         ysuccess(false);
@@ -1265,102 +1185,13 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) {
 
     owindow *current;
     TAILQ_FOREACH(current, &owindows, owindows) {
-        Output *current_output = get_output_of_con(current->con);
-        if (!current_output) {
-            ELOG("Cannot get current output. This is a bug in i3.\n");
-            ysuccess(false);
-            return;
-        }
-        Output *output = get_output_from_string(current_output, name);
-        if (!output) {
-            ELOG("Could not get output from string \"%s\"\n", name);
+        Con *ws = con_get_workspace(current->con);
+        bool success = workspace_move_to_output(ws, name);
+        if (!success) {
+            ELOG("Failed to move workspace to output.\n");
             ysuccess(false);
             return;
         }
-
-        Con *content = output_get_content(output->con);
-        LOG("got output %p with content %p\n", output, content);
-
-        Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
-        LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
-
-        Con *ws = con_get_workspace(current->con);
-        LOG("should move workspace %p / %s\n", ws, ws->name);
-        bool workspace_was_visible = workspace_is_visible(ws);
-
-        if (con_num_children(ws->parent) == 1) {
-            LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
-
-            /* check if we can find a workspace assigned to this output */
-            bool used_assignment = false;
-            struct Workspace_Assignment *assignment;
-            TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
-                if (strcmp(assignment->output, current_output->name) != 0)
-                    continue;
-
-                /* check if this workspace is already attached to the tree */
-                Con *workspace = NULL, *out;
-                TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
-                GREP_FIRST(workspace, output_get_content(out),
-                           !strcasecmp(child->name, assignment->name));
-                if (workspace != NULL)
-                    continue;
-
-                /* so create the workspace referenced to by this assignment */
-                LOG("Creating workspace from assignment %s.\n", assignment->name);
-                workspace_get(assignment->name, NULL);
-                used_assignment = true;
-                break;
-            }
-
-            /* if we couldn't create the workspace using an assignment, create
-             * it on the output */
-            if (!used_assignment)
-                create_workspace_on_output(current_output, ws->parent);
-
-            /* notify the IPC listeners */
-            ipc_send_workspace_event("init", ws, NULL);
-        }
-        DLOG("Detaching\n");
-
-        /* detach from the old output and attach to the new output */
-        Con *old_content = ws->parent;
-        con_detach(ws);
-        if (workspace_was_visible) {
-            /* The workspace which we just detached was visible, so focus
-             * the next one in the focus-stack. */
-            Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
-            LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
-            workspace_show(focus_ws);
-        }
-        con_attach(ws, content, false);
-
-        /* fix the coordinates of the floating containers */
-        Con *floating_con;
-        TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
-        floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
-
-        ipc_send_workspace_event("move", ws, NULL);
-        if (workspace_was_visible) {
-            /* Focus the moved workspace on the destination output. */
-            workspace_show(ws);
-        }
-
-        /* NB: We cannot simply work with previously_visible_ws since it might
-         * have been cleaned up by workspace_show() already, depending on the
-         * focus order/number of other workspaces on the output.
-         * Instead, we loop through the available workspaces and only work with
-         * previously_visible_ws if we still find it. */
-        TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
-            if (ws != previously_visible_ws)
-                continue;
-
-            /* Call the on_remove_child callback of the workspace which previously
-             * was visible on the destination output. Since it is no longer
-             * visible, it might need to get cleaned up. */
-            CALL(previously_visible_ws, on_remove_child);
-            break;
-        }
     }
 
     cmd_output->needs_tree_render = true;
@@ -2034,6 +1865,24 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
     Con *parent = workspace->parent;
     con_detach(workspace);
     con_attach(workspace, parent, false);
+
+    /* Move the workspace to the correct output if it has an assignment */
+    struct Workspace_Assignment *assignment = NULL;
+    TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
+        if (assignment->output == NULL)
+            continue;
+        if (strcmp(assignment->name, workspace->name) != 0 && (!name_is_digits(assignment->name) || ws_name_to_number(assignment->name) != workspace->num)) {
+            continue;
+        }
+
+        workspace_move_to_output(workspace, assignment->output);
+
+        if (previously_focused)
+            workspace_show(con_get_workspace(previously_focused));
+
+        break;
+    }
+
     /* Restore the previous focus since con_attach messes with the focus. */
     con_focus(previously_focused);
 
@@ -2044,6 +1893,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) {
     ewmh_update_desktop_names();
     ewmh_update_desktop_viewport();
     ewmh_update_current_desktop();
+
+    startup_sequence_rename_workspace(old_name, new_name);
 }
 
 /*
index f325a048afc6ebf6859ee81347fafd7c8dc4de28..fa4c2360101513e4796490c9720ff9b63533915f 100644 (file)
@@ -216,8 +216,9 @@ char *parse_string(const char **walk, bool as_word) {
     if (**walk == '"') {
         beginning++;
         (*walk)++;
-        while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\'))
-            (*walk)++;
+        for (; **walk != '\0' && **walk != '"'; (*walk)++)
+            if (**walk == '\\' && *(*walk + 1) != '\0')
+                (*walk)++;
     } else {
         if (!as_word) {
             /* For a string (starting with 's'), the delimiters are
@@ -248,10 +249,10 @@ char *parse_string(const char **walk, bool as_word) {
     for (inpos = 0, outpos = 0;
          inpos < (*walk - beginning);
          inpos++, outpos++) {
-        /* We only handle escaped double quotes to not break
-         * backwards compatibility with people using \w in
-         * regular expressions etc. */
-        if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
+        /* We only handle escaped double quotes and backslashes to not break
+         * backwards compatibility with people using \w in regular expressions
+         * etc. */
+        if (beginning[inpos] == '\\' && (beginning[inpos + 1] == '"' || beginning[inpos + 1] == '\\'))
             inpos++;
         str[outpos] = beginning[inpos];
     }
index 4957c303b5bcced0c2e461e652c1c6052c9091f7..b610b0c60c1c9cfb792d107a7ff6d05acdc27487 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -1149,12 +1149,12 @@ Rect con_border_style_rect(Con *con) {
     /* Shortcut to avoid calling con_adjacent_borders() on dock containers. */
     int border_style = con_border_style(con);
     if (border_style == BS_NONE)
-        return (Rect) {0, 0, 0, 0};
+        return (Rect){0, 0, 0, 0};
     borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
     if (border_style == BS_NORMAL) {
-        result = (Rect) {border_width, 0, -(2 * border_width), -(border_width)};
+        result = (Rect){border_width, 0, -(2 * border_width), -(border_width)};
     } else {
-        result = (Rect) {border_width, border_width, -(2 * border_width), -(2 * border_width)};
+        result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)};
     }
 
     /* Floating windows are never adjacent to any other window, so
@@ -1473,7 +1473,7 @@ Rect con_minimum_size(Con *con) {
 
     if (con_is_leaf(con)) {
         DLOG("leaf node, returning 75x50\n");
-        return (Rect) {0, 0, 75, 50};
+        return (Rect){0, 0, 75, 50};
     }
 
     if (con->type == CT_FLOATING_CON) {
@@ -1493,7 +1493,7 @@ Rect con_minimum_size(Con *con) {
         }
         DLOG("stacked/tabbed now, returning %d x %d + deco_rect = %d\n",
              max_width, max_height, deco_height);
-        return (Rect) {0, 0, max_width, max_height + deco_height};
+        return (Rect){0, 0, max_width, max_height + deco_height};
     }
 
     /* For horizontal/vertical split containers we sum up the width (h-split)
@@ -1513,7 +1513,7 @@ Rect con_minimum_size(Con *con) {
             }
         }
         DLOG("split container, returning width = %d x height = %d\n", width, height);
-        return (Rect) {0, 0, width, height};
+        return (Rect){0, 0, width, height};
     }
 
     ELOG("Unhandled case, type = %d, layout = %d, split = %d\n",
index b41f0e1b7eb2f909aaf7afe9141ddf8c586bc71f..6f906b8ce02e7218828ae2f449bf3223a82dd205 100644 (file)
@@ -158,8 +158,6 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
             assign = TAILQ_FIRST(&assignments);
             if (assign->type == A_TO_WORKSPACE)
                 FREE(assign->dest.workspace);
-            else if (assign->type == A_TO_OUTPUT)
-                FREE(assign->dest.output);
             else if (assign->type == A_COMMAND)
                 FREE(assign->dest.command);
             match_free(&(assign->match));
index 80d7876b927f573d080a52a5b5c1aa259d593a1a..c8b25c7645f6cfc7e8c2d8ab210101138719957d 100644 (file)
@@ -421,6 +421,11 @@ CFGFUN(bar_font, const char *font) {
     current_bar.font = sstrdup(font);
 }
 
+CFGFUN(bar_separator_symbol, const char *separator) {
+    FREE(current_bar.separator_symbol);
+    current_bar.separator_symbol = sstrdup(separator);
+}
+
 CFGFUN(bar_mode, const char *mode) {
     current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE));
 }
index 24cebcece9707755087d83928a49030629ecea63..eef03cae238e0cf7455fd64ca1b18658e47d977f 100644 (file)
@@ -778,14 +778,9 @@ static char *migrate_config(char *input, off_t size) {
 
     /* write the whole config file to the pipe, the script will read everything
      * immediately */
-    int written = 0;
-    int ret;
-    while (written < size) {
-        if ((ret = write(writepipe[1], input + written, size - written)) < 0) {
-            warn("Could not write to pipe");
-            return NULL;
-        }
-        written += ret;
+    if (writeall(writepipe[1], input, size) == -1) {
+        warn("Could not write to pipe");
+        return NULL;
     }
     close(writepipe[1]);
 
@@ -795,7 +790,7 @@ static char *migrate_config(char *input, off_t size) {
     /* read the script’s output */
     int conv_size = 65535;
     char *converted = malloc(conv_size);
-    int read_bytes = 0;
+    int read_bytes = 0, ret;
     do {
         if (read_bytes == conv_size) {
             conv_size += 65535;
@@ -823,11 +818,11 @@ static char *migrate_config(char *input, off_t size) {
         fprintf(stderr, "Migration process exit code was != 0\n");
         if (returncode == 2) {
             fprintf(stderr, "could not start the migration script\n");
-            /* TODO: script was not found. tell the user to fix his system or create a v4 config */
+            /* TODO: script was not found. tell the user to fix their system or create a v4 config */
         } else if (returncode == 1) {
             fprintf(stderr, "This already was a v4 config. Please add the following line to your config file:\n");
             fprintf(stderr, "# i3 config file (v4)\n");
-            /* TODO: nag the user with a message to include a hint for i3 in his config file */
+            /* TODO: nag the user with a message to include a hint for i3 in their config file */
         }
         return NULL;
     }
index 8a2fde2c745c96e7d479dca5dafa35e6244309f6..e6ca2d7188d6392c172aa09bf17d6eb0588f6544 100644 (file)
@@ -511,7 +511,7 @@ DRAGGING_CB(resize_window_callback) {
         dest_height = max(dest_height, (int)(dest_width / ratio));
     }
 
-    con->rect = (Rect) {dest_x, dest_y, dest_width, dest_height};
+    con->rect = (Rect){dest_x, dest_y, dest_width, dest_height};
 
     /* Obey window size */
     floating_check_size(con);
index 7a6ab8dbc620d4e997c2fccd4e35a7540bd5d8fa..041f7e366f76a96da1620c1ab950ffacc084edc5 100644 (file)
@@ -106,7 +106,7 @@ static void check_crossing_screen_boundary(uint32_t x, uint32_t y) {
         return;
     }
 
-    /* Focus the output on which the user moved his cursor */
+    /* Focus the output on which the user moved their cursor */
     Con *old_focused = focused;
     Con *next = con_descend_focused(output_get_content(output->con));
     /* Since we are switching outputs, this *must* be a different workspace, so
@@ -149,7 +149,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) {
         enter_child = true;
     }
 
-    /* If not, then the user moved his cursor to the root window. In that case, we adjust c_ws */
+    /* If not, then the user moved their cursor to the root window. In that case, we adjust c_ws */
     if (con == NULL) {
         DLOG("Getting screen at %d x %d\n", event->root_x, event->root_y);
         check_crossing_screen_boundary(event->root_x, event->root_y);
@@ -651,18 +651,18 @@ static void handle_expose_event(xcb_expose_event_t *event) {
     return;
 }
 
-#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
-#define _NET_WM_MOVERESIZE_SIZE_TOP          1
-#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
-#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
-#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
-#define _NET_WM_MOVERESIZE_MOVE              8  /* movement only */
-#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9  /* size via keyboard */
-#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD     10 /* move via keyboard */
-#define _NET_WM_MOVERESIZE_CANCEL            11 /* cancel operation */
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
+#define _NET_WM_MOVERESIZE_SIZE_TOP 1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
+#define _NET_WM_MOVERESIZE_MOVE 8           /* movement only */
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9  /* size via keyboard */
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
+#define _NET_WM_MOVERESIZE_CANCEL 11        /* cancel operation */
 
 /*
  * Handle client messages (EWMH)
@@ -874,12 +874,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             .root_y = y_root,
             .event_x = x_root - (con->rect.x),
             .event_y = y_root - (con->rect.y)};
-        if (direction == _NET_WM_MOVERESIZE_MOVE) {
-            floating_drag_window(con->parent, &fake);
-        } else if (direction >= _NET_WM_MOVERESIZE_SIZE_TOPLEFT && direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
-            floating_resize_window(con->parent, FALSE, &fake);
-        } else {
-            DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
+        switch (direction) {
+            case _NET_WM_MOVERESIZE_MOVE:
+                floating_drag_window(con->parent, &fake);
+                break;
+            case _NET_WM_MOVERESIZE_SIZE_TOPLEFT... _NET_WM_MOVERESIZE_SIZE_LEFT:
+                floating_resize_window(con->parent, FALSE, &fake);
+                break;
+            default:
+                DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction);
+                break;
         }
     } else {
         DLOG("unhandled clientmessage\n");
@@ -1159,6 +1163,87 @@ static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t stat
     return true;
 }
 
+/*
+ * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients.
+ *
+ */
+static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+                                        xcb_atom_t name, xcb_get_property_reply_t *prop) {
+    DLOG("strut partial change for window 0x%08x\n", window);
+
+    Con *con;
+    if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
+        return false;
+    }
+
+    if (prop == NULL) {
+        xcb_generic_error_t *err = NULL;
+        xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL,
+                                                                  XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX);
+        prop = xcb_get_property_reply(conn, strut_cookie, &err);
+
+        if (err != NULL) {
+            DLOG("got error when getting strut partial property: %d\n", err->error_code);
+            free(err);
+            return false;
+        }
+
+        if (prop == NULL) {
+            return false;
+        }
+    }
+
+    DLOG("That is con %p / %s\n", con, con->name);
+
+    window_update_strut_partial(con->window, prop);
+
+    /* we only handle this change for dock clients */
+    if (con->parent == NULL || con->parent->type != CT_DOCKAREA) {
+        return true;
+    }
+
+    Con *search_at = croot;
+    Con *output = con_get_output(con);
+    if (output != NULL) {
+        DLOG("Starting search at output %s\n", output->name);
+        search_at = output;
+    }
+
+    /* find out the desired position of this dock window */
+    if (con->window->reserved.top > 0 && con->window->reserved.bottom == 0) {
+        DLOG("Top dock client\n");
+        con->window->dock = W_DOCK_TOP;
+    } else if (con->window->reserved.top == 0 && con->window->reserved.bottom > 0) {
+        DLOG("Bottom dock client\n");
+        con->window->dock = W_DOCK_BOTTOM;
+    } else {
+        DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
+        if (con->geometry.y < (search_at->rect.height / 2)) {
+            DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
+                 con->geometry.y, (search_at->rect.height / 2));
+            con->window->dock = W_DOCK_TOP;
+        } else {
+            DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
+                 con->geometry.y, (search_at->rect.height / 2));
+            con->window->dock = W_DOCK_BOTTOM;
+        }
+    }
+
+    /* find the dockarea */
+    Con *dockarea = con_for_window(search_at, con->window, NULL);
+    assert(dockarea != NULL);
+
+    /* attach the dock to the dock area */
+    con_detach(con);
+    con->parent = dockarea;
+    TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused);
+    TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes);
+
+    tree_render();
+
+    return true;
+}
+
 /* Returns false if the event could not be processed (e.g. the window could not
  * be found), true otherwise */
 typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
@@ -1177,7 +1262,8 @@ static struct property_handler_t property_handlers[] = {
     {0, UINT_MAX, handle_clientleader_change},
     {0, UINT_MAX, handle_transient_for},
     {0, 128, handle_windowrole_change},
-    {0, 128, handle_class_change}};
+    {0, 128, handle_class_change},
+    {0, UINT_MAX, handle_strut_partial_change}};
 #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
 
 /*
@@ -1196,6 +1282,7 @@ void property_handlers_init(void) {
     property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
     property_handlers[6].atom = A_WM_WINDOW_ROLE;
     property_handlers[7].atom = XCB_ATOM_WM_CLASS;
+    property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
 }
 
 static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
@@ -1315,7 +1402,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
             handle_motion_notify((xcb_motion_notify_event_t *)event);
             break;
 
-        /* Enter window = user moved his mouse over the window */
+        /* Enter window = user moved their mouse over the window */
         case XCB_ENTER_NOTIFY:
             handle_enter_notify((xcb_enter_notify_event_t *)event);
             break;
index 7dbb6632cb88191f77a86cfe398efaeff1c57297..52f7db2efe432c50fedecefb0940b8404b8ef2b2 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -157,7 +157,7 @@ static void dump_binding(yajl_gen gen, Binding *bind) {
     y(integer, bind->keycode);
 
     ystr("input_type");
-    ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
+    ystr((const char *)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse"));
 
     ystr("symbol");
     if (bind->symbol == NULL)
@@ -397,7 +397,8 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
         ystr("transient_for");
         if (con->window->transient_for == XCB_NONE)
             y(null);
-        else y(integer, con->window->transient_for);
+        else
+            y(integer, con->window->transient_for);
 
         y(map_close);
     }
@@ -449,6 +450,10 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     y(array_open);
     Match *match;
     TAILQ_FOREACH(match, &(con->swallow_head), matches) {
+        /* We will generate a new restart_mode match specification after this
+         * loop, so skip this one. */
+        if (match->restart_mode)
+            continue;
         y(map_open);
         if (match->dock != -1) {
             ystr("dock");
@@ -593,6 +598,11 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
     YSTR_IF_SET(status_command);
     YSTR_IF_SET(font);
 
+    if (config->separator_symbol) {
+        ystr("separator_symbol");
+        ystr(config->separator_symbol);
+    }
+
     ystr("workspace_buttons");
     y(bool, !config->hide_workspace_buttons);
 
index ccd71c3736db139623305628fdc40694847ba4b1..c4d39fcee57fa569be1ac07fced152b57b56b55f 100644 (file)
@@ -105,7 +105,7 @@ static int json_end_map(void *ctx) {
             int cnt = 1;
             while (workspace != NULL) {
                 FREE(json_node->name);
-                asprintf(&(json_node->name), "%s_%d", base, cnt++);
+                sasprintf(&(json_node->name), "%s_%d", base, cnt++);
                 workspace = NULL;
                 TAILQ_FOREACH(output, &(croot->nodes_head), nodes)
                 GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name));
index 473be1e5a46e72a9719256deedf0a0590c3efac7..8b514178bbbf3283fc9391f11ef5c6f42643ba1a 100644 (file)
@@ -502,7 +502,7 @@ int main(int argc, char *argv[]) {
     }
 
     xcb_void_cookie_t cookie;
-    cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {ROOT_EVENT_MASK});
+    cookie = xcb_change_window_attributes_checked(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
     check_error(conn, cookie, "Another window manager seems to be running");
 
     xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(conn, gcookie, NULL);
@@ -742,10 +742,10 @@ int main(int argc, char *argv[]) {
 
         xcb_create_gc(conn, gc, root->root,
                       XCB_GC_FUNCTION | XCB_GC_PLANE_MASK | XCB_GC_FILL_STYLE | XCB_GC_SUBWINDOW_MODE,
-                      (uint32_t[]) {XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
+                      (uint32_t[]){XCB_GX_COPY, ~0, XCB_FILL_STYLE_SOLID, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS});
 
         xcb_copy_area(conn, root->root, pixmap, gc, 0, 0, 0, 0, width, height);
-        xcb_change_window_attributes_checked(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]) {pixmap});
+        xcb_change_window_attributes_checked(conn, root->root, XCB_CW_BACK_PIXMAP, (uint32_t[]){pixmap});
         xcb_flush(conn);
         xcb_free_gc(conn, gc);
         xcb_free_pixmap(conn, pixmap);
@@ -778,7 +778,7 @@ int main(int argc, char *argv[]) {
         ELOG("Could not setup signal handler");
 
     /* Ignore SIGPIPE to survive errors when an IPC client disconnects
-     * while we are sending him a message */
+     * while we are sending them a message */
     signal(SIGPIPE, SIG_IGN);
 
     /* Autostarting exec-lines */
index b7ea5e69e1aa3ce08e8ba864ef97fb368148d51a..2b3c6743a20df8b8b1c4525c9719fa11cdf6f01a 100644 (file)
@@ -69,7 +69,7 @@ void restore_geometry(void) {
 
     /* Strictly speaking, this line doesn’t really belong here, but since we
      * are syncing, let’s un-register as a window manager first */
-    xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
+    xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
 
     /* Make sure our changes reach the X server, we restart/exit now */
     xcb_aux_sync(conn);
@@ -253,23 +253,20 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     /* See if any container swallows this new window */
     nc = con_for_window(search_at, cwindow, &match);
     if (nc == NULL) {
-        /* If not, check if it is assigned to a specific workspace / output */
-        if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE | A_TO_OUTPUT))) {
+        /* If not, check if it is assigned to a specific workspace */
+        if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) {
             DLOG("Assignment matches (%p)\n", match);
-            if (assignment->type == A_TO_WORKSPACE) {
-                Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL);
-                nc = con_descend_tiling_focused(assigned_ws);
-                DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name);
-                if (nc->type == CT_WORKSPACE)
-                    nc = tree_open_con(nc, cwindow);
-                else
-                    nc = tree_open_con(nc->parent, cwindow);
-
-                /* set the urgency hint on the window if the workspace is not visible */
-                if (!workspace_is_visible(assigned_ws))
-                    urgency_hint = true;
-            }
-            /* TODO: handle assignments with type == A_TO_OUTPUT */
+            Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL);
+            nc = con_descend_tiling_focused(assigned_ws);
+            DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name);
+            if (nc->type == CT_WORKSPACE)
+                nc = tree_open_con(nc, cwindow);
+            else
+                nc = tree_open_con(nc->parent, cwindow);
+
+            /* set the urgency hint on the window if the workspace is not visible */
+            if (!workspace_is_visible(assigned_ws))
+                urgency_hint = true;
         } else if (startup_ws) {
             /* If it’s not assigned, but was started on a specific workspace,
              * we want to open it there */
@@ -434,7 +431,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
      * which are not managed by the wm anyways). We store the original geometry
      * here because it’s used for dock clients. */
     if (nc->geometry.width == 0)
-        nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height};
+        nc->geometry = (Rect){geom->x, geom->y, geom->width, geom->height};
 
     if (motif_border_style != BS_NORMAL) {
         DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
@@ -455,7 +452,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     }
 
     /* explicitly set the border width to the default */
-    nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
+    if (nc->current_border_width == -1) {
+        nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
+    }
 
     /* to avoid getting an UnmapNotify event due to reparenting, we temporarily
      * declare no interest in any state change event of this window */
index 6499c65df77d46dea56fb8c75b432ebedb1dad61..822a0f8891004b5c8c8b14cc4c8f68deace51f3b 100644 (file)
@@ -24,3 +24,25 @@ Con *output_get_content(Con *output) {
 
     return NULL;
 }
+
+/*
+ * Returns an 'output' corresponding to one of left/right/down/up or a specific
+ * output name.
+ *
+ */
+Output *get_output_from_string(Output *current_output, const char *output_str) {
+    Output *output;
+
+    if (strcasecmp(output_str, "left") == 0)
+        output = get_output_next_wrap(D_LEFT, current_output);
+    else if (strcasecmp(output_str, "right") == 0)
+        output = get_output_next_wrap(D_RIGHT, current_output);
+    else if (strcasecmp(output_str, "up") == 0)
+        output = get_output_next_wrap(D_UP, current_output);
+    else if (strcasecmp(output_str, "down") == 0)
+        output = get_output_next_wrap(D_DOWN, current_output);
+    else
+        output = get_output_by_name(output_str);
+
+    return output;
+}
index 7f92d4d441071601c7871e19b7f57992fec390d9..d7d576673e4b65bb13dcf9ddfbf1e5e20e2bf070 100644 (file)
@@ -154,7 +154,7 @@ void render_con(Con *con, bool render_fullscreen) {
         /* depending on the border style, the rect of the child window
          * needs to be smaller */
         Rect *inset = &(con->window_rect);
-        *inset = (Rect) {0, 0, con->rect.width, con->rect.height};
+        *inset = (Rect){0, 0, con->rect.width, con->rect.height};
         if (!render_fullscreen)
             *inset = rect_add(*inset, con_border_style_rect(con));
 
@@ -299,7 +299,7 @@ void render_con(Con *con, bool render_fullscreen) {
                            transient_con->window != NULL &&
                            transient_con->window->transient_for != XCB_NONE) {
                         DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n",
-                                transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id);
+                             transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id);
                         if (transient_con->window->transient_for == fullscreen->window->id) {
                             is_transient_for = true;
                             break;
index f4ef54754419cc71414ceadfe9c357403ee76011..53a70d69afe7345c8ed792951303e98aa7c4b18d 100644 (file)
@@ -125,9 +125,9 @@ void restore_connect(void) {
 
 static void update_placeholder_contents(placeholder_state *state) {
     xcb_change_gc(restore_conn, state->gc, XCB_GC_FOREGROUND,
-                  (uint32_t[]) {config.client.placeholder.background});
+                  (uint32_t[]){config.client.placeholder.background});
     xcb_poly_fill_rectangle(restore_conn, state->pixmap, state->gc, 1,
-                            (xcb_rectangle_t[]) {{0, 0, state->rect.width, state->rect.height}});
+                            (xcb_rectangle_t[]){{0, 0, state->rect.width, state->rect.height}});
 
     // TODO: make i3font functions per-connection, at least these two for now…?
     xcb_flush(restore_conn);
@@ -180,7 +180,9 @@ static void update_placeholder_contents(placeholder_state *state) {
 
 static void open_placeholder_window(Con *con) {
     if (con_is_leaf(con) &&
-        (con->window == NULL || con->window->id == XCB_NONE)) {
+        (con->window == NULL || con->window->id == XCB_NONE) &&
+        !TAILQ_EMPTY(&(con->swallow_head)) &&
+        con->type == CT_CON) {
         xcb_window_t placeholder = create_window(
             restore_conn,
             con->rect,
@@ -190,10 +192,15 @@ static void open_placeholder_window(Con *con) {
             XCURSOR_CURSOR_POINTER,
             true,
             XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
-            (uint32_t[]) {
+            (uint32_t[]){
                 config.client.placeholder.background,
                 XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
             });
+        /* Make i3 not focus this window. */
+        xcb_icccm_wm_hints_t hints;
+        xcb_icccm_wm_hints_set_none(&hints);
+        xcb_icccm_wm_hints_set_input(&hints, 0);
+        xcb_icccm_set_wm_hints(restore_conn, placeholder, &hints);
         /* Set the same name as was stored in the layout file. While perhaps
          * slightly confusing in the first instant, this brings additional
          * clarity to which placeholder is waiting for which actual window. */
@@ -211,7 +218,7 @@ static void open_placeholder_window(Con *con) {
         xcb_create_pixmap(restore_conn, root_depth, state->pixmap,
                           state->window, state->rect.width, state->rect.height);
         state->gc = xcb_generate_id(restore_conn);
-        xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]) {0});
+        xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
         update_placeholder_contents(state);
         TAILQ_INSERT_TAIL(&state_head, state, state);
 
@@ -323,7 +330,7 @@ static void configure_notify(xcb_configure_notify_event_t *event) {
         xcb_create_pixmap(restore_conn, root_depth, state->pixmap,
                           state->window, state->rect.width, state->rect.height);
         state->gc = xcb_generate_id(restore_conn);
-        xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]) {0});
+        xcb_create_gc(restore_conn, state->gc, state->pixmap, XCB_GC_GRAPHICS_EXPOSURES, (uint32_t[]){0});
 
         update_placeholder_contents(state);
         xcb_copy_area(restore_conn, state->pixmap, state->window, state->gc,
index 8b5bfed4c015745390dd4a645645dfc87b4619d4..e971f6bdd18c8f8f3b2e38b0aeb165d50a6bb574 100644 (file)
@@ -70,8 +70,14 @@ static int backtrace(void) {
         int stdin_pipe[2],
             stdout_pipe[2];
 
-        pipe(stdin_pipe);
-        pipe(stdout_pipe);
+        if (pipe(stdin_pipe) == -1) {
+            ELOG("Failed to init stdin_pipe\n");
+            return -1;
+        }
+        if (pipe(stdout_pipe) == -1) {
+            ELOG("Failed to init stdout_pipe\n");
+            return -1;
+        }
 
         /* close standard streams in case i3 is started from a terminal; gdb
          * needs to run without controlling terminal for it to work properly in
@@ -129,9 +135,9 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei
     /* re-draw the background */
     xcb_rectangle_t border = {0, 0, width, height},
                     inner = {2, 2, width - 4, height - 4};
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#FF0000")});
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")});
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
-    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
+    xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
     xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &inner);
 
     /* restore font color */
index ebe8c1d9439601a426bee70aa512d95c70a7a44f..aa347bd75fe86096096b7bc0b6148f7859fe8a91 100644 (file)
@@ -257,6 +257,22 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) {
     }
 }
 
+/**
+ * Renames workspaces that are mentioned in the startup sequences.
+ *
+ */
+void startup_sequence_rename_workspace(char *old_name, char *new_name) {
+    struct Startup_Sequence *current;
+    TAILQ_FOREACH(current, &startup_sequences, sequences) {
+        if (strcmp(current->workspace, old_name) != 0)
+            continue;
+        DLOG("Renaming workspace \"%s\" to \"%s\" in startup sequence %s.\n",
+             old_name, new_name, current->id);
+        free(current->workspace);
+        current->workspace = sstrdup(new_name);
+    }
+}
+
 /**
  * Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
  *
index e99c5063b3b26ed5bb363874a8a5d6be35e291b2..b40ba2a2879203b503d7640937184fae91f3ea13 100644 (file)
@@ -76,7 +76,7 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
 
     /* TODO: refactor the following */
     croot = con_new(NULL, NULL);
-    croot->rect = (Rect) {
+    croot->rect = (Rect){
         geometry->x,
         geometry->y,
         geometry->width,
@@ -104,6 +104,8 @@ bool tree_restore(const char *path, xcb_get_geometry_reply_t *geometry) {
         TAILQ_INSERT_HEAD(&(croot->nodes_head), __i3, nodes);
     }
 
+    restore_open_placeholder_windows(croot);
+
     return true;
 }
 
@@ -118,7 +120,7 @@ void tree_init(xcb_get_geometry_reply_t *geometry) {
     croot->name = "root";
     croot->type = CT_ROOT;
     croot->layout = L_SPLITH;
-    croot->rect = (Rect) {
+    croot->rect = (Rect){
         geometry->x,
         geometry->y,
         geometry->width,
@@ -237,7 +239,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool
              * unmap the window,
              * then reparent it to the root window. */
             xcb_change_window_attributes(conn, con->window->id,
-                                         XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_NONE});
+                                         XCB_CW_EVENT_MASK, (uint32_t[]){XCB_NONE});
             xcb_unmap_window(conn, con->window->id);
             cookie = xcb_reparent_window(conn, con->window->id, root, 0, 0);
 
index b382a586e09f2d8b545e663bcaa26b5b4eaf61cc..5760ae72588f52afc5cd6caf617a4b184e344ab9 100644 (file)
@@ -42,17 +42,17 @@ bool rect_contains(Rect rect, uint32_t x, uint32_t y) {
 }
 
 Rect rect_add(Rect a, Rect b) {
-    return (Rect) {a.x + b.x,
-                   a.y + b.y,
-                   a.width + b.width,
-                   a.height + b.height};
+    return (Rect){a.x + b.x,
+                  a.y + b.y,
+                  a.width + b.width,
+                  a.height + b.height};
 }
 
 Rect rect_sub(Rect a, Rect b) {
-    return (Rect) {a.x - b.x,
-                   a.y - b.y,
-                   a.width - b.width,
-                   a.height - b.height};
+    return (Rect){a.x - b.x,
+                  a.y - b.y,
+                  a.width - b.width,
+                  a.height - b.height};
 }
 
 /*
@@ -265,25 +265,13 @@ char *store_restart_layout(void) {
         return NULL;
     }
 
-    size_t written = 0;
-    while (written < length) {
-        int n = write(fd, payload + written, length - written);
-        /* TODO: correct error-handling */
-        if (n == -1) {
-            perror("write()");
-            free(filename);
-            close(fd);
-            return NULL;
-        }
-        if (n == 0) {
-            DLOG("write == 0?\n");
-            free(filename);
-            close(fd);
-            return NULL;
-        }
-        written += n;
-        DLOG("written: %zd of %zd\n", written, length);
+    if (writeall(fd, payload, length) == -1) {
+        ELOG("Could not write restart layout to \"%s\", layout will be lost: %s\n", filename, strerror(errno));
+        free(filename);
+        close(fd);
+        return NULL;
     }
+
     close(fd);
 
     if (length > 0) {
index e406752a54c63fa5bd2f2b77ddb96419cef407c3..ace109e3e068e7e95dc383a33a0a9c16771d6bd5 100644 (file)
@@ -189,7 +189,7 @@ void window_update_strut_partial(i3Window *win, xcb_get_property_reply_t *prop)
     DLOG("Reserved pixels changed to: left = %d, right = %d, top = %d, bottom = %d\n",
          strut[0], strut[1], strut[2], strut[3]);
 
-    win->reserved = (struct reservedpx) {strut[0], strut[1], strut[2], strut[3]};
+    win->reserved = (struct reservedpx){strut[0], strut[1], strut[2], strut[3]};
 
     free(prop);
 }
index a30566339f246b40e10de9932d1eac7f82a1ab6e..f55c920ebd4697b7cd9346c8d233993d7625c202 100644 (file)
@@ -879,3 +879,111 @@ Con *workspace_encapsulate(Con *ws) {
 
     return new;
 }
+
+/**
+ * Move the given workspace to the specified output.
+ * This returns true if and only if moving the workspace was successful.
+ */
+bool workspace_move_to_output(Con *ws, char *name) {
+    LOG("Trying to move workspace %p / %s to output \"%s\".\n", ws, ws->name, name);
+
+    Con *current_output_con = con_get_output(ws);
+    if (!current_output_con) {
+        ELOG("Could not get the output container for workspace %p / %s.\n", ws, ws->name);
+        return false;
+    }
+
+    Output *current_output = get_output_by_name(current_output_con->name);
+    if (!current_output) {
+        ELOG("Cannot get current output. This is a bug in i3.\n");
+        return false;
+    }
+    Output *output = get_output_from_string(current_output, name);
+    if (!output) {
+        ELOG("Could not get output from string \"%s\"\n", name);
+        return false;
+    }
+
+    Con *content = output_get_content(output->con);
+    LOG("got output %p with content %p\n", output, content);
+
+    Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head));
+    LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name);
+
+    bool workspace_was_visible = workspace_is_visible(ws);
+    if (con_num_children(ws->parent) == 1) {
+        LOG("Creating a new workspace to replace \"%s\" (last on its output).\n", ws->name);
+
+        /* check if we can find a workspace assigned to this output */
+        bool used_assignment = false;
+        struct Workspace_Assignment *assignment;
+        TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
+            if (assignment->output == NULL || strcmp(assignment->output, current_output->name) != 0)
+                continue;
+
+            /* check if this workspace is already attached to the tree */
+            Con *workspace = NULL, *out;
+            TAILQ_FOREACH(out, &(croot->nodes_head), nodes)
+            GREP_FIRST(workspace, output_get_content(out),
+                       !strcasecmp(child->name, assignment->name));
+            if (workspace != NULL)
+                continue;
+
+            /* so create the workspace referenced to by this assignment */
+            LOG("Creating workspace from assignment %s.\n", assignment->name);
+            workspace_get(assignment->name, NULL);
+            used_assignment = true;
+            break;
+        }
+
+        /* if we couldn't create the workspace using an assignment, create
+         * it on the output */
+        if (!used_assignment)
+            create_workspace_on_output(current_output, ws->parent);
+
+        /* notify the IPC listeners */
+        ipc_send_workspace_event("init", ws, NULL);
+    }
+    DLOG("Detaching\n");
+
+    /* detach from the old output and attach to the new output */
+    Con *old_content = ws->parent;
+    con_detach(ws);
+    if (workspace_was_visible) {
+        /* The workspace which we just detached was visible, so focus
+         * the next one in the focus-stack. */
+        Con *focus_ws = TAILQ_FIRST(&(old_content->focus_head));
+        LOG("workspace was visible, focusing %p / %s now\n", focus_ws, focus_ws->name);
+        workspace_show(focus_ws);
+    }
+    con_attach(ws, content, false);
+
+    /* fix the coordinates of the floating containers */
+    Con *floating_con;
+    TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows)
+    floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect));
+
+    ipc_send_workspace_event("move", ws, NULL);
+    if (workspace_was_visible) {
+        /* Focus the moved workspace on the destination output. */
+        workspace_show(ws);
+    }
+
+    /* NB: We cannot simply work with previously_visible_ws since it might
+     * have been cleaned up by workspace_show() already, depending on the
+     * focus order/number of other workspaces on the output.
+     * Instead, we loop through the available workspaces and only work with
+     * previously_visible_ws if we still find it. */
+    TAILQ_FOREACH(ws, &(content->nodes_head), nodes) {
+        if (ws != previously_visible_ws)
+            continue;
+
+        /* Call the on_remove_child callback of the workspace which previously
+         * was visible on the destination output. Since it is no longer
+         * visible, it might need to get cleaned up. */
+        CALL(previously_visible_ws, on_remove_child);
+        break;
+    }
+
+    return true;
+}
diff --git a/src/x.c b/src/x.c
index b39c19d002f2515d7dd1843b640ea047dac4dc4a..9dd09117cf72eaeb234b2f1c10b7c49a9e89b17b 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -352,8 +352,8 @@ void x_draw_decoration(Con *con) {
 
     Rect *r = &(con->rect);
     Rect *w = &(con->window_rect);
-    p->con_rect = (struct width_height) {r->width, r->height};
-    p->con_window_rect = (struct width_height) {w->width, w->height};
+    p->con_rect = (struct width_height){r->width, r->height};
+    p->con_window_rect = (struct width_height){w->width, w->height};
     p->con_deco_rect = con->deco_rect;
     p->background = config.client.background;
     p->con_is_leaf = con_is_leaf(con);
@@ -403,7 +403,7 @@ void x_draw_decoration(Con *con) {
                 );
 #endif
 
-        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {config.client.background});
+        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){config.client.background});
         xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, sizeof(background) / sizeof(xcb_rectangle_t), background);
     }
 
@@ -424,7 +424,7 @@ void x_draw_decoration(Con *con) {
          * (left, bottom and right part). We don’t just fill the whole
          * rectangle because some childs are not freely resizable and we want
          * their background color to "shine through". */
-        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
+        xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
         if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
             xcb_rectangle_t leftline = {0, 0, br.x, r->height};
             xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline);
@@ -450,12 +450,12 @@ void x_draw_decoration(Con *con) {
         if (TAILQ_NEXT(con, nodes) == NULL &&
             TAILQ_PREV(con, nodes_head, nodes) == NULL &&
             con->parent->type != CT_FLOATING_CON) {
-            xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator});
+            xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->indicator});
             if (p->parent_layout == L_SPLITH)
-                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
+                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
                                                                               {r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}});
             else if (p->parent_layout == L_SPLITV)
-                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) {
+                xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]){
                                                                               {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}});
         }
     }
@@ -466,20 +466,20 @@ void x_draw_decoration(Con *con) {
         goto copy_pixmaps;
 
     /* 4: paint the bar */
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
+    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
     xcb_rectangle_t drect = {con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height};
     xcb_poly_fill_rectangle(conn, parent->pixmap, parent->pm_gc, 1, &drect);
 
     /* 5: draw two unconnected horizontal lines in border color */
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
+    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
     Rect *dr = &(con->deco_rect);
     adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
     int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width;
-    int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con-> current_border_width;
+    int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con->current_border_width;
     if (parent->layout == L_TABBED ||
         (parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) {
-            deco_diff_l = 0;
-            deco_diff_r = 0;
+        deco_diff_l = 0;
+        deco_diff_r = 0;
     }
     xcb_segment_t segments[] = {
         {dr->x, dr->y,
@@ -545,12 +545,12 @@ after_title:
     /* Draw a 1px separator line before and after every tab, so that tabs can
      * be easily distinguished. */
     if (parent->layout == L_TABBED) {
-        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
+        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
     } else {
-        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->background});
+        xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->background});
     }
     xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, parent->pixmap, parent->pm_gc, 6,
-                  (xcb_point_t[]) {
+                  (xcb_point_t[]){
                       {dr->x + dr->width, dr->y},
                       {dr->x + dr->width, dr->y + dr->height},
                       {dr->x + dr->width - 1, dr->y},
@@ -559,7 +559,7 @@ after_title:
                       {dr->x, dr->y},
                   });
 
-    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border});
+    xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]){p->color->border});
     xcb_poly_segment(conn, parent->pixmap, parent->pm_gc, 2, segments);
 
 copy_pixmaps:
@@ -975,9 +975,9 @@ void x_push_changes(Con *con) {
             Output *target = get_output_containing(mid_x, mid_y);
             if (current != target) {
                 /* Ignore MotionNotify events generated by warping */
-                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
+                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT});
                 xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, mid_x, mid_y);
-                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]) {ROOT_EVENT_MASK});
+                xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ROOT_EVENT_MASK});
             }
         }
         warp_to = NULL;
index cb0c29f16b545414c0971f3b3b34412f32a80135..5dda5ccee1bc6d940842af8100fb04209923ff03 100644 (file)
--- a/src/xcb.c
+++ b/src/xcb.c
@@ -70,9 +70,9 @@ xcb_window_t create_window(xcb_connection_t *conn, Rect dims,
  */
 void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
                    uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t to_x, uint32_t to_y) {
-    xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]) {colorpixel});
+    xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]){colorpixel});
     xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, drawable, gc, 2,
-                  (xcb_point_t[]) {{x, y}, {to_x, to_y}});
+                  (xcb_point_t[]){{x, y}, {to_x, to_y}});
 }
 
 /*
@@ -81,7 +81,7 @@ void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext
  */
 void xcb_draw_rect(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
                    uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
-    xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]) {colorpixel});
+    xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]){colorpixel});
     xcb_rectangle_t rect = {x, y, width, height};
     xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
 }
index 6daeedba5aab23f58de903e8c89c1dd6c38c50fa..0a8da42545c5384f405b7de67a60675e7b419ccc 100644 (file)
@@ -56,7 +56,7 @@ void xcursor_load_cursors(void) {
  */
 void xcursor_set_root_cursor(int cursor_id) {
     xcb_change_window_attributes(conn, root, XCB_CW_CURSOR,
-                                 (uint32_t[]) {xcursor_get_cursor(cursor_id)});
+                                 (uint32_t[]){xcursor_get_cursor(cursor_id)});
 }
 
 xcb_cursor_t xcursor_get_cursor(enum xcursor_cursor_t c) {
index 61f2ef52451115e3051e3a6d88d0945fb41350f7..eaf57bde1620495c3ef168018a76189c39c32bf5 100755 (executable)
@@ -227,7 +227,7 @@ if ($numtests == 1) {
 
 END { cleanup() }
 
-exit 0;
+exit ($aggregator->failed > 0);
 
 #
 # Takes a test from the beginning of @testfiles and runs it.
@@ -324,8 +324,9 @@ sub take_job {
 }
 
 sub cleanup {
+    my $exitcode = $?;
     $_->() for our @CLEANUP;
-    exit;
+    exit $exitcode;
 }
 
 # must be in a begin block because we C<exit 0> above
index d32f6051d9fa17f4af26e252892f22c71d937c1e..83ca92555ca5fe7391a0f739a599f24847d7423c 100644 (file)
@@ -109,7 +109,7 @@ sub activate_i3 {
 
         if ($args{valgrind}) {
             $i3cmd =
-                qq|valgrind -v --log-file="$outdir/valgrind-for-$test.log" | .
+                qq|valgrind --log-file="$outdir/valgrind-for-$test.log" | .
                 qq|--suppressions="./valgrind.supp" | .
                 qq|--leak-check=full --track-origins=yes --num-callers=20 | .
                 qq|--tool=memcheck -- $i3cmd|;
index 823c6713df080808134aa17685db046faf8a10a2..f2487797876b7f7520db35c5dbcb0997d51d3b15 100644 (file)
@@ -15,6 +15,15 @@ my %ansi_line_upwards;
 
 my $tests_total;
 
+sub noninteractive {
+    # CONTINUOUS_INTEGRATION gets set when running under Travis, see
+    # http://docs.travis-ci.com/user/ci-environment/ and
+    # https://github.com/travis-ci/travis-ci/issues/1337
+    return (! -t STDOUT) || (
+        defined($ENV{CONTINUOUS_INTEGRATION}) &&
+        $ENV{CONTINUOUS_INTEGRATION} eq 'true');
+}
+
 # setup %ansi_line_upwards to map all working displays to the
 # specific movement commands and initialize all status lines
 sub status_init {
@@ -22,6 +31,8 @@ sub status_init {
     my $displays = $args{displays};
     $tests_total = $args{tests};
 
+    return if noninteractive();
+
     for my $n (1 .. @$displays) {
         # since we are moving upwards, get $display in reverse order
         my $display = $displays->[-$n];
@@ -41,6 +52,8 @@ sub status {
     my ($display, $msg) = @_;
     my $status = "[$display] $msg";
 
+    return $status if noninteractive();
+
     print
         $ansi_save_cursor,
         $ansi_line_upwards{$display},
@@ -53,6 +66,9 @@ sub status {
 
 sub status_completed {
     my $num = shift;
+
+    return if noninteractive();
+
     print
         $ansi_save_cursor,
         $ansi_clear_line,
index 212e78fd64f79b3aa8f58b296465860379f5bd74..c149cbd1204a0ddf63bfeff9eff6c710c0dde4c7 100644 (file)
@@ -226,7 +226,7 @@ you might have to map it on your own and use this function:
 sub wait_for_map {
     my ($win) = @_;
     my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
-    wait_for_event 2, sub {
+    wait_for_event 4, sub {
         $_[0]->{response_type} == MAP_NOTIFY and $_[0]->{window} == $id
     };
 }
@@ -248,7 +248,7 @@ event.
 sub wait_for_unmap {
     my ($win) = @_;
     # my $id = (blessed($win) && $win->isa('X11::XCB::Window')) ? $win->id : $win;
-    wait_for_event 2, sub {
+    wait_for_event 4, sub {
         $_[0]->{response_type} == UNMAP_NOTIFY # and $_[0]->{window} == $id
     };
     sync_with_i3();
@@ -718,7 +718,7 @@ sub sync_with_i3 {
     return $myrnd if $args{dont_wait_for_event};
 
     # now wait until the reply is here
-    return wait_for_event 2, sub {
+    return wait_for_event 4, sub {
         my ($event) = @_;
         # TODO: const
         return 0 unless $event->{response_type} == 161;
index 1bac40f053e43318b242c0b61b2d1b6175e83b4b..bb9c71c9283826b1b6e2cae025b109e8d7cddecd 100644 (file)
@@ -143,6 +143,22 @@ wait_for_map $window;
 @docked = get_dock_clients('top');
 is(@docked, 1, 'dock client on top');
 
+# now change strut_partial to reserve space on the bottom and the dock should
+# be moved to the bottom dock area
+$x->change_property(
+    PROP_MODE_REPLACE,
+    $window->id,
+    $atomname->id,
+    $atomtype->id,
+    32,         # 32 bit integer
+    12,
+    pack('L12', 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 1280, 0)
+);
+
+sync_with_i3;
+@docked = get_dock_clients('bottom');
+is(@docked, 1, 'dock client on bottom');
+
 $window->destroy;
 
 wait_for_unmap $window;
index 9f0c046ab2dd0dd51d8cc62ff8adb780605df2e1..4ca41799ea5a06f1754c5b4f6333dcca930465d3 100644 (file)
@@ -142,12 +142,17 @@ is_num_children($first_ws, 2, 'two containers on the first workspace');
 complete_startup();
 sync_with_i3;
 
+# even when renaming the workspace, windows should end up on the correct one
+cmd "rename workspace $first_ws to temp";
+
 # Startup has completed but the 30-second deletion time hasn't elapsed,
 # so this window should still go on the leader's initial workspace.
 $win = open_window({ dont_map => 1, client_leader => $leader });
 $win->map;
 sync_with_i3;
 
+cmd "rename workspace temp to $first_ws";
+
 is_num_children($first_ws, 3, 'three containers on the first workspace');
 
 # Switch to the first workspace and move the focused window to the
index 5ee94f876f141b5c8133b47c506582ef9259a10a..6d67731a1e71a9914485962ebc3fc73215d1b042 100644 (file)
@@ -156,7 +156,7 @@ is(parser_calls('move something to somewhere'),
    'error for unknown literal ok');
 
 ################################################################################
-# 3: Verify that escaping of double quotes works correctly
+# 3: Verify that escaping works correctly
 ################################################################################
 
 is(parser_calls('workspace "foo"'),
@@ -171,6 +171,18 @@ is(parser_calls('workspace "foo \"bar"'),
    'cmd_workspace_name(foo "bar)',
    'Command with escaped double quotes ok');
 
+is(parser_calls('workspace "foo \\'),
+   'cmd_workspace_name(foo \\)',
+   'Command with single backslash in the end ok');
+
+is(parser_calls('workspace "foo\\\\bar"'),
+   'cmd_workspace_name(foo\\bar)',
+   'Command with escaped backslashes ok');
+
+is(parser_calls('workspace "foo\\\\\\"bar"'),
+   'cmd_workspace_name(foo\\"bar)',
+   'Command with escaped double quotes after escaped backslashes ok');
+
 ################################################################################
 # 4: Verify that resize commands with a "px or ppt"-construction are parsed
 # correctly
index 0fb8c8be364a09a638bcbc5c7d858fdeafdedab3..b6ebc876a14c9020a154f828e0646c6c11a09869 100644 (file)
@@ -31,7 +31,7 @@ my $config = <<EOT;
 # i3 config file (v4)
 font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
 
-force_display_urgency_hint 150ms
+force_display_urgency_hint 500ms
 EOT
 my $pid = launch_with_config($config);
 
@@ -70,7 +70,7 @@ is(@urgent, 1, 'window still marked as urgent');
 # now check if the timer was triggered
 cmd "workspace $tmp2";
 
-sleep(0.1);
+sleep(0.5);
 @content = @{get_ws_content($tmp1)};
 @urgent = grep { $_->{urgent} } @content;
 is(@urgent, 0, 'window not marked as urgent anymore');
@@ -142,7 +142,7 @@ cmd "workspace $tmp3";
 $split_left->delete_hint('urgency');
 sync_with_i3;
 
-sleep(0.2);
+sleep(0.6);
 is(count_total_urgent(get_ws($tmp3)), 0, "no more urgent windows on workspace $tmp3");
 
 exit_gracefully($pid);
index 9643aa42f5f5678536a181e481cc1a1da00f1fdf..1e91d47f9cf5cdcb71f9ccf350aefd1f61f2e364 100644 (file)
@@ -647,7 +647,7 @@ EOT
 
 $expected = <<'EOT';
 cfg_bar_output(LVDS-1)
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', '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
diff --git a/testcases/t/232-cmd-workspace-number-selection.t b/testcases/t/232-cmd-workspace-number-selection.t
deleted file mode 100644 (file)
index bda0564..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#!perl
-# vim:ts=4:sw=4:expandtab
-#
-# Please read the following documents before working on tests:
-# • http://build.i3wm.org/docs/testsuite.html
-#   (or docs/testsuite)
-#
-# • http://build.i3wm.org/docs/lib-i3test.html
-#   (alternatively: perldoc ./testcases/lib/i3test.pm)
-#
-# • http://build.i3wm.org/docs/ipc.html
-#   (or docs/ipc)
-#
-# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
-#   (unless you are already familiar with Perl)
-#
-# Test that `workspace {N}` acts like `workspace number {N}` when N is a plain
-# digit, and likewise for `move to workspace {N}`.
-# Ticket: #1238
-# Bug still in: 4.8-16-g3f5a0f0
-use i3test;
-
-cmd 'workspace 5:foo';
-open_window;
-fresh_workspace;
-cmd 'workspace 5';
-
-is(focused_ws, '5:foo',
-    'a command to switch to a workspace with a bare number should switch to a workspace of that number');
-
-fresh_workspace;
-my $win = open_window;
-cmd '[id="' . $win->{id} . '"] move to workspace 5';
-
-is(@{get_ws('5:foo')->{nodes}}, 2,
-    'a command to move a container to a workspace with a bare number should move that container to a workspace of that number');
-
-fresh_workspace;
-cmd 'workspace 7';
-open_window;
-cmd 'workspace 7:foo';
-$win = open_window;
-
-cmd 'workspace 7';
-is(focused_ws, '7',
-    'a workspace with a name that is a matching plain number should be preferred when switching');
-
-cmd '[id="' . $win->{id} . '"] move to workspace 7';
-is(@{get_ws('7')->{nodes}}, 2,
-    'a workspace with a name that is a matching plain number should be preferred when moving');
-
-done_testing;
diff --git a/testcases/t/522-rename-assigned-workspace.t b/testcases/t/522-rename-assigned-workspace.t
new file mode 100644 (file)
index 0000000..aa69d66
--- /dev/null
@@ -0,0 +1,90 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+#
+# Tests that workspaces are moved to the assigned output if they
+# are renamed to an assigned name.
+# Ticket: #1473
+
+use i3test i3_autostart => 0;
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+fake-outputs 1024x768+0+0,1024x768+1024+0
+
+workspace 1 output fake-0
+workspace 2 output fake-1
+workspace 3:foo output fake-1
+workspace baz output fake-1
+EOT
+
+my $pid = launch_with_config($config);
+my $i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+# Returns the name of the output on which this workspace resides
+sub get_output_for_workspace {
+    my $ws_name = shift @_;
+
+    foreach (grep { not $_->{name} =~ /^__/ } @{$i3->get_tree->recv->{nodes}}) {
+        my $output = $_->{name};
+        foreach (grep { $_->{name} =~ "content" } @{$_->{nodes}}) {
+            return $output if $_->{nodes}[0]->{name} =~ $ws_name;
+        }
+    }
+}
+
+##########################################################################
+# Renaming the workspace to an unassigned name does not move the workspace
+# (regression test)
+##########################################################################
+
+cmd 'focus output fake-0';
+cmd 'rename workspace to unassigned';
+is(get_output_for_workspace('unassigned'), 'fake-0',
+    'Unassigned workspace should stay on its output when being renamed');
+
+##########################################################################
+# Renaming a workspace by number only triggers the assignment
+##########################################################################
+
+cmd 'focus output fake-0';
+cmd 'rename workspace to 2';
+is(get_output_for_workspace('2'), 'fake-1',
+    'Renaming the workspace to a number should move it to the assigned output');
+
+##########################################################################
+# Renaming a workspace by number and name triggers the assignment
+##########################################################################
+
+cmd 'focus output fake-0';
+cmd 'rename workspace to "2:foo"';
+is(get_output_for_workspace('2:foo'), 'fake-1',
+    'Renaming the workspace to a number and name should move it to the assigned output');
+
+##########################################################################
+# Renaming a workspace by name only triggers the assignment
+##########################################################################
+
+cmd 'focus output fake-0';
+cmd 'rename workspace to baz';
+is(get_output_for_workspace('baz'), 'fake-1',
+    'Renaming the workspace to a number and name should move it to the assigned output');
+
+
+exit_gracefully($pid);
+done_testing;