]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Tue, 2 Apr 2013 21:59:48 +0000 (23:59 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 2 Apr 2013 21:59:48 +0000 (23:59 +0200)
29 files changed:
debian/changelog
debian/rules
docs/hacking-howto
docs/i3bar-protocol
docs/userguide
i3-config-wizard/main.c
i3-dmenu-desktop
i3bar/include/child.h
i3bar/include/common.h
i3bar/src/child.c
i3bar/src/main.c
i3bar/src/parse_json_header.c
i3bar/src/xcb.c
include/con.h
include/data.h
include/randr.h
parser-specs/config.spec
src/con.c
src/config_parser.c
src/floating.c
src/ipc.c
src/load_layout.c
src/log.c
src/randr.c
src/scratchpad.c
src/workspace.c
testcases/t/201-config-parser.t
testcases/t/202-scratchpad-criteria.t
testcases/t/514-ipc-workspace-multi-monitor.t [new file with mode: 0644]

index ebdfbcef3ec7369c850fa23966f225ab4b92a40a..80a20e3c2d092e10066df87bb3a5db21d72d528e 100644 (file)
@@ -1,8 +1,20 @@
-i3-wm (4.4.1-0) unstable; urgency=low
+i3-wm (4.5.2-1) experimental; urgency=low
 
   * NOT YET RELEASED
 
- -- Michael Stapelberg <stapelberg@debian.org>  Wed, 12 Dec 2012 00:23:32 +0100
+ -- Michael Stapelberg <stapelberg@debian.org>  Mon, 18 Mar 2013 23:01:30 +0100
+
+i3-wm (4.5.1-1) experimental; urgency=low
+
+  * New upstream release
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Mon, 18 Mar 2013 22:50:12 +0100
+
+i3-wm (4.5-1) experimental; urgency=low
+
+  * New upstream release
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Tue, 12 Mar 2013 13:51:04 +0100
 
 i3-wm (4.4-1) experimental; urgency=low
 
index b119c47e84347a2c9a82230c4704cf151791e16d..3ae79dddf30ae59db5de256e4db3422b10e19f2d 100755 (executable)
@@ -38,7 +38,7 @@ override_dh_auto_build:
        $(MAKE) -C docs
 
 override_dh_installchangelogs:
-       dh_installchangelogs RELEASE-NOTES-4.4
+       dh_installchangelogs RELEASE-NOTES-4.5.1
 
 override_dh_install:
        $(MAKE) DESTDIR=$(CURDIR)/debian/i3-wm/ install
index 633c277165f40b3a4d22315573367f0b2641f31b..c601bf9a1c17e69830c855ee9d20c05d4c90d777 100644 (file)
@@ -944,9 +944,11 @@ Without much ado, here is the list of cases which need to be considered:
 
 == Using git / sending patches
 
+=== Introduction
+
 For a short introduction into using git, see
-http://www.spheredev.org/wiki/Git_for_the_lazy or, for more documentation, see
-http://git-scm.com/documentation
+http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy
+or, for more documentation, see http://git-scm.com/documentation
 
 Please talk to us before working on new features to see whether they will be
 accepted. There are a few things which we don’t want to see in i3, e.g. a
@@ -963,6 +965,17 @@ them in the bugtracker, since all reviews should be done in public at
 http://cr.i3wm.org/. In order to make your review go as fast as possible, you
 could have a look at previous reviews and see what the common mistakes are.
 
+=== Which branch to use?
+
+Work on i3 generally happens in two branches: “master” and “next”. Since
+“master” is what people get when they check out the git repository, its
+contents are always stable. That is, it contains the source code of the latest
+release, plus any bugfixes that were applied since that release.
+
+New features are only found in the “next” branch. Therefore, if you are working
+on a new feature, use the “next” branch. If you are working on a bugfix, use
+the “next” branch, too, but make sure your code also works on “master”.
+
 == Thought experiments
 
 In this section, we collect thought experiments, so that we don’t forget our
index 9225d97eab134f90b9567095f2cb2eea622ef3e0..bd8ea5365a4a8883d023f9e4c6e2b88d8251626c 100644 (file)
@@ -51,7 +51,7 @@ consists of a single JSON hash:
 
 *All features example*:
 ------------------------------
-{ "version": 1, "stop_signal": 10, "cont_signal": 12 }
+{ "version": 1, "stop_signal": 10, "cont_signal": 12, "click_events": true }
 ------------------------------
 
 (Note that before i3 v4.3 the precise format had to be +{"version":1}+,
@@ -110,6 +110,9 @@ cont_signal::
        Specify to i3bar the signal (as an integer)to send to continue your
        processing.
        The default value (if none is specified) is SIGCONT.
+click_events::
+       If specified and true i3bar will write a infinite array (same as above)
+       to your stdin.
 
 === Blocks in detail
 
@@ -210,3 +213,28 @@ An example of a block which uses all possible entries follows:
  "separator_block_width": 9
 }
 ------------------------------------------
+
+=== Click events
+
+If enabled i3bar will send you notifications if the user clicks on a block and
+looks like this:
+
+name::
+       Name of the block, if set
+instance::
+       Instance of the block, if set
+x, y::
+       X11 root window coordinates where the click occured
+button:
+       X11 button ID (for example 1 to 3 for left/middle/right mouse button)
+
+*Example*:
+------------------------------------------
+{
+ "name": "ethernet",
+ "instance": "eth0",
+ "button": 1,
+ "x": 1320,
+ "y": 1400
+}
+------------------------------------------
index a0f521c27b2613806742b35e494e15d351bea26f..befdb36c00c64343ff31106136e389faa281e39e 100644 (file)
@@ -1,7 +1,7 @@
 i3 User’s Guide
 ===============
 Michael Stapelberg <michael@i3wm.org>
-February 2013
+March 2013
 
 This document contains all the information you need to configure and use the i3
 window manager. If it does not, please check http://faq.i3wm.org/ first, then
@@ -1319,9 +1319,9 @@ bindsym $mod+h split horizontal
 
 === Manipulating layout
 
-Use +layout toggle split+, +layout stacking+ or +layout tabbed+ to change the
-current container layout to splith/splitv, stacking or tabbed layout,
-respectively.
+Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+
+or +layout splith+ to change the current container layout to splith/splitv,
+stacking, tabbed layout, splitv or splith, respectively.
 
 To make the current window (!) fullscreen, use +fullscreen+, to make
 it floating (or tiling again) use +floating enable+ respectively +floating disable+
@@ -1329,7 +1329,7 @@ it floating (or tiling again) use +floating enable+ respectively +floating disab
 
 *Syntax*:
 --------------
-layout <tabbed|stacking>
+layout <default|tabbed|stacking|splitv|splith>
 layout toggle [split|all]
 --------------
 
index 54c8e0245478d11b97677d8d445e4518d7ee8510..ffc3df93011869e88561e523445fa9e267aeb68e 100644 (file)
@@ -13,7 +13,7 @@
 #endif
 
 /* For systems without getline, fall back to fgetln */
-#if defined(__APPLE__) || (defined(__FreeBSD__) && __FreeBSD_version < 800000)
+#if defined(__APPLE__)
 #define USE_FGETLN
 #elif defined(__FreeBSD__)
 /* Defining this macro before including stdio.h is necessary in order to have
index 65e99ec09b13c663df6820dbcbf293c588bb463d..cf5b41e0f8f97d943aebc44e3ec6824d6d49de47 100755 (executable)
@@ -346,7 +346,13 @@ if (exists($choices{$choice})) {
         last;
     }
     if (!defined($app)) {
-        die "Invalid input: “$choice” does not match any application.";
+        warn "Invalid input: “$choice” does not match any application. Trying to execute nevertheless.";
+        $app->{Name} = '';
+        $app->{Exec} = $choice;
+        # We assume that the app is old and does not support startup
+        # notifications because it doesn’t ship a desktop file.
+        $app->{StartupNotify} = 0;
+        $app->{_Location} = '';
     }
 }
 
index d1c46890bc28e6534fdebf7987da54572c1e5bc0..dc244befe83080808246db148fb68ee8b0930ce8 100644 (file)
@@ -33,6 +33,12 @@ typedef struct {
      * The signal requested by the client to inform it of theun hidden state of i3bar
      */
     int cont_signal;
+
+    /**
+     * Enable click events
+     */
+    bool click_events;
+    bool click_events_init;
 } i3bar_child;
 
 /*
@@ -68,4 +74,10 @@ void stop_child(void);
  */
 void cont_child(void);
 
