]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #3475 from Gravemind/fix-i3bar-re-hidden-on-any-modifier
authorOrestis <orestisf1993@gmail.com>
Tue, 23 Oct 2018 15:41:11 +0000 (18:41 +0300)
committerGitHub <noreply@github.com>
Tue, 23 Oct 2018 15:41:11 +0000 (18:41 +0300)
i3bar: Fix i3bar re-hidden by any modifier (#3474)

58 files changed:
.travis.yml
docs/userguide
i3-config-wizard/main.c
i3-input/main.c
i3-nagbar/main.c
i3bar/include/xcb.h
i3bar/src/child.c
i3bar/src/ipc.c
i3bar/src/main.c
i3bar/src/xcb.c
include/commands.h
include/commands_parser.h
include/con.h
include/config_directives.h
include/config_parser.h
include/configuration.h
include/libi3.h
include/match.h
include/output.h
include/randr.h
include/render.h
include/shmlog.h
include/startup.h
include/util.h
libi3/dpi.c
libi3/draw_util.c
libi3/font.c
libi3/is_debug_build.c
libi3/string.c
parser-specs/config.spec
src/bindings.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/move.c
src/output.c
src/randr.c
src/sighandler.c
src/startup.c
src/util.c
src/workspace.c
src/x.c
testcases/inject_randr1.5.c
testcases/t/189-floating-constraints.t
testcases/t/201-config-parser.t
testcases/t/276-ipc-window-move.t
testcases/t/285-sticky.t
testcases/t/293-focus-follows-mouse.t
testcases/t/294-focus-order.t

index 87c996fb53bc2c5c29684aa67d00e08ccc3655a1..8098035c84f2f640a135356ac11c3983485f3e1f 100644 (file)
@@ -35,7 +35,7 @@ install:
 script:
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-safe-wrappers.sh
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-formatting.sh
-  - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Werror"'
+  - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} /bin/sh -c 'autoreconf -fi && mkdir -p build && cd build && (../configure || (cat config.log; false)) && make -j CFLAGS="-Wformat -Wformat-security -Wextra -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Werror"'
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/check-spelling.pl
   - docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 -e CC ${BASENAME} ./travis/run-tests.sh
   - ./travis/skip-pkg.sh || docker run -v $PWD:/usr/src/i3/ -w /usr/src/i3 ${BASENAME} ./travis/debian-build.sh deb/debian-amd64/DIST
index c051125de763e4d40e0e89a0246220f1d00520ae..91060ab2cc6df006f4c23a80665976d0c503041b 100644 (file)
@@ -245,9 +245,11 @@ you open a new terminal, it will open below the current one.
 
 So, how can you open a new terminal window to the *right* of the current one?
 The solution is to use +focus parent+, which will focus the +Parent Container+ of
-the current +Container+. In this case, you would focus the +Vertical Split
-Container+ which is *inside* the horizontally oriented workspace. Thus, now new
-windows will be opened to the right of the +Vertical Split Container+:
+the current +Container+. In default configuration, use +$mod+a+ to navigate one 
++Container+ up the tree (you can repeat this multiple times until you get to the
++Workspace Container+). In this case, you would focus the +Vertical Split Container+
+which is *inside* the horizontally oriented workspace. Thus, now new windows will be
+opened to the right of the +Vertical Split Container+:
 
 image::tree-shot3.png["shot3",title="Focus parent, then open new terminal"]
 
@@ -585,6 +587,16 @@ workspace_layout default|stacking|tabbed
 workspace_layout tabbed
 ---------------------
 
+=== Window title alignment
+
+This option determines the window title's text alignment.
+Default is +left+
+
+*Syntax*:
+---------------------------------------------
+title_align left|center|right
+---------------------------------------------
+
 === Default border style for new windows
 
 This option determines which border style new windows will have. The default is
index 0c8c705a784024429238ff98055f1f945d20d642..4b55665773e39821ed175efdb58df00664d87659 100644 (file)
@@ -104,7 +104,7 @@ static struct xkb_keymap *xkb_keymap;
 static uint8_t xkb_base_event;
 static uint8_t xkb_base_error;
 
-static void finish();
+static void finish(void);
 
 #include "GENERATED_config_enums.h"
 
