]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #1549 from shdown/y-offset-fix
authorMichael Stapelberg <stapelberg@users.noreply.github.com>
Sat, 21 Mar 2015 15:32:19 +0000 (16:32 +0100)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Sat, 21 Mar 2015 15:32:19 +0000 (16:32 +0100)
Fix incorrect y-offset for text in i3bar (2)

31 files changed:
.travis.yml [new file with mode: 0644]
docs/hacking-howto
i3-dmenu-desktop
i3-migrate-config-to-v4
i3-nagbar/main.c
i3.config
include/config.h
include/data.h
include/key_press.h
include/output.h
include/startup.h
include/workspace.h
libi3/font.c
man/i3-sensible-editor.man
man/i3-sensible-pager.man
man/i3-sensible-terminal.man
src/click.c
src/commands.c
src/config.c
src/config_parser.c
src/handlers.c
src/main.c
src/manage.c
src/output.c
src/startup.c
src/workspace.c
testcases/complete-run.pl
testcases/lib/StatusLine.pm
testcases/t/175-startup-notification.t
testcases/t/200-urgency-timer.t
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..5876ae6
--- /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:
+  - make -j
+  - (cd testcases && xvfb-run ./complete-run.pl --parallel=1 || (cat latest/complete-run.log; false))
+  - clang-format-3.5 -i **/*.[ch] && git diff --exit-code || (echo 'Code was not formatted using clang-format!'; false)
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 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 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 bdf6582bd04f917fe642892702c90349249d7e6c..b501ff6a304536437e02525d9f4846bd860922a8 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>
index c53ecadf5f0d654af1d32b1f6b42cf7a1122bd10..f7722d361957c5d48b8aedc42c3d2033bba89d36 100644 (file)
--- a/i3.config
+++ b/i3.config
@@ -169,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 dea26d96344548a5573ebdb6de2813b4a1ccc32e..afafb64be0cb8da35017684d0ec354e7cd03f50c 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;
 
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 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 b6d9d42e696d4a8e34865b13e970cc68aea3ccfb..847bc61beb30c2d474ef4ee5c96639305f4e91e3 100644 (file)
@@ -458,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 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 51ffcf3ab3c4361452897713bf399bdf70c87302..55e7147c557237c6cef5d7a1ff10978c86bc154b 100644 (file)
@@ -380,11 +380,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 +389,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..80f57b810ba1f9c7b8cb941ca79d4873b20f2521 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.
  */
@@ -1177,28 +1155,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 +1228,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 +1908,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 +1936,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 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 24cebcece9707755087d83928a49030629ecea63..b229b445ba24ae71358cfcc891e4fafa06d40490 100644 (file)
@@ -823,11 +823,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 1e6b634c6cfd33de2f9367525deaa8b91b5e7f04..0cd397fd0669e6f855dc80e6a4957498f3c8c1b6 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);
@@ -1315,7 +1315,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 708df38e0af57f77fe0a655adf5c2884c709dad7..8b514178bbbf3283fc9391f11ef5c6f42643ba1a 100644 (file)
@@ -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 7c464b43efa91c69d3d105db51861acda280f615..077d5720b540366445b3fe813febfc3dbe9fbe78 100644 (file)
@@ -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 */
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 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 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;
+}
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 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 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 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);
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;