+/*
+ * Generates a click event, if enabled.
+ *
+ */
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y);
+
 #endif
index 1365082f54591c0720712ce346ce7c37373ff36d..cb55e0d679d6f751ef5ed74d4a9bbc1c375ef829 100644 (file)
@@ -54,6 +54,10 @@ struct status_block {
     uint32_t x_offset;
     uint32_t x_append;
 
+    /* Optional */
+    char *name;
+    char *instance;
+
     TAILQ_ENTRY(status_block) blocks;
 };
 
index e5f4ea213cd10a5f16d9a2eb0a21343f45fdae63..ad93681c65f3d352b085347f802d9dd1aeeb5327 100644 (file)
@@ -21,6 +21,7 @@
 #include <yajl/yajl_common.h>
 #include <yajl/yajl_parse.h>
 #include <yajl/yajl_version.h>
+#include <yajl/yajl_gen.h>
 
 #include "common.h"
 
@@ -35,6 +36,9 @@ ev_child *child_sig;
 yajl_callbacks callbacks;
 yajl_handle parser;
 
+/* JSON generator for stdout */
+yajl_gen gen;
+
 typedef struct parser_ctx {
     /* True if one of the parsed blocks was urgent */
     bool has_urgent;
@@ -53,6 +57,8 @@ parser_ctx parser_context;
 struct statusline_head statusline_head = TAILQ_HEAD_INITIALIZER(statusline_head);
 char *statusline_buffer = NULL;
 
+int child_stdin;
+
 /*
  * Stop and free() the stdin- and sigchild-watchers
  *
@@ -85,6 +91,8 @@ static int stdin_start_array(void *context) {
         first = TAILQ_FIRST(&statusline_head);
         I3STRING_FREE(first->full_text);
         FREE(first->color);
+        FREE(first->name);
+        FREE(first->instance);
         TAILQ_REMOVE(&statusline_head, first, blocks);
         free(first);
     }
@@ -152,6 +160,18 @@ static int stdin_string(void *context, const unsigned char *val, unsigned int le
         ctx->block.min_width = (uint32_t)predict_text_width(text);
         i3string_free(text);
     }
+    if (strcasecmp(ctx->last_map_key, "name") == 0) {
+        char *copy = (char*)malloc(len+1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.name = copy;
+    }
+    if (strcasecmp(ctx->last_map_key, "instance") == 0) {
+        char *copy = (char*)malloc(len+1);
+        strncpy(copy, (const char *)val, len);
+        copy[len] = 0;
+        ctx->block.instance = copy;
+    }
     return 1;
 }
 
@@ -336,6 +356,17 @@ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
     cleanup();
 }
 
+void child_write_output(void) {
+    if (child.click_events) {
+        const unsigned char *output;
+        size_t size;
+        yajl_gen_get_buf(gen, &output, &size);
+        write(child_stdin, output, size);
+        write(child_stdin, "\n", 1);
+        yajl_gen_clear(gen);
+    }
+}
+
 /*
  * Start a child-process with the specified command and reroute stdin.
  * We actually start a $SHELL to execute the command so we don't have to care
@@ -361,10 +392,16 @@ void start_child(char *command) {
     parser = yajl_alloc(&callbacks, NULL, &parser_context);
 #endif
 
+    gen = yajl_gen_alloc(NULL);
+
     if (command != NULL) {
-        int fd[2];
-        if (pipe(fd) == -1)
-            err(EXIT_FAILURE, "pipe(fd)");
+        int pipe_in[2]; /* pipe we read from */
+        int pipe_out[2]; /* pipe we write to */
+
+        if (pipe(pipe_in) == -1)
+            err(EXIT_FAILURE, "pipe(pipe_in)");
+        if (pipe(pipe_out) == -1)
+            err(EXIT_FAILURE, "pipe(pipe_out)");
 
         child.pid = fork();
         switch (child.pid) {
@@ -372,10 +409,13 @@ void start_child(char *command) {
                 ELOG("Couldn't fork(): %s\n", strerror(errno));
                 exit(EXIT_FAILURE);
             case 0:
-                /* Child-process. Reroute stdout and start shell */
-                close(fd[0]);
+                /* Child-process. Reroute streams and start shell */
+
+                close(pipe_in[0]);
+                close(pipe_out[1]);
 
-                dup2(fd[1], STDOUT_FILENO);
+                dup2(pipe_in[1], STDOUT_FILENO);
+                dup2(pipe_out[0], STDIN_FILENO);
 
                 static const char *shell = NULL;
 
@@ -385,10 +425,13 @@ void start_child(char *command) {
                 execl(shell, shell, "-c", command, (char*) NULL);
                 return;
             default:
-                /* Parent-process. Rerout stdin */
-                close(fd[1]);
+                /* Parent-process. Reroute streams */
 
-                dup2(fd[0], STDIN_FILENO);
+                close(pipe_in[1]);
+                close(pipe_out[0]);
+
+                dup2(pipe_in[0], STDIN_FILENO);
+                child_stdin = pipe_out[1];
 
                 break;
         }
@@ -409,6 +452,52 @@ void start_child(char *command) {
     atexit(kill_child_at_exit);
 }
 
+void child_click_events_initialize(void) {
+    if (!child.click_events_init) {
+        yajl_gen_array_open(gen);
+        child_write_output();
+        child.click_events_init = true;
+    }
+}
+
+void child_click_events_key(const char *key) {
+    yajl_gen_string(gen, (const unsigned char *)key, strlen(key));
+}
+
+/*
+ * Generates a click event, if enabled.
+ *
+ */
+void send_block_clicked(int button, const char *name, const char *instance, int x, int y) {
+    if (child.click_events) {
+        child_click_events_initialize();
+
+        yajl_gen_map_open(gen);
+
+        if (name) {
+            child_click_events_key("name");
+            yajl_gen_string(gen, (const unsigned char *)name, strlen(name));
+        }
+
+        if (instance) {
+            child_click_events_key("instance");
+            yajl_gen_string(gen, (const unsigned char *)instance, strlen(instance));
+        }
+
+        child_click_events_key("button");
+        yajl_gen_integer(gen, button);
+
+        child_click_events_key("x");
+        yajl_gen_integer(gen, x);
+
+        child_click_events_key("y");
+        yajl_gen_integer(gen, y);
+
+        yajl_gen_map_close(gen);
+        child_write_output();
+    }
+}
+
 /*
  * kill()s the child-process (if any). Called when exit()ing.
  *
index ea6056470c5f05381fc4c1e9efffddb1e3abbb7b..c62f7b3c96d74c8a1d0a802caf4ac9aeb455fcd5 100644 (file)
@@ -53,12 +53,12 @@ char *expand_path(char *path) {
 }
 
 void print_usage(char *elf_name) {
-    printf("Usage: %s [-b bar_id] [-s sock_path] [-h] [-v]\n", elf_name);
+    printf("Usage: %s -b bar_id [-s sock_path] [-h] [-v]\n", elf_name);
     printf("\n");
-    printf("--bar_id <bar_id>\tBar ID for which to get the configuration\n");
-    printf("-s <sock_path>\tConnect to i3 via <sock_path>\n");
-    printf("-h\t\tDisplay this help-message and exit\n");
-    printf("-v\t\tDisplay version number and exit\n");
+    printf("-b, --bar_id  <bar_id>\tBar ID for which to get the configuration\n");
+    printf("-s, --socket  <sock_path>\tConnect to i3 via <sock_path>\n");
+    printf("-h, --help    Display this help-message and exit\n");
+    printf("-v, --version Display version number and exit\n");
     printf("\n");
     printf(" PLEASE NOTE that i3bar will be automatically started by i3\n"
            " as soon as there is a 'bar' configuration block in your\n"
@@ -97,13 +97,13 @@ int main(int argc, char **argv) {
 
     static struct option long_opt[] = {
         { "socket",               required_argument, 0, 's' },
-        { "bar_id",               required_argument, 0, 0 },
+        { "bar_id",               required_argument, 0, 'b' },
         { "help",                 no_argument,       0, 'h' },
         { "version",              no_argument,       0, 'v' },
         { NULL,                   0,                 0, 0}
     };
 
-    while ((opt = getopt_long(argc, argv, "s:hv", long_opt, &option_index)) != -1) {
+    while ((opt = getopt_long(argc, argv, "b:s:hv", long_opt, &option_index)) != -1) {
         switch (opt) {
             case 's':
                 socket_path = expand_path(optarg);
@@ -112,11 +112,8 @@ int main(int argc, char **argv) {
                 printf("i3bar version " I3_VERSION " © 2010-2011 Axel Wagner and contributors\n");
                 exit(EXIT_SUCCESS);
                 break;
-            case 0:
-                if (!strcmp(long_opt[option_index].name, "bar_id")) {
-                    FREE(config.bar_id);
-                    config.bar_id = sstrdup(optarg);
-                }
+            case 'b':
+                config.bar_id = sstrdup(optarg);
                 break;
             default:
                 print_usage(argv[0]);
index 80ec5af8f78d4de61bda169e442b5552ac57631e..c09e0f499aaf107bedc0f531dd391483b0dd2622 100644 (file)
@@ -31,6 +31,7 @@ static enum {
     KEY_VERSION,
     KEY_STOP_SIGNAL,
     KEY_CONT_SIGNAL,
+    KEY_CLICK_EVENTS,
     NO_KEY
 } current_key;
 
@@ -54,6 +55,21 @@ static int header_integer(void *ctx, long val) {
         default:
             break;
     }
+
+    return 1;
+}
+
+static int header_boolean(void *ctx, int val) {
+    i3bar_child *child = ctx;
+
+    switch (current_key) {
+        case KEY_CLICK_EVENTS:
+            child->click_events = val;
+            break;
+        default:
+            break;
+    }
+
     return 1;
 }
 
@@ -71,13 +87,15 @@ static int header_map_key(void *ctx, const unsigned char *stringval, unsigned in
         current_key = KEY_STOP_SIGNAL;
     } else if (CHECK_KEY("cont_signal")) {
         current_key = KEY_CONT_SIGNAL;
+    } else if (CHECK_KEY("click_events")) {
+        current_key = KEY_CLICK_EVENTS;
     }
     return 1;
 }
 
 static yajl_callbacks version_callbacks = {
     NULL, /* null */
-    NULL, /* boolean */
+    &header_boolean, /* boolean */
     &header_integer,
     NULL, /* double */
     NULL, /* number */
index d5d4eb8659cfedf7a9ecfbc134e72638e6d5fd8d..69cd810e7ec4c58dcf1d4d86ac43d835096d05e7 100644 (file)
@@ -320,24 +320,11 @@ void handle_button(xcb_button_press_event_t *event) {
     }
 
     int32_t x = event->event_x >= 0 ? event->event_x : 0;
+    int32_t original_x = x;
 
     DLOG("Got Button %d\n", event->detail);
 
     switch (event->detail) {
-        case 1:
-            /* Left Mousbutton. We determine, which button was clicked
-             * and set cur_ws accordingly */
-            TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
-                DLOG("x = %d\n", x);
-                if (x >= 0 && x < cur_ws->name_width + 10) {
-                    break;
-                }
-                x -= cur_ws->name_width + 11;
-            }
-            if (cur_ws == NULL) {
-                return;
-            }
-            break;
         case 4:
             /* Mouse wheel up. We select the previous ws, if any.
              * If there is no more workspace, don’t even send the workspace
@@ -358,6 +345,52 @@ void handle_button(xcb_button_press_event_t *event) {
 
             cur_ws = TAILQ_NEXT(cur_ws, tailq);
             break;
+        default:
+            /* Check if this event regards a workspace button */
+            TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) {
+                DLOG("x = %d\n", x);
+                if (x >= 0 && x < cur_ws->name_width + 10) {
+                    break;
+                }
+                x -= cur_ws->name_width + 11;
+            }
+            if (cur_ws == NULL) {
+                /* No workspace button was pressed.
+                 * Check if a status block has been clicked.
+                 * This of course only has an effect,
+                 * if the child reported bidirectional protocol usage. */
+
+                /* First calculate width of tray area */
+                trayclient *trayclient;
+                int tray_width = 0;
+                TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) {
+                    if (!trayclient->mapped)
+                        continue;
+                    tray_width += (font.height + 2);
+                }
+
+                int block_x = 0, last_block_x;
+                int offset = (walk->rect.w - (statusline_width + tray_width)) - 10;
+
+                x = original_x - offset;
+                if (x < 0)
+                    return;
+
+                struct status_block *block;
+
+                TAILQ_FOREACH(block, &statusline_head, blocks) {
+                    last_block_x = block_x;
+                    block_x += block->width + block->x_offset + block->x_append;
+
+                    if (x <= block_x && x >= last_block_x) {
+                        send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y);
+                        return;
+                    }
+                }
+                return;
+            }
+            if (event->detail != 1)
+                return;
     }
 
     /* To properly handle workspace names with double quotes in them, we need
index 62eb12d0d7584255c5a93aff2000fa07b4debffb..7c60211515f9ffe878fc0a6a3d6568ac7abb4675 100644 (file)
 
 /**
  * Create a new container (and attach it to the given parent, if not NULL).
- * This function initializes the data structures and creates the appropriate
- * X11 IDs using x_con_init().
+ * This function only initializes the data structures.
+ *
+ */
+Con *con_new_skeleton(Con *parent, i3Window *window);
+
+
+/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
  *
  */
 Con *con_new(Con *parent, i3Window *window);
