]> git.sur5r.net Git - i3/i3/commitdiff
Merge branch 'master' into next
authorMichael Stapelberg <michael@stapelberg.de>
Thu, 16 Apr 2015 07:03:28 +0000 (09:03 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Thu, 16 Apr 2015 07:03:28 +0000 (09:03 +0200)
61 files changed:
.gitignore
Makefile
common.mk
debian/changelog
docs/hacking-howto
docs/testsuite
docs/userguide
i3-config-wizard/main.c
i3-input/main.c
i3-nagbar/i3-nagbar.mk
i3-nagbar/main.c
include/commands.h
include/config.h
include/config_directives.h
include/data.h
include/floating.h
include/i3.h
include/ipc.h
include/libi3.h
include/util.h
libi3/get_config_path.c [new file with mode: 0644]
libi3/mkdirp.c [new file with mode: 0644]
libi3/resolve_tilde.c [new file with mode: 0644]
parser-specs/commands.spec
parser-specs/config.spec
release.sh
src/click.c
src/commands.c
src/con.c
src/config.c
src/config_directives.c
src/config_parser.c
src/floating.c
src/handlers.c
src/i3.mk
src/ipc.c
src/main.c
src/manage.c
src/move.c
src/scratchpad.c
src/util.c
src/version.c [new file with mode: 0644]
src/workspace.c
src/x.c
testcases/Makefile.PL
testcases/complete-run.pl
testcases/lib/i3test.pm
testcases/t/113-urgent.t
testcases/t/175-startup-notification.t
testcases/t/201-config-parser.t
testcases/t/210-mark-unmark.t
testcases/t/235-wm-class-change-handler.t
testcases/t/240-focus-on-window-activation.t [new file with mode: 0644]
testcases/t/240-tabbed-floating-disable-crash.t [new file with mode: 0644]
testcases/t/241-consistent-center.t [new file with mode: 0644]
testcases/t/242-no-focus.t [new file with mode: 0644]
testcases/t/523-move-position-center.t [new file with mode: 0644]
testcases/t/524-move.t [new file with mode: 0644]
tests/queue.h [deleted file]
tests/swap.c [deleted file]
tests/test_table.c [deleted file]

index 1d4c1678aa4ed2abc89b7e5d856be089a3741022..617421ac1e92f3edc338c7b7c75d868e6696a608 100644 (file)
@@ -33,3 +33,4 @@ docs/*.html
 i3-command-parser.stamp
 i3-config-parser.stamp
 .clang_complete
+LAST_VERSION
index 1fed4df1ac59185a4540413db3c5b9e436f15344..fd3021017650d11c1daa6cfed7ee327a853b7dc8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,12 @@ include i3-dump-log/i3-dump-log.mk
 include docs/docs.mk
 include man/man.mk
 
+# Update $(TOPDIR)/LAST_VERSION if it differs from $I3_VERSION
+CACHED_VERSION := '$(shell [ -f $(TOPDIR)/LAST_VERSION ] && cat $(TOPDIR)/LAST_VERSION)'
+ifneq ($(CACHED_VERSION),$(I3_VERSION))
+$(shell echo -n ${I3_VERSION} > $(TOPDIR)/LAST_VERSION)
+endif
+
 real-all: $(ALL_TARGETS)
 
 install: $(INSTALL_TARGETS)
index b9e15a286fc27d335e4ae8c12a36ec8188dbd972..dcc90c964f968c975c7a7bbf2e62352d895de524 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -1,6 +1,5 @@
 UNAME=$(shell uname)
 DEBUG=1
-COVERAGE=0
 INSTALL=install
 LN=ln
 ifndef PREFIX
index cc0e532875100a733545d5415e0a7a2693d63b9c..183e59406b9d8d96a388158a725ef9ac956ef34e 100644 (file)
@@ -1,8 +1,14 @@
-i3-wm (4.10.1-1) experimental; urgency=medium
+i3-wm (4.10.2-1) experimental; urgency=medium
 
   * NOT YET RELEASED.
 
- -- Michael Stapelberg <stapelberg@debian.org>  Sun, 29 Mar 2015 18:08:13 +0200
+ -- Michael Stapelberg <stapelberg@debian.org>  Sun, 29 Mar 2015 19:10:38 +0200
+
+i3-wm (4.10.1-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Michael Stapelberg <stapelberg@debian.org>  Sun, 29 Mar 2015 18:54:07 +0200
 
 i3-wm (4.10-1) experimental; urgency=medium
 
index 12d6b14ee1b2da3406d4b78e7960c66288207749..e6313f7d6837fd85da3059116fd73e962e9751fe 100644 (file)
@@ -404,10 +404,14 @@ can reconfigure themselves).
 
 == _NET_WM_STATE
 
-Only the _NET_WM_STATE_FULLSCREEN atom is handled. It calls
-``toggle_fullscreen()'' for the specific client which just configures the
-client to use the whole screen on which it currently is. Also, it is set as
-fullscreen_client for the i3Screen.
+Only the _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION atoms
+are handled.
+
+The former calls ``toggle_fullscreen()'' for the specific client which just
+configures the client to use the whole screen on which it currently is.
+Also, it is set as fullscreen_client for the i3Screen.
+
+The latter is used to set, read and display urgency hints.
 
 == WM_NAME
 
@@ -629,8 +633,8 @@ unmapped if it should not be visible anymore. +WM_STATE+ will be set to
 +x_draw_decoration+ draws window decorations. It is run for every leaf
 container (representing an actual X11 window) and for every non-leaf container
 which is in a stacked/tabbed container (because stacked/tabbed containers
-display a window decoration for split containers, which at the moment just says
-"another container").
+display a window decoration for split containers, which consists of a representation
+of the child container's names.
 
 Then, parameters are collected to be able to determine whether this decoration
 drawing is actually necessary or was already done. This saves a substantial
@@ -686,9 +690,9 @@ all commands.
 In earlier versions of i3, interpreting these commands was done using lex and
 yacc, but experience has shown that lex and yacc are not well suited for our
 command language. Therefore, starting from version 4.2, we use a custom parser
-for user commands (not yet for the configuration file).
+for user commands and the configuration file.
 The input specification for this parser can be found in the file
-+parser-specs/commands.spec+. Should you happen to use Vim as an editor, use
++parser-specs/*.spec+. Should you happen to use Vim as an editor, use
 :source parser-specs/highlighting.vim to get syntax highlighting for this file
 (highlighting files for other editors are welcome).
 
@@ -729,11 +733,14 @@ features. This is its definition:
 # workspace next|prev|next_on_output|prev_on_output
 # workspace back_and_forth
 # workspace <name>
+# workspace number <number>
 state WORKSPACE:
   direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
       -> call cmd_workspace($direction)
   'back_and_forth'
       -> call cmd_workspace_back_and_forth()
+  'number'
+      -> WORKSPACE_NUMBER
   workspace = string
       -> call cmd_workspace_name($workspace)
 ----------------------------------------------------------------
@@ -772,6 +779,10 @@ workspace <name>::
        single quotes), but just called string. Other possible tokens are word
        (the same as string, but stops matching at a whitespace) and end
        (matches the end of the input).
+workspace number <number>::
+        The workspace command has to be followed by the keyword +number+. It
+        then transitions into the state +WORKSPACE_NUMBER+, where the actual
+        parameter will be read.
 
 === Introducing a new command
 
@@ -952,18 +963,21 @@ http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_
 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
-command which will focus windows in an alt+tab like way.
+accepted. A good way for this is to open an issue and asking for opinions on it.
+Even for accepted features, this can be a good way to refine an idea upfront. However,
+we don't want to see certain features in i3, e.g., switching window focus in an
+Alt+Tab like way.
 
 When working on bugfixes, please make sure you mention that you are working on
-it in the corresponding bugreport at https://github.com/i3/i3/issues In case
-there is no bugreport yet, please create one.
+it in the corresponding bug report at https://github.com/i3/i3/issues. In case
+there is no bug report yet, please create one.
 
-After you are done, please submit your work for review at https://github.com/i3/i3
+After you are done, please submit your work for review as a pull request at
+https://github.com/i3/i3.
 
 Do not send emails to the mailing list or any author directly, and don’t submit
 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
+https://github.com/i3/i3. 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?
index 29a352185da2f2f39ec2e24cf47224671fda86d6..8fdb9635b9648fcc6e8e7e1b380a80801033c8e8 100644 (file)
@@ -160,6 +160,27 @@ $ ./complete-run.pl --parallel=1 --keep-xserver-output
 This will show the output of Xephyr, which is the X server implementation we
 use for testing.
 
+==== Coverage testing
+
+Coverage testing is possible with +lcov+, the front-end for GCC's coverage
+testing tool +gcov+. The testcases can generate a nice html report that tells
+you which functions and lines were covered during a run of the tests. You can
+use this tool to judge how effective your tests are.
+
+To use test coverage tools, first compile with coverage enabled.
+
+---------------------------------------------------
+COVERAGE=1 make
+---------------------------------------------------
+
+Then run the tests with the +--coverage-testing+ flag.
+
+---------------------------------------------------
+./complete-run.pl --coverage-testing
+---------------------------------------------------
+
+Then open +latest/i3-coverage/index.html+ in your web browser.
+
 ==== IPC interface
 
 The testsuite makes extensive use of the IPC (Inter-Process Communication)
index 80de5c88e37af292682c070a00817653602e845a..687dff10e5221c48ebfd3f3f1207eff76c4199c8 100644 (file)
@@ -256,8 +256,9 @@ workspace node’s orientation will be changed to +vertical+. The terminal windo
 you moved down is directly attached to the workspace and appears on the bottom
 of the screen. A new (horizontal) container was created to accommodate the
 other two terminal windows. You will notice this when switching to tabbed mode
-(for example). You would end up having one tab called "another container" and
-the other one being the terminal window you moved down.
+(for example). You would end up having one tab with a representation of the split
+container (e.g., "H[urxvt firefox]") and the other one being the terminal window
+you moved down.
 
 [[configuring]]
 == Configuring i3
@@ -589,6 +590,27 @@ for_window [title="x200: ~/work"] floating enable
 
 The valid criteria are the same as those for commands, see <<command_criteria>>.
 
+=== Don't focus window upon opening
+
+[[no_focus]]
+
+When a new window appears, it will be focused. The +no_focus+ directive allows preventing
+this from happening and can be used in combination with <<command_criteria>>.
+
+Note that this does not apply to all cases, e.g., when feeding data into a running application
+causing it to request being focused. To configure the behavior in such cases, refer to
+<<focus_on_window_activation>>.
+
+*Syntax*:
+-------------------
+no_focus <criteria>
+-------------------
+
+*Example*:
+-------------------------------
+no_focus [window_role="pop-up"]
+-------------------------------
+
 === Variables
 
 As you learned in the section about keyboard bindings, you will have
@@ -981,6 +1003,51 @@ force_display_urgency_hint <timeout> ms
 force_display_urgency_hint 500 ms
 ---------------------------------
 
+=== Focus on window activation
+
+[[focus_on_window_activation]]
+
+If a window is activated, e.g., via +google-chrome www.google.com+, it may request
+to take focus. Since this may not preferable, different reactions can be configured.
+
+Note that this may not affect windows that are being opened. To prevent new windows
+from being focused, see <<no_focus>>.
+
+*Syntax*:
+----------------------------------------------------
+focus_on_window_activation <smart|urgent|focus|none>
+----------------------------------------------------
+
+The different modes will act as follows:
+
+smart::
+    This is the default behavior. If the window requesting focus is on an active
+    workspace, it will receive the focus. Otherwise, the urgency hint will be set.
+urgent::
+    The window will always be marked urgent, but the focus will not be stolen.
+focus::
+    The window will always be focused and not be marked urgent.
+none::
+    The window will neither be focused, nor be marked urgent.
+
+=== Drawing marks on window decoration
+
+If activated, marks on windows are drawn in their window decoration. However,
+any mark starting with an underscore in its name (+_+) will not be drawn even if
+this option is activated.
+
+The default for this option is +yes+.
+
+*Syntax*:
+-------------------
+show_marks [yes|no]
+-------------------
+
+*Example*:
+--------------
+show_marks yes
+--------------
+
 == Configuring i3bar
 
 The bar at the bottom of your monitor is drawn by a separate process called
@@ -1856,9 +1923,15 @@ window, you cannot simply bind it to a key.  +i3-input+ is a tool created
 for this purpose: It lets you input a command and sends the command to i3. It
 can also prefix this command and display a custom prompt for the input dialog.
 
+The additional +--toggle+ option will remove the mark if the window already has
+this mark, add it if the window has none or replace the current mark if it has
+another mark.
+
+Refer to +show_marks+ if you don't want marks to be shown in the window decoration.
+
 *Syntax*:
 ------------------------------
-mark identifier
+mark [--toggle] identifier
 [con_mark="identifier"] focus
 unmark identifier
 ------------------------------
index 35770dc9fa807c38feda80b71c3c97425e5a12f5..9c9241cd0ad5223d67c77ba20b7ee1a01de31227 100644 (file)
@@ -461,38 +461,6 @@ void errorlog(char *fmt, ...) {
 void debuglog(char *fmt, ...) {
 }
 
-/*
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-static char *resolve_tilde(const char *path) {
-    static glob_t globbuf;
-    char *head, *tail, *result;
-
-    tail = strchr(path, '/');
-    head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
-
-    int res = glob(head, GLOB_TILDE, NULL, &globbuf);
-    free(head);
-    /* no match, or many wildcard matches are bad */
-    if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
-        result = strdup(path);
-    else if (res != 0) {
-        err(1, "glob() failed");
-    } else {
-        head = globbuf.gl_pathv[0];
-        result = calloc(1, strlen(head) + (tail ? strlen(tail) : 0) + 1);
-        strncpy(result, head, strlen(head));
-        if (tail)
-            strncat(result, tail, strlen(tail));
-    }
-    globfree(&globbuf);
-
-    return result;
-}
-
 /*
  * Handles expose events, that is, draws the window contents.
  *
@@ -514,17 +482,23 @@ static int handle_expose() {
         set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
 
         txt(logical_px(10), 2, "You have not configured i3 yet.");
-        txt(logical_px(10), 3, "Do you want me to generate ~/.i3/config?");
-        txt(logical_px(85), 5, "Yes, generate ~/.i3/config");
-        txt(logical_px(85), 7, "No, I will use the defaults");
+        txt(logical_px(10), 3, "Do you want me to generate a config at");
+
+        char *msg;
+        sasprintf(&msg, "%s?", config_path);
+        txt(logical_px(10), 4, msg);
+        free(msg);
+
+        txt(logical_px(85), 6, "Yes, generate the config");
+        txt(logical_px(85), 8, "No, I will use the defaults");
 
         /* green */
         set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