@@ -216,7 +216,7 @@ static const char *get_string(const char *identifier) {
 
 static void clear_stack(void) {
     for (int c = 0; c < 10; c++) {
-        if (stack[c].type == STACK_STR && stack[c].val.str != NULL)
+        if (stack[c].type == STACK_STR)
             free(stack[c].val.str);
         stack[c].identifier = NULL;
         stack[c].val.str = NULL;
@@ -482,7 +482,7 @@ static void txt(int col, int row, char *text, color_t fg, color_t bg) {
  * Handles expose events, that is, draws the window contents.
  *
  */
-static int handle_expose() {
+static int handle_expose(void) {
     const color_t black = draw_util_hex_to_color("#000000");
     const color_t white = draw_util_hex_to_color("#FFFFFF");
     const color_t green = draw_util_hex_to_color("#00FF00");
@@ -641,7 +641,7 @@ static void handle_button_press(xcb_button_press_event_t *event) {
  * Creates the config file and tells i3 to reload.
  *
  */
-static void finish() {
+static void finish(void) {
     printf("creating \"%s\"...\n", config_path);
 
     struct xkb_context *xkb_context;
index efb7b20c2b87a48183e7796427c0e4c60e5349b0..d1a2efd7cab6b485b4652542e2f4d9459fa237b1 100644 (file)
@@ -156,7 +156,7 @@ static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_rel
     return 1;
 }
 
-static void finish_input() {
+static void finish_input(void) {
     char *command = (char *)concat_strings(glyphs_utf8, input_position);
 
     /* count the occurrences of %s in the string */
index fd7acd6e734215b319d53ecea18e6eed777ba3ae..4ce7493961caf7ed5e35674cbc441843aed0d1eb 100644 (file)
@@ -103,10 +103,10 @@ void debuglog(char *fmt, ...) {
 }
 
 /*
- * Starts the given application by passing it through a shell. We use double fork
- * to avoid zombie processes. As the started application’s parent exits (immediately),
- * the application is reparented to init (process-id 1), which correctly handles
- * childs, so we don’t have to do it :-).
+ * Starts the given application by passing it through a shell. We use double
+ * fork to avoid zombie processes. As the started application’s parent exits
+ * (immediately), the application is reparented to init (process-id 1), which
+ * correctly handles children, so we don’t have to do it :-).
  *
  * The shell is determined by looking for the SHELL environment variable. If it
  * does not exist, /bin/sh is used.
index 7783e8778575b26e309c8415c1684985d68eb418..760ebcdb6480b9a2b929833d672f0ad26b6bfb5a 100644 (file)
@@ -60,7 +60,7 @@ int separator_symbol_width;
  * depend on 'config'.
  *
  */
-char *init_xcb_early();
+char *init_xcb_early(void);
 
 /**
  * Initialization which depends on 'config' being usable. Called after the
index 1cd7d512aa0e6c128e66996b508d7696add950c1..bb5ceaff0ee2cc1592e1edf157becdcdd4e7fdc9 100644 (file)
@@ -133,7 +133,7 @@ finish:
  * Stop and free() the stdin- and SIGCHLD-watchers
  *
  */
-void cleanup(void) {
+static void cleanup(void) {
     if (stdin_io != NULL) {
         ev_io_stop(main_loop, stdin_io);
         FREE(stdin_io);
@@ -400,7 +400,7 @@ static bool read_json_input(unsigned char *input, int length) {
  * in statusline
  *
  */
-void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+static void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
     int rec;
     unsigned char *buffer = get_buffer(watcher, &rec);
     if (buffer == NULL)
@@ -420,7 +420,7 @@ void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
  * whether this is JSON or plain text
  *
  */
-void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+static void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
     int rec;
     unsigned char *buffer = get_buffer(watcher, &rec);
     if (buffer == NULL)
@@ -457,7 +457,7 @@ void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
  * anymore
  *
  */
-void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
+static void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
     int exit_status = WEXITSTATUS(watcher->rstatus);
 
     ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
@@ -477,7 +477,7 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
     draw_bars(false);
 }
 
-void child_write_output(void) {
+static void child_write_output(void) {
     if (child.click_events) {
         const unsigned char *output;
         size_t size;
@@ -580,7 +580,7 @@ void start_child(char *command) {
     atexit(kill_child_at_exit);
 }
 
-void child_click_events_initialize(void) {
+static void child_click_events_initialize(void) {
     if (!child.click_events_init) {
         yajl_gen_array_open(gen);
         child_write_output();
@@ -588,7 +588,7 @@ void child_click_events_initialize(void) {
     }
 }
 
-void child_click_events_key(const char *key) {
+static void child_click_events_key(const char *key) {
     yajl_gen_string(gen, (const unsigned char *)key, strlen(key));
 }
 
index 9a7fb21eb401038c1a63376d36a2874ddc3d4a25..df5a12cfc73cb0929c68a4b2cd1ae62cf7d66fef 100644 (file)
@@ -34,7 +34,7 @@ typedef void (*handler_t)(char *);
  * Since i3 does not give us much feedback on commands, we do not much
  *
  */
-void got_command_reply(char *reply) {
+static void got_command_reply(char *reply) {
     /* TODO: Error handling for command replies */
 }
 
@@ -42,7 +42,7 @@ void got_command_reply(char *reply) {
  * Called, when we get a reply with workspaces data
  *
  */
-void got_workspace_reply(char *reply) {
+static void got_workspace_reply(char *reply) {
     DLOG("Got workspace data!\n");
     parse_workspaces_json(reply);
     draw_bars(false);
@@ -53,7 +53,7 @@ void got_workspace_reply(char *reply) {
  * Since i3 does not give us much feedback on commands, we do not much
  *
  */
-void got_subscribe_reply(char *reply) {
+static void got_subscribe_reply(char *reply) {
     DLOG("Got subscribe reply: %s\n", reply);
     /* TODO: Error handling for subscribe commands */
 }
@@ -62,7 +62,7 @@ void got_subscribe_reply(char *reply) {
  * Called, when we get a reply with outputs data
  *
  */
-void got_output_reply(char *reply) {
+static void got_output_reply(char *reply) {
     DLOG("Clearing old output configuration...\n");
     free_outputs();
 
@@ -87,7 +87,7 @@ void got_output_reply(char *reply) {
  * Called when we get the configuration for our bar instance
  *
  */
-void got_bar_config(char *reply) {
+static 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
      * workspaces. Everything else (creating the bars, showing the right workspace-
@@ -132,7 +132,7 @@ handler_t reply_handlers[] = {
  * Called, when a workspace event arrives (i.e. the user changed the workspace)
  *
  */
-void got_workspace_event(char *event) {
+static void got_workspace_event(char *event) {
     DLOG("Got workspace event!\n");
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
 }
@@ -141,7 +141,7 @@ void got_workspace_event(char *event) {
  * Called, when an output event arrives (i.e. the screen configuration changed)
  *
  */
-void got_output_event(char *event) {
+static void got_output_event(char *event) {
     DLOG("Got output event!\n");
     i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
     if (!config.disable_ws) {
@@ -153,7 +153,7 @@ void got_output_event(char *event) {
  * Called, when a mode event arrives (i3 changed binding mode).
  *
  */
-void got_mode_event(char *event) {
+static void got_mode_event(char *event) {
     DLOG("Got mode event!\n");
     parse_mode_json(event);
     draw_bars(false);
@@ -163,7 +163,7 @@ void got_mode_event(char *event) {
  * Called, when a barconfig_update event arrives (i.e. i3 changed the bar hidden_state or mode)
  *
  */
-void got_bar_config_update(char *event) {
+static void got_bar_config_update(char *event) {
     /* check whether this affect this bar instance by checking the bar_id */
     char *expected_id;
     sasprintf(&expected_id, "\"id\":\"%s\"", config.bar_id);
@@ -213,7 +213,7 @@ handler_t event_handlers[] = {
  * Called, when we get a message from i3
  *
  */
-void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
+static void got_data(struct ev_loop *loop, ev_io *watcher, int events) {
     DLOG("Got data!\n");
     int fd = watcher->fd;
 
index f90bb31297867decc9e4701aa7fcd9c2fe5b0108..a818dd9710eda58c3647a029f612ff5e0bf6146b 100644 (file)
@@ -44,7 +44,7 @@ void debuglog(char *fmt, ...) {
  * Glob path, i.e. expand ~
  *
  */
-char *expand_path(char *path) {
+static char *expand_path(char *path) {
     static glob_t globbuf;
     if (glob(path, GLOB_NOCHECK | GLOB_TILDE, NULL, &globbuf) < 0) {
         ELOG("glob() failed\n");
@@ -55,7 +55,7 @@ char *expand_path(char *path) {
     return result;
 }
 
-void print_usage(char *elf_name) {
+static void print_usage(char *elf_name) {
     printf("Usage: %s -b bar_id [-s sock_path] [-h] [-v]\n", elf_name);
     printf("\n");
     printf("-b, --bar_id  <bar_id>\tBar ID for which to get the configuration\n");
@@ -76,7 +76,7 @@ void print_usage(char *elf_name) {
  * in main() with that
  *
  */
-void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
+static void sig_cb(struct ev_loop *loop, ev_signal *watcher, int revents) {
     switch (watcher->signum) {
         case SIGTERM:
             DLOG("Got a SIGTERM, stopping\n");
index 39368c88bdd673546572d875e9866a1fad288628..c0486181b02933db87200396c318e0ee322924e2 100644 (file)
@@ -92,6 +92,9 @@ static mode binding;
 /* Indicates whether a new binding mode was recently activated */
 bool activated_mode = false;
 
+/* The output in which the tray should be displayed. */
+static i3_output *output_for_tray;
+
 /* The parsed colors */
 struct xcb_colors_t {
     color_t bar_fg;
@@ -146,13 +149,13 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) {
     return 0;
 }
 
-uint32_t get_sep_offset(struct status_block *block) {
+static uint32_t get_sep_offset(struct status_block *block) {
     if (!block->no_separator && block->sep_block_width > 0)
         return block->sep_block_width / 2 + block->sep_block_width % 2;
     return 0;
 }
 
-int get_tray_width(struct tc_head *trayclients) {
+static int get_tray_width(struct tc_head *trayclients) {
     trayclient *trayclient;
     int tray_width = 0;
     TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
@@ -193,7 +196,7 @@ static void draw_separator(i3_output *output, uint32_t x, struct status_block *b
     }
 }
 
-uint32_t predict_statusline_length(bool use_short_text) {
+static uint32_t predict_statusline_length(bool use_short_text) {
     uint32_t width = 0;
     struct status_block *block;
 
@@ -245,7 +248,7 @@ uint32_t predict_statusline_length(bool use_short_text) {
 /*
  * Redraws the statusline to the output's statusline_buffer
  */
-void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
+static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
     struct status_block *block;
 
     color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
@@ -330,7 +333,7 @@ void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_color
  * Hides all bars (unmaps them)
  *
  */
-void hide_bars(void) {
+static void hide_bars(void) {
     if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW && config.hide_on_modifier == M_HIDE)) {
         return;
     }
@@ -349,7 +352,7 @@ void hide_bars(void) {
  * Unhides all bars (maps them)
  *
  */
-void unhide_bars(void) {
+static void unhide_bars(void) {
     if (config.hide_on_modifier != M_HIDE) {
         return;
     }
@@ -457,7 +460,7 @@ static bool execute_custom_command(xcb_keycode_t input_code, bool event_is_relea
  * wheel was used and change the workspace appropriately
  *
  */
-void handle_button(xcb_button_press_event_t *event) {
+static void handle_button(xcb_button_press_event_t *event) {
     /* Determine, which bar was clicked */
     i3_output *walk;
     xcb_window_t bar = event->event;
@@ -758,58 +761,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             }
 
             DLOG("X window %08x requested docking\n", client);
-            i3_output *output = NULL;
-            i3_output *walk = NULL;
-            tray_output_t *tray_output = NULL;
-            /* We need to iterate through the tray_output assignments first in
-             * order to prioritize them. Otherwise, if this bar manages two
-             * outputs and both are assigned as tray_output as well, the first
-             * output in our list would receive the tray rather than the first
-             * one defined via tray_output. */
-            TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
-                SLIST_FOREACH(walk, outputs, slist) {
-                    if (!walk->active)
-                        continue;
-
-                    if (strcasecmp(walk->name, tray_output->output) == 0) {
-                        DLOG("Found tray_output assignment for output %s.\n", walk->name);
-                        output = walk;
-                        break;
-                    }
 
-                    if (walk->primary && strcasecmp("primary", tray_output->output) == 0) {
-                        DLOG("Found tray_output assignment on primary output %s.\n", walk->name);
-                        output = walk;
-                        break;
-                    }
-                }
-
-                /* If we found an output, we're done. */
-                if (output != NULL)
-                    break;
-            }
-
-            /* If no tray_output has been specified, we fall back to the first
-             * available output. */
-            if (output == NULL && TAILQ_EMPTY(&(config.tray_outputs))) {
-                SLIST_FOREACH(walk, outputs, slist) {
-                    if (!walk->active)
-                        continue;
-                    DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
-                    output = walk;
-                    break;
-                }
-            }
-
-            if (output == NULL) {
-                ELOG("No output found\n");
+            if (output_for_tray == NULL) {
+                ELOG("No output found for tray\n");
                 return;
             }
 
             xcb_void_cookie_t rcookie = xcb_reparent_window(xcb_connection,
                                                             client,
-                                                            output->bar.id,
-                                                            output->rect.w - icon_size - logical_px(config.tray_padding),
+                                                            output_for_tray->bar.id,
+                                                            output_for_tray->rect.w - icon_size - logical_px(config.tray_padding),
                                                             logical_px(config.tray_padding));
             if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?"))
                 return;
@@ -836,7 +797,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             ev->format = 32;
             ev->data.data32[0] = XCB_CURRENT_TIME;
             ev->data.data32[1] = XEMBED_EMBEDDED_NOTIFY;
-            ev->data.data32[2] = output->bar.id;
+            ev->data.data32[2] = output_for_tray->bar.id;
             ev->data.data32[3] = xe_version;
             xcb_send_event(xcb_connection,
                            0,
@@ -856,7 +817,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             tc->win = client;
             tc->xe_version = xe_version;
             tc->mapped = false;
-            TAILQ_INSERT_TAIL(output->trayclients, tc, tailq);
+            TAILQ_INSERT_TAIL(output_for_tray->trayclients, tc, tailq);
 
             if (map_it) {
                 DLOG("Mapping dock client\n");
@@ -1080,7 +1041,7 @@ static void handle_resize_request(xcb_resize_request_event_t *event) {
  * events from X11, handle them, then flush our outgoing queue.
  *
  */
-void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
+static void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
     xcb_generic_event_t *event;
 
     if (xcb_connection_has_error(xcb_connection)) {
@@ -1180,7 +1141,7 @@ void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
  * are triggered
  *
  */
-void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+static void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
 }
 
 /*
@@ -1188,7 +1149,7 @@ void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
  * depend on 'config'.
  *
  */
-char *init_xcb_early() {
+char *init_xcb_early(void) {
     /* FIXME: xcb_connect leaks memory */
     xcb_connection = xcb_connect(NULL, &screen);
     if (xcb_connection_has_error(xcb_connection)) {
@@ -1251,7 +1212,7 @@ char *init_xcb_early() {
  * in xcb.
  *
  */
-void register_xkb_keyevents() {
+static void register_xkb_keyevents(void) {
     const xcb_query_extension_reply_t *extreply;
     extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
     if (!extreply->present) {
@@ -1275,7 +1236,7 @@ void register_xkb_keyevents() {
  * Deregister from xkb keyevents.
  *
  */
-void deregister_xkb_keyevents() {
+static void deregister_xkb_keyevents(void) {
     xcb_xkb_select_events(conn,
                           XCB_XKB_ID_USE_CORE_KBD,
                           0,
@@ -1341,7 +1302,7 @@ static void send_tray_clientmessage(void) {
  * atom. Afterwards, tray clients will send ClientMessages to our window.
  *
  */
-void init_tray(void) {
+static void init_tray(void) {
     DLOG("Initializing system tray functionality\n");
     /* request the tray manager atom for the X11 display we are running on */
     char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
@@ -1570,7 +1531,7 @@ void destroy_window(i3_output *output) {
 
 /* 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) {
+static xcb_void_cookie_t config_strut_partial(i3_output *output) {
     /* A local struct to save the strut_partial property */
     struct {
         uint32_t left;
@@ -1612,6 +1573,56 @@ xcb_void_cookie_t config_strut_partial(i3_output *output) {
                                &strut_partial);
 }
 
+/*
+ * Returns the output which should hold the tray, if one exists.
+ *
+ * An output is returned in these scenarios:
+ *   1. A specific output was listed in tray_outputs which is also in the list
+ *   of outputs managed by this bar.
+ *   2. No tray_output directive was specified. In this case, we use the first
+ *   available output.
+ *   3. 'tray_output primary' was specified. In this case we use the primary
+ *   output.
+ *
+ * Three scenarios in which we specifically don't want to use a tray:
+ *   1. 'tray_output none' was specified.
+ *   2. A specific output was listed as a tray_output, but is not one of the
+ *   outputs managed by this bar. For example, consider tray_outputs == [VGA-1],
+ *   but outputs == [HDMI-1].
+ *   3. 'tray_output primary' was specified and no output in the list is
+ *   primary.
+ */
+static i3_output *get_tray_output(void) {
+    i3_output *output = NULL;
+    if (TAILQ_EMPTY(&(config.tray_outputs))) {
+        /* No tray_output specified, use first active output. */
+        SLIST_FOREACH(output, outputs, slist) {
+            if (output->active) {
+                return output;
+            }
+        }
+        return NULL;
+    } else if (strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0) {
+        /* Check for "tray_output none" */
+        return NULL;
+    }
+
+    /* If one or more tray_output assignments were specified, we ensure that at
+     * least one of them is actually an output managed by this instance. */
+    tray_output_t *tray_output;
+    TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
+        SLIST_FOREACH(output, outputs, slist) {
+            if (output->active &&
+                (strcasecmp(output->name, tray_output->output) == 0 ||
+                 (strcasecmp(tray_output->output, "primary") == 0 && output->primary))) {
+                return output;
+            }
+        }
+    }
+
+    return NULL;
+}
+
 /*
  * Reconfigure all bars and create new bars for recently activated outputs
  *
@@ -1619,7 +1630,6 @@ xcb_void_cookie_t config_strut_partial(i3_output *output) {
 void reconfig_windows(bool redraw_bars) {
     uint32_t mask;
     uint32_t values[6];
-    static bool tray_configured = false;
 
     i3_output *walk;
     SLIST_FOREACH(walk, outputs, slist) {
@@ -1747,58 +1757,6 @@ void reconfig_windows(bool redraw_bars) {
                 exit(EXIT_FAILURE);
             }
 
-            /* Unless "tray_output none" was specified, we need to initialize the tray. */
-            bool no_tray = false;
-            if (!(TAILQ_EMPTY(&(config.tray_outputs)))) {
-                no_tray = strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0;
-            }
-
-            /*
-             * There are three scenarios in which we need to initialize the tray:
-             *   1. A specific output was listed in tray_outputs which is also
-             *      in the list of outputs managed by this bar.
-             *   2. No tray_output directive was specified. In this case, we
-             *      use the first available output.
-             *   3. 'tray_output primary' was specified. In this case we use the
-             *      primary output.
-             *
-             * Three scenarios in which we specifically don't want to
-             * initialize the tray are:
-             *   1. 'tray_output none' was specified.
-             *   2. A specific output was listed as a tray_output, but is not
-             *      one of the outputs managed by this bar. For example, consider
-             *      tray_outputs == [VGA-1], but outputs == [HDMI-1].
-             *   3. 'tray_output primary' was specified and no output in the list
-             *      is primary.
-             */
-            if (!tray_configured && !no_tray) {
-                /* If no tray_output was specified, we go ahead and initialize the tray as
-                 * we will be using the first available output. */
-                if (TAILQ_EMPTY(&(config.tray_outputs))) {
-                    init_tray();
-                }
-
-                /* If one or more tray_output assignments were specified, we ensure that at least one of
-                 * them is actually an output managed by this instance. */
-                tray_output_t *tray_output;
-                TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
-                    i3_output *output;
-                    bool found = false;
-                    SLIST_FOREACH(output, outputs, slist) {
-                        if (strcasecmp(output->name, tray_output->output) == 0 ||
-                            (strcasecmp(tray_output->output, "primary") == 0 && output->primary)) {
-                            found = true;
-                            init_tray();
-                            break;
-                        }
-                    }
-
-                    if (found)
-                        break;
-                }
-
-                tray_configured = true;
-            }
         } else {
             /* We already have a bar, so we just reconfigure it */
             mask = XCB_CONFIG_WINDOW_X |
@@ -1892,6 +1850,19 @@ void reconfig_windows(bool redraw_bars) {
             }
         }
     }
+
+    /* Finally, check if we want to initialize the tray or destroy the selection
+     * window. The result of get_tray_output() is cached. */
+    output_for_tray = get_tray_output();
+    if (output_for_tray) {
+        if (selwin == XCB_NONE) {
+            init_tray();
+        }
+    } else if (selwin != XCB_NONE) {
+        DLOG("Destroying tray selection window\n");
+        xcb_destroy_window(xcb_connection, selwin);
+        selwin = XCB_NONE;
+    }
 }
 
 /*
index b4b3da386b59b67da4e5d5a8b2457d60c9ca3df5..0137460feae0624218af57ec10d2426ab6b5dc6b 100644 (file)
@@ -314,13 +314,13 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name);
  */
 void cmd_bar(I3_CMD, const char *bar_type, const char *bar_value, const char *bar_id);
 
-/*
+/**
  * Implementation of 'shmlog <size>|toggle|on|off'
  *
  */
 void cmd_shmlog(I3_CMD, const char *argument);
 
-/*
+/**
  * Implementation of 'debuglog toggle|on|off'
  *
  */
index 88b3f6d09c6540972b701907fea56dcf071f8642..b65ae93f85c51e16d49806a232bf1d272b7477a8 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <yajl/yajl_gen.h>
 
-/*
+/**
  * Holds an intermediate represenation of the result of a call to any command.
  * When calling parse_command("floating enable, border none"), the parser will
  * internally use this struct when calling cmd_floating and cmd_border.
index 672d80ac882ed6049ba48b852d70d616f29f7255..2c991b0cb6ec90b91ccbe82fc314a7f3edfe6e9c 100644 (file)
@@ -20,7 +20,8 @@
  */
 Con *con_new_skeleton(Con *parent, i3Window *window);
 
-/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
+/**
+ * A wrapper for con_new_skeleton, to retain the old con_new behaviour
  *
  */
 Con *con_new(Con *parent, i3Window *window);
@@ -220,7 +221,7 @@ void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode);
  */
 void con_mark(Con *con, const char *mark, mark_mode_t mode);
 
-/*
+/**
  * Removes marks from containers.
  * If con is NULL, all containers are considered.
  * If name is NULL, this removes all existing marks.
@@ -402,7 +403,7 @@ Con *con_descend_focused(Con *con);
  */
 Con *con_descend_tiling_focused(Con *con);
 
-/*
+/**
  * Returns the leftmost, rightmost, etc. container in sub-tree. For example, if
  * direction is D_LEFT, then we return the rightmost container and if direction
  * is D_RIGHT, we return the leftmost container.  This is because if we are
index 4a20a1f5a6ce9d4c9c31454337b5d8258a95f284..72b59ea2b9171e2d0487bd3827e73732b241cffa 100644 (file)
@@ -56,6 +56,7 @@ CFGFUN(disable_randr15, const char *value);
 CFGFUN(fake_outputs, const char *outputs);
 CFGFUN(force_display_urgency_hint, const long duration_ms);
 CFGFUN(focus_on_window_activation, const char *mode);
+CFGFUN(title_align, const char *alignment);
 CFGFUN(show_marks, const char *value);
 CFGFUN(hide_edge_borders, const char *borders);
 CFGFUN(assign_output, const char *output);
index ace4041dd2ab2e4169b838330e5ef54f690ce0ce..009538f2d40625d31ba19976aeab2e4d88a63a8f 100644 (file)
@@ -16,7 +16,7 @@
 SLIST_HEAD(variables_head, Variable);
 extern pid_t config_error_nagbar_pid;
 
-/*
+/**
  * An intermediate reprsentation of the result of a parse_config call.
  * Currently unused, but the JSON output will be useful in the future when we
  * implement a config parsing IPC command.
index 3eccca4cef604d3ebbd89ab4650ffb682d075700..6f55ac2a57f7fe7ef280ca956267b46c63b82db9 100644 (file)
@@ -201,6 +201,13 @@ struct Config {
      * decoration. Marks starting with a "_" will be ignored either way. */
     bool show_marks;
 
+    /** Title alignment options. */
+    enum {
+        ALIGN_LEFT,
+        ALIGN_CENTER,
+        ALIGN_RIGHT
+    } title_align;
+
     /** The default border style for new windows. */
     border_style_t default_border;
 
@@ -427,7 +434,7 @@ void ungrab_all_keys(xcb_connection_t *conn);
  * Sends the current bar configuration as an event to all barconfig_update listeners.
  *
  */
-void update_barconfig();
+void update_barconfig(void);
 
 /**
  * Kills the configerror i3-nagbar process, if any.
index d27437ba25ae23a1c4d6af459e603d8f4d626d81..790baba98578d3903ee651cba6e4b47fb8eb7456 100644 (file)
@@ -343,7 +343,7 @@ uint32_t get_colorpixel(const char *hex) __attribute__((const));
 
 #if defined(__APPLE__)
 
-/*
+/**
  * Taken from FreeBSD
  * Returns a pointer to a new string which is a duplicate of the
  * string, but only copies at most n characters.
@@ -472,7 +472,7 @@ xcb_visualtype_t *get_visualtype(xcb_screen_t *screen);
  * release version), based on the git version number.
  *
  */
-bool is_debug_build() __attribute__((const));
+bool is_debug_build(void) __attribute__((const));
 
 /**
  * Returns the name of a temporary file with the specified prefix.
index 4ff8c485bbed18fd45a27e290b7a40368f382882..043c3a8f114e77401a5a5df2f8792b8780e4f150 100644 (file)
@@ -15,7 +15,7 @@
 
 #include <config.h>
 
-/*
+/**
  * Initializes the Match data structure. This function is necessary because the
  * members representing boolean values (like dock) need to be initialized with
  * -1 instead of 0.
index 31084da19772c74e743d7025f79a0ef6afd772cd..a2ad97b037290f15a887ca9bb44fc68d4e0b0e6d 100644 (file)
@@ -40,5 +40,12 @@ Output *get_output_for_con(Con *con);
  * Iterates over all outputs and pushes sticky windows to the currently visible
  * workspace on that output.
  *
+ * old_focus is used to determine if a sticky window is going to be focused.
+ * old_focus might be different than the currently focused container because the
+ * caller might need to temporarily change the focus and then call
+ * output_push_sticky_windows. For example, workspace_show needs to set focus to
+ * one of its descendants first, then call output_push_sticky_windows that
+ * should focus a sticky window if it was the focused in the previous workspace.
+ *
  */
-void output_push_sticky_windows(Con *to_focus);
+void output_push_sticky_windows(Con *old_focus);
index 39182c54b3e76d0851cebeb7d3ed99f68e4109b3..ec533a28045f92f4719c4f810efd8b56d0fa90f2 100644 (file)
@@ -137,7 +137,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far
  */
 Output *get_output_next_wrap(direction_t direction, Output *current);
 
-/*
+/**
  * Creates an output covering the root window.
  *
  */
index 750b7d310317901155a626656517db9102afedde..2b2c8dad74b291347144e9dc9189da570cb9c695 100644 (file)
 
 #include <config.h>
 
-/* This is used to keep a state to pass around when rendering a con in render_con(). */
+/**
+ * This is used to keep a state to pass around when rendering a con in render_con().
+ *
+ */
 typedef struct render_params {
     /* A copy of the coordinates of the container which is being rendered. */
     int x;
@@ -39,7 +42,8 @@ typedef struct render_params {
  */
 void render_con(Con *con, bool render_fullscreen);
 
-/*
+/**
  * Returns the height for the decorations
+ *
  */
 int render_deco_height(void);
index b90211ef1a9ca5a612e71a3028eb65f984fb77ab..dc8081f1329c8e6a24d2646c19428127efe594ce 100644 (file)
@@ -20,7 +20,7 @@
 /* Default shmlog size if not set by user. */
 extern const int default_shmlog_size;
 
-/*
+/**
  * Header of the shmlog file. Used by i3/src/log.c and i3/i3-dump-log/main.c.
  *
  */
index 5da221e1679ca364da489db753145f0eb32266c8..feece575c36f8a9d8361fdb08ef81b5b1a01ce43 100644 (file)
@@ -21,7 +21,7 @@
  * Starts the given application by passing it through a shell. We use double
  * fork to avoid zombie processes. As the started application’s parent exits
  * (immediately), the application is reparented to init (process-id 1), which
- * correctly handles childs, so we don’t have to do it :-).
+ * correctly handles children, so we don’t have to do it :-).
  *
  * The shell used to start applications is the system's bourne shell (i.e.,
  * /bin/sh).
index 90ddc4a4615b97943e1283710d73f651aa68c8c4..d08ac69df9cc4205dfcc791b7d8b76f77baa943e 100644 (file)
@@ -125,7 +125,7 @@ void i3_restart(bool forget_layout);
 
 #if defined(__OpenBSD__) || defined(__APPLE__)
 
-/*
+/**
  * Taken from FreeBSD
  * Find the first occurrence of the byte string s in byte string l.
  *
index a2c40319a52b492fcd57b6168495cc5d127257af..d15e35be78effe198e52e569d85bd2c8025a927e 100644 (file)
@@ -49,14 +49,12 @@ void init_dpi(void) {
         dpi = 0;
         goto init_dpi_end;
     }
-    dpi = (long)round(in_dpi);
+    dpi = lround(in_dpi);
 
     DLOG("Found Xft.dpi = %ld.\n", dpi);
 
 init_dpi_end:
-    if (resource != NULL) {
-        free(resource);
-    }
+    free(resource);
 
     if (database != NULL) {
         xcb_xrm_database_free(database);
index 6a2e93dcd8e3a413099e2e34b119fcfbf13faf93..f88360dc32ecab74a146a6e7e35789c9c35efc0b 100644 (file)
@@ -121,7 +121,7 @@ static void draw_util_set_source_color(surface_t *surface, color_t color) {
     cairo_set_source_rgba(surface->cr, color.red, color.green, color.blue, color.alpha);
 }
 
-/**
+/*
  * Draw the given text using libi3.
  * This function also marks the surface dirty which is needed if other means of
  * drawing are used. This will be the case when using XCB to draw text.
@@ -140,7 +140,7 @@ void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_
     cairo_surface_mark_dirty(surface->surface);
 }
 
-/**
+/*
  * Draws a filled rectangle.
  * This function is a convenience wrapper and takes care of flushing the
  * surface as well as restoring the cairo state.
@@ -167,7 +167,7 @@ void draw_util_rectangle(surface_t *surface, color_t color, double x, double y,
     cairo_restore(surface->cr);
 }
 
-/**
+/*
  * Clears a surface with the given color.
  *
  */
@@ -191,7 +191,7 @@ void draw_util_clear_surface(surface_t *surface, color_t color) {
     cairo_restore(surface->cr);
 }
 
-/**
+/*
  * Copies a surface onto another surface.
  *
  */
index 3dca8124bbce32c7cdd97738244ec7af4e3c5fa7..7be84ee07775474a1efe9b18aafaa44e04475b15 100644 (file)
@@ -224,9 +224,7 @@ i3Font load_font(const char *pattern, const bool fallback) {
                      error->error_code);
         }
     }
-    if (error != NULL) {
-        free(error);
-    }
+    free(error);
 
     font.pattern = sstrdup(pattern);
     LOG("Using X font %s\n", pattern);
@@ -275,8 +273,7 @@ void free_font(void) {
         case FONT_TYPE_XCB: {
             /* Close the font and free the info */
             xcb_close_font(conn, savedFont->specific.xcb.id);
-            if (savedFont->specific.xcb.info)
-                free(savedFont->specific.xcb.info);
+            free(savedFont->specific.xcb.info);
             break;
         }
         case FONT_TYPE_PANGO:
index 4e583622b3f78ec1050b1755cf40d2c908dc5faa..52187bdaecae374fac59618d188b61c7f2b26301 100644 (file)
@@ -15,7 +15,7 @@
  * release version), based on the git version number.
  *
  */
-bool is_debug_build() {
+bool is_debug_build(void) {
     /* i3_version contains either something like this:
      *     "4.0.2 (2011-11-11, branch "release")".
      * or: "4.0.2-123-gCOFFEEBABE (2011-11-11, branch "next")".
index a078b33e2b955494f34a2a7c37c21c398bd1a2bc..9efa369037a9b96a8d115dfd4c4366afa7873fdc 100644 (file)
@@ -99,7 +99,7 @@ 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.
  */
index 5cdb7c32dd981518dbcb8325286ce80fd7529a7b..43181c59e1bb96f52563e5063e719daa9a7848fe 100644 (file)
@@ -45,6 +45,7 @@ state INITIAL:
   'fake_outputs', 'fake-outputs'           -> FAKE_OUTPUTS
   'force_display_urgency_hint'             -> FORCE_DISPLAY_URGENCY_HINT
   'focus_on_window_activation'             -> FOCUS_ON_WINDOW_ACTIVATION
+  'title_align'                            -> TITLE_ALIGN
   'show_marks'                             -> SHOW_MARKS
   'workspace'                              -> WORKSPACE
   'ipc_socket', 'ipc-socket'               -> IPC_SOCKET
@@ -248,6 +249,11 @@ state FORCE_DISPLAY_URGENCY_HINT:
   duration_ms = number
       -> FORCE_DISPLAY_URGENCY_HINT_MS
 
+# title_align [left|center|right]
+state TITLE_ALIGN:
+  alignment = 'left', 'center', 'right'
+      -> call cfg_title_align($alignment)
+
 # show_marks
 state SHOW_MARKS:
   value = word
index ad5fe4f3209bcbbe8a6a7b9d0edd3a903b82857f..6704c816e148170a987088c505c2298245c7e33c 100644 (file)
@@ -859,8 +859,6 @@ static int fill_rmlvo_from_root(struct xkb_rule_names *xkb_names) {
     xcb_intern_atom_reply_t *atom_reply;
     size_t content_max_words = 256;
 
-    xcb_window_t root = root_screen->root;
-
     atom_reply = xcb_intern_atom_reply(
         conn, xcb_intern_atom(conn, 0, strlen("_XKB_RULES_NAMES"), "_XKB_RULES_NAMES"), NULL);
     if (atom_reply == NULL)
index 70f3bf18564bf93c3d48ccdc706de17d6af52b9e..eecd59fc063f4f7cff0ff9c398ce1bcc3c205c3c 100644 (file)
@@ -330,7 +330,7 @@ void cmd_move_con_to_workspace(I3_CMD, const char *which) {
     ysuccess(true);
 }
 
-/**
+/*
  * Implementation of 'move [window|container] [to] workspace back_and_forth'.
  *
  */
@@ -2034,7 +2034,7 @@ void cmd_rename_workspace(I3_CMD, const char *old_name, const char *new_name) {
  * Implementation of 'bar mode dock|hide|invisible|toggle [<bar_id>]'
  *
  */
-bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
+static bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
     int mode = M_DOCK;
     bool toggle = false;
     if (strcmp(bar_mode, "dock") == 0)
@@ -2079,7 +2079,7 @@ bool cmd_bar_mode(const char *bar_mode, const char *bar_id) {
  * Implementation of 'bar hidden_state hide|show|toggle [<bar_id>]'
  *
  */
-bool cmd_bar_hidden_state(const char *bar_hidden_state, const char *bar_id) {
+static bool cmd_bar_hidden_state(const char *bar_hidden_state, const char *bar_id) {
     int hidden_state = S_SHOW;
     bool toggle = false;
     if (strcmp(bar_hidden_state, "hide") == 0)
index 98f0665907dd36d759abebd4a55b09024a2f55c6..4299c008335570ced9c8daebc12bdaa1cefd74e9 100644 (file)
@@ -157,7 +157,7 @@ static long get_long(const char *identifier) {
 // TODO move to a common util
 static void clear_stack(void) {
     for (int c = 0; c < 10; c++) {
-        if (stack[c].type == STACK_STR && stack[c].val.str != NULL)
+        if (stack[c].type == STACK_STR)
             free(stack[c].val.str);
         stack[c].identifier = NULL;
         stack[c].val.str = NULL;
index d50c29be7a64003a92ad9ba0ca6eae330c4deebd..c9ff44a2cd484913065b8993b105375de9125e6c 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -312,7 +312,7 @@ bool con_has_managed_window(Con *con) {
     return (con != NULL && con->window != NULL && con->window->id != XCB_WINDOW_NONE && con_get_workspace(con) != NULL);
 }
 
-/**
+/*
  * Returns true if this node has regular or floating children.
  *
  */
@@ -526,7 +526,7 @@ Con *con_get_fullscreen_covering_ws(Con *ws) {
     return fs;
 }
 
-/**
+/*
  * Returns true if the container is internal, such as __i3_scratch
  *
  */
@@ -896,7 +896,7 @@ int con_num_children(Con *con) {
     return children;
 }
 
-/**
+/*
  * Returns the number of visible non-floating children of this container.
  * For example, if the container contains a hsplit which has two children,
  * this will return 2 instead of 1.
index 5be1fd521faa58a1f99c52333ca7c9b60f866f95..9631b2160237732ed404ad02757ab064b8d87b9b 100644 (file)
@@ -18,7 +18,7 @@ Config config;
 struct modes_head modes;
 struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
 
-/**
+/*
  * Ungrabs all keys, to be called before re-grabbing the keys because of a
  * mapping_notify event or a configuration file reload
  *
@@ -32,7 +32,7 @@ void ungrab_all_keys(xcb_connection_t *conn) {
  * Sends the current bar configuration as an event to all barconfig_update listeners.
  *
  */
-void update_barconfig() {
+void update_barconfig(void) {
     Barconfig *current;
     TAILQ_FOREACH(current, &barconfigs, configs) {
         ipc_send_barconfig_update_event(current);
index 5c85197f6e0194cf702bff727ef8c45ba4d67c1a..0b01d54a4acaf4ffa4a1fa0a6b3ff26ccf1109fc 100644 (file)
@@ -318,6 +318,18 @@ CFGFUN(focus_on_window_activation, const char *mode) {
     DLOG("Set new focus_on_window_activation mode = %i.\n", config.focus_on_window_activation);
 }
 
+CFGFUN(title_align, const char *alignment) {
+    if (strcmp(alignment, "left") == 0) {
+        config.title_align = ALIGN_LEFT;
+    } else if (strcmp(alignment, "center") == 0) {
+        config.title_align = ALIGN_CENTER;
+    } else if (strcmp(alignment, "right") == 0) {
+        config.title_align = ALIGN_RIGHT;
+    } else {
+        assert(false);
+    }
+}
+
 CFGFUN(show_marks, const char *value) {
     config.show_marks = eval_boolstr(value);
 }
index 2d3f3bb919eb2d134baa61f9e510880275fd8210..9f972fed8f80b4510931cac6edb52cef431c832a 100644 (file)
@@ -171,7 +171,7 @@ static long get_long(const char *identifier) {
 
 static void clear_stack(void) {
     for (int c = 0; c < 10; c++) {
-        if (stack[c].type == STACK_STR && stack[c].val.str != NULL)
+        if (stack[c].type == STACK_STR)
             free(stack[c].val.str);
         stack[c].identifier = NULL;
         stack[c].val.str = NULL;
index be514ce20e259f6f5db222bfc9639474f8e3eb54..a99d0970f356156abbd29e96a4167e7185ece558 100644 (file)
@@ -59,7 +59,7 @@ static void floating_set_hint_atom(Con *con, bool floating) {
     xcb_flush(conn);
 }
 
-/**
+/*
  * Called when a floating window is created or resized.
  * This function resizes the window if its size is higher or lower than the
  * configured maximum/minimum size, respectively.
@@ -899,13 +899,17 @@ bool floating_reposition(Con *con, Rect newrect) {
 
     con->rect = newrect;
 
-    floating_maybe_reassign_ws(con);
+    bool reassigned = floating_maybe_reassign_ws(con);
 
     /* If this is a scratchpad window, don't auto center it from now on. */
     if (con->scratchpad_state == SCRATCHPAD_FRESH)
         con->scratchpad_state = SCRATCHPAD_CHANGED;
 
-    tree_render();
+    /* Workspace change will already result in a tree_render. */
+    if (!reassigned) {
+        render_con(con, false);
+        x_push_node(con);
+    }
     return true;
 }
 
index c76f1935d34a1a34909391cad046fc12fae8cd30..238cf26f536cbcc883fc3c10193da864ebffa939 100644 (file)
@@ -949,8 +949,8 @@ static void handle_client_message(xcb_client_message_event_t *event) {
     }
 }
 
-bool handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
-                        xcb_atom_t atom, xcb_get_property_reply_t *reply) {
+static bool handle_window_type(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+                               xcb_atom_t atom, xcb_get_property_reply_t *reply) {
     Con *con;
     if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
         return false;
index 3022d5b399bd99d6f382a33e9d533e0f2cb85b4d..d0fb965c3d8e55dafbd5fdbd8c97f63e1794d913 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -1580,7 +1580,7 @@ void ipc_send_workspace_event(const char *change, Con *current, Con *old) {
     y(free);
 }
 
-/**
+/*
  * For the window events we send, along the usual "change" field,
  * also the window container, in "container".
  */
@@ -1610,7 +1610,7 @@ void ipc_send_window_event(const char *property, Con *con) {
     setlocale(LC_NUMERIC, "");
 }
 
-/**
+/*
  * For the barconfig update events, we send the serialized barconfig.
  */
 void ipc_send_barconfig_update_event(Barconfig *barconfig) {
index 32da9c47f107ef9ddf8b12b2522a0c8218dd57e5..5a340d2c6c28bfc1befb96829a9276172dcd3b74 100644 (file)
@@ -153,8 +153,7 @@ static int json_end_map(void *ctx) {
                 free(marks[i]);
             }
 
-            free(marks);
-            marks = NULL;
+            FREE(marks);
             num_marks = 0;
         }
 
index eeeb3419c366b959feb01d5f7a9fefd27c540eda..7eb47c822fa83550b0d61df0f8d127a27cac1e8d 100644 (file)
@@ -32,7 +32,7 @@
  * RLIM_INFINITY for i3 debugging versions. */
 struct rlimit original_rlimit_core;
 
-/** The number of file descriptors passed via socket activation. */
+/* The number of file descriptors passed via socket activation. */
 int listen_fds;
 
 /* We keep the xcb_prepare watcher around to be able to enable and disable it
index 4031ade63b859cc55d709a47c33d8ad1b04cc91e..c4706b0dd20892b4c2493f9344f34ce753e31dac 100644 (file)
@@ -246,17 +246,13 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
 
     DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height);
 
-    Con *nc = NULL;
-    Match *match = NULL;
-    Assignment *assignment;
-
-    /* TODO: two matches for one container */
-
     /* See if any container swallows this new window */
-    nc = con_for_window(search_at, cwindow, &match);
+    Match *match = NULL;
+    Con *nc = con_for_window(search_at, cwindow, &match);
     const bool match_from_restart_mode = (match && match->restart_mode);
     if (nc == NULL) {
         Con *wm_desktop_ws = NULL;
+        Assignment *assignment;
 
         /* If not, check if it is assigned to a specific workspace */
         if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE)) ||
index 32178e6b2890837d46fdf9044bd5f1b2aab239e0..545a910af24b48188685ccaef23dda50eb0d2642 100644 (file)
@@ -179,18 +179,16 @@ void insert_con_into(Con *con, Con *target, position_t position) {
 static void attach_to_workspace(Con *con, Con *ws, direction_t direction) {
     con_detach(con);
     con_fix_percent(con->parent);
-
     CALL(con->parent, on_remove_child);
 
     con->parent = ws;
 
     if (direction == D_RIGHT || direction == D_DOWN) {
         TAILQ_INSERT_HEAD(&(ws->nodes_head), con, nodes);
-        TAILQ_INSERT_HEAD(&(ws->focus_head), con, focused);
     } else {
         TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes);
-        TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused);
     }
+    TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused);
 
     /* Pretend the con was just opened with regards to size percent values.
      * Since the con is moved to a completely different con, the old value
@@ -205,7 +203,6 @@ static void attach_to_workspace(Con *con, Con *ws, direction_t direction) {
  *
  */
 static void move_to_output_directed(Con *con, direction_t direction) {
-    Con *old_ws = con_get_workspace(con);
     Output *current_output = get_output_for_con(con);
     Output *output = get_output_next(direction, current_output, CLOSEST_OUTPUT);
 
@@ -222,17 +219,26 @@ static void move_to_output_directed(Con *con, direction_t direction) {
         return;
     }
 
+    Con *old_ws = con_get_workspace(con);
+    const bool moves_focus = (focused == con);
     attach_to_workspace(con, ws, direction);
-
-    /* fix the focus stack */
-    con_activate(con);
+    if (moves_focus) {
+        /* workspace_show will not correctly update the active workspace because
+         * the focused container, con, is now a child of ws. To work around this
+         * and still produce the correct workspace focus events (see
+         * 517-regress-move-direction-ipc.t) we need to temporarily set focused
+         * to the old workspace. */
+        focused = old_ws;
+        workspace_show(ws);
+        con_focus(con);
+    }
 
     /* force re-painting the indicators */
     FREE(con->deco_render_params);
 
     tree_flatten(croot);
-
-    ipc_send_workspace_event("focus", ws, old_ws);
+    ipc_send_window_event("move", con);
+    ewmh_update_wm_desktop();
 }
 
 /*
@@ -292,9 +298,10 @@ void tree_move(Con *con, int direction) {
 
         /* easy case: the move is within this container */
         if (same_orientation == con->parent) {
-            DLOG("We are in the same container\n");
-            Con *swap;
-            if ((swap = (direction == D_LEFT || direction == D_UP ? TAILQ_PREV(con, nodes_head, nodes) : TAILQ_NEXT(con, nodes)))) {
+            Con *swap = (direction == D_LEFT || direction == D_UP)
+                            ? TAILQ_PREV(con, nodes_head, nodes)
+                            : TAILQ_NEXT(con, nodes);
+            if (swap) {
                 if (!con_is_leaf(swap)) {
                     DLOG("Moving into our bordering branch\n");
                     target = con_descend_direction(swap, direction);
@@ -306,26 +313,22 @@ void tree_move(Con *con, int direction) {
                     insert_con_into(con, target, position);
                     goto end;
                 }
-                if (direction == D_LEFT || direction == D_UP)
+
+                DLOG("Swapping with sibling.\n");
+                if (direction == D_LEFT || direction == D_UP) {
                     TAILQ_SWAP(swap, con, &(swap->parent->nodes_head), nodes);
-                else
+                } else {
                     TAILQ_SWAP(con, swap, &(swap->parent->nodes_head), nodes);
+                }
 
-                TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
-                TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused);
-
-                DLOG("Swapped.\n");
                 ipc_send_window_event("move", con);
-                ewmh_update_wm_desktop();
                 return;
             }
 
             if (con->parent == con_get_workspace(con)) {
-                /*  If we couldn't find a place to move it on this workspace,
-                 *  try to move it to a workspace on a different output */
+                /* If we couldn't find a place to move it on this workspace, try
+                 * to move it to a workspace on a different output */
                 move_to_output_directed(con, direction);
-                ipc_send_window_event("move", con);
-                ewmh_update_wm_desktop();
                 return;
             }
 
@@ -370,6 +373,7 @@ void tree_move(Con *con, int direction) {
          * and move it to the next output. */
         DLOG("Grandparent is workspace\n");
         move_to_output_directed(con, direction);
+        return;
     } else {
         DLOG("Moving into container above\n");
         position = (direction == D_UP || direction == D_LEFT ? BEFORE : AFTER);
index 19a7c4afafcecddccfb40e199449bf0b13e788f7..ebba5c77d40670afefe7b83f92245a5afea3b71f 100644 (file)
@@ -72,8 +72,15 @@ Output *get_output_for_con(Con *con) {
  * Iterates over all outputs and pushes sticky windows to the currently visible
  * workspace on that output.
  *
+ * old_focus is used to determine if a sticky window is going to be focused.
+ * old_focus might be different than the currently focused container because the
+ * caller might need to temporarily change the focus and then call
+ * output_push_sticky_windows. For example, workspace_show needs to set focus to
+ * one of its descendants first, then call output_push_sticky_windows that
+ * should focus a sticky window if it was the focused in the previous workspace.
+ *
  */
-void output_push_sticky_windows(Con *to_focus) {
+void output_push_sticky_windows(Con *old_focus) {
     Con *output;
     TAILQ_FOREACH(output, &(croot->focus_head), focused) {
         Con *workspace, *visible_ws = NULL;
@@ -95,18 +102,17 @@ void output_push_sticky_windows(Con *to_focus) {
                  child != TAILQ_END(&(current_ws->focus_head));) {
                 Con *current = child;
                 child = TAILQ_NEXT(child, focused);
-                if (current->type != CT_FLOATING_CON)
+                if (current->type != CT_FLOATING_CON || !con_is_sticky(current)) {
                     continue;
+                }
 
-                if (con_is_sticky(current)) {
-                    bool ignore_focus = (to_focus == NULL) || (current != to_focus->parent);
-                    con_move_to_workspace(current, visible_ws, true, false, ignore_focus);
-                    if (!ignore_focus) {
-                        Con *current_ws = con_get_workspace(focused);
-                        con_activate(con_descend_focused(current));
-                        /* Pushing sticky windows shouldn't change the focused workspace. */
-                        con_activate(con_descend_focused(current_ws));
-                    }
+                bool ignore_focus = (old_focus == NULL) || (current != old_focus->parent);
+                con_move_to_workspace(current, visible_ws, true, false, ignore_focus);
+                if (!ignore_focus) {
+                    Con *current_ws = con_get_workspace(focused);
+                    con_activate(con_descend_focused(current));
+                    /* Pushing sticky windows shouldn't change the focused workspace. */
+                    con_activate(con_descend_focused(current_ws));
                 }
             }
         }
index 54d61dddbeaa28bd496b65ec907616ebeb52605d..6bb8d9e649e5137f08da46c1db9e2c35747e4184 100644 (file)
@@ -550,7 +550,7 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) {
     }
 
     /* If default_orientation is NO_ORIENTATION, we change the orientation of
-     * the workspaces and their childs depending on output resolution. This is
+     * the workspaces and their children depending on output resolution. This is
      * only done for workspaces with maximum one child. */
     if (config.default_orientation == NO_ORIENTATION) {
         TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) {
index 12ed0ef990dc7fc25bf15bb84f374d071d0c869a..e49c30ba668efa3883ca048682481e16fb638e5b 100644 (file)
@@ -62,14 +62,13 @@ static int sighandler_backtrace(void) {
 
     char *filename = NULL;
     int suffix = 0;
-    struct stat bt;
     /* Find a unique filename for the backtrace (since the PID of i3 stays the
      * same), so that we don’t overwrite earlier backtraces. */
     do {
         FREE(filename);
         sasprintf(&filename, "%s/i3-backtrace.%d.%d.txt", tmpdir, pid_parent, suffix);
         suffix++;
-    } while (stat(filename, &bt) == 0);
+    } while (path_exists(filename));
 
     pid_t pid_gdb = fork();
     if (pid_gdb < 0) {
@@ -130,7 +129,7 @@ static int sighandler_backtrace(void) {
     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
         DLOG("GDB did not run properly\n");
         return -1;
-    } else if (stat(filename, &bt) == -1) {
+    } else if (!path_exists(filename)) {
         DLOG("GDB executed successfully, but no backtrace was generated\n");
         return -1;
     }
@@ -300,7 +299,7 @@ static void sighandler_handle_key_press(xcb_key_press_event_t *event) {
     }
 }
 
-void handle_signal(int sig, siginfo_t *info, void *data) {
+static void handle_signal(int sig, siginfo_t *info, void *data) {
     DLOG("i3 crashed. SIG: %d\n", sig);
 
     struct sigaction action;
index b1a2f60215550d9585a7f24efbebb243e2c672cd..6302d811e3b4b3688ff59cc8bbfe7108edcfc390 100644 (file)
@@ -95,7 +95,7 @@ static int _prune_startup_sequences(void) {
     return active_sequences;
 }
 
-/**
+/*
  * Deletes a startup sequence, ignoring whether its timeout has elapsed.
  * Useful when e.g. a window is moved between workspaces and its children
  * shouldn't spawn on the original workspace.
@@ -118,10 +118,10 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) {
 }
 
 /*
- * Starts the given application by passing it through a shell. We use double fork
- * to avoid zombie processes. As the started application’s parent exits (immediately),
- * the application is reparented to init (process-id 1), which correctly handles
- * childs, so we don’t have to do it :-).
+ * Starts the given application by passing it through a shell. We use double
+ * fork to avoid zombie processes. As the started application’s parent exits
+ * (immediately), the application is reparented to init (process-id 1), which
+ * correctly handles children, so we don’t have to do it :-).
  *
  * The shell used to start applications is the system's bourne shell (i.e.,
  * /bin/sh).
@@ -257,7 +257,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) {
     }
 }
 
-/**
+/*
  * Renames workspaces that are mentioned in the startup sequences.
  *
  */
@@ -273,7 +273,7 @@ void startup_sequence_rename_workspace(const char *old_name, const char *new_nam
     }
 }
 
-/**
+/*
  * Gets the stored startup sequence for the _NET_STARTUP_ID of a given window.
  *
  */
index a59283a8dd044a788a645b3296b481bcce4c621d..85f359c0fa801b45e20a9ee9515253cd0316d934 100644 (file)
@@ -217,7 +217,7 @@ static char **add_argument(char **original, char *opt_char, char *opt_arg, char
 #define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__)
 #define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str))
 
-char *store_restart_layout(void) {
+static char *store_restart_layout(void) {
     setlocale(LC_NUMERIC, "C");
     yajl_gen gen = yajl_gen_alloc(NULL);
 
@@ -501,8 +501,7 @@ ssize_t slurp(const char *path, char **buf) {
     fclose(f);
     if ((ssize_t)n != stbuf.st_size) {
         ELOG("File \"%s\" could not be read entirely: got %zd, want %" PRIi64 "\n", path, n, (int64_t)stbuf.st_size);
-        free(*buf);
-        *buf = NULL;
+        FREE(*buf);
         return -1;
     }
     return (ssize_t)n;
index 5f5c8d4f3e7987410baa2b5e7c64450c85f0399e..a2d9b0e8ae469068d8a92bffc20708cb81ce51e8 100644 (file)
@@ -308,7 +308,7 @@ bool workspace_is_visible(Con *ws) {
  * XXX: we need to clean up all this recursive walking code.
  *
  */
-Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
+static Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) {
     Con *current;
 
     TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
@@ -410,7 +410,6 @@ static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents
  */
 void workspace_show(Con *workspace) {
     Con *current, *old = NULL;
-    Con *old_focus = focused;
 
     /* safe-guard against showing i3-internal workspaces like __i3_scratch */
     if (con_is_internal(workspace))
@@ -433,6 +432,13 @@ void workspace_show(Con *workspace) {
         return;
     }
 
+    /* Used to correctly update focus when pushing sticky windows. Holds the
+     * previously focused container in the same output as workspace. For
+     * example, if a sticky window is focused and then we switch focus to a
+     * workspace in another output and then switch to a third workspace in the
+     * first output, the sticky window needs to be refocused. */
+    Con *old_focus = old ? con_descend_focused(old) : NULL;
+
     /* Remember currently focused workspace for switching back to it later with
      * the 'workspace back_and_forth' command.
      * NOTE: We have to duplicate the name as the original will be freed when
@@ -914,7 +920,7 @@ Con *workspace_attach_to(Con *ws) {
     return new;
 }
 
-/**
+/*
  * Creates a new container and re-parents all of children from the given
  * workspace into it.
  *
@@ -949,7 +955,7 @@ 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.
  */
diff --git a/src/x.c b/src/x.c
index 1f54db83afb923b024d426431be784b74a8eb6ab..0fc6e714e5ab27f28c1ae929ed6849ceb4adbb50 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -42,7 +42,7 @@ typedef struct con_state {
     bool child_mapped;
     bool is_hidden;
 
-    /** The con for which this state is. */
+    /* The con for which this state is. */
     Con *con;
 
     /* For reparenting, we have a flag (need_reparent) and the X ID of the old
@@ -504,14 +504,12 @@ void x_draw_decoration(Con *con) {
     /* 3: draw a rectangle in border color around the client */
     if (p->border_style != BS_NONE && p->con_is_leaf) {
         /* We might hide some borders adjacent to the screen-edge */
-        adjacent_t borders_to_hide = ADJ_NONE;
-        borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
-
+        adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders;
         Rect br = con_border_style_rect(con);
 
         /* These rectangles represent the border around the child window
          * (left, bottom and right part). We don’t just fill the whole
-         * rectangle because some childs are not freely resizable and we want
+         * rectangle because some children are not freely resizable and we want
          * their background color to "shine through". */
         if (!(borders_to_hide & ADJ_LEFT_SCREEN_EDGE)) {
             draw_util_rectangle(&(con->frame_buffer), p->color->child_border, 0, 0, br.x, r->height);
@@ -602,6 +600,8 @@ void x_draw_decoration(Con *con) {
         goto after_title;
     }
 
+    const int title_padding = logical_px(2);
+    const int deco_width = (int)con->deco_rect.width;
     int mark_width = 0;
     if (config.show_marks && !TAILQ_EMPTY(&(con->marks_head))) {
         char *formatted_mark = sstrdup("");
@@ -623,12 +623,17 @@ void x_draw_decoration(Con *con) {
             i3String *mark = i3string_from_utf8(formatted_mark);
             mark_width = predict_text_width(mark);
 
+            int mark_offset_x = (config.title_align == ALIGN_RIGHT)
+                                    ? title_padding
+                                    : deco_width - mark_width - title_padding;
+
             draw_util_text(mark, &(parent->frame_buffer),
                            p->color->text, p->color->background,
-                           con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2),
+                           con->deco_rect.x + mark_offset_x,
                            con->deco_rect.y + text_offset_y, mark_width);
-
             I3STRING_FREE(mark);
+
+            mark_width += title_padding;
         }
 
         FREE(formatted_mark);
@@ -639,11 +644,33 @@ void x_draw_decoration(Con *con) {
         goto copy_pixmaps;
     }
 
+    int title_offset_x;
+    switch (config.title_align) {
+        case ALIGN_LEFT:
+            /* (pad)[text    ](pad)[mark + its pad) */
+            title_offset_x = title_padding;
+            break;
+        case ALIGN_CENTER:
+            /* (pad)[  text  ](pad)[mark + its pad)
+             * To center the text inside its allocated space, the surface
+             * between the brackets, we use the formula
+             * (surface_width - predict_text_width) / 2
+             * where surface_width = deco_width - 2 * pad - mark_width
+             * so, offset = pad + (surface_width - predict_text_width) / 2 =
+             * = … = (deco_width - mark_width - predict_text_width) / 2 */
+            title_offset_x = max(title_padding, (deco_width - mark_width - predict_text_width(title)) / 2);
+            break;
+        case ALIGN_RIGHT:
+            /* [mark + its pad](pad)[    text](pad) */
+            title_offset_x = max(title_padding + mark_width, deco_width - title_padding - predict_text_width(title));
+            break;
+    }
+
     draw_util_text(title, &(parent->frame_buffer),
                    p->color->text, p->color->background,
-                   con->deco_rect.x + logical_px(2),
+                   con->deco_rect.x + title_offset_x,
                    con->deco_rect.y + text_offset_y,
-                   con->deco_rect.width - mark_width - 2 * logical_px(2));
+                   deco_width - mark_width - 2 * title_padding);
 
     if (con->title_format != NULL) {
         I3STRING_FREE(title);
@@ -1254,7 +1281,7 @@ void x_set_name(Con *con, const char *name) {
  * Set up the I3_SHMLOG_PATH atom.
  *
  */
-void update_shmlog_atom() {
+void update_shmlog_atom(void) {
     if (*shmlogname == '\0') {
         xcb_delete_property(conn, root, A_I3_SHMLOG_PATH);
     } else {
index 5506d67e78b1bf748edd692a9a2f7e7f5450a4fe..520b021315a61013a071a5d5a34197d575d37054 100644 (file)
@@ -34,7 +34,7 @@ static void read_server_x11_packet_cb(EV_P_ ev_io *w, int revents);
 
 static char *sun_path = NULL;
 
-void cleanup_socket(void) {
+static void cleanup_socket(void) {
     if (sun_path != NULL) {
         unlink(sun_path);
         free(sun_path);
index 4ac95e1563d018d3d02f0d59c94d037dbb244a91..ba5f76aa11634fa0b7cec14e01429133c3af8b6d 100644 (file)
@@ -265,11 +265,13 @@ my sub open_with_max_size {
 }
 
 my sub check_minsize {
+    sync_with_i3;
     is($window->rect->{width}, $min_width, 'width = min_width');
     is($window->rect->{height}, $min_height, 'height = min_height');
 }
 
 my sub check_maxsize {
+    sync_with_i3;
     is($window->rect->{width}, $max_width, 'width = max_width');
     is($window->rect->{height}, $max_height, 'height = max_height');
 }
@@ -279,7 +281,6 @@ $pid = launch_with_config($config);
 $window = open_with_max_size;
 cmd 'floating enable';
 cmd 'border none';
-sync_with_i3;
 
 cmd "resize set $min_width px $min_height px";
 check_minsize;
index a58f33c1339db17bcc631132e7e36a84e630d8a6..c6ce22eb9582eeaa35e1402c72d3ad3fb5c76377 100644 (file)
@@ -497,6 +497,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: <end>, '
         fake-outputs
         force_display_urgency_hint
         focus_on_window_activation
+        title_align
         show_marks
         workspace
         ipc_socket
index f3606b4e8106b1c32d34a463cca45bb2d29d5d24..708963dfd649997f68d77f6161ee7e839fb299c0 100644 (file)
@@ -34,7 +34,7 @@ sub move_subtest {
     is($move[0]->{container}->{window}, $window->{id}, 'window id matches');
 }
 
-subtest 'move right', \&move_subtest, 'move right';
+subtest 'move left', \&move_subtest, 'move left';
 subtest 'move to workspace', \&move_subtest, 'move to workspace ws_new';
 
 done_testing;
index f53e4a9341c09e8fd3f65f1c0a7913cbb81080d2..8dfe9aeaeb78f6be2248e78b3da3fd47a9c29638 100644 (file)
 #
 # Tests sticky windows.
 # Ticket: #1455
-use i3test;
+use i3test i3_config => <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+workspace ws-on-0 output fake-0
+
+fake-outputs 1024x768+0+0,1024x768+1024+0
+EOT
 
 my ($ws, $tmp, $focused);
 
@@ -25,41 +32,41 @@ my ($ws, $tmp, $focused);
 #    nothing happens.
 ###############################################################################
 fresh_workspace;
-open_window(wm_class => 'findme');
+open_window;
 cmd 'sticky enable';
 $ws = fresh_workspace;
 
 is(@{get_ws($ws)->{nodes}}, 0, 'tiling sticky container did not move');
 is(@{get_ws($ws)->{floating_nodes}}, 0, 'tiling sticky container did not move');
-cmd '[class="findme"] kill';
+kill_all_windows;
 
 ###############################################################################
 # 2: Given a sticky floating container, when the workspace is switched, then
 #    the container moves to the new workspace.
 ###############################################################################
 $ws = fresh_workspace;
-open_floating_window(wm_class => 'findme');
+open_floating_window;
 $focused = get_focused($ws);
 cmd 'sticky enable';
 $ws = fresh_workspace;
 
 is(@{get_ws($ws)->{floating_nodes}}, 1, 'floating sticky container moved to new workspace');
 is(get_focused($ws), $focused, 'sticky container has focus');
-cmd '[class="findme"] kill';
+kill_all_windows;
 
 ###############################################################################
 # 3: Given two sticky floating containers, when the workspace is switched,
 #    then both containers move to the new workspace.
 ###############################################################################
 fresh_workspace;
-open_floating_window(wm_class => 'findme');
+open_floating_window;
 cmd 'sticky enable';
-open_floating_window(wm_class => 'findme');
+open_floating_window;
 cmd 'sticky enable';
 $ws = fresh_workspace;
 
 is(@{get_ws($ws)->{floating_nodes}}, 2, 'multiple sticky windows can be used at the same time');
-cmd '[class="findme"] kill';
+kill_all_windows;
 
 ###############################################################################
 # 4: Given an unfocused sticky floating container and a tiling container on the
@@ -70,13 +77,13 @@ $ws = fresh_workspace;
 open_window;
 $focused = get_focused($ws);
 fresh_workspace;
-open_floating_window(wm_class => 'findme');
+open_floating_window;
 cmd 'sticky enable';
 open_window;
 cmd 'workspace ' . $ws;
 
 is(get_focused($ws), $focused, 'the tiling container has focus');
-cmd '[class="findme"] kill';
+kill_all_windows;
 
 ###############################################################################
 # 5: Given a focused sticky floating container and a tiling container on the
@@ -86,13 +93,13 @@ cmd '[class="findme"] kill';
 $ws = fresh_workspace;
 open_window;
 $tmp = fresh_workspace;
-open_floating_window(wm_class => 'findme');
+open_floating_window;
 $focused = get_focused($tmp);
 cmd 'sticky enable';
 cmd 'workspace ' . $ws;
 
 is(get_focused($ws), $focused, 'the sticky container has focus');
-cmd '[class="findme"] kill';
+kill_all_windows;
 
 ###############################################################################
 # 6: Given a floating container on a non-visible workspace, when the window
@@ -100,13 +107,31 @@ cmd '[class="findme"] kill';
 #    visible workspace.
 ###############################################################################
 fresh_workspace;
-open_floating_window(wm_class => 'findme');
+open_floating_window;
 cmd 'mark sticky';
 $ws = fresh_workspace;
 cmd '[con_mark=sticky] sticky enable';
 
 is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window jumps to the front');
-cmd '[class="findme"] kill';
+kill_all_windows;
+
+###############################################################################
+# 7: Given a sticky floating container and a workspace on another output, when
+#    a new workspace assigned to the first output is focused, then the sticky
+#    container should jump to the new workspace and have input focus correctly.
+###############################################################################
+$ws = fresh_workspace(output => 0);
+open_floating_window;
+cmd 'sticky enabled';
+$focused = get_focused($ws);
+$ws = fresh_workspace(output => 1);
+
+is(@{get_ws($ws)->{floating_nodes}}, 0, 'the sticky window didn\'t jump to a workspace on a different output');
+$ws = 'ws-on-0';
+cmd "workspace $ws";
+is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window moved to new workspace on first output');
+is(get_focused($ws), $focused, 'the sticky window has focus');
+kill_all_windows;
 
 ###############################################################################
 
index 0cd6e5c3228f72d405237eb4a55ba6d7351f05c6..55958c3ea3b8bd218f35a2f29e3f3af9130448a4 100644 (file)
@@ -67,11 +67,8 @@ my $tmp = fresh_workspace;
 my ($first_floating, $second_floating);
 
 synced_warp_pointer(0, 0);
-$first_floating = open_floating_window;
-$first_floating->rect(X11::XCB::Rect->new(x => 1, y => 1, width => 100, height => 100));
-$second_floating = open_floating_window;
-$second_floating->rect(X11::XCB::Rect->new(x => 50, y => 50, width => 100, height => 100));
-sync_with_i3;
+$first_floating = open_floating_window(rect => [ 1, 1, 100, 100 ]);
+$second_floating = open_floating_window(rect => [ 50, 50, 100, 100 ]);
 $first = open_window;
 
 is($x->input_focus, $first->id, 'first (tiling) window focused');
index c818f1d441e36aea832e802163c2f42de8604f6b..6b16540c85d68e17285969962687ac6ea17e8867 100644 (file)
 #
 # Verify that the corrent focus stack order is preserved after various
 # operations.
-use i3test;
+use i3test i3_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
+EOT
 
 sub kill_and_confirm_focus {
     my $focus = shift;
@@ -68,7 +72,6 @@ confirm_focus('tabbed');
 #####################################################################
 
 fresh_workspace;
-
 $windows[3] = open_window;
 $windows[1] = open_window;
 $windows[0] = open_window;
@@ -107,6 +110,37 @@ $windows[0] = open_window;
 cmd 'move left';
 confirm_focus('split-v + move');
 
+#####################################################################
+# Test that moving an unfocused container from another output
+# maintains the correct focus order.
+#####################################################################
+
+fresh_workspace(output => 0);
+$windows[3] = open_window;
+fresh_workspace(output => 1);
+$windows[2] = open_window;
+$windows[1] = open_window;
+$windows[0] = open_window;
+
+cmd '[id=' . $windows[3]->id . '] move right';
+confirm_focus('unfocused move from other output');
+
+#####################################################################
+# Test that moving an unfocused container inside its original parent
+# maintains the correct focus order.
+#####################################################################
+
+fresh_workspace;
+$windows[0] = open_window;
+$windows[1] = open_window;
+cmd 'split v';
+$windows[2] = open_window;
+$windows[3] = open_window;
+focus_windows;
+
+cmd '[id=' . $windows[2]->id . '] move up';
+confirm_focus('split-v + unfocused move inside parent');
+
 ######################################################################
 # Test that moving an unfocused container maintains the correct focus
 # order.