index 1632efc76b8e7c6c21082fd09934930c05f544e7..13acc063284669a54013fc7716d23fc883c0b8d8 100644 (file)
@@ -584,6 +584,9 @@ struct Con {
     /* The ID of this container before restarting. Necessary to correctly
      * interpret back-references in the JSON (such as the focus stack). */
     int old_id;
+
+    /* Depth of the container window */
+    uint16_t depth;
 };
 
 #endif
index 8222b99ac886520b8b1a5c7763a3e6ea0895d1f8..dadcfd64912bf55836d3486014582e4ef87da9e1 100644 (file)
@@ -87,6 +87,16 @@ Output *get_output_by_name(const char *name);
  */
 Output *get_output_containing(int x, int y);
 
+/*
+ * In contained_by_output, we check if any active output contains part of the container.
+ * We do this by checking if the output rect is intersected by the Rect.
+ * This is the 2-dimensional counterpart of get_output_containing.
+ * Since we don't actually need the outputs intersected by the given Rect (There could
+ * be many), we just return true or false for convenience.
+ *
+ */
+bool contained_by_output(Rect rect);
+
 /**
  * Gets the output which is the next one in the given direction.
  *
index 6960510d4d386a731b92a3a997befeaf74f9c44c..c6328a0eae0e1b70f063d2c46ce52962f8d56af9 100644 (file)
@@ -49,7 +49,7 @@ state INITIAL:
 
 # We ignore comments and 'set' lines (variables).
 state IGNORE_LINE:
-  end, string
+  line
       -> INITIAL
 
 # floating_minimum_size <width> x <height>
@@ -311,7 +311,7 @@ state MODE:
 
 # We ignore comments and 'set' lines (variables).
 state MODE_IGNORE_LINE:
-  end, string
+  line
       -> MODE
 
 state MODE_BINDING:
@@ -362,7 +362,7 @@ state BAR:
 
 # We ignore comments and 'set' lines (variables).
 state BAR_IGNORE_LINE:
-  end, string
+  line
       -> BAR
 
 state BAR_BAR_COMMAND:
@@ -428,7 +428,7 @@ state BAR_COLORS:
 
 # We ignore comments and 'set' lines (variables).
 state BAR_COLORS_IGNORE_LINE:
-  end, string
+  line
       -> BAR_COLORS
 
 state BAR_COLORS_SINGLE:
index 1050513ae593a76e8981e8d93055be0a07f550bf..79872f9251d64fa7cd4aabd07b8a1be2a4d55bb5 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -44,11 +44,10 @@ static void con_force_split_parents_redraw(Con *con) {
 
 /*
  * Create a new container (and attach it to the given parent, if not NULL).
- * This function initializes the data structures and creates the appropriate
- * X11 IDs using x_con_init().
+ * This function only initializes the data structures.
  *
  */