-        txt(logical_px(25), 5, "<Enter>");
+        txt(logical_px(25), 6, "<Enter>");
 
         /* red */
         set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
-        txt(logical_px(31), 7, "<ESC>");
+        txt(logical_px(31), 8, "<ESC>");
     }
 
     if (current_step == STEP_GENERATE) {
@@ -534,7 +508,7 @@ static int handle_expose() {
         txt(logical_px(85), 4, "Win as default modifier");
         txt(logical_px(85), 5, "Alt as default modifier");
         txt(logical_px(10), 7, "Afterwards, press");
-        txt(logical_px(85), 9, "to write ~/.i3/config");
+        txt(logical_px(85), 9, "to write the config");
         txt(logical_px(85), 10, "to abort");
 
         /* the not-selected modifier */
@@ -772,7 +746,7 @@ static void finish() {
 }
 
 int main(int argc, char *argv[]) {
-    config_path = resolve_tilde("~/.i3/config");
+    char *xdg_config_home;
     socket_path = getenv("I3SOCK");
     char *pattern = "pango:monospace 8";
     char *patternbold = "pango:monospace bold 8";
@@ -806,20 +780,29 @@ int main(int argc, char *argv[]) {
         }
     }
 
-    /* Check if the destination config file does not exist but the path is
-     * writable. If not, exit now, this program is not useful in that case. */
-    struct stat stbuf;
-    if (stat(config_path, &stbuf) == 0) {
-        printf("The config file \"%s\" already exists. Exiting.\n", config_path);
+    char *path = get_config_path(NULL, false);
+    if (path != NULL) {
+        printf("The config file \"%s\" already exists. Exiting.\n", path);
+        free(path);
         return 0;
     }
 
-    /* Create ~/.i3 if it does not yet exist */
-    char *config_dir = resolve_tilde("~/.i3");
+    /* Always write to $XDG_CONFIG_HOME/i3/config by default. */
+    if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
+        xdg_config_home = "~/.config";
+
+    xdg_config_home = resolve_tilde(xdg_config_home);
+    sasprintf(&config_path, "%s/i3/config", xdg_config_home);
+
+    /* Create $XDG_CONFIG_HOME/i3 if it does not yet exist */
+    char *config_dir;
+    struct stat stbuf;
+    sasprintf(&config_dir, "%s/i3", xdg_config_home);
     if (stat(config_dir, &stbuf) != 0)
-        if (mkdir(config_dir, 0755) == -1)
-            err(1, "mkdir(%s) failed", config_dir);
+        if (!mkdirp(config_dir))
+            err(EXIT_FAILURE, "mkdirp(%s) failed", config_dir);
     free(config_dir);
+    free(xdg_config_home);
 
     int fd;
     if ((fd = open(config_path, O_CREAT | O_RDWR, 0644)) == -1) {
index 996fc0ea9f08958a3b563a359e85ad6c7388b798..b32d7b6629b25dcd109e4f9ab1af6d059bfe8ca0 100644 (file)
@@ -314,6 +314,50 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
     return 1;
 }
 
+static xcb_rectangle_t get_window_position(void) {
+    xcb_rectangle_t result = (xcb_rectangle_t){logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8)};
+
+    xcb_get_input_focus_reply_t *input_focus = NULL;
+    xcb_get_geometry_reply_t *geometry = NULL;
+    xcb_translate_coordinates_reply_t *coordinates = NULL;
+
+    /* In rare cases, the window holding the input focus might disappear while we are figuring out its
+     * position. To avoid this, we grab the server in the meantime. */
+    xcb_grab_server(conn);
+
+    input_focus = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
+    if (input_focus == NULL || input_focus->focus == XCB_NONE) {
+        DLOG("Failed to receive the current input focus or no window has the input focus right now.\n");
+        goto free_resources;
+    }
+
+    geometry = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, input_focus->focus), NULL);
+    if (geometry == NULL) {
+        DLOG("Failed to received window geometry.\n");
+        goto free_resources;
+    }
+
+    coordinates = xcb_translate_coordinates_reply(
+        conn, xcb_translate_coordinates(conn, input_focus->focus, root, geometry->x, geometry->y), NULL);
+    if (coordinates == NULL) {
+        DLOG("Failed to translate coordinates.\n");
+        goto free_resources;
+    }
+
+    DLOG("Determined coordinates of window with input focus at x = %i / y = %i", coordinates->dst_x, coordinates->dst_y);
+    result.x += coordinates->dst_x;
+    result.y += coordinates->dst_y;
+
+free_resources:
+    xcb_ungrab_server(conn);
+    xcb_flush(conn);
+
+    FREE(input_focus);
+    FREE(geometry);
+    FREE(coordinates);
+    return result;
+}
+
 int main(int argc, char *argv[]) {
     format = strdup("%s");
     socket_path = getenv("I3SOCK");
@@ -402,15 +446,17 @@ int main(int argc, char *argv[]) {
     if (prompt != NULL)
         prompt_offset = predict_text_width(prompt);
 
+    const xcb_rectangle_t win_pos = get_window_position();
+
     /* Open an input window */
     win = xcb_generate_id(conn);
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                                                                          /* the window id */
-        root,                                                                         /* parent == root */
-        logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8), /* dimensions */
-        0,                                                                            /* X11 border = 0, we draw our own */
+        win,                                                 /* the window id */
+        root,                                                /* parent == root */
+        win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
+        0,                                                   /* X11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
index e54aa6544876739b6289edec0dad9f12acb5c825..e98d65824c845ce3ddc3faf5d6e3617d6118fad3 100644 (file)
@@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-nagbar
 
 i3_nagbar_SOURCES := $(wildcard i3-nagbar/*.c)
 i3_nagbar_HEADERS := $(wildcard i3-nagbar/*.h)
-i3_nagbar_CFLAGS   = $(XCB_CFLAGS) $(PANGO_CFLAGS)
-i3_nagbar_LIBS     = $(XCB_LIBS) $(PANGO_LIBS)
+i3_nagbar_CFLAGS   = $(XCB_CFLAGS) $(XCB_WM_CFLAGS) $(PANGO_CFLAGS)
+i3_nagbar_LIBS     = $(XCB_LIBS) $(XCB_WM_LIBS) $(PANGO_LIBS)
 
 i3_nagbar_OBJECTS := $(i3_nagbar_SOURCES:.c=.o)
 
index bf6d1cca41f29a6ba39bec468c677ef3571b2734..83389b34b240ed7da3f8c62152f25842a30438c6 100644 (file)
@@ -27,6 +27,7 @@
 #include <xcb/xcb.h>
 #include <xcb/xcb_aux.h>
 #include <xcb/xcb_event.h>
+#include <xcb/randr.h>
 
 #include "libi3.h"
 #include "i3-nagbar.h"
@@ -288,6 +289,60 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) {
     return 1;
 }
 
+/**
+ * Return the position and size the i3-nagbar window should use.
+ * This will be the primary output or a fallback if it cannot be determined.
+ */
+static xcb_rectangle_t get_window_position(void) {
+    /* Default values if we cannot determine the primary output or its CRTC info. */
+    xcb_rectangle_t result = (xcb_rectangle_t){50, 50, 500, font.height + logical_px(8) + logical_px(8)};
+
+    xcb_randr_get_screen_resources_current_cookie_t rcookie = xcb_randr_get_screen_resources_current(conn, root);
+    xcb_randr_get_output_primary_cookie_t pcookie = xcb_randr_get_output_primary(conn, root);
+
+    xcb_randr_get_output_primary_reply_t *primary = NULL;
+    xcb_randr_get_screen_resources_current_reply_t *res = NULL;
+
+    if ((primary = xcb_randr_get_output_primary_reply(conn, pcookie, NULL)) == NULL) {
+        DLOG("Could not determine the primary output.\n");
+        goto free_resources;
+    }
+
+    if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
+        goto free_resources;
+    }
+
+    xcb_randr_get_output_info_reply_t *output =
+        xcb_randr_get_output_info_reply(conn,
+                                        xcb_randr_get_output_info(conn, primary->output, res->config_timestamp),
+                                        NULL);
+    if (output == NULL || output->crtc == XCB_NONE)
+        goto free_resources;
+
+    xcb_randr_get_crtc_info_reply_t *crtc =
+        xcb_randr_get_crtc_info_reply(conn,
+                                      xcb_randr_get_crtc_info(conn, output->crtc, res->config_timestamp),
+                                      NULL);
+    if (crtc == NULL)
+        goto free_resources;
+
+    DLOG("Found primary output on position x = %i / y = %i / w = %i / h = %i",
+         crtc->x, crtc->y, crtc->width, crtc->height);
+    if (crtc->width == 0 || crtc->height == 0) {
+        DLOG("Primary output is not active, ignoring it.\n");
+        goto free_resources;
+    }
+
+    result.x = crtc->x;
+    result.y = crtc->y;
+    goto free_resources;
+
+free_resources:
+    FREE(res);
+    FREE(primary);
+    return result;
+}
+
 int main(int argc, char *argv[]) {
     /* The following lines are a terribly horrible kludge. Because terminal
      * emulators have different ways of interpreting the -e command line
@@ -410,16 +465,18 @@ int main(int argc, char *argv[]) {
     font = load_font(pattern, true);
     set_font(&font);
 
+    xcb_rectangle_t win_pos = get_window_position();
+
     /* Open an input window */
     win = xcb_generate_id(conn);
 
     xcb_create_window(
         conn,
         XCB_COPY_FROM_PARENT,
-        win,                                                                         /* the window id */
-        root,                                                                        /* parent == root */
-        50, 50, 500, font.height + logical_px(8) + logical_px(8) /* 8 px padding */, /* dimensions */
-        0,                                                                           /* x11 border = 0, we draw our own */
+        win,                                                 /* the window id */
+        root,                                                /* parent == root */
+        win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
+        0,                                                   /* x11 border = 0, we draw our own */
         XCB_WINDOW_CLASS_INPUT_OUTPUT,
         XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
         XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
index 780a9e8eb781893dd3fb69c8642783b98c73e676..0f7e3635c0964b997f7ea219b930c5359fd50124 100644 (file)
@@ -109,10 +109,10 @@ void cmd_workspace_back_and_forth(I3_CMD);
 void cmd_workspace_name(I3_CMD, char *name);
 
 /**
- * Implementation of 'mark <mark>'
+ * Implementation of 'mark [--toggle] <mark>'
  *
  */
-void cmd_mark(I3_CMD, char *mark);
+void cmd_mark(I3_CMD, char *mark, char *toggle);
 
 /**
  * Implementation of 'unmark [mark]'
index 9a0af0e6cd6b8119e1a5ae4a4bd5fcef915cb66e..fb11cbe3a743bf02b77a247968a39e297740e1b3 100644 (file)
@@ -167,6 +167,22 @@ struct Config {
      * flag can be delayed using an urgency timer. */
     float workspace_urgency_timer;
 
+    /** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
+    enum {
+        /* Focus if the target workspace is visible, set urgency hint otherwise. */
+        FOWA_SMART,
+        /* Always set the urgency hint. */
+        FOWA_URGENT,
+        /* Always focus the window. */
+        FOWA_FOCUS,
+        /* Ignore the request (no focus, no urgency hint). */
+        FOWA_NONE
+    } focus_on_window_activation;
+
+    /** Specifies whether or not marks should be displayed in the window
+     * decoration. Marks starting with a "_" will be ignored either way. */
+    bool show_marks;
+
     /** The default border style for new windows. */
     border_style_t default_border;
 
index 6c960b1f8c765771c2fd6d340872a76d7ea4fae6..f50396244db58571219c91aabe2df1ca7c79f412 100644 (file)
@@ -51,8 +51,11 @@ CFGFUN(force_focus_wrapping, const char *value);
 CFGFUN(force_xinerama, 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(show_marks, const char *value);
 CFGFUN(hide_edge_borders, const char *borders);
 CFGFUN(assign, const char *workspace);
+CFGFUN(no_focus);
 CFGFUN(ipc_socket, const char *path);
 CFGFUN(restart_state, const char *path);
 CFGFUN(popup_during_fullscreen, const char *value);
index cec571e9df8eecf71f10c32ebb8c2098f8770d16..50e1f1807bfc4cf2b16459a3d154f0883024f27f 100644 (file)
@@ -460,6 +460,7 @@ struct Assignment {
      *
      * A_COMMAND = run the specified command for the matching window
      * A_TO_WORKSPACE = assign the matching window to the specified workspace
+     * A_NO_FOCUS = don't focus matched window when it is managed
      *
      * 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
@@ -469,7 +470,8 @@ struct Assignment {
     enum {
         A_ANY = 0,
         A_COMMAND = (1 << 0),
-        A_TO_WORKSPACE = (1 << 1)
+        A_TO_WORKSPACE = (1 << 1),
+        A_NO_FOCUS = (1 << 2)
     } type;
 
     /** the criteria to check if a window matches */
@@ -543,6 +545,8 @@ struct Con {
 
     /* user-definable mark to jump to this container later */
     char *mark;
+    /* cached to decide whether a redraw is needed */
+    bool mark_changed;
 
     double percent;
 
index bea5f7a2885c5474afa5f037e140a4f3c4d15804..8330b6ace0e8504d85b5b5c0093f946d6edc8ffc 100644 (file)
@@ -64,6 +64,12 @@ void floating_raise_con(Con *con);
  */
 bool floating_maybe_reassign_ws(Con *con);
 
+/**
+ * Centers a floating con above the specified rect.
+ *
+ */
+void floating_center(Con *con, Rect rect);
+
 #if 0
 /**
  * Removes the floating client from its workspace and attaches it to the new
index 5ca875417bb163c386e5da8d462ffcc6d1a2c486..70ebc0006110dfd7d301a3cecc1fbae373152679 100644 (file)
@@ -24,6 +24,9 @@
 #include "data.h"
 #include "xcb.h"
 
+/** Git commit identifier, from version.c */
+extern const char *i3_version;
+
 /** The original value of RLIMIT_CORE when i3 was started. We need to restore
  * this before starting any other process, since we set RLIMIT_CORE to
  * RLIM_INFINITY for i3 debugging versions. */
index 96a60a1f29d27ea7f3b9ea719269ea269fef35f5..4eed319a21e86c7a95765c96c03974e9d6c6b7c5 100644 (file)
@@ -50,12 +50,6 @@ typedef void (*handler_t)(int, uint8_t *, int, uint32_t, uint32_t);
                               int size, uint32_t message_size, \
                               uint32_t message_type)
 
-/**
- * Emulates mkdir -p (creates any missing folders)
- *
- */
-bool mkdirp(const char *path);
-
 /**
  * Handler for activity on the listening socket, meaning that a new client
  * has just connected and we should accept() him. Sets up the event handler
index 3a1258273b912847ccc429adafbaba28b86cbd82..da8c8a42377d7ec09748c648cd0f88030dcf146b 100644 (file)
@@ -434,3 +434,27 @@ char *get_exe_path(const char *argv0);
  *
  */
 int logical_px(const int logical);
+
+/**
+ * This function resolves ~ in pathnames.
+ * It may resolve wildcards in the first part of the path, but if no match
+ * or multiple matches are found, it just returns a copy of path as given.
+ *
+ */
+char *resolve_tilde(const char *path);
+
+/**
+ * Get the path of the first configuration file found. If override_configpath
+ * is specified, that path is returned and saved for further calls. Otherwise,
+ * checks the home directory first, then the system directory first, always
+ * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
+ * $XDG_CONFIG_DIRS)
+ *
+ */
+char *get_config_path(const char *override_configpath, bool use_system_paths);
+
+/**
+ * Emulates mkdir -p (creates any missing folders)
+ *
+ */
+bool mkdirp(const char *path);
index dec681168b49ee851995dd3d88444405d598e257..270b2f229dfef7185a02a686db162ddb72e57034 100644 (file)
@@ -106,14 +106,6 @@ void exec_i3_utility(char *name, char *argv[]);
 void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie,
                  char *err_message);
 
-/**
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-char *resolve_tilde(const char *path);
-
 /**
  * Checks if the given path exists by calling stat().
  *
diff --git a/libi3/get_config_path.c b/libi3/get_config_path.c
new file mode 100644 (file)
index 0000000..8b6eeb7
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2015 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+#include "libi3.h"
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/*
+ * Checks if the given path exists by calling stat().
+ *
+ */
+static bool path_exists(const char *path) {
+    struct stat buf;
+    return (stat(path, &buf) == 0);
+}
+
+/*
+ * Get the path of the first configuration file found. If override_configpath
+ * is specified, that path is returned and saved for further calls. Otherwise,
+ * checks the home directory first, then the system directory first, always
+ * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
+ * $XDG_CONFIG_DIRS)
+ *
+ */
+char *get_config_path(const char *override_configpath, bool use_system_paths) {
+    char *xdg_config_home, *xdg_config_dirs, *config_path;
+
+    static const char *saved_configpath = NULL;
+
+    if (override_configpath != NULL) {
+        saved_configpath = override_configpath;
+        return sstrdup(saved_configpath);
+    }
+
+    if (saved_configpath != NULL)
+        return sstrdup(saved_configpath);
+
+    /* 1: check the traditional path under the home directory */
+    config_path = resolve_tilde("~/.i3/config");
+    if (path_exists(config_path))
+        return config_path;
+    free(config_path);
+
+    /* 2: check for $XDG_CONFIG_HOME/i3/config */
+    if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
+        xdg_config_home = "~/.config";
+
+    xdg_config_home = resolve_tilde(xdg_config_home);
+    sasprintf(&config_path, "%s/i3/config", xdg_config_home);
+    free(xdg_config_home);
+
+    if (path_exists(config_path))
+        return config_path;
+    free(config_path);
+
+    /* The below paths are considered system-level, and can be skipped if the
+     * caller only wants user-level configs. */
+    if (!use_system_paths)
+        return NULL;
+
+    /* 3: check the traditional path under /etc */
+    config_path = SYSCONFDIR "/i3/config";
+    if (path_exists(config_path))
+        return sstrdup(config_path);
+
+    /* 4: check for $XDG_CONFIG_DIRS/i3/config */
+    if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
+        xdg_config_dirs = "/etc/xdg";
+
+    char *buf = sstrdup(xdg_config_dirs);
+    char *tok = strtok(buf, ":");
+    while (tok != NULL) {
+        tok = resolve_tilde(tok);
+        sasprintf(&config_path, "%s/i3/config", tok);
+        free(tok);
+        if (path_exists(config_path)) {
+            free(buf);
+            return config_path;
+        }
+        free(config_path);
+        tok = strtok(NULL, ":");
+    }
+    free(buf);
+
+    return NULL;
+}
diff --git a/libi3/mkdirp.c b/libi3/mkdirp.c
new file mode 100644 (file)
index 0000000..a0d35f9
--- /dev/null
@@ -0,0 +1,38 @@
+#include "libi3.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+/*
+ * Emulates mkdir -p (creates any missing folders)
+ *
+ */
+bool mkdirp(const char *path) {
+    if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
+        return true;
+    if (errno != ENOENT) {
+        ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
+        return false;
+    }
+    char *copy = sstrdup(path);
+    /* strip trailing slashes, if any */
+    while (copy[strlen(copy) - 1] == '/')
+        copy[strlen(copy) - 1] = '\0';
+
+    char *sep = strrchr(copy, '/');
+    if (sep == NULL) {
+        if (copy != NULL) {
+            free(copy);
+            copy = NULL;
+        }
+        return false;
+    }
+    *sep = '\0';
+    bool result = false;
+    if (mkdirp(copy))
+        result = mkdirp(path);
+    free(copy);
+
+    return result;
+}
diff --git a/libi3/resolve_tilde.c b/libi3/resolve_tilde.c
new file mode 100644 (file)
index 0000000..a4e8287
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2015 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ */
+
+#include "libi3.h"
+#include <err.h>
+#include <glob.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This function resolves ~ in pathnames.
+ * It may resolve wildcards in the first part of the path, but if no match
+ * or multiple matches are found, it just returns a copy of path as given.
+ *
+ */
+char *resolve_tilde(const char *path) {
+    static glob_t globbuf;
+    char *head, *tail, *result;
+
+    tail = strchr(path, '/');
+    head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
+
+    int res = glob(head, GLOB_TILDE, NULL, &globbuf);
+    free(head);
+    /* no match, or many wildcard matches are bad */
+    if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
+        result = sstrdup(path);
+    else if (res != 0) {
+        err(EXIT_FAILURE, "glob() failed");
+    } else {
+        head = globbuf.gl_pathv[0];
+        result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1);
+        strncpy(result, head, strlen(head));
+        if (tail)
+            strncat(result, tail, strlen(tail));
+    }
+    globfree(&globbuf);
+
+    return result;
+}
index 315a9218dd5930695e540506ce11e0cd30cabbf4..87db6cf082cebfeb04e430cc5a31ba1fe19370e4 100644 (file)
@@ -189,10 +189,12 @@ state FLOATING:
   floating = 'enable', 'disable', 'toggle'
       -> call cmd_floating($floating)
 
-# mark <mark>
+# mark [--toggle] <mark>
 state MARK:
+  toggle = '--toggle'
+      ->
   mark = string
-      -> call cmd_mark($mark)
+      -> call cmd_mark($mark, $toggle)
 
 # unmark [mark]
 state UNMARK:
index 25be5cf107e2bef2eebdba243980ee04085c9efa..d27e0792127a6f78b863995941994cf66523439b 100644 (file)
@@ -31,6 +31,7 @@ state INITIAL:
   'hide_edge_borders'                      -> HIDE_EDGE_BORDERS
   'for_window'                             -> FOR_WINDOW
   'assign'                                 -> ASSIGN
+  'no_focus'                               -> NO_FOCUS
   'focus_follows_mouse'                    -> FOCUS_FOLLOWS_MOUSE
   'mouse_warping'                          -> MOUSE_WARPING
   'force_focus_wrapping'                   -> FORCE_FOCUS_WRAPPING
@@ -38,6 +39,8 @@ state INITIAL:
   'workspace_auto_back_and_forth'          -> WORKSPACE_BACK_AND_FORTH
   'fake_outputs', 'fake-outputs'           -> FAKE_OUTPUTS
   'force_display_urgency_hint'             -> FORCE_DISPLAY_URGENCY_HINT
+  'focus_on_window_activation'             -> FOCUS_ON_WINDOW_ACTIVATION
+  'show_marks'                             -> SHOW_MARKS
   'workspace'                              -> WORKSPACE
   'ipc_socket', 'ipc-socket'               -> IPC_SOCKET
   'restart_state'                          -> RESTART_STATE
@@ -148,6 +151,15 @@ state ASSIGN_WORKSPACE:
   workspace = string
       -> call cfg_assign($workspace)
 
+# no_focus <criteria>
+state NO_FOCUS:
+  '['
+      -> call cfg_criteria_init(NO_FOCUS_END); CRITERIA
+
+state NO_FOCUS_END:
+  end
+      -> call cfg_no_focus()
+
 # Criteria: Used by for_window and assign.
 state CRITERIA:
   ctype = 'class'       -> CRITERION
@@ -204,12 +216,22 @@ state FORCE_DISPLAY_URGENCY_HINT:
   duration_ms = number
       -> FORCE_DISPLAY_URGENCY_HINT_MS
 
+# show_marks
+state SHOW_MARKS:
+  value = word
+      -> call cfg_show_marks($value)
+
 state FORCE_DISPLAY_URGENCY_HINT_MS:
   'ms'
       ->
   end
       -> call cfg_force_display_urgency_hint(&duration_ms)
 
+# focus_on_window_activation <smart|urgent|focus|none>
+state FOCUS_ON_WINDOW_ACTIVATION:
+  mode = word
+      -> call cfg_focus_on_window_activation($mode)
+
 # workspace <workspace> output <output>
 state WORKSPACE:
   workspace = word
@@ -327,6 +349,8 @@ state MODE_IGNORE_LINE:
 state MODE_BINDING:
   release = '--release'
       ->
+  whole_window = '--whole-window'
+      ->
   modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
       ->
   '+'
index 646034a69e1b920256b7ea280282a3c05b08b516..631ff42561f7b527ee80ec659787263c9669ebc6 100755 (executable)
@@ -82,6 +82,12 @@ else
        git merge --no-ff next -m "Merge branch 'next' into master"
 fi
 
+git remote remove origin
+git remote add origin git@github.com:i3/i3.git
+git config --add remote.origin.push "+refs/tags/*:refs/tags/*"
+git config --add remote.origin.push "+refs/heads/next:refs/heads/next"
+git config --add remote.origin.push "+refs/heads/master:refs/heads/master"
+
 ################################################################################
 # Section 2: Debian packaging
 ################################################################################
@@ -164,8 +170,27 @@ done
 
 git commit -a -m "update docs for ${RELEASE_VERSION}"
 
+git remote remove origin
+git remote add origin git@github.com:i3/i3.github.io.git
+
 ################################################################################
-# Section 4: final push instructions
+# Section 4: prepare release announcement email
+################################################################################
+
+cd ${TMPDIR}
+cat >email.txt <<EOT
+From: Michael Stapelberg <michael@i3wm.org>
+To: i3-announce@i3.zekjur.net
+Subject: i3 v${RELEASE_VERSION} released
+
+Hi,
+
+I just released i3 v${RELEASE_VERSION}. Release notes follow:
+EOT
+cat ${TMPDIR}/i3/RELEASE-NOTES-${RELEASE_VERSION}.txt >>email.txt
+
+################################################################################
+# Section 5: final push instructions
 ################################################################################
 
 echo "As a final sanity check, install the debian package and see whether i3 works."
@@ -174,18 +199,17 @@ echo "When satisfied, run:"
 echo "  cd ${TMPDIR}/i3"
 echo "  git checkout next"
 echo "  vi debian/changelog"
-# TODO: can we just set up the remote spec properly?
-echo "  git push git@github.com:i3/i3 next"
-echo "  git push git@github.com:i3/i3 master"
-echo "  git push git@github.com:i3/i3 --tags"
+echo "  git push"
 echo ""
 echo "  cd ${TMPDIR}/i3.github.io"
-# TODO: can we just set up the remote spec properly?
-echo "  git push git@github.com:i3/i3.github.io master"
+echo "  git push"
 echo ""
 echo "  cd ${TMPDIR}/debian"
 echo "  dput *.changes"
 echo ""
+echo "  cd ${TMPDIR}"
+echo "  sendmail < email.txt"
+echo ""
 echo "Announce on:"
 echo "  twitter"
 echo "  google+"
index 10abc0570c3140201a1d46cae53421f3f243d16d..3cd367cc725ee8b439cde59b35f4829a92892ad8 100644 (file)
@@ -180,6 +180,9 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     if (con->parent->type == CT_DOCKAREA)
         goto done;
 
+    const bool is_left_or_right_click = (event->detail == XCB_BUTTON_INDEX_1 ||
+                                         event->detail == XCB_BUTTON_INDEX_3);
+
     /* if the user has bound an action to this click, it should override the
      * default behavior. */
     if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) {
@@ -278,7 +281,8 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
             return 1;
         }
 
-        if (!in_stacked && dest == CLICK_DECORATION) {
+        if (!in_stacked && dest == CLICK_DECORATION &&
+            is_left_or_right_click) {
             /* try tiling resize, but continue if it doesn’t work */
             DLOG("tiling resize with fallback\n");
             if (tiling_resize(con, event, dest))
@@ -291,7 +295,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
             return 1;
         }
 
-        if (dest == CLICK_BORDER) {
+        if (dest == CLICK_BORDER && is_left_or_right_click) {
             DLOG("floating resize due to border click\n");
             floating_resize_window(floatingcon, proportional, event);
             return 1;
@@ -299,7 +303,8 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
 
         /* 6: dragging, if this was a click on a decoration (which did not lead
          * to a resize) */
-        if (!in_stacked && dest == CLICK_DECORATION) {
+        if (!in_stacked && dest == CLICK_DECORATION &&
+            (event->detail == XCB_BUTTON_INDEX_1)) {
             floating_drag_window(floatingcon, event);
             return 1;
         }
@@ -320,8 +325,7 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
     }
     /* 8: otherwise, check for border/decoration clicks and resize */
     else if ((dest == CLICK_BORDER || dest == CLICK_DECORATION) &&
-             (event->detail == XCB_BUTTON_INDEX_1 ||
-              event->detail == XCB_BUTTON_INDEX_3)) {
+             is_left_or_right_click) {
         DLOG("Trying to resize (tiling)\n");
         tiling_resize(con, event, dest);
     }
index fd43a693bf26eb2102a39b5e8b8d8917a01eaaa3..79071d6bb5f451dbae214fcb6fd2a53dbde7813f 100644 (file)
@@ -1037,28 +1037,48 @@ void cmd_workspace_name(I3_CMD, char *name) {
 }
 
 /*
- * Implementation of 'mark <mark>'
+ * Implementation of 'mark [--toggle] <mark>'
  *
  */
-void cmd_mark(I3_CMD, char *mark) {
-    DLOG("Clearing all windows which have that mark first\n");
+void cmd_mark(I3_CMD, char *mark, char *toggle) {
+    HANDLE_EMPTY_MATCH;
 
-    Con *con;
-    TAILQ_FOREACH(con, &all_cons, all_cons) {
-        if (con->mark && strcmp(con->mark, mark) == 0)
-            FREE(con->mark);
+    owindow *current = TAILQ_FIRST(&owindows);
+    if (current == NULL) {
+        ysuccess(false);
+        return;
     }
 
-    DLOG("marking window with str %s\n", mark);
-    owindow *current;
-
-    HANDLE_EMPTY_MATCH;
+    /* Marks must be unique, i.e., no two windows must have the same mark. */
+    if (current != TAILQ_LAST(&owindows, owindows_head)) {
+        yerror("A mark must not be put onto more than one window");
+        return;
+    }
 
-    TAILQ_FOREACH(current, &owindows, owindows) {
-        DLOG("matching: %p / %s\n", current->con, current->con->name);
+    DLOG("matching: %p / %s\n", current->con, current->con->name);
+    current->con->mark_changed = true;
+    if (toggle != NULL && current->con->mark && strcmp(current->con->mark, mark) == 0) {
+        DLOG("removing window mark %s\n", mark);
+        FREE(current->con->mark);
+    } else {
+        DLOG("marking window with str %s\n", mark);
+        FREE(current->con->mark);
         current->con->mark = sstrdup(mark);
     }
 
+    DLOG("Clearing all non-matched windows with this mark\n");
+    Con *con;
+    TAILQ_FOREACH(con, &all_cons, all_cons) {
+        /* Skip matched window, we took care of it already. */
+        if (current->con == con)
+            continue;
+
+        if (con->mark && strcmp(con->mark, mark) == 0) {
+            FREE(con->mark);
+            con->mark_changed = true;
+        }
+    }
+
     cmd_output->needs_tree_render = true;
     // XXX: default reply for now, make this a better reply
     ysuccess(true);
@@ -1072,14 +1092,20 @@ void cmd_unmark(I3_CMD, char *mark) {
     if (mark == NULL) {
         Con *con;
         TAILQ_FOREACH(con, &all_cons, all_cons) {
+            if (con->mark == NULL)
+                continue;
+
             FREE(con->mark);
+            con->mark_changed = true;
         }
         DLOG("removed all window marks");
     } else {
         Con *con;
         TAILQ_FOREACH(con, &all_cons, all_cons) {
-            if (con->mark && strcmp(con->mark, mark) == 0)
+            if (con->mark && strcmp(con->mark, mark) == 0) {
                 FREE(con->mark);
+                con->mark_changed = true;
+            }
         }
         DLOG("removed window mark %s\n", mark);
     }
@@ -1746,25 +1772,18 @@ void cmd_move_window_to_center(I3_CMD, char *method) {
     }
 
     if (strcmp(method, "absolute") == 0) {
-        Rect *rect = &focused->parent->rect;
-
         DLOG("moving to absolute center\n");
-        rect->x = croot->rect.width / 2 - rect->width / 2;
-        rect->y = croot->rect.height / 2 - rect->height / 2;
+        floating_center(focused->parent, croot->rect);
 
         floating_maybe_reassign_ws(focused->parent);
         cmd_output->needs_tree_render = true;
     }
 
     if (strcmp(method, "position") == 0) {
-        Rect *wsrect = &con_get_workspace(focused)->rect;
-        Rect newrect = focused->parent->rect;
-
         DLOG("moving to center\n");
-        newrect.x = wsrect->width / 2 - newrect.width / 2;
-        newrect.y = wsrect->height / 2 - newrect.height / 2;
+        floating_center(focused->parent, con_get_workspace(focused)->rect);
 
-        floating_reposition(focused->parent, newrect);
+        cmd_output->needs_tree_render = true;
     }
 
     // XXX: default reply for now, make this a better reply
index b610b0c60c1c9cfb792d107a7ff6d05acdc27487..dab8d29d439a36e56c21587819e03de0dfaaba99 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -736,6 +736,9 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         }
     }
 
+    /* Save the urgency state so that we can restore it. */
+    bool urgent = con->urgent;
+
     /* Save the current workspace. So we can call workspace_show() by the end
      * of this function. */
     Con *current_ws = con_get_workspace(focused);
@@ -843,7 +846,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
     if (source_ws == current_ws)
         con_focus(con_descend_focused(focus_next));
 
-    /* If anything within the container is associated with a startup sequence,
+    /* 9. If anything within the container is associated with a startup sequence,
      * delete it so child windows won't be created on the old workspace. */
     struct Startup_Sequence *sequence;
     xcb_get_property_cookie_t cookie;
@@ -877,6 +880,12 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
 
     CALL(parent, on_remove_child);
 
+    /* 10. If the container was marked urgent, move the urgency hint. */
+    if (urgent) {
+        workspace_update_urgent_flag(source_ws);
+        con_set_urgency(con, true);
+    }
+
     ipc_send_window_event("move", con);
 }
 
@@ -1346,11 +1355,7 @@ void con_set_layout(Con *con, layout_t layout) {
          * with an orientation). Since we switched to splith/splitv layouts,
          * using the "default" layout (which "only" should happen when using
          * legacy configs) is using the last split layout (either splith or
-         * splitv) in order to still do the same thing.
-         *
-         * Starting from v4.6 though, we will nag users about using "layout
-         * default", and in v4.9 we will remove it entirely (with an
-         * appropriate i3-migrate-config mechanism). */
+         * splitv) in order to still do the same thing. */
         con->layout = con->last_split_layout;
         /* In case last_split_layout was not initialized… */
         if (con->layout == L_DEFAULT)
index 6f906b8ce02e7218828ae2f449bf3223a82dd205..8a1cb99c168e1934bc28063d641ded33d47b47b3 100644 (file)
@@ -39,73 +39,6 @@ void update_barconfig() {
     }
 }
 
-/*
- * Get the path of the first configuration file found. If override_configpath
- * is specified, that path is returned and saved for further calls. Otherwise,
- * checks the home directory first, then the system directory first, always
- * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
- * $XDG_CONFIG_DIRS)
- *
- */
-static char *get_config_path(const char *override_configpath) {
-    char *xdg_config_home, *xdg_config_dirs, *config_path;
-
-    static const char *saved_configpath = NULL;
-
-    if (override_configpath != NULL) {
-        saved_configpath = override_configpath;
-        return sstrdup(saved_configpath);
-    }
-
-    if (saved_configpath != NULL)
-        return sstrdup(saved_configpath);
-
-    /* 1: check the traditional path under the home directory */
-    config_path = resolve_tilde("~/.i3/config");
-    if (path_exists(config_path))
-        return config_path;
-    free(config_path);
-
-    /* 2: check for $XDG_CONFIG_HOME/i3/config */
-    if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
-        xdg_config_home = "~/.config";
-
-    xdg_config_home = resolve_tilde(xdg_config_home);
-    sasprintf(&config_path, "%s/i3/config", xdg_config_home);
-    free(xdg_config_home);
-
-    if (path_exists(config_path))
-        return config_path;
-    free(config_path);
-
-    /* 3: check the traditional path under /etc */
-    config_path = SYSCONFDIR "/i3/config";
-    if (path_exists(config_path))
-        return sstrdup(config_path);
-
-    /* 4: check for $XDG_CONFIG_DIRS/i3/config */
-    if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
-        xdg_config_dirs = "/etc/xdg";
-
-    char *buf = sstrdup(xdg_config_dirs);
-    char *tok = strtok(buf, ":");
-    while (tok != NULL) {
-        tok = resolve_tilde(tok);
-        sasprintf(&config_path, "%s/i3/config", tok);
-        free(tok);
-        if (path_exists(config_path)) {
-            free(buf);
-            return config_path;
-        }
-        free(config_path);
-        tok = strtok(NULL, ":");
-    }
-    free(buf);
-
-    die("Unable to find the configuration file (looked at "
-        "~/.i3/config, $XDG_CONFIG_HOME/i3/config, " SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
-}
-
 /*
  * Finds the configuration file to use (either the one specified by
  * override_configpath), the user’s one or the system default) and calls
@@ -113,7 +46,12 @@ static char *get_config_path(const char *override_configpath) {
  *
  */
 bool parse_configuration(const char *override_configpath, bool use_nagbar) {
-    char *path = get_config_path(override_configpath);
+    char *path = get_config_path(override_configpath, true);
+    if (path == NULL) {
+        die("Unable to find the configuration file (looked at "
+            "~/.i3/config, $XDG_CONFIG_HOME/i3/config, " SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
+    }
+
     LOG("Parsing configfile %s\n", path);
     FREE(current_configpath);
     current_configpath = path;
@@ -252,6 +190,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
     INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000");
     INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000");
 
+    config.show_marks = true;
+
     config.default_border = BS_NORMAL;
     config.default_floating_border = BS_NORMAL;
     config.default_border_width = logical_px(2);
index c8b25c7645f6cfc7e8c2d8ab210101138719957d..398e53bb7c8cb9c7186f2050ba80b6686708e76b 100644 (file)
@@ -329,6 +329,27 @@ CFGFUN(force_display_urgency_hint, const long duration_ms) {
     config.workspace_urgency_timer = duration_ms / 1000.0;
 }
 
+CFGFUN(focus_on_window_activation, const char *mode) {
+    if (strcmp(mode, "smart") == 0)
+        config.focus_on_window_activation = FOWA_SMART;
+    else if (strcmp(mode, "urgent") == 0)
+        config.focus_on_window_activation = FOWA_URGENT;
+    else if (strcmp(mode, "focus") == 0)
+        config.focus_on_window_activation = FOWA_FOCUS;
+    else if (strcmp(mode, "none") == 0)
+        config.focus_on_window_activation = FOWA_NONE;
+    else {
+        ELOG("Unknown focus_on_window_activation mode \"%s\", ignoring it.\n", mode);
+        return;
+    }
+
+    DLOG("Set new focus_on_window_activation mode = %i", config.focus_on_window_activation);
+}
+
+CFGFUN(show_marks, const char *value) {
+    config.show_marks = eval_boolstr(value);
+}
+
 CFGFUN(workspace, const char *workspace, const char *output) {
     DLOG("Assigning workspace \"%s\" to output \"%s\"\n", workspace, output);
     /* Check for earlier assignments of the same workspace so that we
@@ -410,6 +431,19 @@ CFGFUN(assign, const char *workspace) {
     TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
 }
 
+CFGFUN(no_focus) {
+    if (match_is_empty(current_match)) {
+        ELOG("Match is empty, ignoring this assignment\n");
+        return;
+    }
+
+    DLOG("new assignment, using above criteria, to ignore focus on manage");
+    Assignment *assignment = scalloc(sizeof(Assignment));
+    match_copy(&(assignment->match), current_match);
+    assignment->type = A_NO_FOCUS;
+    TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
+}
+
 /*******************************************************************************
  * Bar configuration (i3bar)
  ******************************************************************************/
index eef03cae238e0cf7455fd64ca1b18658e47d977f..f325481275b7f07d09e4dbf02ffbc3d8ade03a63 100644 (file)
@@ -996,7 +996,7 @@ bool parse_file(const char *f, bool use_nagbar) {
     check_for_duplicate_bindings(context);
 
     if (use_nagbar && (context->has_errors || context->has_warnings)) {
-        ELOG("FYI: You are using i3 version " I3_VERSION "\n");
+        ELOG("FYI: You are using i3 version %s\n", i3_version);
         if (version == 3)
             ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n");
 
index e6ca2d7188d6392c172aa09bf17d6eb0588f6544..d01cb43dfec887257d5e842e477583ba1e572513 100644 (file)
@@ -246,12 +246,10 @@ void floating_enable(Con *con, bool automatic) {
         if (con->window && con->window->leader != XCB_NONE &&
             (leader = con_by_window_id(con->window->leader)) != NULL) {
             DLOG("Centering above leader\n");
-            nc->rect.x = leader->rect.x + (leader->rect.width / 2) - (nc->rect.width / 2);
-            nc->rect.y = leader->rect.y + (leader->rect.height / 2) - (nc->rect.height / 2);
+            floating_center(nc, leader->rect);
         } else {
             /* center the window on workspace as fallback */
-            nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
-            nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
+            floating_center(nc, ws->rect);
         }
     }
 
@@ -310,8 +308,7 @@ void floating_enable(Con *con, bool automatic) {
     }
 
     ELOG("No output found at destination coordinates, centering floating window on current ws\n");
-    nc->rect.x = ws->rect.x + (ws->rect.width / 2) - (nc->rect.width / 2);
-    nc->rect.y = ws->rect.y + (ws->rect.height / 2) - (nc->rect.height / 2);
+    floating_center(nc, ws->rect);
 
     ipc_send_window_event("floating", con);
 }
@@ -420,6 +417,15 @@ bool floating_maybe_reassign_ws(Con *con) {
     return true;
 }
 
+/*
+ * Centers a floating con above the specified rect.
+ *
+ */
+void floating_center(Con *con, Rect rect) {
+    con->rect.x = rect.x + (rect.width / 2) - (con->rect.width / 2);
+    con->rect.y = rect.y + (rect.height / 2) - (con->rect.height / 2);
+}
+
 DRAGGING_CB(drag_window_callback) {
     const struct xcb_button_press_event_t *event = extra;
 
index 041f7e366f76a96da1620c1ab950ffacc084edc5..c80c279efe43857b83e9a4e4246e72f3681dc5cb 100644 (file)
@@ -743,16 +743,17 @@ static void handle_client_message(xcb_client_message_event_t *event) {
             workspace_show(ws);
             con_focus(con);
         } else {
-            /* If the request is from an application, only focus if the
-             * workspace is visible. Otherwise set the urgency hint. */
-            if (workspace_is_visible(ws)) {
-                DLOG("Request to focus con on a visible workspace. Focusing con = %p\n", con);
+            /* Request is from an application. */
+
+            if (config.focus_on_window_activation == FOWA_FOCUS || (config.focus_on_window_activation == FOWA_SMART && workspace_is_visible(ws))) {
+                DLOG("Focusing con = %p\n", con);
                 workspace_show(ws);
                 con_focus(con);
-            } else {
-                DLOG("Request to focus con on a hidden workspace. Setting urgent con = %p\n", con);
+            } else if (config.focus_on_window_activation == FOWA_URGENT || (config.focus_on_window_activation == FOWA_SMART && !workspace_is_visible(ws))) {
+                DLOG("Marking con = %p urgent\n", con);
                 con_set_urgency(con, true);
-            }
+            } else
+                DLOG("Ignoring request for con = %p", con);
         }
 
         tree_render();
index 8c7634643ab7cc10292abda8869e24dfd435556d..cfa9fd16834d65fae62dc5238d707aaf0d7e983c 100644 (file)
--- a/src/i3.mk
+++ b/src/i3.mk
@@ -32,6 +32,10 @@ include/all.h.pch: $(i3_HEADERS)
        echo "[i3] PCH all.h"
        $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) -x c-header include/all.h -o include/all.h.pch
 
+src/version.o: src/version.c LAST_VERSION $(i3_HEADERS_DEP)
+       echo "[i3] CC $<"
+       $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$<
+
 src/%.o: src/%.c $(i3_HEADERS_DEP)
        echo "[i3] CC $<"
        $(CC) $(I3_CPPFLAGS) $(XCB_CPPFLAGS) $(CPPFLAGS) $(i3_CFLAGS) $(I3_CFLAGS) $(CFLAGS) $(PCH_FLAGS) -c -o $@ ${canonical_path}/$<
@@ -92,4 +96,4 @@ install-i3: i3
 
 clean-i3:
        echo "[i3] Clean"
-       rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3-config-parser.stamp i3 test.config_parser test.commands_parser src/*.gcno src/cfgparse.* src/cmdparse.*
+       rm -f $(i3_OBJECTS) $(i3_SOURCES_GENERATED) $(i3_HEADERS_CMDPARSER) include/loglevels.h loglevels.tmp include/all.h.pch i3-command-parser.stamp i3-config-parser.stamp i3 test.config_parser test.commands_parser src/*.gcno src/cfgparse.* src/cmdparse.* LAST_VERSION
index 52f7db2efe432c50fedecefb0940b8404b8ef2b2..f8138f0a995bb47685b61f024f2a8c9f6e80a7d2 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -37,36 +37,6 @@ static void set_nonblock(int sockfd) {
         err(-1, "Could not set O_NONBLOCK");
 }
 
-/*
- * Emulates mkdir -p (creates any missing folders)
- *
- */
-bool mkdirp(const char *path) {
-    if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
-        return true;
-    if (errno != ENOENT) {
-        ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
-        return false;
-    }
-    char *copy = sstrdup(path);
-    /* strip trailing slashes, if any */
-    while (copy[strlen(copy) - 1] == '/')
-        copy[strlen(copy) - 1] = '\0';
-
-    char *sep = strrchr(copy, '/');
-    if (sep == NULL) {
-        FREE(copy);
-        return false;
-    }
-    *sep = '\0';
-    bool result = false;
-    if (mkdirp(copy))
-        result = mkdirp(path);
-    free(copy);
-
-    return result;
-}
-
 /*
  * Sends the specified event to all IPC clients which are currently connected
  * and subscribed to this kind of event.
@@ -821,7 +791,7 @@ IPC_HANDLER(get_version) {
     y(integer, PATCH_VERSION);
 
     ystr("human_readable");
-    ystr(I3_VERSION);
+    ystr(i3_version);
 
     y(map_close);
 
index 8b514178bbbf3283fc9391f11ef5c6f42643ba1a..ac40e7a389bc2eb41c303a2dd28637e4fea8007c 100644 (file)
@@ -188,7 +188,7 @@ static void handle_signal(int sig, siginfo_t *info, void *data) {
 int main(int argc, char *argv[]) {
     /* Keep a symbol pointing to the I3_VERSION string constant so that we have
      * it in gdb backtraces. */
-    const char *i3_version __attribute__((unused)) = I3_VERSION;
+    const char *_i3_version __attribute__((unused)) = i3_version;
     char *override_configpath = NULL;
     bool autostart = true;
     char *layout_path = NULL;
@@ -261,11 +261,11 @@ int main(int argc, char *argv[]) {
                 only_check_config = true;
                 break;
             case 'v':
-                printf("i3 version " I3_VERSION " © 2009-2014 Michael Stapelberg and contributors\n");
+                printf("i3 version %s © 2009-2014 Michael Stapelberg and contributors\n", i3_version);
                 exit(EXIT_SUCCESS);
                 break;
             case 'm':
-                printf("Binary i3 version:  " I3_VERSION " © 2009-2014 Michael Stapelberg and contributors\n");
+                printf("Binary i3 version:  %s © 2009-2014 Michael Stapelberg and contributors\n", i3_version);
                 display_running_version();
                 exit(EXIT_SUCCESS);
                 break;
@@ -456,7 +456,7 @@ int main(int argc, char *argv[]) {
         free(cwd);
     }
 
-    LOG("i3 " I3_VERSION " starting\n");
+    LOG("i3 %s starting\n", i3_version);
 
     conn = xcb_connect(NULL, &conn_screen);
     if (xcb_connection_has_error(conn))
index 2b3c6743a20df8b8b1c4525c9719fa11cdf6f01a..3499963b97647d4b6fa7796cd0978fc8feb5f3fa 100644 (file)
@@ -512,8 +512,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     /* Defer setting focus after the 'new' event has been sent to ensure the
      * proper window event sequence. */
     if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
-        DLOG("Now setting focus.\n");
-        con_focus(nc);
+        if (assignment_for(cwindow, A_NO_FOCUS) == NULL) {
+            DLOG("Now setting focus.\n");
+            con_focus(nc);
+        }
     }
 
     tree_render();
index 1999a1fe901853d06924df49f5c026cc4f7977e2..e4da191a656752932b0c5fc420bbc12f6a7435ba 100644 (file)
@@ -249,6 +249,14 @@ void tree_move(Con *con, int direction) {
                         ? AFTER
                         : BEFORE);
         insert_con_into(con, target, position);
+    } else if (con->parent->parent->type == CT_WORKSPACE &&
+               con->parent->layout != L_DEFAULT &&
+               con_num_children(con->parent) == 1) {
+        /* Con is the lone child of a non-default layout container at the edge
+         * of the workspace. Treat it as though the workspace is its parent
+         * and move it to the next output. */
+        DLOG("Grandparent is workspace\n");
+        move_to_output_directed(con, direction);
     } else {
         DLOG("Moving into container above\n");
         position = (direction == D_UP || direction == D_LEFT ? BEFORE : AFTER);
index 3b7c2a72b009131e616cd57cf0665af3499c46bc..75b8e56c2bbbfe6064189e563eccc72ebee7acba 100644 (file)
@@ -198,10 +198,7 @@ void scratchpad_show(Con *con) {
         con->rect.width = output->rect.width * 0.5;
         con->rect.height = output->rect.height * 0.75;
         floating_check_size(con);
-        con->rect.x = output->rect.x +
-                      ((output->rect.width / 2.0) - (con->rect.width / 2.0));
-        con->rect.y = output->rect.y +
-                      ((output->rect.height / 2.0) - (con->rect.height / 2.0));
+        floating_center(con, con_get_workspace(con)->rect);
     }
 
     /* Activate active workspace if window is from another workspace to ensure
index 5760ae72588f52afc5cd6caf617a4b184e344ab9..c891a6bc5c9678c998c4cf2a7cca348d40b56cfa 100644 (file)
@@ -159,38 +159,6 @@ void check_error(xcb_connection_t *conn, xcb_void_cookie_t cookie, char *err_mes
     }
 }
 
-/*
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-char *resolve_tilde(const char *path) {
-    static glob_t globbuf;
-    char *head, *tail, *result;
-
-    tail = strchr(path, '/');
-    head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
-
-    int res = glob(head, GLOB_TILDE, NULL, &globbuf);
-    free(head);
-    /* no match, or many wildcard matches are bad */
-    if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
-        result = sstrdup(path);
-    else if (res != 0) {
-        die("glob() failed");
-    } else {
-        head = globbuf.gl_pathv[0];
-        result = scalloc(strlen(head) + (tail ? strlen(tail) : 0) + 1);
-        strncpy(result, head, strlen(head));
-        if (tail)
-            strncat(result, tail, strlen(tail));
-    }
-    globfree(&globbuf);
-
-    return result;
-}
-
 /*
  * Checks if the given path exists by calling stat().
  *
diff --git a/src/version.c b/src/version.c
new file mode 100644 (file)
index 0000000..d7d31b3
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2015 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * Stores the latest Git commit identifier so that it can be linked into i3
+ * and used dynamically without recompiling every object file.
+ *
+ */
+const char *i3_version = I3_VERSION;
index f55c920ebd4697b7cd9346c8d233993d7625c202..4a16f3d1c7b31d587466bcc4b27cc0570ae80b75 100644 (file)
@@ -846,6 +846,9 @@ Con *workspace_attach_to(Con *ws) {
     DLOG("Attaching new split %p to workspace %p\n", new, ws);
     con_attach(new, ws, false);
 
+    /* 5: fix the percentages */
+    con_fix_percent(ws);
+
     return new;
 }
 
diff --git a/src/x.c b/src/x.c
index 9dd09117cf72eaeb234b2f1c10b7c49a9e89b17b..d29d4befeb5fb944ae748954760d0d7120cb258e 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -363,6 +363,7 @@ void x_draw_decoration(Con *con) {
         (con->window == NULL || !con->window->name_x_changed) &&
         !parent->pixmap_recreated &&
         !con->pixmap_recreated &&
+        !con->mark_changed &&
         memcmp(p, con->deco_render_params, sizeof(struct deco_render_params)) == 0) {
         free(p);
         goto copy_pixmaps;
@@ -381,6 +382,7 @@ void x_draw_decoration(Con *con) {
 
     parent->pixmap_recreated = false;
     con->pixmap_recreated = false;
+    con->mark_changed = false;
 
     /* 2: draw the client.background, but only for the parts around the client_rect */
     if (con->window != NULL) {
@@ -531,10 +533,25 @@ void x_draw_decoration(Con *con) {
     //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult);
     int indent_px = (indent_level * 5) * indent_mult;
 
+    int mark_width = 0;
+    if (config.show_marks && con->mark != NULL && (con->mark)[0] != '_') {
+        char *formatted_mark;
+        sasprintf(&formatted_mark, "[%s]", con->mark);
+        i3String *mark = i3string_from_utf8(formatted_mark);
+        FREE(formatted_mark);
+        mark_width = predict_text_width(mark);
+
+        draw_text(mark, parent->pixmap, parent->pm_gc,
+                  con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2),
+                  con->deco_rect.y + text_offset_y, mark_width);
+
+        I3STRING_FREE(mark);
+    }
+
     draw_text(win->name,
               parent->pixmap, parent->pm_gc,
-              con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y,
-              con->deco_rect.width - 2 - indent_px);
+              con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y,
+              con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2));
 
 after_title:
     /* Since we don’t clip the text at all, it might in some cases be painted
index f2c15013f5246b7e9346819d60365818d4fd68dd..3c2a26f97255c09161e13c03f48ee4658aaa8b0e 100755 (executable)
@@ -8,8 +8,8 @@ WriteMakefile(
     MIN_PERL_VERSION => '5.010000', # 5.10.0
     PREREQ_PM => {
         'AnyEvent'     => 0,
-        'AnyEvent::I3' => '0.15',
-        'X11::XCB'     => '0.09',
+        'AnyEvent::I3' => '0.16',
+        'X11::XCB'     => '0.12',
         'Inline'       => 0,
         'Inline::C'    => 0,
         'ExtUtils::PkgConfig' => 0,
index eaf57bde1620495c3ef168018a76189c39c32bf5..911558e51c658721ae138e37503c558f9b4dfc66 100755 (executable)
@@ -8,7 +8,6 @@ use v5.10;
 use utf8;
 # the following are modules which ship with Perl (>= 5.10):
 use Pod::Usage;
-use Cwd qw(abs_path);
 use File::Temp qw(tempfile tempdir);
 use Getopt::Long;
 use POSIX ();
@@ -17,8 +16,20 @@ use TAP::Parser;
 use TAP::Parser::Aggregator;
 use Time::HiRes qw(time);
 use IO::Handle;
+
+my $dirname;
+
+BEGIN {
+    use File::Basename;
+    use Cwd qw(abs_path);
+
+    # fileparse()[1] contains the directory portion of the specified path.
+    # See File::Basename(3p) for more details.
+    $dirname = (fileparse(abs_path($0)))[1];
+}
+
 # these are shipped with the testsuite
-use lib qw(lib);
+use lib $dirname . 'lib';
 use StartXServer;
 use StatusLine;
 use TestWorker;
@@ -70,6 +81,8 @@ my $result = GetOptions(
 
 pod2usage(-verbose => 2, -exitcode => 0) if $help;
 
+chdir $dirname or die "Could not chdir into $dirname";
+
 # Check for missing executables
 my @binaries = qw(
                    ../i3
@@ -86,6 +99,16 @@ foreach my $binary (@binaries) {
     die "$binary is not an executable" unless -x $binary;
 }
 
+if ($options{coverage}) {
+    qx(command -v lcov &> /dev/null);
+    die "Cannot find lcov needed for coverage testing." if $?;
+    qx(command -v genhtml &> /dev/null);
+    die "Cannot find genhtml needed for coverage testing." if $?;
+
+    # clean out the counters that may be left over from previous tests.
+    qx(lcov -d ../ --zerocounters &> /dev/null);
+}
+
 qx(Xephyr -help 2>&1);
 die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?;
 
@@ -227,6 +250,17 @@ if ($numtests == 1) {
 
 END { cleanup() }
 
+if ($options{coverage}) {
+    print("\nGenerating test coverage report...\n");
+    qx(lcov -d ../ -b ../ --capture -o latest/i3-coverage.info);
+    qx(genhtml -o latest/i3-coverage latest/i3-coverage.info);
+    if ($?) {
+        print("Could not generate test coverage html. Did you compile i3 with test coverage support?\n");
+    } else {
+        print("Test coverage report generated in latest/i3-coverage\n");
+    }
+}
+
 exit ($aggregator->failed > 0);
 
 #
@@ -391,12 +425,15 @@ available in C<latest/xtrace-for-$test.log>.
 
 =item B<--coverage-testing>
 
-Exits i3 cleanly (instead of kill -9) to make coverage testing work properly.
+Generates a test coverage report at C<latest/i3-coverage>. Exits i3 cleanly
+during tests (instead of kill -9) to make coverage testing work properly.
 
 =item B<--parallel>
 
-Number of Xephyr instances to start (if you dont want to start num_cores * 2
+Number of Xephyr instances to start (if you don't want to start num_cores * 2
 instances for some reason).
 
   # Run all tests on a single Xephyr instance
   ./complete-run.pl -p 1
+
+=back
index c149cbd1204a0ddf63bfeff9eff6c710c0dde4c7..ac1a26cadbd4d691350f3a53e02e102c2d84f917 100644 (file)
@@ -606,7 +606,7 @@ sub get_dock_clients {
 
 =head2 cmd($command)
 
-Sends the specified command to i3.
+Sends the specified command to i3 and returns the output.
 
   my $ws = unused_workspace;
   cmd "workspace $ws";
index bb913819b108526371dbc1430813197e8d7dd80a..3b82fe222cf1944811ea87536265d3b91b0cefb2 100644 (file)
@@ -307,6 +307,33 @@ for ($type = 1; $type <= 2; $type++) {
     my $ws = get_ws($tmp);
     ok(!$ws->{urgent}, 'urgent flag not set on workspace');
 
+##############################################################################
+# Regression test for #1187: Urgency hint moves to new workspace when moving
+# a container to another workspace.
+##############################################################################
+
+    my $tmp_source = fresh_workspace;
+    my $tmp_target = fresh_workspace;
+    cmd 'workspace ' . $tmp_source;
+    sync_with_i3;
+    my $w1 = open_window;
+    my $w2 = open_window;
+    sync_with_i3;
+    cmd '[id="' . $w1->id . '"] focus';
+    sync_with_i3;
+    cmd 'mark urgent_con';
+    cmd '[id="' . $w2->id . '"] focus';
+    set_urgency($w1, 1, $type);
+    sync_with_i3;
+    cmd '[con_mark="urgent_con"] move container to workspace ' . $tmp_target;
+    sync_with_i3;
+    my $source_ws = get_ws($tmp_source);
+    my $target_ws = get_ws($tmp_target);
+    ok(!$source_ws->{urgent}, 'Source workspace is no longer marked urgent');
+    is($target_ws->{urgent}, 1, 'Target workspace is now marked urgent');
+
+##############################################################################
+
     exit_gracefully($pid);
 }
 
index 4ca41799ea5a06f1754c5b4f6333dcca930465d3..b27bed53ed0648c05a742919d26ab1fc98aa3c08 100644 (file)
@@ -155,6 +155,11 @@ cmd "rename workspace temp to $first_ws";
 
 is_num_children($first_ws, 3, 'three containers on the first workspace');
 
+# empty 'from' workspaces should not crash the renaming of startup sequences
+cmd "workspace $first_ws";
+cmd "rename workspace to temp";
+cmd "rename workspace to $first_ws";
+
 # Switch to the first workspace and move the focused window to the
 # second workspace.
 cmd "workspace $first_ws";
index 1e91d47f9cf5cdcb71f9ccf350aefd1f61f2e364..9ec8b5ebe75de6f4f4b9265541a5f8f73c50dd7f 100644 (file)
@@ -45,6 +45,8 @@ mode "meh" {
     bindsym Mod1 + Shift +   x resize grow
     bindcode Mod1+44 resize shrink
     bindsym --release Mod1+x exec foo
+    bindsym --whole-window button3 nop
+    bindsym --release --whole-window button3 nop
 }
 EOT
 
@@ -53,11 +55,13 @@ cfg_enter_mode(meh)
 cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), resize grow)
 cfg_mode_binding(bindcode, Mod1, 44, (null), (null), resize shrink)
 cfg_mode_binding(bindsym, Mod1, x, --release, (null), exec foo)
+cfg_mode_binding(bindsym, (null), button3, (null), --whole-window, nop)
+cfg_mode_binding(bindsym, (null), button3, --release, --whole-window, nop)
 EOT
 
 is(parser_calls($config),
    $expected,
-   'single number (move workspace 3) ok');
+   'mode bindings ok');
 
 ################################################################################
 # exec and exec_always
@@ -433,7 +437,7 @@ client.focused          #4c7899 #285577 #ffffff #2e9ef4
 EOT
 
 my $expected_all_tokens = <<'EOT';
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
 EOT
 
 my $expected_end = <<'EOT';
index f285338b8208efc2b1e1985d86069405bd0e1b3f..99fc92c81a475543b362e7e891413cbb628509ab 100644 (file)
 #
 # checks if mark and unmark work correctly
 use i3test;
+use List::Util qw(first);
 
 sub get_marks {
     return i3(get_socket_path())->get_marks->recv;
 }
 
+sub get_mark_for_window_on_workspace {
+    my ($ws, $con) = @_;
+
+    my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
+    return $current->{mark};
+}
+
 ##############################################################
 # 1: check that there are no marks set yet
 ##############################################################
@@ -76,4 +84,78 @@ cmd 'unmark';
 
 is_deeply(get_marks(), [], 'all marks removed');
 
+##############################################################
+# 4: mark a con, use same mark to mark another con,
+#    check that only the latter is marked
+##############################################################
+
+my $first = open_window;
+my $second = open_window;
+
+cmd 'mark important';
+cmd 'focus left';
+cmd 'mark important';
+
+is(get_mark_for_window_on_workspace($tmp, $first), 'important', 'first container now has the mark');
+ok(!get_mark_for_window_on_workspace($tmp, $second), 'second container lost the mark');
+
+##############################################################
+# 5: mark a con, toggle the mark, check that the mark is gone
+##############################################################
+
+my $con = open_window;
+cmd 'mark important';
+cmd 'mark --toggle important';
+ok(!get_mark_for_window_on_workspace($tmp, $con), 'container no longer has the mark');
+
+##############################################################
+# 6: toggle a mark on an unmarked con, check it is marked
+##############################################################
+
+my $con = open_window;
+cmd 'mark --toggle important';
+is(get_mark_for_window_on_workspace($tmp, $con), 'important', 'container now has the mark');
+
+##############################################################
+# 7: mark a con, toggle a different mark, check it is marked
+#    with the new mark
+##############################################################
+
+my $con = open_window;
+cmd 'mark boring';
+cmd 'mark --toggle important';
+is(get_mark_for_window_on_workspace($tmp, $con), 'important', 'container has the most recent mark');
+
+##############################################################
+# 8: mark a con, toggle the mark on another con,
+#    check only the latter has the mark
+##############################################################
+
+my $first = open_window;
+my $second = open_window;
+
+cmd 'mark important';
+cmd 'focus left';
+cmd 'mark --toggle important';
+
+is(get_mark_for_window_on_workspace($tmp, $first), 'important', 'left container has the mark now');
+ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark');
+
+##############################################################
+# 9: try to mark two cons with the same mark and check that
+#    it fails
+##############################################################
+
+my $first = open_window(wm_class => 'iamnotunique');
+my $second = open_window(wm_class => 'iamnotunique');
+
+my $result = cmd "[instance=iamnotunique] mark important";
+
+is($result->[0]->{success}, 0, 'command was unsuccessful');
+is($result->[0]->{error}, 'A mark must not be put onto more than one window', 'correct error is returned');
+ok(!get_mark_for_window_on_workspace($tmp, $first), 'first container is not marked');
+ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr is not marked');
+
+##############################################################
+
 done_testing;
index e6cacdedfeaf3ee153409bb2406020e8c94cb94f..3685b30ce52ad7205d8382a602b1dfa4a778938e 100644 (file)
@@ -31,16 +31,17 @@ EOT
 my $pid = launch_with_config($config);
 
 sub change_window_class {
-    my ($window, $class) = @_;
+    my ($window, $class, $length) = @_;
     my $atomname = $x->atom(name => 'WM_CLASS');
     my $atomtype = $x->atom(name => 'STRING');
+    $length ||= length($class) + 1;
     $x->change_property(
         PROP_MODE_REPLACE,
         $window->id,
         $atomname->id,
         $atomtype->id,
         8,
-        length($class) + 1,
+        $length,
         $class
     );
     sync_with_i3;
@@ -65,6 +66,13 @@ is($con->{window_properties}->{instance}, 'special',
 is($con->{mark}, 'special_class_mark',
     'A `for_window` assignment should run for a match when the window changes class');
 
+change_window_class($win, "abcdefghijklmnopqrstuv\0abcd", 24);
+
+$con = @{get_ws_content($ws)}[0];
+
+is($con->{window_properties}->{class}, 'a',
+    'Non-null-terminated strings should be handled correctly');
+
 exit_gracefully($pid);
 
 done_testing;
diff --git a/testcases/t/240-focus-on-window-activation.t b/testcases/t/240-focus-on-window-activation.t
new file mode 100644 (file)
index 0000000..5f0b6e1
--- /dev/null
@@ -0,0 +1,206 @@
+#!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 for the focus_on_window_activation directive
+# Ticket: #1426
+use i3test i3_autostart => 0;
+use List::Util qw(first);
+
+sub send_net_active_window {
+    my ($id) = @_; 
+
+    my $msg = pack "CCSLLLLLLL",
+        X11::XCB::CLIENT_MESSAGE, # response_type
+        32, # format
+        0, # sequence
+        $id, # destination window
+        $x->atom(name => '_NET_ACTIVE_WINDOW')->id,
+        0, # source
+        0, 0, 0, 0;
+
+    $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
+}
+
+sub get_urgency_for_window_on_workspace {
+    my ($ws, $con) = @_;
+
+    my $current = first { $_->{window} == $con->{id} } @{get_ws_content($ws)};
+    return $current->{urgent};
+}
+
+#####################################################################
+# 1: If mode is set to 'urgent' and the target workspace is visible,
+#    check that the urgent flag is set and focus is not lost.
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation urgent
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+my $first = open_window;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is($x->input_focus, $second->id, 'second window is still focused');
+is(get_urgency_for_window_on_workspace($ws, $first), 1, 'first window is marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 2: If mode is set to 'urgent' and the target workspace is not
+#    visible, check that the urgent flag is set and focus is not lost.
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation urgent
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws1 = fresh_workspace;
+my $first = open_window;
+my $ws2 = fresh_workspace;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is(focused_ws(), $ws2, 'second workspace is still focused');
+is($x->input_focus, $second->id, 'second window is still focused');
+is(get_urgency_for_window_on_workspace($ws1, $first), 1, 'first window is marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 3: If mode is set to 'focus' and the target workspace is visible,
+#    check that the focus is switched.
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation focus
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+my $first = open_window;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is($x->input_focus, $first->id, 'first window is now focused');
+ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 4: If mode is set to 'focus' and the target workspace is not
+#    visible, check that the focus switched.
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation focus
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws1 = fresh_workspace;
+my $first = open_window;
+my $ws2 = fresh_workspace;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is(focused_ws(), $ws1, 'first workspace is now focused');
+is($x->input_focus, $first->id, 'first window is now focused');
+ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 5: If mode is set to 'none' and the target workspace is visible,
+#    check that nothing happens.
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation none
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws = fresh_workspace;
+my $first = open_window;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is($x->input_focus, $second->id, 'second window is still focused');
+ok(!get_urgency_for_window_on_workspace($ws, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 6: If mode is set to 'none' and the target workspace is not
+#    visible, check that nothing happens.
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+focus_on_window_activation none
+EOT
+
+my $pid = launch_with_config($config);
+
+my $ws1 = fresh_workspace;
+my $first = open_window;
+my $ws2 = fresh_workspace;
+my $second = open_window;
+
+send_net_active_window($first->id);
+sync_with_i3;
+
+is(focused_ws(), $ws2, 'second workspace is still focused');
+is($x->input_focus, $second->id, 'second window is still focused');
+ok(!get_urgency_for_window_on_workspace($ws1, $first), 'first window is not marked urgent');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/240-tabbed-floating-disable-crash.t b/testcases/t/240-tabbed-floating-disable-crash.t
new file mode 100644 (file)
index 0000000..7947158
--- /dev/null
@@ -0,0 +1,44 @@
+#!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)
+#
+# Verifies that i3 does not crash when floating and then unfloating an
+# unfocused window within a tabbed container.
+# Ticket: #1484
+# Bug still in: 4.9.1-124-g856e1f9
+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
+workspace_layout tabbed
+EOT
+
+my $pid = launch_with_config($config);
+
+open_window;
+open_window;
+
+# Mark the second window, then focus the workspace.
+cmd 'mark foo, focus parent, focus parent';
+
+# Float and unfloat the marked window (without it being focused).
+cmd '[con_mark=foo] floating enable, floating disable';
+
+does_i3_live;
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/241-consistent-center.t b/testcases/t/241-consistent-center.t
new file mode 100644 (file)
index 0000000..283d173
--- /dev/null
@@ -0,0 +1,106 @@
+#!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)
+#
+# Verifies that most of i3's centering methods produce consistent results.
+# Decorations are disabled to avoid floating_enable's logic which shifts
+# windows upwards dependent on their decoration height.
+#
+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
+
+new_window none
+new_float none
+EOT
+
+my $pid = launch_with_config($config);
+
+#####################################################################
+# Open a floating window, verifying that its initial position is
+# centered, and also verify that both centering methods leave it in
+# its original spot.
+#####################################################################
+
+my $first = open_floating_window;
+
+my $initial = $first->rect;
+is(int($initial->{x} + $initial->{width} / 2), int($x->root->rect->width / 2),
+   'x coordinates match');
+is(int($initial->{y} + $initial->{height} / 2), int($x->root->rect->height / 2),
+   'y coordinates match');
+
+cmd 'move position center';
+
+my $new = $first->rect;
+is($initial->{x}, $new->{x}, 'x coordinates match');
+is($initial->{y}, $new->{y}, 'y coordinates match');
+
+cmd 'move absolute position center';
+
+$new = $first->rect;
+is($initial->{x}, $new->{x}, 'x coordinates match');
+is($initial->{y}, $new->{y}, 'y coordinates match');
+
+#####################################################################
+# Create a second window and move it into and out of the scratchpad.
+# Because it hasn't been moved or resized, it should be floated in
+# the center of the screen when pulled out of the scratchpad.
+#####################################################################
+
+my $second = open_window;
+
+cmd 'move scratchpad, scratchpad show';
+
+$new = $second->rect;
+my $mid_init = $initial->{x} + int($initial->{width} / 2);
+my $mid_new = $new->{x} + int($new->{width} / 2);
+is($mid_init, $mid_new, 'x midpoint is ws center');
+
+$mid_init = $initial->{y} + int($initial->{height} / 2);
+$mid_new = $new->{y} + int($new->{height} / 2);
+is($mid_init, $mid_new, 'y midpoint is ws center');
+
+#####################################################################
+# Verify that manually floating a tiled window results in proper
+# centering.
+#####################################################################
+
+my $third = open_window;
+
+cmd 'floating enable';
+
+$new = $third->rect;
+is($initial->{x}, $new->{x}, 'x coordinates match');
+is($initial->{y}, $new->{y}, 'y coordinates match');
+
+#####################################################################
+# Create a child window of the previous window, which should result
+# in the new window being centered over the last one.
+#####################################################################
+
+my $fourth = open_window( dont_map => 1, client_leader => $third );
+$fourth->map;
+sync_with_i3;
+
+my $child = $fourth->rect;
+is($new->{x}, $child->{x}, 'x coordinates match');
+is($new->{y}, $child->{y}, 'y coordinates match');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/242-no-focus.t b/testcases/t/242-no-focus.t
new file mode 100644 (file)
index 0000000..b398343
--- /dev/null
@@ -0,0 +1,66 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Test the 'no_focus' directive.
+# Ticket: #1416
+use i3test i3_autostart => 0;
+
+#####################################################################
+# 1: open a window and check that it takes focus
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+EOT
+
+my $pid = launch_with_config($config);
+my $ws = fresh_workspace;
+my $first = open_window;
+my $focused = get_focused($ws);
+my $second = open_window;
+
+isnt(get_focused($ws), $focused, 'focus has changed');
+
+exit_gracefully($pid);
+
+#####################################################################
+# 2: open a window matched by a no_focus directive and check that
+#    it doesn't take focus
+#####################################################################
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+no_focus [instance=notme]
+EOT
+
+my $pid = launch_with_config($config);
+my $ws = fresh_workspace;
+my $first = open_window;
+my $focused = get_focused($ws);
+my $second = open_window(wm_class => 'notme');
+
+is(get_focused($ws), $focused, 'focus has not changed');
+
+exit_gracefully($pid);
+
+#####################################################################
+
+done_testing;
diff --git a/testcases/t/523-move-position-center.t b/testcases/t/523-move-position-center.t
new file mode 100644 (file)
index 0000000..6b58424
--- /dev/null
@@ -0,0 +1,63 @@
+#!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)
+#
+# Verifies that 'move position center' moves floating cons to the center of
+# the appropriate output.
+# Ticket: #1211
+# Bug still in: 4.9.1-108-g037cb31
+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 left output fake-0
+workspace right output fake-1
+EOT
+
+my $pid = launch_with_config($config);
+
+#####################################################################
+# Verify that 'move position center' on a floating window does not
+# move it to another output.
+#####################################################################
+
+cmd 'workspace left';
+
+# Sync in case focus switched outputs due to the workspace change.
+sync_with_i3;
+
+my $floating = open_floating_window;
+
+# Center the window on the left workspace
+cmd 'move position center';
+sync_with_i3;
+
+is(scalar @{get_ws('left')->{floating_nodes}}, 1, 'one floating node on left ws');
+is(scalar @{get_ws('right')->{floating_nodes}}, 0, 'no floating nodes on right ws');
+
+# Center the window on the right workspace
+cmd 'move workspace right; workspace right; move position center';
+sync_with_i3;
+
+is(scalar @{get_ws('left')->{floating_nodes}}, 0, 'no floating nodes on left ws');
+is(scalar @{get_ws('right')->{floating_nodes}}, 1, 'one floating node on right ws');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/testcases/t/524-move.t b/testcases/t/524-move.t
new file mode 100644 (file)
index 0000000..473bf23
--- /dev/null
@@ -0,0 +1,99 @@
+#!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 the behaviour of 'move <direction>' when moving containers across
+# outputs on workspaces that have non-default layouts.
+# Ticket: #1603
+# Bug still in: 4.10.1-40-g0ad097e
+use List::Util qw(first);
+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,1024x768+1024+768,1024x768+0+768
+
+workspace left-top output fake-0
+workspace right-top output fake-1
+workspace right-bottom output fake-2
+workspace left-bottom output fake-3
+
+workspace_layout stacked
+EOT
+
+my $pid = launch_with_config($config);
+
+#####################################################################
+# Create two windows in the upper left workspace and move them
+# clockwise around the workspaces until the end up where they began.
+#####################################################################
+
+cmd 'workspace left-top';
+
+my $first = open_window(wm_class => 'first');
+my $second = open_window(wm_class => 'second');
+
+is_num_children('left-top', 1, 'one child on left-top');
+is_num_children('right-top', 0, 'no children on right-top');
+
+# Move the second window into its own stacked container.
+cmd 'move right';
+is_num_children('left-top', 2, 'two children on left-top');
+
+# Move the second window onto the upper right workspace.
+cmd 'move right';
+is_num_children('left-top', 1, 'one child on left-top');
+is_num_children('right-top', 1, 'one child on right-top');
+
+# Move the first window onto the upper right workspace.
+cmd '[class="first"] move right';
+is_num_children('left-top', 0, 'no children on left-top');
+is_num_children('right-top', 2, 'two children on right-top');
+
+# Move the second window onto the lower right workspace.
+cmd '[class="second"] move down, move down';
+is_num_children('right-top', 1, 'one child on right-top');
+is_num_children('right-bottom', 1, 'two children on right-bottom');
+
+# Move the first window onto the lower right workspace.
+cmd '[class="first"] move down';
+is_num_children('right-top', 0, 'no children on right-top');
+is_num_children('right-bottom', 2, 'two children on right-bottom');
+
+# Move the second windo onto the lower left workspace.
+cmd '[class="second"] move left, move left';
+is_num_children('right-bottom', 1, 'one child on right-bottom');
+is_num_children('left-bottom', 1, 'one on left-bottom');
+
+# Move the first window onto the lower left workspace.
+cmd '[class="first"] move left';
+is_num_children('right-bottom', 0, 'no children on right-bottom');
+is_num_children('left-bottom', 2, 'two children on left-bottom');
+
+# Move the second window onto the upper left workspace.
+cmd '[class="second"] move up, move up';
+is_num_children('left-bottom', 1, 'one child on left-bottom');
+is_num_children('left-top', 1, 'one child on left-top');
+
+# Move the first window onto the upper left workspace.
+cmd '[class="first"] move up';
+is_num_children('left-bottom', 0, 'no children on left-bottom');
+is_num_children('left-top', 2, 'two children on left-top');
+
+exit_gracefully($pid);
+
+done_testing;
diff --git a/tests/queue.h b/tests/queue.h
deleted file mode 100644 (file)
index 0b3a9c0..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-/*     $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $        */
-/*     $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $       */
-
-/*
- * Copyright (c) 1991, 1993
- *     The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *     @(#)queue.h     8.5 (Berkeley) 8/20/94
- */
-
-#pragma once
-
-/*
- * This file defines five types of data structures: singly-linked lists,
- * lists, simple queues, tail queues, and circular queues.
- *
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction.  Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may only be traversed in the forward direction.
- *
- * A simple queue is headed by a pair of pointers, one the head of the
- * list and the other to the tail of the list. The elements are singly
- * linked to save space, so elements can only be removed from the
- * head of the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the
- * list. A simple queue may only be traversed in the forward direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * A circle queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the list.
- * A circle queue may be traversed in either direction, but has a more
- * complex end of list detection.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- */
-
-#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
-#define _Q_INVALIDATE(a) (a) = ((void *)-1)
-#else
-#define _Q_INVALIDATE(a)
-#endif
-
-/*
- * Singly-linked List definitions.
- */
-#define SLIST_HEAD(name, type)                      \
-    struct name {                                   \
-        struct type *slh_first; /* first element */ \
-    }
-
-#define SLIST_HEAD_INITIALIZER(head) \
-    { NULL }
-
-#define SLIST_ENTRY(type)                         \
-    struct {                                      \
-        struct type *sle_next; /* next element */ \
-    }
-
-/*
- * Singly-linked List access methods.
- */
-#define SLIST_FIRST(head) ((head)->slh_first)
-#define SLIST_END(head) NULL
-#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_FOREACH(var, head, field) \
-    for ((var) = SLIST_FIRST(head);     \
-         (var) != SLIST_END(head);      \
-         (var) = SLIST_NEXT(var, field))
-
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
-    for ((varp) = &SLIST_FIRST((head));               \
-         ((var) = *(varp)) != SLIST_END(head);        \
-         (varp) = &SLIST_NEXT((var), field))
-
-/*
- * Singly-linked List functions.
- */
-#define SLIST_INIT(head)                     \
-    {                                        \
-        SLIST_FIRST(head) = SLIST_END(head); \
-    }
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field)            \
-    do {                                                    \
-        (elm)->field.sle_next = (slistelm)->field.sle_next; \
-        (slistelm)->field.sle_next = (elm);                 \
-    } while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field)        \
-    do {                                           \
-        (elm)->field.sle_next = (head)->slh_first; \
-        (head)->slh_first = (elm);                 \
-    } while (0)
-
-#define SLIST_REMOVE_NEXT(head, elm, field)                            \
-    do {                                                               \
-        (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
-    } while (0)
-
-#define SLIST_REMOVE_HEAD(head, field)                         \
-    do {                                                       \
-        (head)->slh_first = (head)->slh_first->field.sle_next; \
-    } while (0)
-
-#define SLIST_REMOVE(head, elm, type, field)                                 \
-    do {                                                                     \
-        if ((head)->slh_first == (elm)) {                                    \
-            SLIST_REMOVE_HEAD((head), field);                                \
-        } else {                                                             \
-            struct type *curelm = (head)->slh_first;                         \
-                                                                             \
-            while (curelm->field.sle_next != (elm))                          \
-                curelm = curelm->field.sle_next;                             \
-            curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \
-            _Q_INVALIDATE((elm)->field.sle_next);                            \
-        }                                                                    \
-    } while (0)
-
-/*
- * List definitions.
- */
-#define LIST_HEAD(name, type)                      \
-    struct name {                                  \
-        struct type *lh_first; /* first element */ \
-    }
-
-#define LIST_HEAD_INITIALIZER(head) \
-    { NULL }
-
-#define LIST_ENTRY(type)                                              \
-    struct {                                                          \
-        struct type *le_next;  /* next element */                     \
-        struct type **le_prev; /* address of previous next element */ \
-    }
-
-/*
- * List access methods
- */
-#define LIST_FIRST(head) ((head)->lh_first)
-#define LIST_END(head) NULL
-#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_FOREACH(var, head, field) \
-    for ((var) = LIST_FIRST(head);     \
-         (var) != LIST_END(head);      \
-         (var) = LIST_NEXT(var, field))
-
-/*
- * List functions.
- */
-#define LIST_INIT(head)                    \
-    do {                                   \
-        LIST_FIRST(head) = LIST_END(head); \
-    } while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field)                               \
-    do {                                                                     \
-        if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)       \
-            (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next; \
-        (listelm)->field.le_next = (elm);                                    \
-        (elm)->field.le_prev = &(listelm)->field.le_next;                    \
-    } while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field)           \
-    do {                                                  \
-        (elm)->field.le_prev = (listelm)->field.le_prev;  \
-        (elm)->field.le_next = (listelm);                 \
-        *(listelm)->field.le_prev = (elm);                \
-        (listelm)->field.le_prev = &(elm)->field.le_next; \
-    } while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field)                           \
-    do {                                                             \
-        if (((elm)->field.le_next = (head)->lh_first) != NULL)       \
-            (head)->lh_first->field.le_prev = &(elm)->field.le_next; \
-        (head)->lh_first = (elm);                                    \
-        (elm)->field.le_prev = &(head)->lh_first;                    \
-    } while (0)
-
-#define LIST_REMOVE(elm, field)                                         \
-    do {                                                                \
-        if ((elm)->field.le_next != NULL)                               \
-            (elm)->field.le_next->field.le_prev = (elm)->field.le_prev; \
-        *(elm)->field.le_prev = (elm)->field.le_next;                   \
-        _Q_INVALIDATE((elm)->field.le_prev);                            \
-        _Q_INVALIDATE((elm)->field.le_next);                            \
-    } while (0)
-
-#define LIST_REPLACE(elm, elm2, field)                                     \
-    do {                                                                   \
-        if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)        \
-            (elm2)->field.le_next->field.le_prev = &(elm2)->field.le_next; \
-        (elm2)->field.le_prev = (elm)->field.le_prev;                      \
-        *(elm2)->field.le_prev = (elm2);                                   \
-        _Q_INVALIDATE((elm)->field.le_prev);                               \
-        _Q_INVALIDATE((elm)->field.le_next);                               \
-    } while (0)
-
-/*
- * Simple queue definitions.
- */
-#define SIMPLEQ_HEAD(name, type)                                \
-    struct name {                                               \
-        struct type *sqh_first; /* first element */             \
-        struct type **sqh_last; /* addr of last next element */ \
-    }
-
-#define SIMPLEQ_HEAD_INITIALIZER(head) \
-    { NULL, &(head).sqh_first }
-
-#define SIMPLEQ_ENTRY(type)                       \
-    struct {                                      \
-        struct type *sqe_next; /* next element */ \
-    }
-
-/*
- * Simple queue access methods.
- */
-#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
-#define SIMPLEQ_END(head) NULL
-#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
-#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
-
-#define SIMPLEQ_FOREACH(var, head, field) \
-    for ((var) = SIMPLEQ_FIRST(head);     \
-         (var) != SIMPLEQ_END(head);      \
-         (var) = SIMPLEQ_NEXT(var, field))
-
-/*
- * Simple queue functions.
- */
-#define SIMPLEQ_INIT(head)                     \
-    do {                                       \
-        (head)->sqh_first = NULL;              \
-        (head)->sqh_last = &(head)->sqh_first; \
-    } while (0)
-
-#define SIMPLEQ_INSERT_HEAD(head, elm, field)                    \
-    do {                                                         \
-        if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
-            (head)->sqh_last = &(elm)->field.sqe_next;           \
-        (head)->sqh_first = (elm);                               \
-    } while (0)
-
-#define SIMPLEQ_INSERT_TAIL(head, elm, field)      \
-    do {                                           \
-        (elm)->field.sqe_next = NULL;              \
-        *(head)->sqh_last = (elm);                 \
-        (head)->sqh_last = &(elm)->field.sqe_next; \
-    } while (0)
-
-#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field)                  \
-    do {                                                                 \
-        if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
-            (head)->sqh_last = &(elm)->field.sqe_next;                   \
-        (listelm)->field.sqe_next = (elm);                               \
-    } while (0)
-
-#define SIMPLEQ_REMOVE_HEAD(head, field)                                     \
-    do {                                                                     \
-        if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
-            (head)->sqh_last = &(head)->sqh_first;                           \
-    } while (0)
-
-/*
- * Tail queue definitions.
- */
-#define TAILQ_HEAD(name, type)                                  \
-    struct name {                                               \
-        struct type *tqh_first; /* first element */             \
-        struct type **tqh_last; /* addr of last next element */ \
-    }
-
-#define TAILQ_HEAD_INITIALIZER(head) \
-    { NULL, &(head).tqh_first }
-
-#define TAILQ_ENTRY(type)                                              \
-    struct {                                                           \
-        struct type *tqe_next;  /* next element */                     \
-        struct type **tqe_prev; /* address of previous next element */ \
-    }
-
-/*
- * tail queue access methods
- */
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-#define TAILQ_END(head) NULL
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-#define TAILQ_LAST(head, headname) \
-    (*(((struct headname *)((head)->tqh_last))->tqh_last))
-/* XXX */
-#define TAILQ_PREV(elm, headname, field) \
-    (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-#define TAILQ_EMPTY(head) \
-    (TAILQ_FIRST(head) == TAILQ_END(head))
-
-#define TAILQ_FOREACH(var, head, field) \
-    for ((var) = TAILQ_FIRST(head);     \
-         (var) != TAILQ_END(head);      \
-         (var) = TAILQ_NEXT(var, field))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
-    for ((var) = TAILQ_LAST(head, headname);              \
-         (var) != TAILQ_END(head);                        \
-         (var) = TAILQ_PREV(var, headname, field))
-
-/*
- * Tail queue functions.
- */
-#define TAILQ_INIT(head)                       \
-    do {                                       \
-        (head)->tqh_first = NULL;              \
-        (head)->tqh_last = &(head)->tqh_first; \
-    } while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field)                             \
-    do {                                                                \
-        if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)        \
-            (head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
-        else                                                            \
-            (head)->tqh_last = &(elm)->field.tqe_next;                  \
-        (head)->tqh_first = (elm);                                      \
-        (elm)->field.tqe_prev = &(head)->tqh_first;                     \
-    } while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field)        \
-    do {                                           \
-        (elm)->field.tqe_next = NULL;              \
-        (elm)->field.tqe_prev = (head)->tqh_last;  \
-        *(head)->tqh_last = (elm);                 \
-        (head)->tqh_last = &(elm)->field.tqe_next; \
-    } while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field)                       \
-    do {                                                                    \
-        if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)    \
-            (elm)->field.tqe_next->field.tqe_prev = &(elm)->field.tqe_next; \
-        else                                                                \
-            (head)->tqh_last = &(elm)->field.tqe_next;                      \
-        (listelm)->field.tqe_next = (elm);                                  \
-        (elm)->field.tqe_prev = &(listelm)->field.tqe_next;                 \
-    } while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field)            \
-    do {                                                    \
-        (elm)->field.tqe_prev = (listelm)->field.tqe_prev;  \
-        (elm)->field.tqe_next = (listelm);                  \
-        *(listelm)->field.tqe_prev = (elm);                 \
-        (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
-    } while (0)
-
-#define TAILQ_REMOVE(head, elm, field)                                     \
-    do {                                                                   \
-        if (((elm)->field.tqe_next) != NULL)                               \
-            (elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
-        else                                                               \
-            (head)->tqh_last = (elm)->field.tqe_prev;                      \
-        *(elm)->field.tqe_prev = (elm)->field.tqe_next;                    \
-        _Q_INVALIDATE((elm)->field.tqe_prev);                              \
-        _Q_INVALIDATE((elm)->field.tqe_next);                              \
-    } while (0)
-
-#define TAILQ_REPLACE(head, elm, elm2, field)                                 \
-    do {                                                                      \
-        if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)         \
-            (elm2)->field.tqe_next->field.tqe_prev = &(elm2)->field.tqe_next; \
-        else                                                                  \
-            (head)->tqh_last = &(elm2)->field.tqe_next;                       \
-        (elm2)->field.tqe_prev = (elm)->field.tqe_prev;                       \
-        *(elm2)->field.tqe_prev = (elm2);                                     \
-        _Q_INVALIDATE((elm)->field.tqe_prev);                                 \
-        _Q_INVALIDATE((elm)->field.tqe_next);                                 \
-    } while (0)
-
-/*
- * Circular queue definitions.
- */
-#define CIRCLEQ_HEAD(name, type)                    \
-    struct name {                                   \
-        struct type *cqh_first; /* first element */ \
-        struct type *cqh_last;  /* last element */  \
-    }
-
-#define CIRCLEQ_HEAD_INITIALIZER(head) \
-    { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
-
-#define CIRCLEQ_ENTRY(type)                           \
-    struct {                                          \
-        struct type *cqe_next; /* next element */     \
-        struct type *cqe_prev; /* previous element */ \
-    }
-
-/*
- * Circular queue access methods
- */
-#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
-#define CIRCLEQ_LAST(head) ((head)->cqh_last)
-#define CIRCLEQ_END(head) ((void *)(head))
-#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
-#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
-#define CIRCLEQ_EMPTY(head) \
-    (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
-
-#define CIRCLEQ_FOREACH(var, head, field) \
-    for ((var) = CIRCLEQ_FIRST(head);     \
-         (var) != CIRCLEQ_END(head);      \
-         (var) = CIRCLEQ_NEXT(var, field))
-
-#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
-    for ((var) = CIRCLEQ_LAST(head);              \
-         (var) != CIRCLEQ_END(head);              \
-         (var) = CIRCLEQ_PREV(var, field))
-
-/*
- * Circular queue functions.
- */
-#define CIRCLEQ_INIT(head)                     \
-    do {                                       \
-        (head)->cqh_first = CIRCLEQ_END(head); \
-        (head)->cqh_last = CIRCLEQ_END(head);  \
-    } while (0)
-
-#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field)        \
-    do {                                                       \
-        (elm)->field.cqe_next = (listelm)->field.cqe_next;     \
-        (elm)->field.cqe_prev = (listelm);                     \
-        if ((listelm)->field.cqe_next == CIRCLEQ_END(head))    \
-            (head)->cqh_last = (elm);                          \
-        else                                                   \
-            (listelm)->field.cqe_next->field.cqe_prev = (elm); \
-        (listelm)->field.cqe_next = (elm);                     \
-    } while (0)
-
-#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field)       \
-    do {                                                       \
-        (elm)->field.cqe_next = (listelm);                     \
-        (elm)->field.cqe_prev = (listelm)->field.cqe_prev;     \
-        if ((listelm)->field.cqe_prev == CIRCLEQ_END(head))    \
-            (head)->cqh_first = (elm);                         \
-        else                                                   \
-            (listelm)->field.cqe_prev->field.cqe_next = (elm); \
-        (listelm)->field.cqe_prev = (elm);                     \
-    } while (0)
-
-#define CIRCLEQ_INSERT_HEAD(head, elm, field)          \
-    do {                                               \
-        (elm)->field.cqe_next = (head)->cqh_first;     \
-        (elm)->field.cqe_prev = CIRCLEQ_END(head);     \
-        if ((head)->cqh_last == CIRCLEQ_END(head))     \
-            (head)->cqh_last = (elm);                  \
-        else                                           \
-            (head)->cqh_first->field.cqe_prev = (elm); \
-        (head)->cqh_first = (elm);                     \
-    } while (0)
-
-#define CIRCLEQ_INSERT_TAIL(head, elm, field)         \
-    do {                                              \
-        (elm)->field.cqe_next = CIRCLEQ_END(head);    \
-        (elm)->field.cqe_prev = (head)->cqh_last;     \
-        if ((head)->cqh_first == CIRCLEQ_END(head))   \
-            (head)->cqh_first = (elm);                \
-        else                                          \
-            (head)->cqh_last->field.cqe_next = (elm); \
-        (head)->cqh_last = (elm);                     \
-    } while (0)
-
-#define CIRCLEQ_REMOVE(head, elm, field)                                   \
-    do {                                                                   \
-        if ((elm)->field.cqe_next == CIRCLEQ_END(head))                    \
-            (head)->cqh_last = (elm)->field.cqe_prev;                      \
-        else                                                               \
-            (elm)->field.cqe_next->field.cqe_prev = (elm)->field.cqe_prev; \
-        if ((elm)->field.cqe_prev == CIRCLEQ_END(head))                    \
-            (head)->cqh_first = (elm)->field.cqe_next;                     \
-        else                                                               \
-            (elm)->field.cqe_prev->field.cqe_next = (elm)->field.cqe_next; \
-        _Q_INVALIDATE((elm)->field.cqe_prev);                              \
-        _Q_INVALIDATE((elm)->field.cqe_next);                              \
-    } while (0)
-
-#define CIRCLEQ_REPLACE(head, elm, elm2, field)                                    \
-    do {                                                                           \
-        if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == CIRCLEQ_END(head)) \
-            (head)->cqh_last = (elm2);                                             \
-        else                                                                       \
-            (elm2)->field.cqe_next->field.cqe_prev = (elm2);                       \
-        if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == CIRCLEQ_END(head)) \
-            (head)->cqh_first = (elm2);                                            \
-        else                                                                       \
-            (elm2)->field.cqe_prev->field.cqe_next = (elm2);                       \
-        _Q_INVALIDATE((elm)->field.cqe_prev);                                      \
-        _Q_INVALIDATE((elm)->field.cqe_next);                                      \
-    } while (0)
diff --git a/tests/swap.c b/tests/swap.c
deleted file mode 100644 (file)
index abc3b3b..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * vim:ts=4:sw=4:expandtab
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "queue.h"
-
-struct obj {
-    int abc;
-    TAILQ_ENTRY(obj) entry;
-};
-
-TAILQ_HEAD(objhead, obj) head;
-
-void dump() {
-    struct obj *e;
-    printf("dump:\n");
-    e = TAILQ_FIRST(&head);
-    printf("first: %d\n", e->abc);
-    e = TAILQ_LAST(&head, objhead);
-    printf("last: %d\n", e->abc);
-    TAILQ_FOREACH(e, &head, entry) {
-        printf("  %d\n", e->abc);
-    }
-    printf("again, but reverse:\n");
-    TAILQ_FOREACH_REVERSE(e, &head, objhead, entry) {
-        printf("  %d\n", e->abc);
-    }
-    printf("done\n\n");
-}
-
-#define TAILQ_SWAP(first, second, head, field)                                     \
-    do {                                                                           \
-        *((first)->field.tqe_prev) = (second);                                     \
-        (second)->field.tqe_prev = (first)->field.tqe_prev;                        \
-        (first)->field.tqe_prev = &((second)->field.tqe_next);                     \
-        (first)->field.tqe_next = (second)->field.tqe_next;                        \
-        if ((second)->field.tqe_next)                                              \
-            (second)->field.tqe_next->field.tqe_prev = &((first)->field.tqe_next); \
-        (second)->field.tqe_next = first;                                          \
-        if ((head)->tqh_last == &((second)->field.tqe_next))                       \
-            (head)->tqh_last = &((first)->field.tqe_next);                         \
-    } while (0)
-
-void _TAILQ_SWAP(struct obj *first, struct obj *second, struct objhead *head) {
-    struct obj **tqe_prev = first->entry.tqe_prev;
-    *tqe_prev = second;
-
-    second->entry.tqe_prev = first->entry.tqe_prev;
-
-    first->entry.tqe_prev = &(second->entry.tqe_next);
-
-    first->entry.tqe_next = second->entry.tqe_next;
-
-    if (second->entry.tqe_next) {
-        struct obj *tqe_next = second->entry.tqe_next;
-        tqe_next->entry.tqe_prev = &(first->entry.tqe_next);
-    }
-
-    second->entry.tqe_next = first;
-
-    if (head->tqh_last == &(second->entry.tqe_next))
-        head->tqh_last = &(first->entry.tqe_next);
-}
-
-int main() {
-    printf("hello\n");
-
-    TAILQ_INIT(&head);
-
-    struct obj first;
-    first.abc = 123;
-
-    struct obj second;
-    second.abc = 456;
-
-    struct obj third;
-    third.abc = 789;
-
-    struct obj fourth;
-    fourth.abc = 999;
-
-    struct obj fifth;
-    fifth.abc = 5555;
-
-    /*
-     * ************************************************
-     */
-    printf("swapping first two elements:\n");
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-
-    dump();
-
-    TAILQ_SWAP(&first, &second, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("swapping last two elements:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&first, &second, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list 2:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list, swap, then insert:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    TAILQ_INSERT_AFTER(&head, &third, &fifth, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list, swap, then append:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    TAILQ_INSERT_TAIL(&head, &fifth, entry);
-
-    dump();
-
-    /*
-     * ************************************************
-     */
-    printf("longer list, swap, then remove:\n");
-
-    TAILQ_INIT(&head);
-
-    TAILQ_INSERT_TAIL(&head, &first, entry);
-    TAILQ_INSERT_TAIL(&head, &second, entry);
-    TAILQ_INSERT_TAIL(&head, &third, entry);
-    TAILQ_INSERT_TAIL(&head, &fourth, entry);
-
-    dump();
-
-    TAILQ_SWAP(&second, &third, &head, entry);
-
-    dump();
-
-    TAILQ_REMOVE(&head, &second, entry);
-
-    dump();
-}
diff --git a/tests/test_table.c b/tests/test_table.c
deleted file mode 100644 (file)
index 554be3e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <stdio.h>
-#include "table.h"
-
-void print_table() {
-    int r, c;
-    printf("printing table...\n");
-    for (c = 0; c < table_dims.x; c++)
-        for (r = 0; r < table_dims.y; r++)
-            printf("table[%d][%d] = %p\n", c, r, table[c][r]);
-    printf("done\n");
-}
-
-int main() {
-    printf("table.c tests\n");
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    init_table();
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    print_table();
-
-    printf("expand_table_cols()\n");
-    expand_table_cols();
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    print_table();
-
-    printf("expand_table_rows()\n");
-    expand_table_rows();
-    printf("table_dimensions = %d, %d\n", table_dims.x, table_dims.y);
-    print_table();
-}