-Con *con_new(Con *parent, i3Window *window) {
+Con *con_new_skeleton(Con *parent, i3Window *window) {
     Con *new = scalloc(sizeof(Con));
     new->on_remove_child = con_on_remove_child;
     TAILQ_INSERT_TAIL(&all_cons, new, all_cons);
@@ -56,6 +55,10 @@ Con *con_new(Con *parent, i3Window *window) {
     new->window = window;
     new->border_style = config.default_border;
     new->current_border_width = -1;
+    if (window)
+        new->depth = window->depth;
+    else
+        new->depth = XCB_COPY_FROM_PARENT;
     static int cnt = 0;
     DLOG("opening window %d\n", cnt);
 
@@ -66,10 +69,6 @@ Con *con_new(Con *parent, i3Window *window) {
     cnt++;
     if ((cnt % (sizeof(colors) / sizeof(char*))) == 0)
         cnt = 0;
-    if (window)
-        x_con_init(new, window->depth);
-    else
-        x_con_init(new, XCB_COPY_FROM_PARENT);
 
     TAILQ_INIT(&(new->floating_head));
     TAILQ_INIT(&(new->nodes_head));
@@ -82,6 +81,15 @@ Con *con_new(Con *parent, i3Window *window) {
     return new;
 }
 
+/* A wrapper for con_new_skeleton, to retain the old con_new behaviour
+ *
+ */
+Con *con_new(Con *parent, i3Window *window) {
+    Con *new = con_new_skeleton(parent, window);
+    x_con_init(new, new->depth);
+    return new;
+}
+
 /*
  * Attaches the given container to the given parent. This happens when moving
  * a container or when inserting a new container at a specific place in the
index 1cdc7acba8cd745fd1ba4803dbb41233996d5061..f197c39d8a62274b900104e72765833185e04d43 100644 (file)
@@ -446,6 +446,16 @@ struct ConfigResult *parse_config(const char *input, struct context *context) {
                 }
             }
 
+            if (strcmp(token->name, "line") == 0) {
+               while (*walk != '\0' && *walk != '\n' && *walk != '\r')
+                  walk++;
+               next_state(token);
+               token_handled = true;
+               linecnt++;
+               walk++;
+               break;
+            }
+
             if (strcmp(token->name, "end") == 0) {
                 //printf("checking for end: *%s*\n", walk);
                 if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
index 49a4122ec2253ea67229e8b11c5efa92060395a8..b7bf3ff65c9537a25765ecb5047aa615595fa2a9 100644 (file)
@@ -659,11 +659,7 @@ void drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_window_t
 void floating_reposition(Con *con, Rect newrect) {
     /* Sanity check: Are the new coordinates on any output? If not, we
      * ignore that request. */
-    Output *output = get_output_containing(
-        newrect.x + (newrect.width / 2),
-        newrect.y + (newrect.height / 2));
-
-    if (!output) {
+    if (!contained_by_output(newrect)) {
         ELOG("No output found at destination coordinates. Not repositioning.\n");
         return;
     }
index cf2535733e46123fe8aa7a0540d6c18b052a4441..8161b1d27603e940e82a6bf213f9d15d2cc37159 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -354,6 +354,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
     }
     y(array_close);
 
+    if (inplace_restart && con->window != NULL) {
+        ystr("depth");
+        y(integer, con->depth);
+    }
+
     y(map_close);
 }
 
index ca4c87ef827b1537735a1c46c407e488ef7362a9..1b08f8c17eae72821d8b69499a92f8039b8d7e88 100644 (file)
@@ -51,12 +51,12 @@ static int json_start_map(void *ctx) {
             if (last_key && strcasecmp(last_key, "floating_nodes") == 0) {
                 DLOG("New floating_node\n");
                 Con *ws = con_get_workspace(json_node);
-                json_node = con_new(NULL, NULL);
+                json_node = con_new_skeleton(NULL, NULL);
                 json_node->parent = ws;
                 DLOG("Parent is workspace = %p\n", ws);
             } else {
                 Con *parent = json_node;
-                json_node = con_new(NULL, NULL);
+                json_node = con_new_skeleton(NULL, NULL);
                 json_node->parent = parent;
             }
         }
@@ -69,6 +69,8 @@ static int json_end_map(void *ctx) {
     if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) {
         LOG("attaching\n");
         con_attach(json_node, json_node->parent, true);
+        LOG("Creating window\n");
+        x_con_init(json_node, json_node->depth);
         json_node = json_node->parent;
     }
     if (parsing_rect)
@@ -277,6 +279,9 @@ static int json_int(void *ctx, long val) {
     if (strcasecmp(last_key, "current_border_width") == 0)
         json_node->current_border_width = val;
 
+    if (strcasecmp(last_key, "depth") == 0)
+        json_node->depth = val;
+
     if (!parsing_swallows && strcasecmp(last_key, "id") == 0)
         json_node->old_id = val;
 
index 42e714ef0bcee0f65445a557c15f7019d5453707..0269fb21ff9b7078a6f94ecd787c7e4a2dd4a1f6 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -81,11 +81,11 @@ static void store_log_markers(void) {
 void init_logging(void) {
     if (!errorfilename) {
         if (!(errorfilename = get_process_filename("errorlog")))
-            ELOG("Could not initialize errorlog\n");
+            fprintf(stderr, "Could not initialize errorlog\n");
         else {
             errorfile = fopen(errorfilename, "w");
             if (fcntl(fileno(errorfile), F_SETFD, FD_CLOEXEC)) {
-                ELOG("Could not set close-on-exec flag\n");
+                fprintf(stderr, "Could not set close-on-exec flag\n");
             }
         }
     }
@@ -107,25 +107,29 @@ void init_logging(void) {
                                         sysconf(_SC_PAGESIZE);
 #endif
         logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
+#if defined(__FreeBSD__)
+        sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid());
+#else
         sasprintf(&shmlogname, "/i3-log-%d", getpid());
+#endif
         logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
         if (logbuffer_shm == -1) {
-            ELOG("Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
+            fprintf(stderr, "Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
             return;
         }
 
         if (ftruncate(logbuffer_shm, logbuffer_size) == -1) {
             close(logbuffer_shm);
-            shm_unlink("/i3-log-");
-            ELOG("Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
+            shm_unlink(shmlogname);
+            fprintf(stderr, "Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
             return;
         }
 
         logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
         if (logbuffer == MAP_FAILED) {
             close(logbuffer_shm);
-            shm_unlink("/i3-log-");
-            ELOG("Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
+            shm_unlink(shmlogname);
+            fprintf(stderr, "Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
             logbuffer = NULL;
             return;
         }
@@ -138,7 +142,7 @@ void init_logging(void) {
         pthread_condattr_t cond_attr;
         pthread_condattr_init(&cond_attr);
         if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0)
-            ELOG("pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
+            fprintf(stderr, "pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
         pthread_cond_init(&(header->condvar), &cond_attr);
 
         logwalk = logbuffer + sizeof(i3_shmlog_header);
index 10b085cbb9825006a58ad44bbefd3e7bc1c910c6..1aef9c9c620c59af800c4a1e972f6dd6cbb0aecb 100644 (file)
@@ -92,6 +92,31 @@ Output *get_output_containing(int x, int y) {
     return NULL;
 }
 
+/*
+ * In contained_by_output, we check if any active output contains part of the container.
+ * We do this by checking if the output rect is intersected by the Rect.
+ * This is the 2-dimensional counterpart of get_output_containing.
+ * Since we don't actually need the outputs intersected by the given Rect (There could
+ * be many), we just return true or false for convenience.
+ *
+ */
+bool contained_by_output(Rect rect){
+    Output *output;
+    int lx = rect.x, uy = rect.y;
+    int rx = rect.x + rect.width, by = rect.y + rect.height;
+    TAILQ_FOREACH(output, &outputs, outputs) {
+        if (!output->active)
+            continue;
+        DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n",
+                        rect.x, rect.y, output->rect.x, output->rect.y, output->rect.width, output->rect.height);
+        if (rx >= (int)output->rect.x && lx <= (int)(output->rect.x + output->rect.width) &&
+            by >= (int)output->rect.y && uy <= (int)(output->rect.y + output->rect.height))
+            return true;
+    }
+    return false;
+
+}
+
 /*
  * Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps.
  *
index a4d2950519619bd563f804c36854c666f6cfcbad..9997f17df2037e9a981e2ae1a6375854391a53c9 100644 (file)
@@ -88,12 +88,23 @@ void scratchpad_show(Con *con) {
         con_toggle_fullscreen(focused, CF_OUTPUT);
     }
 
+    /* If this was 'scratchpad show' without criteria, we check if the
+     * currently focused window is a scratchpad window and should be hidden
+     * again. */
+    if (!con &&
+        (floating = con_inside_floating(focused)) &&
+        floating->scratchpad_state != SCRATCHPAD_NONE) {
+        DLOG("Focused window is a scratchpad window, hiding it.\n");
+        scratchpad_move(focused);
+        return;
+    }
+
     /* If this was 'scratchpad show' without criteria, we check if there is a
      * unfocused scratchpad on the current workspace and focus it */
     Con *walk_con;
     Con *focused_ws = con_get_workspace(focused);
     TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) {
-        if ((floating = con_inside_floating(walk_con)) &&
+        if (!con && (floating = con_inside_floating(walk_con)) &&
             floating->scratchpad_state != SCRATCHPAD_NONE &&
             floating != con_inside_floating(focused)) {
                 DLOG("Found an unfocused scratchpad window on this workspace\n");
@@ -112,7 +123,7 @@ void scratchpad_show(Con *con) {
     focused_ws = con_get_workspace(focused);
     TAILQ_FOREACH(walk_con, &all_cons, all_cons) {
         Con *walk_ws = con_get_workspace(walk_con);
-        if (walk_ws &&
+        if (!con && walk_ws &&
             !con_is_internal(walk_ws) && focused_ws != walk_ws &&
             (floating = con_inside_floating(walk_con)) &&
             floating->scratchpad_state != SCRATCHPAD_NONE) {
@@ -123,14 +134,10 @@ void scratchpad_show(Con *con) {
         }
     }
 
-    /* If this was 'scratchpad show' without criteria, we check if the
-     * currently focused window is a scratchpad window and should be hidden
-     * again. */
-    if (!con &&
-        (floating = con_inside_floating(focused)) &&
-        floating->scratchpad_state != SCRATCHPAD_NONE) {
-        DLOG("Focused window is a scratchpad window, hiding it.\n");
-        scratchpad_move(focused);
+    /* If this was 'scratchpad show' with criteria, we check if the window
+     * is actually in the scratchpad */
+    if (con && con->parent->scratchpad_state == SCRATCHPAD_NONE) {
+        DLOG("Window is not in the scratchpad, doing nothing.\n");
         return;
     }
 
index 5a0913bfcc77852c1084fe2f2908097000714b8c..17f5ac33d27b3d4fe2ce2d7bf8404e1f920273c9 100644 (file)
@@ -442,7 +442,7 @@ static void _workspace_show(Con *workspace) {
     } else
         con_focus(next);
 
-    ipc_send_workspace_focus_event(workspace, old);
+    ipc_send_workspace_focus_event(workspace, current);
 
     DLOG("old = %p / %s\n", old, (old ? old->name : "(null)"));
     /* Close old workspace if necessary. This must be done *after* doing
index 6a49ff7844eddebfda36bec143ac450e8a83bec4..151103d7503d1f4770c46436445a66edc46de924 100644 (file)
@@ -448,6 +448,21 @@ is(parser_calls($config),
    $expected,
    'errors dont harm subsequent statements');
 
+################################################################################
+# Regression: semicolons end comments, but shouldn’t
+################################################################################
+
+$config = <<'EOT';
+# "foo" client.focused          #4c7899 #285577 #ffffff #2e9ef4
+EOT
+
+$expected = <<'EOT';
+
+EOT
+
+is(parser_calls($config),
+   $expected,
+   'semicolon does not end a comment line');
 
 ################################################################################
 # Error message with 2+2 lines of context
index 2603f65af7e6328b7153e50914329d4e7e95563f..5ba8bfdddcdbd5d74067df85ab87a16fa4e9518d 100644 (file)
 # Verifies that using criteria to address scratchpad windows works.
 use i3test;
 
-################################################################################
+#####################################################################
 # Verify that using scratchpad show with criteria works as expected:
-# When matching a scratchpad window which is visible, it should hide it.
-# When matching a scratchpad window which is on __i3_scratch, it should show it.
-# When matching a non-scratchpad window, it should be a no-op.
-################################################################################
+# - When matching a scratchpad window which is visible,
+#   it should hide it.
+# - When matching a scratchpad window which is on __i3_scratch,
+#   it should show it.
+# - When matching a non-scratchpad window, it should be a no-op.
+# - When matching a scratchpad window,
+#   non-matching windows shouldn't appear
+######################################################################
 
 my $tmp = fresh_workspace;
 
 my $third_window = open_window(name => 'scratch-match');
 cmd 'move scratchpad';
 
-# Verify that using 'scratchpad show' without any matching windows is a no-op.
+#####################################################################
+# Verify that using 'scratchpad show' without any matching windows is
+# a no-op.
+#####################################################################
 my $old_focus = get_focused($tmp);
 
 cmd '[title="nomatch"] scratchpad show';
 
 is(get_focused($tmp), $old_focus, 'non-matching criteria have no effect');
 
+#####################################################################
 # Verify that we can use criteria to show a scratchpad window.
+#####################################################################
 cmd '[title="scratch-match"] scratchpad show';
 
 my $scratch_focus = get_focused($tmp);
@@ -47,12 +56,102 @@ cmd '[title="scratch-match"] scratchpad show';
 isnt(get_focused($tmp), $scratch_focus, 'matching criteria works');
 is(get_focused($tmp), $old_focus, 'focus restored');
 
+
+#####################################################################
 # Verify that we cannot use criteria to show a non-scratchpad window.
+#####################################################################
 my $tmp2 = fresh_workspace;
 my $non_scratch_window = open_window(name => 'non-scratch');
 cmd "workspace $tmp";
 is(get_focused($tmp), $old_focus, 'focus still ok');
-cmd '[title="non-match"] scratchpad show';
+cmd '[title="non-scratch"] scratchpad show';
 is(get_focused($tmp), $old_focus, 'focus unchanged');
 
+#####################################################################
+# Verify that non-matching windows doesn't appear
+#####################################################################
+# Subroutine to clear scratchpad
+sub clear_scratchpad {
+    while (scalar @{get_ws('__i3_scratch')->{floating_nodes}}) {
+        cmd 'scratchpad show';
+        cmd 'kill';
+    }
+}
+
+#Start from an empty fresh workspace
+my $empty_ws = fresh_workspace;
+cmd "workspace $empty_ws";
+
+my $no_focused = get_focused($empty_ws);
+cmd '[title="nothingmatchthistitle"] scratchpad show';
+#Check nothing match
+is(get_focused($empty_ws), $no_focused, "no window to focus on");
+
+clear_scratchpad;
+
+open_window(name => "my-scratch-window");
+my $w1_focus = get_focused($empty_ws);
+cmd 'move scratchpad';
+cmd '[title="my-scratch-window"] scratchpad show';
+#Check we created and shown a scratchpad window
+is(get_focused($empty_ws), $w1_focus, "focus on scratchpad window");
+
+#Switching workspace
+my $empty_ws2 = fresh_workspace;
+cmd "workspace $empty_ws2";
+open_window(name => "my-second-scratch-window");
+
+my $w2_focus = get_focused($empty_ws2);
+cmd 'move scratchpad';
+cmd '[title="my-second-scratch-window"] scratchpad show';
+
+#Check we got the correct window
+is(get_focused($empty_ws2), $w2_focus, "focus is on second window");
+
+#####################################################################
+# Verify that 'scratchpad show' correctly hide multiple scratchpad
+# windows
+#####################################################################
+clear_scratchpad;
+
+sub check_floating {
+    my($rws, $n) = @_;
+    my $ws = get_ws($rws);
+    is(scalar @{$ws->{nodes}}, 0, 'no windows on ws');
+    is(scalar @{$ws->{floating_nodes}}, $n, "$n floating windows on ws");
+}
+
+my $empty_ws3 = fresh_workspace;
+cmd "workspace $empty_ws3";
+
+check_floating($empty_ws3, 0);
+
+#Creating two scratchpad windows
+open_window(name => "toggle-1");
+cmd 'move scratchpad';
+open_window(name => "toggle-2");
+cmd 'move scratchpad';
+check_floating($empty_ws3, 0);
+#Showing both
+cmd '[title="toggle-"] scratchpad show';
+
+check_floating($empty_ws3, 2);
+
+#Hiding both
+cmd '[title="toggle-"] scratchpad show';
+check_floating($empty_ws3, 0);
+
+#Showing both again
+cmd '[title="toggle-"] scratchpad show';
+check_floating($empty_ws3, 2);
+
+
+#Hiding one
+cmd 'scratchpad show';
+check_floating($empty_ws3, 1);
+
+#Hiding the last
+cmd 'scratchpad show';
+check_floating($empty_ws3, 0);
+
 done_testing;
diff --git a/testcases/t/514-ipc-workspace-multi-monitor.t b/testcases/t/514-ipc-workspace-multi-monitor.t
new file mode 100644 (file)
index 0000000..360bd42
--- /dev/null
@@ -0,0 +1,72 @@
+#!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)
+# 
+# Ticket: #990
+# Bug still in: 4.5.1-23-g82b5978
+
+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
+EOT
+
+my $pid = launch_with_config($config);
+
+my $i3 = i3(get_socket_path());
+
+$i3->connect()->recv;
+
+################################
+# Workspaces requests and events
+################################
+
+my $focused = get_ws(focused_ws());
+
+# Events
+
+# We are switching to an empty workpspace on the output to the right from an empty workspace on the output on the left, so we expect
+# to receive "init", "focus", and "empty".
+my $focus = AnyEvent->condvar;
+$i3->subscribe({
+    workspace => sub {
+        my ($event) = @_;
+        if ($event->{change} eq 'focus') {
+            # Check that we have the old and new workspace
+            $focus->send(
+                $event->{current}->{name} == '2' &&
+                $event->{old}->{name} == $focused->{name}
+            );
+        }
+    }
+})->recv;
+
+cmd 'focus output right';
+
+my $t;
+$t = AnyEvent->timer(
+    after => 0.5,
+    cb => sub {
+        $focus->send(0);
+    }
+);
+
+ok($focus->recv, 'Workspace "focus" event received');
+
+exit_gracefully($pid);
+
+done_testing;