From e77b7e616765e7c190aefbbe5a3b1e377b1eb651 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 15 Jun 2014 19:15:44 +0200 Subject: [PATCH 001/103] debian: update changelog --- debian/changelog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/debian/changelog b/debian/changelog index ac79c8bb..5c4341fc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,8 @@ -i3-wm (4.7.3-1) unstable; urgency=low +i3-wm (4.8-1) unstable; urgency=medium - * NOT YET RELEASED + * New upstream release. - -- Michael Stapelberg Thu, 23 Jan 2014 23:11:48 +0100 + -- Michael Stapelberg Sun, 15 Jun 2014 19:15:29 +0200 i3-wm (4.7.2-1) unstable; urgency=low From d75c5a18d79f52e5b81a0487e149dab74e76227b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 15 Jun 2014 19:36:54 +0200 Subject: [PATCH 002/103] debian: install i3-save-tree(1) manpage --- debian/i3-wm.manpages | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/i3-wm.manpages b/debian/i3-wm.manpages index 58569b87..1953b6a8 100644 --- a/debian/i3-wm.manpages +++ b/debian/i3-wm.manpages @@ -9,4 +9,5 @@ man/i3-sensible-pager.1 man/i3-sensible-editor.1 man/i3-sensible-terminal.1 man/i3-dmenu-desktop.1 +man/i3-save-tree.1 man/i3bar.1 From 29240445056faf84326f07f65e970cb4f66bd939 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 15 Jun 2014 19:37:03 +0200 Subject: [PATCH 003/103] debian: bump standards-version to 3.9.5 (no changes necessary) --- debian/changelog | 1 + debian/control | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 5c4341fc..12214cbc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ i3-wm (4.8-1) unstable; urgency=medium * New upstream release. + * Bump standards-version to 3.9.5 (no changes necessary) -- Michael Stapelberg Sun, 15 Jun 2014 19:15:29 +0200 diff --git a/debian/control b/debian/control index 97b11a5e..b36d5db4 100644 --- a/debian/control +++ b/debian/control @@ -21,7 +21,7 @@ Build-Depends: debhelper (>= 7.0.50~), libcairo2-dev, libpango1.0-dev, libpod-simple-perl -Standards-Version: 3.9.4 +Standards-Version: 3.9.5 Homepage: http://i3wm.org/ Package: i3 From d8328c78962bd5bddae1edddf3c9f34018b7d711 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 15 Jun 2014 19:37:38 +0200 Subject: [PATCH 004/103] debian: add placeholder changelog entry --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 12214cbc..5e3d174b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +i3-wm (4.8.1-1) unstable; urgency=medium + + * NOT YET RELEASED + + -- Michael Stapelberg Sun, 15 Jun 2014 19:37:32 +0200 + i3-wm (4.8-1) unstable; urgency=medium * New upstream release. From fef46de28337afe790dd724de5f76c4999502f41 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 13 Jun 2014 20:06:22 -0400 Subject: [PATCH 005/103] docs: fix ipc library list formatting --- docs/ipc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/ipc b/docs/ipc index c9768705..4c82d40d 100644 --- a/docs/ipc +++ b/docs/ipc @@ -754,15 +754,14 @@ all this on your own). This list names some (if you wrote one, please let me know): C:: - i3 includes a headerfile +i3/ipc.h+ which provides you all constants. - - https://github.com/acrisci/i3ipc-glib + * i3 includes a headerfile +i3/ipc.h+ which provides you all constants. + * https://github.com/acrisci/i3ipc-glib Go:: * https://github.com/proxypoke/i3ipc JavaScript:: * https://github.com/acrisci/i3ipc-gjs Lua:: - * https:/github.com/acrisci/i3ipc-lua + * https://github.com/acrisci/i3ipc-lua Perl:: * https://metacpan.org/module/AnyEvent::I3 Python:: @@ -770,4 +769,4 @@ Python:: * https://github.com/whitelynx/i3ipc (not maintained) * https://github.com/ziberna/i3-py (not maintained) Ruby:: - http://github.com/badboy/i3-ipc + * http://github.com/badboy/i3-ipc From f4a82535937787d25405bff56a1b2aeb6a209bed Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 15 Jun 2014 00:21:33 -0400 Subject: [PATCH 006/103] userguide: document exit confirmation dialog In the section "Exiting i3", document the confirmation dialog (nagbar) that will show when the user presses the default keys to exit in such a way to hint that it is configurable. --- docs/userguide | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/userguide b/docs/userguide index ff46d62f..681c19f1 100644 --- a/docs/userguide +++ b/docs/userguide @@ -153,6 +153,7 @@ to upgrade to a newer version of i3) you can use +$mod+Shift+r+. === Exiting i3 To cleanly exit i3 without killing your X server, you can use +$mod+Shift+e+. +By default, a dialog will ask you to confirm if you really want to quit. === Floating From 4211274fcd028a8e33a084e5695290ae0e9f3020 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 19 Jun 2014 11:17:46 +0200 Subject: [PATCH 007/103] =?UTF-8?q?clang-format:=20don=E2=80=99t=20define?= =?UTF-8?q?=20ForeachMacros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise clang-formatting **/*.h breaks include/queue.h --- .clang-format | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-format b/.clang-format index 57948c31..1d840132 100644 --- a/.clang-format +++ b/.clang-format @@ -7,5 +7,4 @@ AlwaysBreakBeforeMultilineStrings: false IndentWidth: 4 PointerBindsToType: false ColumnLimit: 0 -ForEachMacros: [ TAILQ_FOREACH, TAILQ_FOREACH_REVERSE, SLIST_FOREACH, CIRCLEQ_FOREACH, CIRCLEQ_FOREACH_REVERSE, NODES_FOREACH, NODES_FOREACH_REVERSE ] SpaceBeforeParens: ControlStatements From 4c06e7a573e450329212b28a3b8f4a5e89b326d4 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 19 Jun 2014 11:20:32 +0200 Subject: [PATCH 008/103] clang-format-3.5 **/*.h **/*.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should be the last commit that formats a big bunch of files. From here on, whenever I merge patches, I’ll run clang-format like described in the title. --- i3-config-wizard/xcb.h | 4 +- i3-input/i3-input.h | 12 +- i3-nagbar/i3-nagbar.h | 14 +- i3bar/include/common.h | 6 +- i3bar/include/config.h | 31 +- i3bar/include/ipc.h | 2 +- i3bar/include/outputs.h | 22 +- i3bar/include/trayclients.h | 8 +- i3bar/include/util.h | 69 ++-- i3bar/include/workspaces.h | 20 +- i3bar/include/xcb.h | 10 +- i3bar/src/child.c | 2 +- i3bar/src/ipc.c | 2 +- i3bar/src/outputs.c | 2 +- i3bar/src/workspaces.c | 4 +- i3bar/src/xcb.c | 62 ++-- include/bindings.h | 2 +- include/con.h | 1 - include/config.h | 10 +- include/config_directives.h | 2 +- include/data.h | 59 +-- include/floating.h | 20 +- include/i3.h | 2 +- include/i3/ipc.h | 48 +-- include/ipc.h | 20 +- include/libi3.h | 28 +- include/log.h | 6 +- include/queue.h | 704 ++++++++++++++++++------------------ include/sd-daemon.h | 24 +- include/startup.h | 2 +- include/util.h | 56 +-- include/workspace.h | 9 +- include/xcb.h | 45 ++- include/yajl_utils.h | 4 +- src/assignments.c | 4 +- src/bindings.c | 16 +- src/click.c | 4 +- src/commands.c | 104 +++--- src/con.c | 58 +-- src/config.c | 6 +- src/config_directives.c | 2 +- src/config_parser.c | 8 +- src/ewmh.c | 4 +- src/fake_outputs.c | 6 +- src/floating.c | 4 +- src/handlers.c | 16 +- src/ipc.c | 30 +- src/load_layout.c | 4 +- src/main.c | 6 +- src/manage.c | 20 +- src/match.c | 4 +- src/output.c | 6 +- src/randr.c | 66 ++-- src/render.c | 24 +- src/restore_layout.c | 16 +- src/scratchpad.c | 8 +- src/sighandler.c | 2 +- src/startup.c | 6 +- src/tree.c | 14 +- src/workspace.c | 78 ++-- src/x.c | 50 +-- src/xinerama.c | 6 +- tests/queue.h | 681 +++++++++++++++++----------------- tests/swap.c | 4 +- 64 files changed, 1317 insertions(+), 1252 deletions(-) diff --git a/i3-config-wizard/xcb.h b/i3-config-wizard/xcb.h index d51f979a..f3e204e8 100644 --- a/i3-config-wizard/xcb.h +++ b/i3-config-wizard/xcb.h @@ -1,8 +1,8 @@ #pragma once /* from X11/keysymdef.h */ -#define XCB_NUM_LOCK 0xff7f +#define XCB_NUM_LOCK 0xff7f -#define xmacro(atom) xcb_atom_t A_ ## atom; +#define xmacro(atom) xcb_atom_t A_##atom; #include "atoms.xmacro" #undef xmacro diff --git a/i3-input/i3-input.h b/i3-input/i3-input.h index 104296cf..fcf9edc3 100644 --- a/i3-input/i3-input.h +++ b/i3-input/i3-input.h @@ -3,12 +3,12 @@ #include #define die(...) errx(EXIT_FAILURE, __VA_ARGS__); -#define FREE(pointer) do { \ +#define FREE(pointer) \ + do { \ if (pointer != NULL) { \ - free(pointer); \ - pointer = NULL; \ - } \ -} \ -while (0) + free(pointer); \ + pointer = NULL; \ + } \ + } while (0) extern xcb_window_t root; diff --git a/i3-nagbar/i3-nagbar.h b/i3-nagbar/i3-nagbar.h index 9aac709b..fc076d72 100644 --- a/i3-nagbar/i3-nagbar.h +++ b/i3-nagbar/i3-nagbar.h @@ -3,15 +3,15 @@ #include #define die(...) errx(EXIT_FAILURE, __VA_ARGS__); -#define FREE(pointer) do { \ +#define FREE(pointer) \ + do { \ if (pointer != NULL) { \ - free(pointer); \ - pointer = NULL; \ - } \ -} \ -while (0) + free(pointer); \ + pointer = NULL; \ + } \ + } while (0) -#define xmacro(atom) xcb_atom_t A_ ## atom; +#define xmacro(atom) xcb_atom_t A_##atom; #include "atoms.xmacro" #undef xmacro diff --git a/i3bar/include/common.h b/i3bar/include/common.h index d63780dc..31ac5161 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -15,9 +15,9 @@ typedef struct rect_t rect; -struct ev_loop* main_loop; -char *statusline; -char *statusline_buffer; +struct ev_loop *main_loop; +char *statusline; +char *statusline_buffer; struct rect_t { int x; diff --git a/i3bar/include/config.h b/i3bar/include/config.h index a2c16887..730d3ef0 100644 --- a/i3bar/include/config.h +++ b/i3bar/include/config.h @@ -18,27 +18,30 @@ typedef enum { } position_t; /* Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */ -typedef enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } bar_display_mode_t; +typedef enum { M_DOCK = 0, + M_HIDE = 1, + M_INVISIBLE = 2 } bar_display_mode_t; typedef struct config_t { - int modifier; - position_t position; - int verbose; + int modifier; + position_t position; + int verbose; struct xcb_color_strings_t colors; - bool disable_binding_mode_indicator; - bool disable_ws; - bool strip_ws_numbers; - char *bar_id; - char *command; - char *fontname; - char *tray_output; - int num_outputs; - char **outputs; + bool disable_binding_mode_indicator; + bool disable_ws; + bool strip_ws_numbers; + char *bar_id; + char *command; + char *fontname; + char *tray_output; + int num_outputs; + char **outputs; bar_display_mode_t hide_on_modifier; /* The current hidden_state of the bar, which indicates whether it is hidden or shown */ - enum { S_HIDE = 0, S_SHOW = 1 } hidden_state; + enum { S_HIDE = 0, + S_SHOW = 1 } hidden_state; } config_t; config_t config; diff --git a/i3bar/include/ipc.h b/i3bar/include/ipc.h index 5de23878..c357be89 100644 --- a/i3bar/include/ipc.h +++ b/i3bar/include/ipc.h @@ -29,7 +29,7 @@ void destroy_connection(void); * type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information) * */ -int i3_send_msg(uint32_t type, const char* payload); +int i3_send_msg(uint32_t type, const char *payload); /* * Subscribe to all the i3-events, we need diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 9f6add11..e0beae89 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -16,7 +16,7 @@ typedef struct i3_output i3_output; SLIST_HEAD(outputs_head, i3_output); -struct outputs_head *outputs; +struct outputs_head* outputs; /* * Start parsing the received json-string @@ -37,18 +37,18 @@ void init_outputs(void); i3_output* get_output_by_name(char* name); struct i3_output { - char* name; /* Name of the output */ - bool active; /* If the output is active */ - bool primary; /* If it is the primary output */ - int ws; /* The number of the currently visible ws */ - rect rect; /* The rect (relative to the root-win) */ + char* name; /* Name of the output */ + bool active; /* If the output is active */ + bool primary; /* If it is the primary output */ + int ws; /* The number of the currently visible ws */ + rect rect; /* The rect (relative to the root-win) */ - xcb_window_t bar; /* The id of the bar of the output */ - xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */ - xcb_gcontext_t bargc; /* The graphical context of the bar */ + xcb_window_t bar; /* The id of the bar of the output */ + xcb_pixmap_t buffer; /* An extra pixmap for double-buffering */ + xcb_gcontext_t bargc; /* The graphical context of the bar */ - struct ws_head *workspaces; /* The workspaces on this output */ - struct tc_head *trayclients; /* The tray clients on this output */ + struct ws_head* workspaces; /* The workspaces on this output */ + struct tc_head* trayclients; /* The tray clients on this output */ SLIST_ENTRY(i3_output) slist; /* Pointer for the SLIST-Macro */ }; diff --git a/i3bar/include/trayclients.h b/i3bar/include/trayclients.h index 7a7e537e..1720ec3b 100644 --- a/i3bar/include/trayclients.h +++ b/i3bar/include/trayclients.h @@ -14,9 +14,9 @@ typedef struct trayclient trayclient; TAILQ_HEAD(tc_head, trayclient); struct trayclient { - xcb_window_t win; /* The window ID of the tray client */ - bool mapped; /* Whether this window is mapped */ - int xe_version; /* The XEMBED version supported by the client */ + xcb_window_t win; /* The window ID of the tray client */ + bool mapped; /* Whether this window is mapped */ + int xe_version; /* The XEMBED version supported by the client */ - TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */ + TAILQ_ENTRY(trayclient) tailq; /* Pointer for the TAILQ-Macro */ }; diff --git a/i3bar/include/util.h b/i3bar/include/util.h index 9ffd4467..cb0903c1 100644 --- a/i3bar/include/util.h +++ b/i3bar/include/util.h @@ -11,55 +11,60 @@ /* Get the maximum/minimum of x and y */ #undef MAX -#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) #undef MIN -#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) #define STARTS_WITH(string, len, needle) ((len >= strlen(needle)) && strncasecmp(string, needle, strlen(needle)) == 0) /* Securely free p */ -#define FREE(p) do { \ - if (p != NULL) { \ - free(p); \ - p = NULL; \ - } \ -} while (0) +#define FREE(p) \ + do { \ + if (p != NULL) { \ + free(p); \ + p = NULL; \ + } \ + } while (0) /* Securely fee single-linked list */ -#define FREE_SLIST(l, type) do { \ - type *walk = SLIST_FIRST(l); \ - while (!SLIST_EMPTY(l)) { \ - SLIST_REMOVE_HEAD(l, slist); \ - FREE(walk); \ - walk = SLIST_FIRST(l); \ - } \ -} while (0) +#define FREE_SLIST(l, type) \ + do { \ + type *walk = SLIST_FIRST(l); \ + while (!SLIST_EMPTY(l)) { \ + SLIST_REMOVE_HEAD(l, slist); \ + FREE(walk); \ + walk = SLIST_FIRST(l); \ + } \ + } while (0) /* Securely fee tail-queues */ -#define FREE_TAILQ(l, type) do { \ - type *walk = TAILQ_FIRST(l); \ - while (!TAILQ_EMPTY(l)) { \ - TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \ - FREE(walk); \ - walk = TAILQ_FIRST(l); \ - } \ -} while (0) +#define FREE_TAILQ(l, type) \ + do { \ + type *walk = TAILQ_FIRST(l); \ + while (!TAILQ_EMPTY(l)) { \ + TAILQ_REMOVE(l, TAILQ_FIRST(l), tailq); \ + FREE(walk); \ + walk = TAILQ_FIRST(l); \ + } \ + } while (0) #if defined(DLOG) #undef DLOG #endif /* Use cool logging-macros */ -#define DLOG(fmt, ...) do { \ - if (config.verbose) { \ - printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ - } \ -} while(0) +#define DLOG(fmt, ...) \ + do { \ + if (config.verbose) { \ + printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ + } \ + } while (0) /* We will include libi3.h which define its own version of ELOG. * We want *our* version, so we undef the libi3 one. */ #if defined(ELOG) #undef ELOG #endif -#define ELOG(fmt, ...) do { \ - fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ -} while(0) +#define ELOG(fmt, ...) \ + do { \ + fprintf(stderr, "[%s:%d] ERROR: " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ + } while (0) diff --git a/i3bar/include/workspaces.h b/i3bar/include/workspaces.h index d3d23c84..b9cefa84 100644 --- a/i3bar/include/workspaces.h +++ b/i3bar/include/workspaces.h @@ -30,15 +30,15 @@ void parse_workspaces_json(char *json); void free_workspaces(void); struct i3_ws { - int num; /* The internal number of the ws */ - char *canonical_name; /* The true name of the ws according to the ipc */ - i3String *name; /* The name of the ws that is displayed on the bar */ - int name_width; /* The rendered width of the name */ - bool visible; /* If the ws is currently visible on an output */ - bool focused; /* If the ws is currently focused */ - bool urgent; /* If the urgent-hint of the ws is set */ - rect rect; /* The rect of the ws (not used (yet)) */ - struct i3_output *output; /* The current output of the ws */ + int num; /* The internal number of the ws */ + char *canonical_name; /* The true name of the ws according to the ipc */ + i3String *name; /* The name of the ws that is displayed on the bar */ + int name_width; /* The rendered width of the name */ + bool visible; /* If the ws is currently visible on an output */ + bool focused; /* If the ws is currently focused */ + bool urgent; /* If the urgent-hint of the ws is set */ + rect rect; /* The rect of the ws (not used (yet)) */ + struct i3_output *output; /* The current output of the ws */ - TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */ + TAILQ_ENTRY(i3_ws) tailq; /* Pointer for the TAILQ-Macro */ }; diff --git a/i3bar/include/xcb.h b/i3bar/include/xcb.h index 2740f330..32b52f4d 100644 --- a/i3bar/include/xcb.h +++ b/i3bar/include/xcb.h @@ -18,11 +18,11 @@ #define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 #define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1 -#define SYSTEM_TRAY_REQUEST_DOCK 0 -#define SYSTEM_TRAY_BEGIN_MESSAGE 1 -#define SYSTEM_TRAY_CANCEL_MESSAGE 2 -#define XEMBED_MAPPED (1 << 0) -#define XEMBED_EMBEDDED_NOTIFY 0 +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_EMBEDDED_NOTIFY 0 struct xcb_color_strings_t { char *bar_fg; diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 5867ce4a..2bf65510 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -243,7 +243,7 @@ static int stdin_end_map(void *context) { static int stdin_end_array(void *context) { DLOG("dumping statusline:\n"); struct status_block *current; - TAILQ_FOREACH (current, &statusline_head, blocks) { + TAILQ_FOREACH(current, &statusline_head, blocks) { DLOG("full_text = %s\n", i3string_as_utf8(current->full_text)); DLOG("color = %s\n", current->color); } diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c index 6ff83979..3eb50beb 100644 --- a/i3bar/src/ipc.c +++ b/i3bar/src/ipc.c @@ -67,7 +67,7 @@ void got_output_reply(char *reply) { reconfig_windows(false); i3_output *o_walk; - SLIST_FOREACH (o_walk, outputs, slist) { + SLIST_FOREACH(o_walk, outputs, slist) { kick_tray_clients(o_walk); } diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index 5508a5a2..02b718d3 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -296,7 +296,7 @@ i3_output *get_output_by_name(char *name) { if (name == NULL) { return NULL; } - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!strcmp(walk->name, name)) { break; } diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index 91307b0c..e8184d40 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -266,9 +266,9 @@ void free_workspaces(void) { } i3_ws *ws_walk; - SLIST_FOREACH (outputs_walk, outputs, slist) { + SLIST_FOREACH(outputs_walk, outputs, slist) { if (outputs_walk->workspaces != NULL && !TAILQ_EMPTY(outputs_walk->workspaces)) { - TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) { + TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { I3STRING_FREE(ws_walk->name); FREE(ws_walk->canonical_name); } diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index a29f9094..763942af 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -128,7 +128,7 @@ void refresh_statusline(void) { statusline_width = 0; /* Predict the text width of all blocks (in pixels). */ - TAILQ_FOREACH (block, &statusline_head, blocks) { + TAILQ_FOREACH(block, &statusline_head, blocks) { if (i3string_get_num_bytes(block->full_text) == 0) continue; @@ -173,7 +173,7 @@ void refresh_statusline(void) { /* Draw the text of each block. */ uint32_t x = 0; - TAILQ_FOREACH (block, &statusline_head, blocks) { + TAILQ_FOREACH(block, &statusline_head, blocks) { if (i3string_get_num_bytes(block->full_text) == 0) continue; @@ -206,7 +206,7 @@ void hide_bars(void) { } i3_output *walk; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) { continue; } @@ -231,7 +231,7 @@ void unhide_bars(void) { cont_child(); - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (walk->bar == XCB_NONE) { continue; } @@ -303,7 +303,7 @@ void handle_button(xcb_button_press_event_t *event) { /* Determine, which bar was clicked */ i3_output *walk; xcb_window_t bar = event->event; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (walk->bar == bar) { break; } @@ -315,7 +315,7 @@ void handle_button(xcb_button_press_event_t *event) { } /* TODO: Move this to extern get_ws_for_output() */ - TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) { + TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) { if (cur_ws->visible) { break; } @@ -338,7 +338,7 @@ void handle_button(xcb_button_press_event_t *event) { /* First calculate width of tray area */ trayclient *trayclient; int tray_width = 0; - TAILQ_FOREACH_REVERSE (trayclient, walk->trayclients, tc_head, tailq) { + TAILQ_FOREACH_REVERSE(trayclient, walk->trayclients, tc_head, tailq) { if (!trayclient->mapped) continue; tray_width += (font.height + logical_px(2)); @@ -351,7 +351,7 @@ void handle_button(xcb_button_press_event_t *event) { if (x >= 0) { struct status_block *block; - TAILQ_FOREACH (block, &statusline_head, blocks) { + TAILQ_FOREACH(block, &statusline_head, blocks) { last_block_x = block_x; block_x += block->width + block->x_offset + block->x_append; @@ -387,7 +387,7 @@ void handle_button(xcb_button_press_event_t *event) { break; case 1: /* Check if this event regards a workspace button */ - TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) { + TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) { DLOG("x = %d\n", x); if (x >= 0 && x < cur_ws->name_width + logical_px(10)) { break; @@ -398,7 +398,7 @@ void handle_button(xcb_button_press_event_t *event) { /* Otherwise, focus our currently visible workspace if it is not * already focused */ if (cur_ws == NULL) { - TAILQ_FOREACH (cur_ws, walk->workspaces, tailq) { + TAILQ_FOREACH(cur_ws, walk->workspaces, tailq) { if (cur_ws->visible && !cur_ws->focused) break; } @@ -455,12 +455,12 @@ void handle_button(xcb_button_press_event_t *event) { static void configure_trayclients(void) { trayclient *trayclient; i3_output *output; - SLIST_FOREACH (output, outputs, slist) { + SLIST_FOREACH(output, outputs, slist) { if (!output->active) continue; int clients = 0; - TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) { + TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) { if (!trayclient->mapped) continue; clients++; @@ -544,7 +544,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { DLOG("X window %08x requested docking\n", client); i3_output *walk, *output = NULL; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) continue; if (config.tray_output) { @@ -562,7 +562,7 @@ static void handle_client_message(xcb_client_message_event_t *event) { if (output == NULL && config.tray_output && strcasecmp("primary", config.tray_output) == 0) { - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) continue; DLOG("Falling back to output %s because no primary output is configured\n", walk->name); @@ -650,12 +650,12 @@ static void handle_destroy_notify(xcb_destroy_notify_event_t *event) { DLOG("DestroyNotify for window = %08x, event = %08x\n", event->window, event->event); i3_output *walk; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) continue; DLOG("checking output %s\n", walk->name); trayclient *trayclient; - TAILQ_FOREACH (trayclient, walk->trayclients, tailq) { + TAILQ_FOREACH(trayclient, walk->trayclients, tailq) { if (trayclient->win != event->window) continue; @@ -679,12 +679,12 @@ static void handle_map_notify(xcb_map_notify_event_t *event) { DLOG("MapNotify for window = %08x, event = %08x\n", event->window, event->event); i3_output *walk; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) continue; DLOG("checking output %s\n", walk->name); trayclient *trayclient; - TAILQ_FOREACH (trayclient, walk->trayclients, tailq) { + TAILQ_FOREACH(trayclient, walk->trayclients, tailq) { if (trayclient->win != event->window) continue; @@ -707,12 +707,12 @@ static void handle_unmap_notify(xcb_unmap_notify_event_t *event) { DLOG("UnmapNotify for window = %08x, event = %08x\n", event->window, event->event); i3_output *walk; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) continue; DLOG("checking output %s\n", walk->name); trayclient *trayclient; - TAILQ_FOREACH (trayclient, walk->trayclients, tailq) { + TAILQ_FOREACH(trayclient, walk->trayclients, tailq) { if (trayclient->win != event->window) continue; @@ -739,11 +739,11 @@ static void handle_property_notify(xcb_property_notify_event_t *event) { DLOG("xembed_info updated\n"); trayclient *trayclient = NULL, *walk; i3_output *o_walk; - SLIST_FOREACH (o_walk, outputs, slist) { + SLIST_FOREACH(o_walk, outputs, slist) { if (!o_walk->active) continue; - TAILQ_FOREACH (walk, o_walk->trayclients, tailq) { + TAILQ_FOREACH(walk, o_walk->trayclients, tailq) { if (walk->win != event->window) continue; trayclient = walk; @@ -802,12 +802,12 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { trayclient *trayclient; i3_output *output; - SLIST_FOREACH (output, outputs, slist) { + SLIST_FOREACH(output, outputs, slist) { if (!output->active) continue; int clients = 0; - TAILQ_FOREACH_REVERSE (trayclient, output->trayclients, tc_head, tailq) { + TAILQ_FOREACH_REVERSE(trayclient, output->trayclients, tc_head, tailq) { if (!trayclient->mapped) continue; clients++; @@ -1280,7 +1280,7 @@ void init_tray_colors(void) { void clean_xcb(void) { i3_output *o_walk; free_workspaces(); - SLIST_FOREACH (o_walk, outputs, slist) { + SLIST_FOREACH(o_walk, outputs, slist) { destroy_window(o_walk); FREE(o_walk->trayclients); FREE(o_walk->workspaces); @@ -1434,7 +1434,7 @@ void reconfig_windows(bool redraw_bars) { static bool tray_configured = false; i3_output *walk; - SLIST_FOREACH (walk, outputs, slist) { + SLIST_FOREACH(walk, outputs, slist) { if (!walk->active) { /* If an output is not active, we destroy its bar */ /* FIXME: Maybe we rather want to unmap? */ @@ -1596,7 +1596,7 @@ void reconfig_windows(bool redraw_bars) { * VGA-1 but output == [HDMI-1]). */ i3_output *output; - SLIST_FOREACH (output, outputs, slist) { + SLIST_FOREACH(output, outputs, slist) { if (strcasecmp(output->name, tray_output) == 0 || (strcasecmp(tray_output, "primary") == 0 && output->primary)) { init_tray(); @@ -1685,7 +1685,7 @@ void draw_bars(bool unhide) { refresh_statusline(); i3_output *outputs_walk; - SLIST_FOREACH (outputs_walk, outputs, slist) { + SLIST_FOREACH(outputs_walk, outputs, slist) { if (!outputs_walk->active) { DLOG("Output %s inactive, skipping...\n", outputs_walk->name); continue; @@ -1715,7 +1715,7 @@ void draw_bars(bool unhide) { * position */ trayclient *trayclient; int traypx = 0; - TAILQ_FOREACH (trayclient, outputs_walk->trayclients, tailq) { + TAILQ_FOREACH(trayclient, outputs_walk->trayclients, tailq) { if (!trayclient->mapped) continue; /* We assume the tray icons are quadratic (we use the font @@ -1737,7 +1737,7 @@ void draw_bars(bool unhide) { if (!config.disable_ws) { i3_ws *ws_walk; - TAILQ_FOREACH (ws_walk, outputs_walk->workspaces, tailq) { + TAILQ_FOREACH(ws_walk, outputs_walk->workspaces, tailq) { DLOG("Drawing Button for WS %s at x = %d, len = %d\n", i3string_as_utf8(ws_walk->name), i, ws_walk->name_width); uint32_t fg_color = colors.inactive_ws_fg; @@ -1853,7 +1853,7 @@ void draw_bars(bool unhide) { */ void redraw_bars(void) { i3_output *outputs_walk; - SLIST_FOREACH (outputs_walk, outputs, slist) { + SLIST_FOREACH(outputs_walk, outputs, slist) { if (!outputs_walk->active) { continue; } diff --git a/include/bindings.h b/include/bindings.h index e51f5e99..8b73e99e 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -24,7 +24,7 @@ const char *DEFAULT_BINDING_MODE; * */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, - const char *release, const char *command, const char *mode); + const char *release, const char *command, const char *mode); /** * Grab the bound keys (tell X to send us keypress events for those keycodes) diff --git a/include/con.h b/include/con.h index b8fd60ad..184bc918 100644 --- a/include/con.h +++ b/include/con.h @@ -18,7 +18,6 @@ */ Con *con_new_skeleton(Con *parent, i3Window *window); - /* A wrapper for con_new_skeleton, to retain the old con_new behaviour * */ diff --git a/include/config.h b/include/config.h index 71b37a80..22110eba 100644 --- a/include/config.h +++ b/include/config.h @@ -241,10 +241,13 @@ struct Barconfig { char *socket_path; /** Bar display mode (hide unless modifier is pressed or show in dock mode or always hide in invisible mode) */ - enum { M_DOCK = 0, M_HIDE = 1, M_INVISIBLE = 2 } mode; + enum { M_DOCK = 0, + M_HIDE = 1, + M_INVISIBLE = 2 } mode; /* The current hidden_state of the bar, which indicates whether it is hidden or shown */ - enum { S_HIDE = 0, S_SHOW = 1 } hidden_state; + enum { S_HIDE = 0, + S_SHOW = 1 } hidden_state; /** Bar modifier (to show bar when in hide mode). */ enum { @@ -259,7 +262,8 @@ struct Barconfig { } modifier; /** Bar position (bottom by default). */ - enum { P_BOTTOM = 0, P_TOP = 1 } position; + enum { P_BOTTOM = 0, + P_TOP = 1 } position; /** Command that should be run to execute i3bar, give a full path if i3bar is not * in your $PATH. diff --git a/include/config_directives.h b/include/config_directives.h index 9dedad15..7fdca8c8 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -24,7 +24,7 @@ uint32_t modifiers_from_str(const char *str); * using 'call cfg_foo()' in parser-specs/.*.spec. Useful so that we don’t need * to repeat the definition all the time. */ #define CFGFUN(name, ...) \ - void cfg_ ## name (I3_CFG, ## __VA_ARGS__ ) + void cfg_##name(I3_CFG, ##__VA_ARGS__) /* The following functions are called by the config parser, see * parser-specs/config.spec. They get the parsed parameters and store them in diff --git a/include/data.h b/include/data.h index 6ac228ec..1a67685d 100644 --- a/include/data.h +++ b/include/data.h @@ -47,34 +47,42 @@ typedef struct Match Match; typedef struct Assignment Assignment; typedef struct Window i3Window; - /****************************************************************************** * Helper types *****************************************************************************/ -typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t; -typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t; -typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_PIXEL = 2 } border_style_t; +typedef enum { D_LEFT, + D_RIGHT, + D_UP, + D_DOWN } direction_t; +typedef enum { NO_ORIENTATION = 0, + HORIZ, + VERT } orientation_t; +typedef enum { BS_NORMAL = 0, + BS_NONE = 1, + BS_PIXEL = 2 } border_style_t; /** parameter to specify whether tree_close() and x_window_kill() should kill * only this specific window or the whole X11 client */ -typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t; +typedef enum { DONT_KILL_WINDOW = 0, + KILL_WINDOW = 1, + KILL_CLIENT = 2 } kill_window_t; /** describes if the window is adjacent to the output (physical screen) edges. */ typedef enum { ADJ_NONE = 0, ADJ_LEFT_SCREEN_EDGE = (1 << 0), ADJ_RIGHT_SCREEN_EDGE = (1 << 1), ADJ_UPPER_SCREEN_EDGE = (1 << 2), - ADJ_LOWER_SCREEN_EDGE = (1 << 4)} adjacent_t; + ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t; enum { BIND_NONE = 0, - BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */ - BIND_CONTROL = XCB_MOD_MASK_CONTROL, /* (1 << 2) */ - BIND_MOD1 = XCB_MOD_MASK_1, /* (1 << 3) */ - BIND_MOD2 = XCB_MOD_MASK_2, /* (1 << 4) */ - BIND_MOD3 = XCB_MOD_MASK_3, /* (1 << 5) */ - BIND_MOD4 = XCB_MOD_MASK_4, /* (1 << 6) */ - BIND_MOD5 = XCB_MOD_MASK_5, /* (1 << 7) */ + BIND_SHIFT = XCB_MOD_MASK_SHIFT, /* (1 << 0) */ + BIND_CONTROL = XCB_MOD_MASK_CONTROL, /* (1 << 2) */ + BIND_MOD1 = XCB_MOD_MASK_1, /* (1 << 3) */ + BIND_MOD2 = XCB_MOD_MASK_2, /* (1 << 4) */ + BIND_MOD3 = XCB_MOD_MASK_3, /* (1 << 5) */ + BIND_MOD4 = XCB_MOD_MASK_4, /* (1 << 6) */ + BIND_MOD5 = XCB_MOD_MASK_5, /* (1 << 7) */ BIND_MODE_SWITCH = (1 << 8) }; @@ -267,7 +275,6 @@ struct Binding { * This is an array of number_keycodes size. */ xcb_keycode_t *translated_to; - /** Command, like in command mode */ char *command; @@ -367,7 +374,9 @@ struct Window { bool doesnt_accept_focus; /** Whether the window says it is a dock window */ - enum { W_NODOCK = 0, W_DOCK_TOP = 1, W_DOCK_BOTTOM = 2 } dock; + enum { W_NODOCK = 0, + W_DOCK_TOP = 1, + W_DOCK_BOTTOM = 2 } dock; /** When this window was marked urgent. 0 means not urgent */ struct timeval urgent; @@ -407,7 +416,9 @@ struct Match { M_DOCK_BOTTOM = 3 } dock; xcb_window_t id; - enum { M_ANY = 0, M_TILING, M_FLOATING } floating; + enum { M_ANY = 0, + M_TILING, + M_FLOATING } floating; Con *con_id; /* Where the window looking for a match should be inserted: @@ -419,7 +430,9 @@ struct Match { * (dockareas) * */ - enum { M_HERE = 0, M_ASSIGN_WS, M_BELOW } insert_where; + enum { M_HERE = 0, + M_ASSIGN_WS, + M_BELOW } insert_where; TAILQ_ENTRY(Match) matches; @@ -450,10 +463,10 @@ struct Assignment { * */ enum { - A_ANY = 0, - A_COMMAND = (1 << 0), + A_ANY = 0, + A_COMMAND = (1 << 0), A_TO_WORKSPACE = (1 << 1), - A_TO_OUTPUT = (1 << 2) + A_TO_OUTPUT = (1 << 2) } type; /** the criteria to check if a window matches */ @@ -470,7 +483,9 @@ struct Assignment { }; /** Fullscreen modes. Used by Con.fullscreen_mode. */ -typedef enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode_t; +typedef enum { CF_NONE = 0, + CF_OUTPUT = 1, + CF_GLOBAL = 2 } fullscreen_mode_t; /** * A 'Con' represents everything from the X11 root window down to a single X11 window. @@ -596,7 +611,7 @@ struct Con { TAILQ_ENTRY(Con) floating_windows; /** callbacks */ - void(*on_remove_child)(Con *); + void (*on_remove_child)(Con *); enum { /* Not a scratchpad window. */ diff --git a/include/floating.h b/include/floating.h index fa3bdcc3..bea5f7a2 100644 --- a/include/floating.h +++ b/include/floating.h @@ -12,18 +12,18 @@ #include "tree.h" /** Callback for dragging */ -typedef void(*callback_t)(Con*, Rect*, uint32_t, uint32_t, const void*); +typedef void (*callback_t)(Con *, Rect *, uint32_t, uint32_t, const void *); /** Macro to create a callback function for dragging */ -#define DRAGGING_CB(name) \ - static void name(Con *con, Rect *old_rect, uint32_t new_x, \ - uint32_t new_y, const void *extra) +#define DRAGGING_CB(name) \ + static void name(Con *con, Rect *old_rect, uint32_t new_x, \ + uint32_t new_y, const void *extra) /** On which border was the dragging initiated? */ -typedef enum { BORDER_LEFT = (1 << 0), - BORDER_RIGHT = (1 << 1), - BORDER_TOP = (1 << 2), - BORDER_BOTTOM = (1 << 3)} border_t; +typedef enum { BORDER_LEFT = (1 << 0), + BORDER_RIGHT = (1 << 1), + BORDER_TOP = (1 << 2), + BORDER_BOTTOM = (1 << 3) } border_t; /** * Enables floating mode for the given container by detaching it from its @@ -164,8 +164,8 @@ typedef enum { * */ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, - xcb_window_t confine_to, border_t border, int cursor, - callback_t callback, const void *extra); + xcb_window_t confine_to, border_t border, int cursor, + callback_t callback, const void *extra); /** * Repositions the CT_FLOATING_CON to have the coordinates specified by diff --git a/include/i3.h b/include/i3.h index 4ed0d8d5..c70a5c5b 100644 --- a/include/i3.h +++ b/include/i3.h @@ -42,7 +42,7 @@ extern xcb_key_symbols_t *keysyms; extern char **start_argv; extern Display *xlibdpy, *xkbdpy; extern int xkb_current_group; -extern TAILQ_HEAD(bindings_head, Binding) *bindings; +extern TAILQ_HEAD(bindings_head, Binding) * bindings; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; extern TAILQ_HEAD(autostarts_always_head, Autostart) autostarts_always; extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments; diff --git a/include/i3/ipc.h b/include/i3/ipc.h index 94a39904..9562a209 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -17,7 +17,7 @@ typedef struct i3_ipc_header { char magic[6]; uint32_t size; uint32_t type; -} __attribute__ ((packed)) i3_ipc_header_t; +} __attribute__((packed)) i3_ipc_header_t; /* * Messages from clients to i3 @@ -25,31 +25,31 @@ typedef struct i3_ipc_header { */ /** Never change this, only on major IPC breakage (don’t do that) */ -#define I3_IPC_MAGIC "i3-ipc" +#define I3_IPC_MAGIC "i3-ipc" /** The payload of the message will be interpreted as a command */ -#define I3_IPC_MESSAGE_TYPE_COMMAND 0 +#define I3_IPC_MESSAGE_TYPE_COMMAND 0 /** Requests the current workspaces from i3 */ -#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1 +#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1 /** Subscribe to the specified events */ -#define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2 +#define I3_IPC_MESSAGE_TYPE_SUBSCRIBE 2 /** Requests the current outputs from i3 */ -#define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3 +#define I3_IPC_MESSAGE_TYPE_GET_OUTPUTS 3 /** Requests the tree layout from i3 */ -#define I3_IPC_MESSAGE_TYPE_GET_TREE 4 +#define I3_IPC_MESSAGE_TYPE_GET_TREE 4 /** Request the current defined marks from i3 */ -#define I3_IPC_MESSAGE_TYPE_GET_MARKS 5 +#define I3_IPC_MESSAGE_TYPE_GET_MARKS 5 /** Request the configuration for a specific 'bar' */ -#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6 +#define I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG 6 /** Request the i3 version */ -#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7 +#define I3_IPC_MESSAGE_TYPE_GET_VERSION 7 /* * Messages from i3 to clients @@ -57,46 +57,46 @@ typedef struct i3_ipc_header { */ /** Command reply type */ -#define I3_IPC_REPLY_TYPE_COMMAND 0 +#define I3_IPC_REPLY_TYPE_COMMAND 0 /** Workspaces reply type */ -#define I3_IPC_REPLY_TYPE_WORKSPACES 1 +#define I3_IPC_REPLY_TYPE_WORKSPACES 1 /** Subscription reply type */ -#define I3_IPC_REPLY_TYPE_SUBSCRIBE 2 +#define I3_IPC_REPLY_TYPE_SUBSCRIBE 2 /** Outputs reply type */ -#define I3_IPC_REPLY_TYPE_OUTPUTS 3 +#define I3_IPC_REPLY_TYPE_OUTPUTS 3 /** Tree reply type */ -#define I3_IPC_REPLY_TYPE_TREE 4 +#define I3_IPC_REPLY_TYPE_TREE 4 /** Marks reply type */ -#define I3_IPC_REPLY_TYPE_MARKS 5 +#define I3_IPC_REPLY_TYPE_MARKS 5 /** Bar config reply type */ -#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6 +#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6 /** i3 version reply type */ -#define I3_IPC_REPLY_TYPE_VERSION 7 +#define I3_IPC_REPLY_TYPE_VERSION 7 /* * Events from i3 to clients. Events have the first bit set high. * */ -#define I3_IPC_EVENT_MASK (1 << 31) +#define I3_IPC_EVENT_MASK (1 << 31) /* The workspace event will be triggered upon changes in the workspace list */ -#define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0) +#define I3_IPC_EVENT_WORKSPACE (I3_IPC_EVENT_MASK | 0) /* The output event will be triggered upon changes in the output list */ -#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1) +#define I3_IPC_EVENT_OUTPUT (I3_IPC_EVENT_MASK | 1) /* The output event will be triggered upon mode changes */ -#define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2) +#define I3_IPC_EVENT_MODE (I3_IPC_EVENT_MASK | 2) /* The window event will be triggered upon window changes */ -#define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3) +#define I3_IPC_EVENT_WINDOW (I3_IPC_EVENT_MASK | 3) /** Bar config update will be triggered to update the bar config */ -#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4) +#define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4) diff --git a/include/ipc.h b/include/ipc.h index 418b040f..b6ee9839 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -23,13 +23,13 @@ extern char *current_socketpath; typedef struct ipc_client { - int fd; + int fd; - /* The events which this client wants to receive */ - int num_events; - char **events; + /* The events which this client wants to receive */ + int num_events; + char **events; - TAILQ_ENTRY(ipc_client) clients; + TAILQ_ENTRY(ipc_client) clients; } ipc_client; /* @@ -42,13 +42,13 @@ typedef struct ipc_client { * message_type is the type of the message as the sender specified it. * */ -typedef void(*handler_t)(int, uint8_t*, int, uint32_t, uint32_t); +typedef void (*handler_t)(int, uint8_t *, int, uint32_t, uint32_t); /* Macro to declare a callback */ -#define IPC_HANDLER(name) \ - static void handle_ ## name (int fd, uint8_t *message, \ - int size, uint32_t message_size, \ - uint32_t message_type) +#define IPC_HANDLER(name) \ + static void handle_##name(int fd, uint8_t *message, \ + int size, uint32_t message_size, \ + uint32_t message_type) /** * Handler for activity on the listening socket, meaning that a new client diff --git a/include/libi3.h b/include/libi3.h index dc5d537f..7c0ead32 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -72,17 +72,17 @@ struct Font { * infrastructure, we define a fallback. */ #if !defined(LOG) void verboselog(char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__((format(printf, 1, 2))); #define LOG(fmt, ...) verboselog("[libi3] " __FILE__ " " fmt, ##__VA_ARGS__) #endif #if !defined(ELOG) void errorlog(char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__((format(printf, 1, 2))); #define ELOG(fmt, ...) errorlog("[libi3] ERROR: " fmt, ##__VA_ARGS__) #endif #if !defined(DLOG) void debuglog(char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__((format(printf, 1, 2))); #define DLOG(fmt, ...) debuglog("%s:%s:%d - " fmt, I3__FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) #endif @@ -167,13 +167,13 @@ void i3string_free(i3String *str); * to prevent accidentally using freed memory. * */ -#define I3STRING_FREE(str) \ -do { \ - if (str != NULL) { \ - i3string_free(str); \ - str = NULL; \ - } \ -} while (0) +#define I3STRING_FREE(str) \ + do { \ + if (str != NULL) { \ + i3string_free(str); \ + str = NULL; \ + } \ + } while (0) /** * Returns the UTF-8 encoded version of the i3String. @@ -285,8 +285,8 @@ uint32_t aio_get_mod_mask_for(uint32_t keysym, xcb_key_symbols_t *symbols); * */ uint32_t get_mod_mask_for(uint32_t keysym, - xcb_key_symbols_t *symbols, - xcb_get_modifier_mapping_reply_t *modmap_reply); + xcb_key_symbols_t *symbols, + xcb_get_modifier_mapping_reply_t *modmap_reply); /** * Loads a font for usage, also getting its height. If fallback is true, @@ -338,14 +338,14 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background * */ void draw_text(i3String *text, xcb_drawable_t drawable, - xcb_gcontext_t gc, int x, int y, int max_width); + xcb_gcontext_t gc, int x, int y, int max_width); /** * ASCII version of draw_text to print static strings. * */ void draw_text_ascii(const char *text, xcb_drawable_t drawable, - xcb_gcontext_t gc, int x, int y, int max_width); + xcb_gcontext_t gc, int x, int y, int max_width); /** * Predict the text width in pixels for the given text. Text must be diff --git a/include/log.h b/include/log.h index 2400092b..a5086dbe 100644 --- a/include/log.h +++ b/include/log.h @@ -78,14 +78,14 @@ void set_verbosity(bool _verbose); * */ void debuglog(char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__((format(printf, 1, 2))); /** * Logs the given message to stdout while prefixing the current time to it. * */ void errorlog(char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__((format(printf, 1, 2))); /** * Logs the given message to stdout while prefixing the current time to it, @@ -93,7 +93,7 @@ void errorlog(char *fmt, ...) * */ void verboselog(char *fmt, ...) - __attribute__ ((format (printf, 1, 2))); + __attribute__((format(printf, 1, 2))); /** * Deletes the unused log files. Useful if i3 exits immediately, eg. diff --git a/include/queue.h b/include/queue.h index 2307149b..9fb9ba5e 100644 --- a/include/queue.h +++ b/include/queue.h @@ -90,448 +90,468 @@ /* * Singly-linked List definitions. */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} +#define SLIST_HEAD(name, type) \ + struct name { \ + struct type *slh_first; /* first element */ \ + } -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} +#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_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(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)) +#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_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_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_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_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(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) +#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(name, type) \ + struct name { \ + struct type *lh_first; /* first element */ \ + } -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } +#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 */ \ -} +#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_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)) +#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_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_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_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_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_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) +#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(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_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} +#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_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)) +#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_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_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_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_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) +#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(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_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 */ \ -} +#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)) +#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_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(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)) +#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_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_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_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_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_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_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) +#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) /* Swaps two consecutive elements. 'second' *MUST* follow 'first' */ -#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) +#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) /* * 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(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_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 */ \ -} +#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_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(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)) +#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_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_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_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_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_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_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) +#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/include/sd-daemon.h b/include/sd-daemon.h index 8746a3ad..543580ec 100644 --- a/include/sd-daemon.h +++ b/include/sd-daemon.h @@ -68,15 +68,15 @@ extern "C" { #ifndef _sd_printf_attr_ #if __GNUC__ >= 4 -#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) +#define _sd_printf_attr_(a, b) __attribute__((format(printf, a, b))) #else -#define _sd_printf_attr_(a,b) +#define _sd_printf_attr_(a, b) #endif #endif #ifndef _sd_hidden_ #if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS) -#define _sd_hidden_ __attribute__ ((visibility("hidden"))) +#define _sd_hidden_ __attribute__((visibility("hidden"))) #else #define _sd_hidden_ #endif @@ -89,14 +89,14 @@ extern "C" { This is similar to printk() usage in the kernel. */ -#define SD_EMERG "<0>" /* system is unusable */ -#define SD_ALERT "<1>" /* action must be taken immediately */ -#define SD_CRIT "<2>" /* critical conditions */ -#define SD_ERR "<3>" /* error conditions */ -#define SD_WARNING "<4>" /* warning conditions */ -#define SD_NOTICE "<5>" /* normal but significant condition */ -#define SD_INFO "<6>" /* informational */ -#define SD_DEBUG "<7>" /* debug-level messages */ +#define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ /* The first passed file descriptor is fd 3 */ #define SD_LISTEN_FDS_START 3 @@ -242,7 +242,7 @@ int sd_notify(int unset_environment, const char *state) _sd_hidden_; See sd_notifyf(3) for more information. */ -int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3) _sd_hidden_; +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2, 3) _sd_hidden_; /* Returns > 0 if the system was booted with systemd. Returns < 0 on diff --git a/include/startup.h b/include/startup.h index fb017103..9cbdf17b 100644 --- a/include/startup.h +++ b/include/startup.h @@ -49,7 +49,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata); * */ struct Startup_Sequence *startup_sequence_get(i3Window *cwindow, - xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader); + xcb_get_property_reply_t *startup_id_reply, bool ignore_mapped_leader); /** * Checks if the given window belongs to a startup notification by checking if diff --git a/include/util.h b/include/util.h index 514a47c2..dec68116 100644 --- a/include/util.h +++ b/include/util.h @@ -15,43 +15,45 @@ #include "data.h" #define die(...) errx(EXIT_FAILURE, __VA_ARGS__); -#define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); } +#define exit_if_null(pointer, ...) \ + { \ + if (pointer == NULL) \ + die(__VA_ARGS__); \ + } #define STARTS_WITH(string, needle) (strncasecmp(string, needle, strlen(needle)) == 0) -#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? \ - CIRCLEQ_NEXT(elm, field) : NULL) -#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? \ - CIRCLEQ_PREV(elm, field) : NULL) -#define FOR_TABLE(workspace) \ - for (int cols = 0; cols < (workspace)->cols; cols++) \ - for (int rows = 0; rows < (workspace)->rows; rows++) +#define CIRCLEQ_NEXT_OR_NULL(head, elm, field) (CIRCLEQ_NEXT(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_NEXT(elm, field) : NULL) +#define CIRCLEQ_PREV_OR_NULL(head, elm, field) (CIRCLEQ_PREV(elm, field) != CIRCLEQ_END(head) ? CIRCLEQ_PREV(elm, field) : NULL) +#define FOR_TABLE(workspace) \ + for (int cols = 0; cols < (workspace)->cols; cols++) \ + for (int rows = 0; rows < (workspace)->rows; rows++) -#define NODES_FOREACH(head) \ - for (Con *child = (Con*)-1; (child == (Con*)-1) && ((child = 0), true);) \ - TAILQ_FOREACH(child, &((head)->nodes_head), nodes) +#define NODES_FOREACH(head) \ + for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \ + TAILQ_FOREACH(child, &((head)->nodes_head), nodes) -#define NODES_FOREACH_REVERSE(head) \ - for (Con *child = (Con*)-1; (child == (Con*)-1) && ((child = 0), true);) \ - TAILQ_FOREACH_REVERSE(child, &((head)->nodes_head), nodes_head, nodes) +#define NODES_FOREACH_REVERSE(head) \ + for (Con *child = (Con *)-1; (child == (Con *)-1) && ((child = 0), true);) \ + TAILQ_FOREACH_REVERSE(child, &((head)->nodes_head), nodes_head, nodes) /* greps the ->nodes of the given head and returns the first node that matches the given condition */ #define GREP_FIRST(dest, head, condition) \ - NODES_FOREACH(head) { \ - if (!(condition)) \ - continue; \ - \ - (dest) = child; \ - break; \ + NODES_FOREACH(head) { \ + if (!(condition)) \ + continue; \ + \ + (dest) = child; \ + break; \ } -#define FREE(pointer) do { \ +#define FREE(pointer) \ + do { \ if (pointer != NULL) { \ - free(pointer); \ - pointer = NULL; \ - } \ -} \ -while (0) + free(pointer); \ + pointer = NULL; \ + } \ + } while (0) -#define CALL(obj, member, ...) obj->member(obj, ## __VA_ARGS__) +#define CALL(obj, member, ...) obj->member(obj, ##__VA_ARGS__) int min(int a, int b); int max(int a, int b); diff --git a/include/workspace.h b/include/workspace.h index 463ccf19..9ee6f156 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -68,25 +68,25 @@ void workspace_show_by_name(const char *num); * Returns the next workspace. * */ -Con* workspace_next(void); +Con *workspace_next(void); /** * Returns the previous workspace. * */ -Con* workspace_prev(void); +Con *workspace_prev(void); /** * Returns the next workspace on the same output * */ -Con* workspace_next_on_output(void); +Con *workspace_next_on_output(void); /** * Returns the previous workspace on the same output * */ -Con* workspace_prev_on_output(void); +Con *workspace_prev_on_output(void); /** * Focuses the previously focused workspace. @@ -100,7 +100,6 @@ void workspace_back_and_forth(void); */ Con *workspace_back_and_forth_get(void); - #if 0 /** * Assigns the given workspace to the given screen by correctly updating its diff --git a/include/xcb.h b/include/xcb.h index 4df7f639..9f4ea91f 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -12,48 +12,48 @@ #include "data.h" #include "xcursor.h" -#define _NET_WM_STATE_REMOVE 0 -#define _NET_WM_STATE_ADD 1 -#define _NET_WM_STATE_TOGGLE 2 +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 /** This is the equivalent of XC_left_ptr. I’m not sure why xcb doesn’t have a * constant for that. */ -#define XCB_CURSOR_LEFT_PTR 68 +#define XCB_CURSOR_LEFT_PTR 68 #define XCB_CURSOR_SB_H_DOUBLE_ARROW 108 #define XCB_CURSOR_SB_V_DOUBLE_ARROW 116 #define XCB_CURSOR_WATCH 150 /* from X11/keysymdef.h */ -#define XCB_NUM_LOCK 0xff7f +#define XCB_NUM_LOCK 0xff7f /* The event masks are defined here because we don’t only set them once but we need to set slight variations of them (without XCB_EVENT_MASK_ENTER_WINDOW while rendering the layout) */ /** The XCB_CW_EVENT_MASK for the child (= real window) */ -#define CHILD_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \ +#define CHILD_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ XCB_EVENT_MASK_FOCUS_CHANGE) /** The XCB_CW_EVENT_MASK for its frame */ -#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \ - XCB_EVENT_MASK_BUTTON_RELEASE | \ - XCB_EVENT_MASK_POINTER_MOTION | /* …mouse is moved */ \ - XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \ - XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* …the frame gets destroyed */ \ +#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ \ + XCB_EVENT_MASK_BUTTON_RELEASE | \ + XCB_EVENT_MASK_POINTER_MOTION | /* …mouse is moved */ \ + XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* …the frame gets destroyed */ \ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | /* …the application tries to resize itself */ \ - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \ + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | /* …subwindows get notifies */ \ XCB_EVENT_MASK_ENTER_WINDOW) /* …user moves cursor inside our window */ -#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ - XCB_EVENT_MASK_BUTTON_PRESS | \ - XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video \ - projector), the root window gets a \ - ConfigureNotify */ \ - XCB_EVENT_MASK_POINTER_MOTION | \ - XCB_EVENT_MASK_PROPERTY_CHANGE | \ - XCB_EVENT_MASK_ENTER_WINDOW) +#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ + XCB_EVENT_MASK_BUTTON_PRESS | \ + XCB_EVENT_MASK_STRUCTURE_NOTIFY | /* when the user adds a screen (e.g. video \ + projector), the root window gets a \ + ConfigureNotify */ \ + XCB_EVENT_MASK_POINTER_MOTION | \ + XCB_EVENT_MASK_PROPERTY_CHANGE | \ + XCB_EVENT_MASK_ENTER_WINDOW) -#define xmacro(atom) xcb_atom_t A_ ## atom; +#define xmacro(atom) xcb_atom_t A_##atom; #include "atoms.xmacro" #undef xmacro @@ -65,7 +65,7 @@ extern unsigned int xcb_numlock_mask; * */ xcb_window_t create_window(xcb_connection_t *conn, Rect r, uint16_t depth, xcb_visualid_t visual, - uint16_t window_class, enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values); + uint16_t window_class, enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values); /** * Draws a line from x,y to to_x,to_y using the given color @@ -108,7 +108,6 @@ void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window); */ void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r); - bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom); /** diff --git a/include/yajl_utils.h b/include/yajl_utils.h index 747e0ca0..e8422aab 100644 --- a/include/yajl_utils.h +++ b/include/yajl_utils.h @@ -14,8 +14,8 @@ #include /* Shorter names for all those yajl_gen_* functions */ -#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__) -#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str)) +#define y(x, ...) yajl_gen_##x(gen, ##__VA_ARGS__) +#define ystr(str) yajl_gen_string(gen, (unsigned char *)str, strlen(str)) #define ygenalloc() yajl_gen_alloc(NULL) #define yalloc(callbacks, client) yajl_alloc(callbacks, NULL, client) diff --git a/src/assignments.c b/src/assignments.c index 2545bde0..96834f64 100644 --- a/src/assignments.c +++ b/src/assignments.c @@ -23,7 +23,7 @@ void run_assignments(i3Window *window) { /* Check if any assignments match */ Assignment *current; - TAILQ_FOREACH (current, &assignments, assignments) { + TAILQ_FOREACH(current, &assignments, assignments) { if (!match_matches_window(&(current->match), window)) continue; @@ -72,7 +72,7 @@ void run_assignments(i3Window *window) { Assignment *assignment_for(i3Window *window, int type) { Assignment *assignment; - TAILQ_FOREACH (assignment, &assignments, assignments) { + TAILQ_FOREACH(assignment, &assignments, assignments) { if ((type != A_ANY && (assignment->type & type) == 0) || !match_matches_window(&(assignment->match), window)) continue; diff --git a/src/bindings.c b/src/bindings.c index b17ec13c..0ad56682 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -25,7 +25,7 @@ static struct Mode *mode_from_name(const char *name) { struct Mode *mode; /* Try to find the mode in the list of modes and return it */ - SLIST_FOREACH (mode, &modes, modes) { + SLIST_FOREACH(mode, &modes, modes) { if (strcmp(mode->name, name) == 0) return mode; } @@ -104,7 +104,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint */ void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) { Binding *bind; - TAILQ_FOREACH (bind, bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { if (bind->input_type != B_KEYBOARD || (bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) || (!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0)) @@ -133,7 +133,7 @@ static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_ if (!is_release) { /* On a press event, we first reset all B_UPON_KEYRELEASE_IGNORE_MODS * bindings back to B_UPON_KEYRELEASE */ - TAILQ_FOREACH (bind, bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { if (bind->input_type != input_type) continue; if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS) @@ -141,7 +141,7 @@ static Binding *get_binding(uint16_t modifiers, bool is_release, uint16_t input_ } } - TAILQ_FOREACH (bind, bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { /* First compare the modifiers (unless this is a * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease * event) */ @@ -248,7 +248,7 @@ void translate_keysyms(void) { min_keycode = xcb_get_setup(conn)->min_keycode; max_keycode = xcb_get_setup(conn)->max_keycode; - TAILQ_FOREACH (bind, bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { if (bind->input_type == B_MOUSE) { int button = atoi(bind->symbol + (sizeof("button") - 1)); bind->keycode = button; @@ -304,7 +304,7 @@ void switch_mode(const char *new_mode) { DLOG("Switching to mode %s\n", new_mode); - SLIST_FOREACH (mode, &modes, modes) { + SLIST_FOREACH(mode, &modes, modes) { if (strcasecmp(mode->name, new_mode) != 0) continue; @@ -334,8 +334,8 @@ void switch_mode(const char *new_mode) { */ void check_for_duplicate_bindings(struct context *context) { Binding *bind, *current; - TAILQ_FOREACH (current, bindings, bindings) { - TAILQ_FOREACH (bind, bindings, bindings) { + TAILQ_FOREACH(current, bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { /* Abort when we reach the current keybinding, only check the * bindings before */ if (bind == current) diff --git a/src/click.c b/src/click.c index 2cf02178..ddc1119b 100644 --- a/src/click.c +++ b/src/click.c @@ -330,7 +330,7 @@ int handle_button_press(xcb_button_press_event_t *event) { * click coordinates and focus the output's active workspace. */ if (event->event == root) { Con *output, *ws; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { if (con_is_internal(output) || !rect_contains(output->rect, event->event_x, event->event_y)) continue; @@ -358,7 +358,7 @@ int handle_button_press(xcb_button_press_event_t *event) { /* Check if the click was on the decoration of a child */ Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (!rect_contains(child->deco_rect, event->event_x, event->event_y)) continue; diff --git a/src/commands.c b/src/commands.c index 03bb10a6..b9c130de 100644 --- a/src/commands.c +++ b/src/commands.c @@ -273,7 +273,7 @@ void cmd_criteria_init(I3_CMD) { } TAILQ_INIT(&owindows); /* copy all_cons */ - TAILQ_FOREACH (con, &all_cons, all_cons) { + TAILQ_FOREACH(con, &all_cons, all_cons) { ow = smalloc(sizeof(owindow)); ow->con = con; TAILQ_INSERT_TAIL(&owindows, ow, owindows); @@ -324,7 +324,7 @@ void cmd_criteria_match_windows(I3_CMD) { } } - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); } } @@ -448,7 +448,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) { return; } - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -475,7 +475,7 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -519,7 +519,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -562,9 +562,9 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { return; } - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), - child->num == parsed_num); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), + child->num == parsed_num); if (!workspace) { workspace = workspace_get(which, NULL); @@ -574,7 +574,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, workspace, true, false); } @@ -728,7 +728,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char /* Ensure all the other children have a percentage set. */ Con *child; - TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { LOG("child->percent = %f (child %p)\n", child->percent, child); if (child->percent == 0.0) child->percent = percentage; @@ -740,7 +740,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char LOG("subtract_percent = %f\n", subtract_percent); /* Ensure that the new percentages are positive and greater than * 0.05 to have a reasonable minimum size. */ - TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { if (child == current) continue; if (!definitelyGreaterThan(child->percent - subtract_percent, 0.05, DBL_EPSILON)) { @@ -758,7 +758,7 @@ static bool cmd_resize_tiling_width_height(I3_CMD, Con *current, char *way, char current->percent += ((double)ppt / 100.0); LOG("current->percent after = %f\n", current->percent); - TAILQ_FOREACH (child, &(current->parent->nodes_head), nodes) { + TAILQ_FOREACH(child, &(current->parent->nodes_head), nodes) { if (child == current) continue; child->percent -= subtract_percent; @@ -786,7 +786,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { /* Don't handle dock windows (issue #1201) */ if (current->con->window && current->con->window->dock) { DLOG("This is a dock window. Not resizing (con = %p)\n)", current->con); @@ -823,7 +823,7 @@ void cmd_border(I3_CMD, char *border_style_str, char *border_width) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); int border_style = current->con->border_style; char *end; @@ -967,9 +967,9 @@ void cmd_workspace_number(I3_CMD, char *which) { return; } - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), - child->num == parsed_num); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), + child->num == parsed_num); if (!workspace) { LOG("There is no workspace with number %ld, creating a new one.\n", parsed_num); @@ -1028,7 +1028,7 @@ void cmd_mark(I3_CMD, char *mark) { DLOG("Clearing all windows which have that mark first\n"); Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) { + TAILQ_FOREACH(con, &all_cons, all_cons) { if (con->mark && strcmp(con->mark, mark) == 0) FREE(con->mark); } @@ -1038,7 +1038,7 @@ void cmd_mark(I3_CMD, char *mark) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); current->con->mark = sstrdup(mark); } @@ -1055,13 +1055,13 @@ void cmd_mark(I3_CMD, char *mark) { void cmd_unmark(I3_CMD, char *mark) { if (mark == NULL) { Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) { + TAILQ_FOREACH(con, &all_cons, all_cons) { FREE(con->mark); } DLOG("removed all window marks"); } else { Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) { + TAILQ_FOREACH(con, &all_cons, all_cons) { if (con->mark && strcmp(con->mark, mark) == 0) FREE(con->mark); } @@ -1101,8 +1101,8 @@ void cmd_move_con_to_output(I3_CMD, char *name) { Output *output; // TODO: fix the handling of criteria - TAILQ_FOREACH (current, &owindows, owindows) - current_output = get_output_of_con(current->con); + TAILQ_FOREACH(current, &owindows, owindows) + current_output = get_output_of_con(current->con); assert(current_output != NULL); @@ -1132,7 +1132,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) { return; } - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_move_to_workspace(current->con, ws, true, false); } @@ -1153,7 +1153,7 @@ void cmd_floating(I3_CMD, char *floating_mode) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); if (strcmp(floating_mode, "toggle") == 0) { DLOG("should toggle mode\n"); @@ -1183,7 +1183,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { HANDLE_EMPTY_MATCH; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { Output *current_output = get_output_of_con(current->con); if (!current_output) { ELOG("Cannot get current output. This is a bug in i3.\n"); @@ -1213,15 +1213,15 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { /* check if we can find a workspace assigned to this output */ bool used_assignment = false; struct Workspace_Assignment *assignment; - TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->output, current_output->name) != 0) continue; /* check if this workspace is already attached to the tree */ Con *workspace = NULL, *out; - TAILQ_FOREACH (out, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(out), - !strcasecmp(child->name, assignment->name)); + TAILQ_FOREACH(out, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(out), + !strcasecmp(child->name, assignment->name)); if (workspace != NULL) continue; @@ -1256,8 +1256,8 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { /* fix the coordinates of the floating containers */ Con *floating_con; - TAILQ_FOREACH (floating_con, &(ws->floating_head), floating_windows) - floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect)); + TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows) + floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect)); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}"); if (workspace_was_visible) { @@ -1270,7 +1270,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { * focus order/number of other workspaces on the output. * Instead, we loop through the available workspaces and only work with * previously_visible_ws if we still find it. */ - TAILQ_FOREACH (ws, &(content->nodes_head), nodes) { + TAILQ_FOREACH(ws, &(content->nodes_head), nodes) { if (ws != previously_visible_ws) continue; @@ -1298,7 +1298,7 @@ void cmd_split(I3_CMD, char *direction) { if (match_is_empty(current_match)) tree_split(focused, (direction[0] == 'v' ? VERT : HORIZ)); else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); tree_split(current->con, (direction[0] == 'v' ? VERT : HORIZ)); } @@ -1335,7 +1335,7 @@ void cmd_kill(I3_CMD, char *kill_mode_str) { if (match_is_empty(current_match)) tree_close_con(kill_mode); else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); tree_close(current->con, kill_mode, false, false); } @@ -1403,7 +1403,7 @@ void cmd_focus_window_mode(I3_CMD, char *window_mode) { else window_mode = "floating"; } - TAILQ_FOREACH (current, &(ws->focus_head), focused) { + TAILQ_FOREACH(current, &(ws->focus_head), focused) { if ((strcmp(window_mode, "floating") == 0 && current->type != CT_FLOATING_CON) || (strcmp(window_mode, "tiling") == 0 && current->type == CT_FLOATING_CON)) continue; @@ -1465,7 +1465,7 @@ void cmd_focus(I3_CMD) { Con *__i3_scratch = workspace_get("__i3_scratch", NULL); int count = 0; owindow *current; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { Con *ws = con_get_workspace(current->con); /* If no workspace could be found, this was a dock window. * Just skip it, you cannot focus dock windows. */ @@ -1534,7 +1534,7 @@ void cmd_fullscreen(I3_CMD, char *fullscreen_mode) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT)); } @@ -1607,7 +1607,7 @@ void cmd_layout(I3_CMD, char *layout_str) { if (match_is_empty(current_match)) con_set_layout(focused, layout); else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_set_layout(current->con, layout); } @@ -1634,7 +1634,7 @@ void cmd_layout_toggle(I3_CMD, char *toggle_mode) { if (match_is_empty(current_match)) con_toggle_layout(focused, toggle_mode); else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); con_toggle_layout(current->con, toggle_mode); } @@ -1733,8 +1733,8 @@ void cmd_focus_output(I3_CMD, char *name) { Output *current_output = NULL; Output *output; - TAILQ_FOREACH (current, &owindows, owindows) - current_output = get_output_of_con(current->con); + TAILQ_FOREACH(current, &owindows, owindows) + current_output = get_output_of_con(current->con); assert(current_output != NULL); output = get_output_from_string(current_output, name); @@ -1844,7 +1844,7 @@ void cmd_move_scratchpad(I3_CMD) { HANDLE_EMPTY_MATCH; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); scratchpad_move(current->con); } @@ -1865,7 +1865,7 @@ void cmd_scratchpad_show(I3_CMD) { if (match_is_empty(current_match)) { scratchpad_show(NULL); } else { - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); scratchpad_show(current->con); } @@ -1894,9 +1894,9 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { Con *output, *workspace = NULL; if (old_name) { - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), - !strcasecmp(child->name, old_name)); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), + !strcasecmp(child->name, old_name)); } else { workspace = con_get_workspace(focused); } @@ -1910,9 +1910,9 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { } Con *check_dest = NULL; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(check_dest, output_get_content(output), - !strcasecmp(child->name, new_name)); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(check_dest, output_get_content(output), + !strcasecmp(child->name, new_name)); if (check_dest != NULL) { // TODO: we should include the new workspace name here and use yajl for @@ -1972,7 +1972,7 @@ bool cmd_bar_mode(char *bar_mode, char *bar_id) { bool changed_sth = false; Barconfig *current = NULL; - TAILQ_FOREACH (current, &barconfigs, configs) { + TAILQ_FOREACH(current, &barconfigs, configs) { if (bar_id && strcmp(current->id, bar_id) != 0) continue; @@ -2015,7 +2015,7 @@ bool cmd_bar_hidden_state(char *bar_hidden_state, char *bar_id) { bool changed_sth = false; Barconfig *current = NULL; - TAILQ_FOREACH (current, &barconfigs, configs) { + TAILQ_FOREACH(current, &barconfigs, configs) { if (bar_id && strcmp(current->id, bar_id) != 0) continue; diff --git a/src/con.c b/src/con.c index 30321ef0..ee4727b3 100644 --- a/src/con.c +++ b/src/con.c @@ -142,7 +142,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) { } else { if (!ignore_focus) { /* Get the first tiling container in focus stack */ - TAILQ_FOREACH (loop, &(parent->focus_head), focused) { + TAILQ_FOREACH(loop, &(parent->focus_head), focused) { if (loop->type == CT_FLOATING_CON) continue; current = loop; @@ -388,13 +388,13 @@ Con *con_get_fullscreen_con(Con *con, fullscreen_mode_t fullscreen_mode) { TAILQ_REMOVE(&bfs_head, entry, entries); free(entry); - TAILQ_FOREACH (child, &(current->nodes_head), nodes) { + TAILQ_FOREACH(child, &(current->nodes_head), nodes) { entry = smalloc(sizeof(struct bfs_entry)); entry->con = child; TAILQ_INSERT_TAIL(&bfs_head, entry, entries); } - TAILQ_FOREACH (child, &(current->floating_head), floating_windows) { + TAILQ_FOREACH(child, &(current->floating_head), floating_windows) { entry = smalloc(sizeof(struct bfs_entry)); entry->con = child; TAILQ_INSERT_TAIL(&bfs_head, entry, entries); @@ -460,9 +460,9 @@ bool con_inside_focused(Con *con) { */ Con *con_by_window_id(xcb_window_t window) { Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) - if (con->window != NULL && con->window->id == window) - return con; + TAILQ_FOREACH(con, &all_cons, all_cons) + if (con->window != NULL && con->window->id == window) + return con; return NULL; } @@ -473,9 +473,9 @@ Con *con_by_window_id(xcb_window_t window) { */ Con *con_by_frame_id(xcb_window_t frame) { Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) - if (con->frame == frame) - return con; + TAILQ_FOREACH(con, &all_cons, all_cons) + if (con->frame == frame) + return con; return NULL; } @@ -490,8 +490,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) { //DLOG("searching con for window %p starting at con %p\n", window, con); //DLOG("class == %s\n", window->class_class); - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { - TAILQ_FOREACH (match, &(child->swallow_head), matches) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(match, &(child->swallow_head), matches) { if (!match_matches_window(match, window)) continue; if (store_match != NULL) @@ -503,8 +503,8 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match) { return result; } - TAILQ_FOREACH (child, &(con->floating_head), floating_windows) { - TAILQ_FOREACH (match, &(child->swallow_head), matches) { + TAILQ_FOREACH(child, &(con->floating_head), floating_windows) { + TAILQ_FOREACH(match, &(child->swallow_head), matches) { if (!match_matches_window(match, window)) continue; if (store_match != NULL) @@ -527,8 +527,8 @@ int con_num_children(Con *con) { Con *child; int children = 0; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) - children++; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + children++; return children; } @@ -547,7 +547,7 @@ void con_fix_percent(Con *con) { // with a percentage set we have double total = 0.0; int children_with_percent = 0; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (child->percent > 0.0) { total += child->percent; ++children_with_percent; @@ -557,7 +557,7 @@ void con_fix_percent(Con *con) { // if there were children without a percentage set, set to a value that // will make those children proportional to all others if (children_with_percent != children) { - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (child->percent <= 0.0) { if (children_with_percent == 0) total += (child->percent = 1.0); @@ -570,11 +570,11 @@ void con_fix_percent(Con *con) { // if we got a zero, just distribute the space equally, otherwise // distribute according to the proportions we got if (total == 0.0) { - TAILQ_FOREACH (child, &(con->nodes_head), nodes) - child->percent = 1.0 / children; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + child->percent = 1.0 / children; } else if (total != 1.0) { - TAILQ_FOREACH (child, &(con->nodes_head), nodes) - child->percent /= total; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + child->percent /= total; } } @@ -807,7 +807,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool if (!con_is_leaf(con)) { Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (!child->window) continue; @@ -1003,7 +1003,7 @@ Con *con_descend_tiling_focused(Con *con) { return next; do { before = next; - TAILQ_FOREACH (child, &(next->focus_head), focused) { + TAILQ_FOREACH(child, &(next->focus_head), focused) { if (child->type == CT_FLOATING_CON) continue; @@ -1038,7 +1038,7 @@ Con *con_descend_direction(Con *con, direction_t direction) { /* Wrong orientation. We use the last focused con. Within that con, * we recurse to chose the left/right con or at least the last * focused one. */ - TAILQ_FOREACH (current, &(con->focus_head), focused) { + TAILQ_FOREACH(current, &(con->focus_head), focused) { if (current->type != CT_FLOATING_CON) { most = current; break; @@ -1063,7 +1063,7 @@ Con *con_descend_direction(Con *con, direction_t direction) { /* Wrong orientation. We use the last focused con. Within that con, * we recurse to chose the top/bottom con or at least the last * focused one. */ - TAILQ_FOREACH (current, &(con->focus_head), focused) { + TAILQ_FOREACH(current, &(con->focus_head), focused) { if (current->type != CT_FLOATING_CON) { most = current; break; @@ -1426,7 +1426,7 @@ Rect con_minimum_size(Con *con) { if (con->layout == L_STACKED || con->layout == L_TABBED) { uint32_t max_width = 0, max_height = 0, deco_height = 0; Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { Rect min = con_minimum_size(child); deco_height += child->deco_rect.height; max_width = max(max_width, min.width); @@ -1443,7 +1443,7 @@ Rect con_minimum_size(Con *con) { if (con_is_split(con)) { uint32_t width = 0, height = 0; Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { Rect min = con_minimum_size(child); if (con->layout == L_SPLITH) { width += min.width; @@ -1538,7 +1538,7 @@ bool con_has_urgent_child(Con *con) { /* We are not interested in floating windows since they can only be * attached to a workspace → nodes_head instead of focus_head */ - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (con_has_urgent_child(child)) return true; } @@ -1651,7 +1651,7 @@ char *con_get_tree_representation(Con *con) { /* 2) append representation of children */ Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { char *child_txt = con_get_tree_representation(child); char *tmp_buf; diff --git a/src/config.c b/src/config.c index 7e88a9ef..781ff6b8 100644 --- a/src/config.c +++ b/src/config.c @@ -36,7 +36,7 @@ void ungrab_all_keys(xcb_connection_t *conn) { */ void update_barconfig() { Barconfig *current; - TAILQ_FOREACH (current, &barconfigs, configs) { + TAILQ_FOREACH(current, &barconfigs, configs) { ipc_send_barconfig_update_event(current); } } @@ -204,8 +204,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, /* Invalidate pixmap caches in case font or colors changed */ Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) - FREE(con->deco_render_params); + TAILQ_FOREACH(con, &all_cons, all_cons) + FREE(con->deco_render_params); /* Get rid of the current font */ free_font(); diff --git a/src/config_directives.c b/src/config_directives.c index e45db0e2..6dfd369d 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -334,7 +334,7 @@ CFGFUN(workspace, const char *workspace, const char *output) { * outputs */ struct Workspace_Assignment *assignment; bool duplicate = false; - TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcasecmp(assignment->name, workspace) == 0) { ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n", workspace); diff --git a/src/config_parser.c b/src/config_parser.c index 21cd06fb..ee02c393 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -917,7 +917,7 @@ void parse_file(const char *f) { * variables (otherwise we will count them twice, which is bad when * 'extra' is negative) */ char *bufcopy = sstrdup(buf); - SLIST_FOREACH (current, &variables, variables) { + SLIST_FOREACH(current, &variables, variables) { int extra = (strlen(current->value) - strlen(current->key)); char *next; for (next = bufcopy; @@ -937,11 +937,11 @@ void parse_file(const char *f) { destwalk = new; while (walk < (buf + stbuf.st_size)) { /* Find the next variable */ - SLIST_FOREACH (current, &variables, variables) - current->next_match = strcasestr(walk, current->key); + SLIST_FOREACH(current, &variables, variables) + current->next_match = strcasestr(walk, current->key); nearest = NULL; int distance = stbuf.st_size; - SLIST_FOREACH (current, &variables, variables) { + SLIST_FOREACH(current, &variables, variables) { if (current->next_match == NULL) continue; if ((current->next_match - walk) < distance) { diff --git a/src/ewmh.c b/src/ewmh.c index 986523dd..0c860ad0 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -24,9 +24,9 @@ void ewmh_update_current_desktop(void) { uint32_t idx = 0; /* We count to get the index of this workspace because named workspaces * don’t have the ->num property */ - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { Con *ws; - TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) { + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { if (STARTS_WITH(ws->name, "__")) continue; diff --git a/src/fake_outputs.c b/src/fake_outputs.c index 548c21c6..4f274099 100644 --- a/src/fake_outputs.c +++ b/src/fake_outputs.c @@ -20,9 +20,9 @@ static int num_screens; */ static Output *get_screen_at(unsigned int x, unsigned int y) { Output *output; - TAILQ_FOREACH (output, &outputs, outputs) - if (output->rect.x == x && output->rect.y == y) - return output; + TAILQ_FOREACH(output, &outputs, outputs) + if (output->rect.x == x && output->rect.y == y) + return output; return NULL; } diff --git a/src/floating.c b/src/floating.c index 7a8df508..f9935952 100644 --- a/src/floating.c +++ b/src/floating.c @@ -21,7 +21,7 @@ static Rect total_outputs_dimensions(void) { Output *output; /* Use Rect to encapsulate dimensions, ignoring x/y */ Rect outputs_dimensions = {0, 0, 0, 0}; - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { outputs_dimensions.height += output->rect.height; outputs_dimensions.width += output->rect.width; } @@ -205,7 +205,7 @@ void floating_enable(Con *con, bool automatic) { if (memcmp(&(nc->rect), &zero, sizeof(Rect)) == 0) { DLOG("Geometry not set, combining children\n"); Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { DLOG("child geometry: %d x %d\n", child->geometry.width, child->geometry.height); nc->rect.width += child->geometry.width; nc->rect.height = max(nc->rect.height, child->geometry.height); diff --git a/src/handlers.c b/src/handlers.c index c217cbbc..ba9b1211 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -62,7 +62,7 @@ bool event_is_ignored(const int sequence, const int response_type) { event = SLIST_NEXT(event, ignore_events); } - SLIST_FOREACH (event, &ignore_events, ignore_events) { + SLIST_FOREACH(event, &ignore_events, ignore_events) { if (event->sequence != sequence) continue; @@ -163,12 +163,12 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { layout_t layout = (enter_child ? con->parent->layout : con->layout); if (layout == L_DEFAULT) { Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) - if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { - LOG("using child %p / %s instead!\n", child, child->name); - con = child; - break; - } + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + if (rect_contains(child->deco_rect, event->event_x, event->event_y)) { + LOG("using child %p / %s instead!\n", child, child->name); + con = child; + break; + } } #if 0 @@ -231,7 +231,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) { /* see over which rect the user is */ Con *current; - TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + TAILQ_FOREACH(current, &(con->nodes_head), nodes) { if (!rect_contains(current->deco_rect, event->event_x, event->event_y)) continue; diff --git a/src/ipc.c b/src/ipc.c index 0f3dcd82..4c8e61a9 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -74,7 +74,7 @@ static bool mkdirp(const char *path) { */ void ipc_send_event(const char *event, uint32_t message_type, const char *payload) { ipc_client *current; - TAILQ_FOREACH (current, &all_clients, clients) { + TAILQ_FOREACH(current, &all_clients, clients) { /* see if this client is interested in this event */ bool interested = false; for (int i = 0; i < current->num_events; i++) { @@ -344,7 +344,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(array_open); Con *node; if (con->type != CT_DOCKAREA || !inplace_restart) { - TAILQ_FOREACH (node, &(con->nodes_head), nodes) { + TAILQ_FOREACH(node, &(con->nodes_head), nodes) { dump_node(gen, node, inplace_restart); } } @@ -352,14 +352,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("floating_nodes"); y(array_open); - TAILQ_FOREACH (node, &(con->floating_head), floating_windows) { + TAILQ_FOREACH(node, &(con->floating_head), floating_windows) { dump_node(gen, node, inplace_restart); } y(array_close); ystr("focus"); y(array_open); - TAILQ_FOREACH (node, &(con->focus_head), focused) { + TAILQ_FOREACH(node, &(con->focus_head), focused) { y(integer, (long int)node); } y(array_close); @@ -386,7 +386,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("swallows"); y(array_open); Match *match; - TAILQ_FOREACH (match, &(con->swallow_head), matches) { + TAILQ_FOREACH(match, &(con->swallow_head), matches) { y(map_open); if (match->dock != -1) { ystr("dock"); @@ -591,11 +591,11 @@ IPC_HANDLER(get_workspaces) { Con *focused_ws = con_get_workspace(focused); Con *output; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { if (con_is_internal(output)) continue; Con *ws; - TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) { + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { assert(ws->type == CT_WORKSPACE); y(map_open); @@ -656,7 +656,7 @@ IPC_HANDLER(get_outputs) { y(array_open); Output *output; - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { y(map_open); ystr("name"); @@ -710,9 +710,9 @@ IPC_HANDLER(get_marks) { y(array_open); Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) - if (con->mark != NULL) - ystr(con->mark); + TAILQ_FOREACH(con, &all_cons, all_cons) + if (con->mark != NULL) + ystr(con->mark); y(array_close); @@ -766,7 +766,7 @@ IPC_HANDLER(get_bar_config) { if (message_size == 0) { y(array_open); Barconfig *current; - TAILQ_FOREACH (current, &barconfigs, configs) { + TAILQ_FOREACH(current, &barconfigs, configs) { ystr(current->id); } y(array_close); @@ -786,7 +786,7 @@ IPC_HANDLER(get_bar_config) { strncpy(bar_id, (const char *)message, message_size); LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id); Barconfig *current, *config = NULL; - TAILQ_FOREACH (current, &barconfigs, configs) { + TAILQ_FOREACH(current, &barconfigs, configs) { if (strcmp(current->id, bar_id) != 0) continue; @@ -852,7 +852,7 @@ IPC_HANDLER(subscribe) { ipc_client *current, *client = NULL; /* Search the ipc_client structure for this connection */ - TAILQ_FOREACH (current, &all_clients, clients) { + TAILQ_FOREACH(current, &all_clients, clients) { if (current->fd != fd) continue; @@ -932,7 +932,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) { /* Delete the client from the list of clients */ ipc_client *current; - TAILQ_FOREACH (current, &all_clients, clients) { + TAILQ_FOREACH(current, &all_clients, clients) { if (current->fd != w->fd) continue; diff --git a/src/load_layout.c b/src/load_layout.c index 494dbd06..6690d2b0 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -110,10 +110,10 @@ static int json_end_array(void *ctx) { if (parsing_focus) { /* Clear the list of focus mappings */ struct focus_mapping *mapping; - TAILQ_FOREACH_REVERSE (mapping, &focus_mappings, focus_mappings_head, focus_mappings) { + TAILQ_FOREACH_REVERSE(mapping, &focus_mappings, focus_mappings_head, focus_mappings) { LOG("focus (reverse) %d\n", mapping->old_id); Con *con; - TAILQ_FOREACH (con, &(json_node->focus_head), focused) { + TAILQ_FOREACH(con, &(json_node->focus_head), focused) { if (con->old_id != mapping->old_id) continue; LOG("got it! %p\n", con); diff --git a/src/main.c b/src/main.c index c839cf3b..632e68c4 100644 --- a/src/main.c +++ b/src/main.c @@ -884,7 +884,7 @@ int main(int argc, char *argv[]) { /* Autostarting exec-lines */ if (autostart) { struct Autostart *exec; - TAILQ_FOREACH (exec, &autostarts, autostarts) { + TAILQ_FOREACH(exec, &autostarts, autostarts) { LOG("auto-starting %s\n", exec->command); start_application(exec->command, exec->no_startup_id); } @@ -892,14 +892,14 @@ int main(int argc, char *argv[]) { /* Autostarting exec_always-lines */ struct Autostart *exec_always; - TAILQ_FOREACH (exec_always, &autostarts_always, autostarts_always) { + TAILQ_FOREACH(exec_always, &autostarts_always, autostarts_always) { LOG("auto-starting (always!) %s\n", exec_always->command); start_application(exec_always->command, exec_always->no_startup_id); } /* Start i3bar processes for all configured bars */ Barconfig *barconfig; - TAILQ_FOREACH (barconfig, &barconfigs, configs) { + TAILQ_FOREACH(barconfig, &barconfigs, configs) { char *command = NULL; sasprintf(&command, "%s --bar_id=%s --socket=\"%s\"", barconfig->i3bar_command ? barconfig->i3bar_command : "i3bar", diff --git a/src/manage.c b/src/manage.c index 8f442e0a..227e9d24 100644 --- a/src/manage.c +++ b/src/manage.c @@ -56,16 +56,16 @@ void restore_geometry(void) { DLOG("Restoring geometry\n"); Con *con; - TAILQ_FOREACH (con, &all_cons, all_cons) - if (con->window) { - DLOG("Re-adding X11 border of %d px\n", con->border_width); - con->window_rect.width += (2 * con->border_width); - con->window_rect.height += (2 * con->border_width); - xcb_set_window_rect(conn, con->window->id, con->window_rect); - DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y); - xcb_reparent_window(conn, con->window->id, root, - con->rect.x, con->rect.y); - } + TAILQ_FOREACH(con, &all_cons, all_cons) + if (con->window) { + DLOG("Re-adding X11 border of %d px\n", con->border_width); + con->window_rect.width += (2 * con->border_width); + con->window_rect.height += (2 * con->border_width); + xcb_set_window_rect(conn, con->window->id, con->window_rect); + DLOG("placing window %08x at %d %d\n", con->window->id, con->rect.x, con->rect.y); + xcb_reparent_window(conn, con->window->id, root, + con->rect.x, con->rect.y); + } /* Strictly speaking, this line doesn’t really belong here, but since we * are syncing, let’s un-register as a window manager first */ diff --git a/src/match.c b/src/match.c index a810068e..dc4d422f 100644 --- a/src/match.c +++ b/src/match.c @@ -136,7 +136,7 @@ bool match_matches_window(Match *match, i3Window *window) { return false; } /* if we find a window that is newer than this one, bail */ - TAILQ_FOREACH (con, &all_cons, all_cons) { + TAILQ_FOREACH(con, &all_cons, all_cons) { if ((con->window != NULL) && _i3_timercmp(con->window->urgent, window->urgent, > )) { return false; @@ -151,7 +151,7 @@ bool match_matches_window(Match *match, i3Window *window) { return false; } /* if we find a window that is older than this one (and not 0), bail */ - TAILQ_FOREACH (con, &all_cons, all_cons) { + TAILQ_FOREACH(con, &all_cons, all_cons) { if ((con->window != NULL) && (con->window->urgent.tv_sec != 0) && _i3_timercmp(con->window->urgent, window->urgent, < )) { diff --git a/src/output.c b/src/output.c index b037335a..6499c65d 100644 --- a/src/output.c +++ b/src/output.c @@ -18,9 +18,9 @@ Con *output_get_content(Con *output) { Con *child; - TAILQ_FOREACH (child, &(output->nodes_head), nodes) - if (child->type == CT_CON) - return child; + TAILQ_FOREACH(child, &(output->nodes_head), nodes) + if (child->type == CT_CON) + return child; return NULL; } diff --git a/src/randr.c b/src/randr.c index 0755c0d7..a4a0f6fd 100644 --- a/src/randr.c +++ b/src/randr.c @@ -37,9 +37,9 @@ static bool randr_disabled = false; */ static Output *get_output_by_id(xcb_randr_output_t id) { Output *output; - TAILQ_FOREACH (output, &outputs, outputs) - if (output->id == id) - return output; + TAILQ_FOREACH(output, &outputs, outputs) + if (output->id == id) + return output; return NULL; } @@ -50,10 +50,10 @@ static Output *get_output_by_id(xcb_randr_output_t id) { */ Output *get_output_by_name(const char *name) { Output *output; - TAILQ_FOREACH (output, &outputs, outputs) - if (output->active && - strcasecmp(output->name, name) == 0) - return output; + TAILQ_FOREACH(output, &outputs, outputs) + if (output->active && + strcasecmp(output->name, name) == 0) + return output; return NULL; } @@ -65,9 +65,9 @@ Output *get_output_by_name(const char *name) { Output *get_first_output(void) { Output *output; - TAILQ_FOREACH (output, &outputs, outputs) - if (output->active) - return output; + TAILQ_FOREACH(output, &outputs, outputs) + if (output->active) + return output; die("No usable outputs available.\n"); } @@ -79,7 +79,7 @@ Output *get_first_output(void) { */ Output *get_output_containing(unsigned int x, unsigned int y) { Output *output; - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active) continue; DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n", @@ -104,7 +104,7 @@ bool contained_by_output(Rect rect) { Output *output; int lx = rect.x, uy = rect.y; int rx = rect.x + rect.width, by = rect.y + rect.height; - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active) continue; DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n", @@ -163,7 +163,7 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far *other; Output *output, *best = NULL; - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active) continue; @@ -256,7 +256,7 @@ void output_init_con(Output *output) { /* Search for a Con with that name directly below the root node. There * might be one from a restored layout. */ - TAILQ_FOREACH (current, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(current, &(croot->nodes_head), nodes) { if (strcmp(current->name, output->name) != 0) continue; @@ -355,15 +355,15 @@ void output_init_con(Output *output) { void init_ws_for_output(Output *output, Con *content) { /* go through all assignments and move the existing workspaces to this output */ struct Workspace_Assignment *assignment; - TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->output, output->name) != 0) continue; /* check if this workspace actually exists */ Con *workspace = NULL, *out; - TAILQ_FOREACH (out, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(out), - !strcasecmp(child->name, assignment->name)); + TAILQ_FOREACH(out, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(out), + !strcasecmp(child->name, assignment->name)); if (workspace == NULL) continue; @@ -401,10 +401,10 @@ void init_ws_for_output(Output *output, Con *content) { Con *ws_out_content = output_get_content(workspace_out); Con *floating_con; - TAILQ_FOREACH (floating_con, &(workspace->floating_head), floating_windows) - /* NB: We use output->con here because content is not yet rendered, + TAILQ_FOREACH(floating_con, &(workspace->floating_head), floating_windows) + /* NB: We use output->con here because content is not yet rendered, * so it has a rect of {0, 0, 0, 0}. */ - floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect)); + floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect)); con_detach(workspace); con_attach(workspace, content, false); @@ -436,7 +436,7 @@ void init_ws_for_output(Output *output, Con *content) { } /* otherwise, we create the first assigned ws for this output */ - TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->output, output->name) != 0) continue; @@ -478,8 +478,8 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) { /* Fix the position of all floating windows on this output. * The 'rect' of each workspace will be updated in src/render.c. */ - TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) { - TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) { + TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) { + TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) { floating_fix_coordinates(child, &(workspace->rect), &(output->con->rect)); } } @@ -488,7 +488,7 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) { * the workspaces and their childs depending on output resolution. This is * only done for workspaces with maximum one child. */ if (config.default_orientation == NO_ORIENTATION) { - TAILQ_FOREACH (workspace, &(content->nodes_head), nodes) { + TAILQ_FOREACH(workspace, &(content->nodes_head), nodes) { /* Workspaces with more than one child are left untouched because * we do not want to change an existing layout. */ if (con_num_children(workspace) > 1) @@ -640,7 +640,7 @@ void randr_query_outputs(void) { /* Check for clones, disable the clones and reduce the mode to the * lowest common mode */ - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active || output->to_be_disabled) continue; DLOG("output %p / %s, position (%d, %d), checking for clones\n", @@ -681,7 +681,7 @@ void randr_query_outputs(void) { * necessary because in the next step, a clone might get disabled. Example: * LVDS1 active, VGA1 gets activated as a clone of LVDS1 (has no con). * LVDS1 gets disabled. */ - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (output->active && output->con == NULL) { DLOG("Need to initialize a Con for output %s\n", output->name); output_init_con(output); @@ -691,7 +691,7 @@ void randr_query_outputs(void) { /* Handle outputs which have a new mode or are disabled now (either * because the user disabled them or because they are clones) */ - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (output->to_be_disabled) { output->active = false; DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); @@ -731,8 +731,8 @@ void randr_query_outputs(void) { con_attach(current, first_content, false); DLOG("Fixing the coordinates of floating containers\n"); Con *floating_con; - TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows) - floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect)); + TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows) + floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect)); DLOG("Done, next\n"); } DLOG("re-attached all workspaces\n"); @@ -745,7 +745,7 @@ void randr_query_outputs(void) { /* 3: move the dock clients to the first output */ Con *child; - TAILQ_FOREACH (child, &(output->con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) { if (child->type != CT_DOCKAREA) continue; DLOG("Handling dock con %p\n", child); @@ -788,7 +788,7 @@ void randr_query_outputs(void) { get_first_output(); /* Just go through each active output and assign one workspace */ - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active) continue; Con *content = output_get_content(output->con); @@ -799,7 +799,7 @@ void randr_query_outputs(void) { } /* Focus the primary screen, if possible */ - TAILQ_FOREACH (output, &outputs, outputs) { + TAILQ_FOREACH(output, &outputs, outputs) { if (!output->primary || !output->con) continue; diff --git a/src/render.c b/src/render.c index 38a4d786..ed35eb0c 100644 --- a/src/render.c +++ b/src/render.c @@ -41,7 +41,7 @@ static void render_l_output(Con *con) { /* Find the content container and ensure that there is exactly one. Also * check for any non-CT_DOCKAREA clients. */ Con *content = NULL; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (child->type == CT_CON) { if (content != NULL) { DLOG("More than one CT_CON on output container\n"); @@ -77,19 +77,19 @@ static void render_l_output(Con *con) { /* First pass: determine the height of all CT_DOCKAREAs (the sum of their * children) and figure out how many pixels we have left for the rest */ - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (child->type != CT_DOCKAREA) continue; child->rect.height = 0; - TAILQ_FOREACH (dockchild, &(child->nodes_head), nodes) - child->rect.height += dockchild->geometry.height; + TAILQ_FOREACH(dockchild, &(child->nodes_head), nodes) + child->rect.height += dockchild->geometry.height; height -= child->rect.height; } /* Second pass: Set the widths/heights */ - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { if (child->type == CT_CON) { child->rect.x = x; child->rect.y = y; @@ -224,7 +224,7 @@ void render_con(Con *con, bool render_fullscreen) { Con *child; int i = 0, assigned = 0; int total = con_orientation(con) == HORIZ ? rect.width : rect.height; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { double percentage = child->percent > 0.0 ? child->percent : 1.0 / children; assigned += sizes[i++] = percentage * total; } @@ -247,7 +247,7 @@ void render_con(Con *con, bool render_fullscreen) { render_l_output(con); } else if (con->type == CT_ROOT) { Con *output; - TAILQ_FOREACH (output, &(con->nodes_head), nodes) { + TAILQ_FOREACH(output, &(con->nodes_head), nodes) { render_con(output, false); } @@ -256,7 +256,7 @@ void render_con(Con *con, bool render_fullscreen) { * all times. This is important when the user places floating * windows/containers so that they overlap on another output. */ DLOG("Rendering floating windows:\n"); - TAILQ_FOREACH (output, &(con->nodes_head), nodes) { + TAILQ_FOREACH(output, &(con->nodes_head), nodes) { if (con_is_internal(output)) continue; /* Get the active workspace of that output */ @@ -268,7 +268,7 @@ void render_con(Con *con, bool render_fullscreen) { Con *workspace = TAILQ_FIRST(&(content->focus_head)); Con *fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT); Con *child; - TAILQ_FOREACH (child, &(workspace->floating_head), floating_windows) { + TAILQ_FOREACH(child, &(workspace->floating_head), floating_windows) { /* Don’t render floating windows when there is a fullscreen window * on that workspace. Necessary to make floating fullscreen work * correctly (ticket #564). */ @@ -321,7 +321,7 @@ void render_con(Con *con, bool render_fullscreen) { } else { /* FIXME: refactor this into separate functions: */ Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { assert(children > 0); /* default layout */ @@ -428,8 +428,8 @@ void render_con(Con *con, bool render_fullscreen) { /* in a stacking or tabbed container, we ensure the focused client is raised */ if (con->layout == L_STACKED || con->layout == L_TABBED) { - TAILQ_FOREACH_REVERSE (child, &(con->focus_head), focus_head, focused) - x_raise_con(child); + TAILQ_FOREACH_REVERSE(child, &(con->focus_head), focus_head, focused) + x_raise_con(child); if ((child = TAILQ_FIRST(&(con->focus_head)))) { /* By rendering the stacked container again, we handle the case * that we have a non-leaf-container inside the stack. In that diff --git a/src/restore_layout.c b/src/restore_layout.c index ba82f76f..d6d7927d 100644 --- a/src/restore_layout.c +++ b/src/restore_layout.c @@ -137,7 +137,7 @@ static void update_placeholder_contents(placeholder_state *state) { Match *swallows; int n = 0; - TAILQ_FOREACH (swallows, &(state->con->swallow_head), matches) { + TAILQ_FOREACH(swallows, &(state->con->swallow_head), matches) { char *serialized = NULL; #define APPEND_REGEX(re_name) \ @@ -222,10 +222,10 @@ static void open_placeholder_window(Con *con) { } Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) { + TAILQ_FOREACH(child, &(con->nodes_head), nodes) { open_placeholder_window(child); } - TAILQ_FOREACH (child, &(con->floating_head), floating_windows) { + TAILQ_FOREACH(child, &(con->floating_head), floating_windows) { open_placeholder_window(child); } } @@ -239,10 +239,10 @@ static void open_placeholder_window(Con *con) { */ void restore_open_placeholder_windows(Con *parent) { Con *child; - TAILQ_FOREACH (child, &(parent->nodes_head), nodes) { + TAILQ_FOREACH(child, &(parent->nodes_head), nodes) { open_placeholder_window(child); } - TAILQ_FOREACH (child, &(parent->floating_head), floating_windows) { + TAILQ_FOREACH(child, &(parent->floating_head), floating_windows) { open_placeholder_window(child); } @@ -258,7 +258,7 @@ void restore_open_placeholder_windows(Con *parent) { */ bool restore_kill_placeholder(xcb_window_t placeholder) { placeholder_state *state; - TAILQ_FOREACH (state, &state_head, state) { + TAILQ_FOREACH(state, &state_head, state) { if (state->window != placeholder) continue; @@ -277,7 +277,7 @@ bool restore_kill_placeholder(xcb_window_t placeholder) { static void expose_event(xcb_expose_event_t *event) { placeholder_state *state; - TAILQ_FOREACH (state, &state_head, state) { + TAILQ_FOREACH(state, &state_head, state) { if (state->window != event->window) continue; @@ -305,7 +305,7 @@ static void expose_event(xcb_expose_event_t *event) { */ static void configure_notify(xcb_configure_notify_event_t *event) { placeholder_state *state; - TAILQ_FOREACH (state, &state_head, state) { + TAILQ_FOREACH(state, &state_head, state) { if (state->window != event->window) continue; diff --git a/src/scratchpad.c b/src/scratchpad.c index 8ca81eae..3b7c2a72 100644 --- a/src/scratchpad.c +++ b/src/scratchpad.c @@ -116,7 +116,7 @@ void scratchpad_show(Con *con) { * unfocused scratchpad on the current workspace and focus it */ Con *walk_con; Con *focused_ws = con_get_workspace(focused); - TAILQ_FOREACH (walk_con, &(focused_ws->floating_head), floating_windows) { + TAILQ_FOREACH(walk_con, &(focused_ws->floating_head), floating_windows) { if (!con && (floating = con_inside_floating(walk_con)) && floating->scratchpad_state != SCRATCHPAD_NONE && floating != con_inside_floating(focused)) { @@ -134,7 +134,7 @@ void scratchpad_show(Con *con) { * visible scratchpad window on another workspace. In this case we move it * to the current workspace. */ focused_ws = con_get_workspace(focused); - TAILQ_FOREACH (walk_con, &all_cons, all_cons) { + TAILQ_FOREACH(walk_con, &all_cons, all_cons) { Con *walk_ws = con_get_workspace(walk_con); if (!con && walk_ws && !con_is_internal(walk_ws) && focused_ws != walk_ws && @@ -257,7 +257,7 @@ void scratchpad_fix_resolution(void) { Con *output; int new_width = -1, new_height = -1; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { if (output == __i3_output) continue; DLOG("output %s's resolution: (%d, %d) %d x %d\n", @@ -288,7 +288,7 @@ void scratchpad_fix_resolution(void) { DLOG("Fixing coordinates of scratchpad windows\n"); Con *con; - TAILQ_FOREACH (con, &(__i3_scratch->floating_head), floating_windows) { + TAILQ_FOREACH(con, &(__i3_scratch->floating_head), floating_windows) { floating_fix_coordinates(con, &old_rect, &new_rect); } } diff --git a/src/sighandler.c b/src/sighandler.c index 77333863..8b5bfed4 100644 --- a/src/sighandler.c +++ b/src/sighandler.c @@ -253,7 +253,7 @@ static void open_popups() { /* Open a popup window on each virtual screen */ Output *screen; xcb_window_t win; - TAILQ_FOREACH (screen, &outputs, outputs) { + TAILQ_FOREACH(screen, &outputs, outputs) { if (!screen->active) continue; win = open_input_window(conn, screen->rect, width, height); diff --git a/src/startup.c b/src/startup.c index a5d8e117..d6fe6d40 100644 --- a/src/startup.c +++ b/src/startup.c @@ -37,7 +37,7 @@ static void startup_timeout(EV_P_ ev_timer *w, int revents) { DLOG("Timeout for startup sequence %s\n", id); struct Startup_Sequence *current, *sequence = NULL; - TAILQ_FOREACH (current, &startup_sequences, sequences) { + TAILQ_FOREACH(current, &startup_sequences, sequences) { if (strcmp(current->id, id) != 0) continue; @@ -219,7 +219,7 @@ void startup_monitor_event(SnMonitorEvent *event, void *userdata) { /* Get the corresponding internal startup sequence */ const char *id = sn_startup_sequence_get_id(snsequence); struct Startup_Sequence *current, *sequence = NULL; - TAILQ_FOREACH (current, &startup_sequences, sequences) { + TAILQ_FOREACH(current, &startup_sequences, sequences) { if (strcmp(current->id, id) != 0) continue; @@ -309,7 +309,7 @@ struct Startup_Sequence *startup_sequence_get(i3Window *cwindow, } struct Startup_Sequence *current, *sequence = NULL; - TAILQ_FOREACH (current, &startup_sequences, sequences) { + TAILQ_FOREACH(current, &startup_sequences, sequences) { if (strcmp(current->id, startup_id) != 0) continue; diff --git a/src/tree.c b/src/tree.c index da73e14d..f22d4ba2 100644 --- a/src/tree.c +++ b/src/tree.c @@ -167,9 +167,9 @@ Con *tree_open_con(Con *con, i3Window *window) { static bool _is_con_mapped(Con *con) { Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) - if (_is_con_mapped(child)) - return true; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + if (_is_con_mapped(child)) + return true; return con->mapped; } @@ -489,13 +489,13 @@ static void mark_unmapped(Con *con) { Con *current; con->mapped = false; - TAILQ_FOREACH (current, &(con->nodes_head), nodes) - mark_unmapped(current); + TAILQ_FOREACH(current, &(con->nodes_head), nodes) + mark_unmapped(current); if (con->type == CT_WORKSPACE) { /* We need to call mark_unmapped on floating nodes aswell since we can * make containers floating. */ - TAILQ_FOREACH (current, &(con->floating_head), floating_windows) - mark_unmapped(current); + TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + mark_unmapped(current); } } diff --git a/src/workspace.c b/src/workspace.c index 739b0e0a..d626d17c 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -44,8 +44,8 @@ static void _workspace_apply_default_orientation(Con *ws) { Con *workspace_get(const char *num, bool *created) { Con *output, *workspace = NULL; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num)); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num)); if (workspace == NULL) { LOG("Creating new workspace \"%s\"\n", num); @@ -59,7 +59,7 @@ Con *workspace_get(const char *num, bool *created) { * -1. */ long parsed_num = ws_name_to_number(num); - TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->name, num) == 0) { DLOG("Found workspace name assignment to output \"%s\"\n", assignment->output); GREP_FIRST(output, croot, !strcmp(child->name, assignment->output)); @@ -117,7 +117,7 @@ Con *create_workspace_on_output(Output *output, Con *content) { /* try the configured workspace bindings first to find a free name */ Binding *bind; - TAILQ_FOREACH (bind, bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { DLOG("binding with command %s\n", bind->command); if (strlen(bind->command) < strlen("workspace ") || strncasecmp(bind->command, "workspace", strlen("workspace")) != 0) @@ -156,7 +156,7 @@ Con *create_workspace_on_output(Output *output, Con *content) { * find a new workspace, etc… */ bool assigned = false; struct Workspace_Assignment *assignment; - TAILQ_FOREACH (assignment, &ws_assignments, ws_assignments) { + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { if (strcmp(assignment->name, ws->name) != 0 || strcmp(assignment->output, output->name) == 0) continue; @@ -169,8 +169,8 @@ Con *create_workspace_on_output(Output *output, Con *content) { continue; current = NULL; - TAILQ_FOREACH (out, &(croot->nodes_head), nodes) - GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name)); + TAILQ_FOREACH(out, &(croot->nodes_head), nodes) + GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name)); exists = (current != NULL); if (!exists) { @@ -201,8 +201,8 @@ Con *create_workspace_on_output(Output *output, Con *content) { ws->num = c; current = NULL; - TAILQ_FOREACH (out, &(croot->nodes_head), nodes) - GREP_FIRST(current, output_get_content(out), child->num == ws->num); + TAILQ_FOREACH(out, &(croot->nodes_head), nodes) + GREP_FIRST(current, output_get_content(out), child->num == ws->num); exists = (current != NULL); DLOG("result for ws %d: exists = %d\n", c, exists); @@ -245,7 +245,7 @@ bool workspace_is_visible(Con *ws) { Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) { Con *current; - TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + TAILQ_FOREACH(current, &(con->nodes_head), nodes) { if (current != exclude && current->sticky_group != NULL && current->window != NULL && @@ -257,7 +257,7 @@ Con *_get_sticky(Con *con, const char *sticky_group, Con *exclude) { return recurse; } - TAILQ_FOREACH (current, &(con->floating_head), floating_windows) { + TAILQ_FOREACH(current, &(con->floating_head), floating_windows) { if (current != exclude && current->sticky_group != NULL && current->window != NULL && @@ -284,7 +284,7 @@ static void workspace_reassign_sticky(Con *con) { /* 1: go through all containers */ /* handle all children and floating windows of this node */ - TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + TAILQ_FOREACH(current, &(con->nodes_head), nodes) { if (current->sticky_group == NULL) { workspace_reassign_sticky(current); continue; @@ -312,8 +312,8 @@ static void workspace_reassign_sticky(Con *con) { LOG("re-assigned window from src %p to dest %p\n", src, current); } - TAILQ_FOREACH (current, &(con->floating_head), floating_windows) - workspace_reassign_sticky(current); + TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + workspace_reassign_sticky(current); } /* @@ -344,7 +344,7 @@ static void _workspace_show(Con *workspace) { /* disable fullscreen for the other workspaces and get the workspace we are * currently on. */ - TAILQ_FOREACH (current, &(workspace->parent->nodes_head), nodes) { + TAILQ_FOREACH(current, &(workspace->parent->nodes_head), nodes) { if (current->fullscreen_mode == CF_OUTPUT) old = current; current->fullscreen_mode = CF_NONE; @@ -472,11 +472,11 @@ Con *workspace_next(void) { next = TAILQ_NEXT(current, nodes); } else { /* If currently a numbered workspace, find next numbered workspace. */ - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH (output_get_content(output)) { + NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child->num == -1) @@ -493,11 +493,11 @@ Con *workspace_next(void) { /* Find next named workspace. */ if (!next) { bool found_current = false; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH (output_get_content(output)) { + NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child == current) { @@ -512,11 +512,11 @@ Con *workspace_next(void) { /* Find first workspace. */ if (!next) { - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH (output_get_content(output)) { + NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!next || (child->num != -1 && child->num < next->num)) @@ -544,11 +544,11 @@ Con *workspace_prev(void) { prev = NULL; } else { /* If numbered workspace, find previous numbered workspace. */ - TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) { + TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH_REVERSE (output_get_content(output)) { + NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE || child->num == -1) continue; /* Need to check child against current and previous because we @@ -563,11 +563,11 @@ Con *workspace_prev(void) { /* Find previous named workspace. */ if (!prev) { bool found_current = false; - TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) { + TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH_REVERSE (output_get_content(output)) { + NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child == current) { @@ -582,11 +582,11 @@ Con *workspace_prev(void) { /* Find last workspace. */ if (!prev) { - TAILQ_FOREACH_REVERSE (output, &(croot->nodes_head), nodes_head, nodes) { + TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) continue; - NODES_FOREACH_REVERSE (output_get_content(output)) { + NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!prev || child->num > prev->num) @@ -613,7 +613,7 @@ Con *workspace_next_on_output(void) { next = TAILQ_NEXT(current, nodes); } else { /* If currently a numbered workspace, find next numbered workspace. */ - NODES_FOREACH (output_get_content(output)) { + NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child->num == -1) @@ -629,7 +629,7 @@ Con *workspace_next_on_output(void) { /* Find next named workspace. */ if (!next) { bool found_current = false; - NODES_FOREACH (output_get_content(output)) { + NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child == current) { @@ -643,7 +643,7 @@ Con *workspace_next_on_output(void) { /* Find first workspace. */ if (!next) { - NODES_FOREACH (output_get_content(output)) { + NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!next || (child->num != -1 && child->num < next->num)) @@ -671,7 +671,7 @@ Con *workspace_prev_on_output(void) { prev = NULL; } else { /* If numbered workspace, find previous numbered workspace. */ - NODES_FOREACH_REVERSE (output_get_content(output)) { + NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE || child->num == -1) continue; /* Need to check child against current and previous because we @@ -685,7 +685,7 @@ Con *workspace_prev_on_output(void) { /* Find previous named workspace. */ if (!prev) { bool found_current = false; - NODES_FOREACH_REVERSE (output_get_content(output)) { + NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (child == current) { @@ -699,7 +699,7 @@ Con *workspace_prev_on_output(void) { /* Find last workspace. */ if (!prev) { - NODES_FOREACH_REVERSE (output_get_content(output)) { + NODES_FOREACH_REVERSE(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; if (!prev || child->num > prev->num) @@ -742,13 +742,13 @@ Con *workspace_back_and_forth_get(void) { static bool get_urgency_flag(Con *con) { Con *child; - TAILQ_FOREACH (child, &(con->nodes_head), nodes) - if (child->urgent || get_urgency_flag(child)) - return true; + TAILQ_FOREACH(child, &(con->nodes_head), nodes) + if (child->urgent || get_urgency_flag(child)) + return true; - TAILQ_FOREACH (child, &(con->floating_head), floating_windows) - if (child->urgent || get_urgency_flag(child)) - return true; + TAILQ_FOREACH(child, &(con->floating_head), floating_windows) + if (child->urgent || get_urgency_flag(child)) + return true; return false; } diff --git a/src/x.c b/src/x.c index ee638fc2..02079a01 100644 --- a/src/x.c +++ b/src/x.c @@ -76,9 +76,9 @@ TAILQ_HEAD(initial_mapping_head, con_state) initial_mapping_head = */ static con_state *state_for_frame(xcb_window_t window) { con_state *state; - CIRCLEQ_FOREACH (state, &state_head, state) - if (state->id == window) - return state; + CIRCLEQ_FOREACH(state, &state_head, state) + if (state->id == window) + return state; /* TODO: better error handling? */ ELOG("No state found\n"); @@ -579,11 +579,11 @@ void x_deco_recurse(Con *con) { con_state *state = state_for_frame(con->frame); if (!leaf) { - TAILQ_FOREACH (current, &(con->nodes_head), nodes) - x_deco_recurse(current); + TAILQ_FOREACH(current, &(con->nodes_head), nodes) + x_deco_recurse(current); - TAILQ_FOREACH (current, &(con->floating_head), floating_windows) - x_deco_recurse(current); + TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + x_deco_recurse(current); if (state->mapped) xcb_copy_area(conn, con->pixmap, con->frame, con->pm_gc, 0, 0, 0, 0, con->rect.width, con->rect.height); @@ -620,7 +620,7 @@ void x_push_node(Con *con) { /* Calculate the height of all window decorations which will be drawn on to * this frame. */ uint32_t max_y = 0, max_height = 0; - TAILQ_FOREACH (current, &(con->nodes_head), nodes) { + TAILQ_FOREACH(current, &(con->nodes_head), nodes) { Rect *dr = &(current->deco_rect); if (dr->y >= max_y && dr->height >= max_height) { max_y = dr->y; @@ -799,8 +799,8 @@ void x_push_node(Con *con) { /* Handle all children and floating windows of this node. We recurse * in focus order to display the focused client in a stack first when * switching workspaces (reduces flickering). */ - TAILQ_FOREACH (current, &(con->focus_head), focused) - x_push_node(current); + TAILQ_FOREACH(current, &(con->focus_head), focused) + x_push_node(current); } /* @@ -844,11 +844,11 @@ static void x_push_node_unmaps(Con *con) { } /* handle all children and floating windows of this node */ - TAILQ_FOREACH (current, &(con->nodes_head), nodes) - x_push_node_unmaps(current); + TAILQ_FOREACH(current, &(con->nodes_head), nodes) + x_push_node_unmaps(current); - TAILQ_FOREACH (current, &(con->floating_head), floating_windows) - x_push_node_unmaps(current); + TAILQ_FOREACH(current, &(con->floating_head), floating_windows) + x_push_node_unmaps(current); } /* @@ -861,7 +861,7 @@ static bool is_con_attached(Con *con) { return false; Con *current; - TAILQ_FOREACH (current, &(con->parent->nodes_head), nodes) { + TAILQ_FOREACH(current, &(con->parent->nodes_head), nodes) { if (current == con) return true; } @@ -892,7 +892,7 @@ void x_push_changes(Con *con) { DLOG("-- PUSHING WINDOW STACK --\n"); //DLOG("Disabling EnterNotify\n"); uint32_t values[1] = {XCB_NONE}; - CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } @@ -903,9 +903,9 @@ void x_push_changes(Con *con) { /* count first, necessary to (re)allocate memory for the bottom-to-top * stack afterwards */ int cnt = 0; - CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) - if (con_has_managed_window(state->con)) - cnt++; + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) + if (con_has_managed_window(state->con)) + cnt++; /* The bottom-to-top window stack of all windows which are managed by i3. * Used for x_get_window_stack(). */ @@ -920,7 +920,7 @@ void x_push_changes(Con *con) { xcb_window_t *walk = client_list_windows; /* X11 correctly represents the stack if we push it from bottom to top */ - CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { if (con_has_managed_window(state->con)) memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t)); @@ -951,7 +951,7 @@ void x_push_changes(Con *con) { walk = client_list_windows; /* reorder by initial mapping */ - TAILQ_FOREACH (state, &initial_mapping_head, initial_mapping_order) { + TAILQ_FOREACH(state, &initial_mapping_head, initial_mapping_order) { if (con_has_managed_window(state->con)) *walk++ = state->con->window->id; } @@ -984,7 +984,7 @@ void x_push_changes(Con *con) { //DLOG("Re-enabling EnterNotify\n"); values[0] = FRAME_EVENT_MASK; - CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } @@ -1056,7 +1056,7 @@ void x_push_changes(Con *con) { * unmapped, the second one appears under the cursor and therefore gets an * EnterNotify event. */ values[0] = FRAME_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; - CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { if (!state->unmap_now) continue; xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); @@ -1066,7 +1066,7 @@ void x_push_changes(Con *con) { x_push_node_unmaps(con); /* save the current stack as old stack */ - CIRCLEQ_FOREACH (state, &state_head, state) { + CIRCLEQ_FOREACH(state, &state_head, state) { CIRCLEQ_REMOVE(&old_state_head, state, old_state); CIRCLEQ_INSERT_TAIL(&old_state_head, state, old_state); } @@ -1155,7 +1155,7 @@ void x_mask_event_mask(uint32_t mask) { uint32_t values[] = {FRAME_EVENT_MASK & mask}; con_state *state; - CIRCLEQ_FOREACH_REVERSE (state, &state_head, state) { + CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) { if (state->mapped) xcb_change_window_attributes(conn, state->id, XCB_CW_EVENT_MASK, values); } diff --git a/src/xinerama.c b/src/xinerama.c index fead802a..9e412e03 100644 --- a/src/xinerama.c +++ b/src/xinerama.c @@ -23,9 +23,9 @@ static int num_screens; */ static Output *get_screen_at(unsigned int x, unsigned int y) { Output *output; - TAILQ_FOREACH (output, &outputs, outputs) - if (output->rect.x == x && output->rect.y == y) - return output; + TAILQ_FOREACH(output, &outputs, outputs) + if (output->rect.x == x && output->rect.y == y) + return output; return NULL; } diff --git a/tests/queue.h b/tests/queue.h index cc129da7..0b3a9c0b 100644 --- a/tests/queue.h +++ b/tests/queue.h @@ -90,435 +90,454 @@ /* * Singly-linked List definitions. */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} +#define SLIST_HEAD(name, type) \ + struct name { \ + struct type *slh_first; /* first element */ \ + } -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} +#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_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(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)) +#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_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_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_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_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(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) +#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(name, type) \ + struct name { \ + struct type *lh_first; /* first element */ \ + } -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } +#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 */ \ -} +#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_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)) +#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_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_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_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_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_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) +#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(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_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} +#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_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)) +#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_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_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_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_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) +#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(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_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 */ \ -} +#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)) +#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_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(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)) +#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_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_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_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_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_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_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) +#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(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_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 */ \ -} +#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_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(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)) +#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_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_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_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_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_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_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) +#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 index d6bf9a24..abc3b3b5 100644 --- a/tests/swap.c +++ b/tests/swap.c @@ -24,11 +24,11 @@ void dump() { printf("first: %d\n", e->abc); e = TAILQ_LAST(&head, objhead); printf("last: %d\n", e->abc); - TAILQ_FOREACH (e, &head, entry) { + TAILQ_FOREACH(e, &head, entry) { printf(" %d\n", e->abc); } printf("again, but reverse:\n"); - TAILQ_FOREACH_REVERSE (e, &head, objhead, entry) { + TAILQ_FOREACH_REVERSE(e, &head, objhead, entry) { printf(" %d\n", e->abc); } printf("done\n\n"); From b47f480728a6d053c117e4b99ee4c78d99ba6a71 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 16 Jun 2014 02:50:47 -0400 Subject: [PATCH 009/103] Implement EWMH number of desktops property _NET_NUMBER_OF_DESKTOPS: > This property SHOULD be set and updated by the Window Manager to > indicate the number of virtual desktops. We interpret this property as the number of noninternal workspaces. --- include/atoms.xmacro | 1 + include/ewmh.h | 6 ++++++ src/ewmh.c | 23 ++++++++++++++++++++++- src/main.c | 3 ++- src/workspace.c | 2 ++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index 90b02616..12e9ee28 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -16,6 +16,7 @@ xmacro(_NET_WM_STRUT_PARTIAL) xmacro(_NET_CLIENT_LIST) xmacro(_NET_CLIENT_LIST_STACKING) xmacro(_NET_CURRENT_DESKTOP) +xmacro(_NET_NUMBER_OF_DESKTOPS) xmacro(_NET_ACTIVE_WINDOW) xmacro(_NET_STARTUP_ID) xmacro(_NET_WORKAREA) diff --git a/include/ewmh.h b/include/ewmh.h index 46d6c985..38d612da 100644 --- a/include/ewmh.h +++ b/include/ewmh.h @@ -18,6 +18,12 @@ */ void ewmh_update_current_desktop(void); +/** + * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of + * noninternal workspaces. + */ +void ewmh_update_number_of_desktops(void); + /** * Updates _NET_ACTIVE_WINDOW with the currently focused window. * diff --git a/src/ewmh.c b/src/ewmh.c index 0c860ad0..7ab0c22a 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -40,6 +40,27 @@ void ewmh_update_current_desktop(void) { } } +/* + * Updates _NET_NUMBER_OF_DESKTOPS which we interpret as the number of + * noninternal workspaces. + */ +void ewmh_update_number_of_desktops(void) { + Con *output; + uint32_t idx = 0; + + TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + ++idx; + } + } + + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, + A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx); +} + /* * Updates _NET_ACTIVE_WINDOW with the currently focused window. * @@ -133,7 +154,7 @@ void ewmh_setup_hints(void) { NULL); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 33, 1, &child_window); /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); diff --git a/src/main.c b/src/main.c index 632e68c4..917ae1cb 100644 --- a/src/main.c +++ b/src/main.c @@ -765,8 +765,9 @@ int main(int argc, char *argv[]) { x_set_i3_atoms(); ewmh_update_workarea(); - /* Set the _NET_CURRENT_DESKTOP property. */ + /* Set the ewmh desktop properties. */ ewmh_update_current_desktop(); + ewmh_update_number_of_desktops(); struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); struct ev_io *xkb = scalloc(sizeof(struct ev_io)); diff --git a/src/workspace.c b/src/workspace.c index d626d17c..41c02952 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -92,6 +92,7 @@ Con *workspace_get(const char *num, bool *created) { con_attach(workspace, content, false); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); + ewmh_update_number_of_desktops(); if (created != NULL) *created = true; } else if (created != NULL) { @@ -424,6 +425,7 @@ static void _workspace_show(Con *workspace) { LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name); tree_close(old, DONT_KILL_WINDOW, false, false); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); + ewmh_update_number_of_desktops(); } } From 8a618e4b006000cbd521031f4c191e862da03914 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 17 Jun 2014 02:49:39 -0400 Subject: [PATCH 010/103] bugfix: don't set input focus if not accepted http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 > Clients using the Globally Active model can only use a SetInputFocus request > to acquire the input focus when they do not already have it on receipt of one > of the following events: > * ButtonPress > * ButtonRelease > * Passive-grabbed KeyPress > * Passive-grabbed KeyRelease Since managing a window happens on a MapNotify (which is absent from this list), the window cannot accept input focus, so we should not try to focus the window at all. Fixes an issue with xfce4-notifyd which (correctly) declines focus when we send WM_TAKE_FOCUS, which puts i3 in a state where i3 focus and X focus are different when a notification appears. --- src/ewmh.c | 6 +++--- src/manage.c | 2 +- testcases/t/158-wm_take_focus.t | 14 +++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/ewmh.c b/src/ewmh.c index 7ab0c22a..3ef79937 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -48,9 +48,9 @@ void ewmh_update_number_of_desktops(void) { Con *output; uint32_t idx = 0; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) { + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { Con *ws; - TAILQ_FOREACH (ws, &(output_get_content(output)->nodes_head), nodes) { + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { if (STARTS_WITH(ws->name, "__")) continue; ++idx; @@ -58,7 +58,7 @@ void ewmh_update_number_of_desktops(void) { } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, - A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx); + A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx); } /* diff --git a/src/manage.c b/src/manage.c index 227e9d24..202b0649 100644 --- a/src/manage.c +++ b/src/manage.c @@ -505,7 +505,7 @@ 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) { + if (set_focus && !nc->window->doesnt_accept_focus) { DLOG("Now setting focus.\n"); con_focus(nc); } diff --git a/testcases/t/158-wm_take_focus.t b/testcases/t/158-wm_take_focus.t index ba03913a..050e1162 100644 --- a/testcases/t/158-wm_take_focus.t +++ b/testcases/t/158-wm_take_focus.t @@ -59,6 +59,18 @@ subtest 'Window without WM_TAKE_FOCUS', sub { done_testing; }; +# http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 +# > Clients using the Globally Active model can only use a SetInputFocus request +# > to acquire the input focus when they do not already have it on receipt of one +# > of the following events: +# > * ButtonPress +# > * ButtonRelease +# > * Passive-grabbed KeyPress +# > * Passive-grabbed KeyRelease +# +# Since managing a window happens on a MapNotify (which is absent from this +# list), the window cannot accept input focus, so we should not try to focus +# the window at all. subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub { fresh_workspace; @@ -74,7 +86,7 @@ subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub { $window->map; - ok(recv_take_focus($window), 'got ClientMessage with WM_TAKE_FOCUS atom'); + ok(!recv_take_focus($window), 'did not receive ClientMessage'); done_testing; }; From 0bc73f526d44fa40023144240b221cbed49da3c6 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 19 Jun 2014 12:17:05 +0200 Subject: [PATCH 011/103] tests: explicitly set input hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since i3 honors the “Globally Active Input” focus model, we need to explicitly state that we are not using that in our testcases :). This requires X11::XCB from git to work (commit 71b25dcaafc509e710b8fd7de20c97ac3549fc39). --- testcases/lib/i3test.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/testcases/lib/i3test.pm b/testcases/lib/i3test.pm index a6b982ba..212e78fd 100644 --- a/testcases/lib/i3test.pm +++ b/testcases/lib/i3test.pm @@ -337,6 +337,7 @@ sub open_window { $args{name} //= 'Window ' . counter_window(); my $window = $x->root->create_child(%args); + $window->add_hint('input'); if ($before_map) { # TODO: investigate why _create is not needed From 0df172fd0510c73e9119304c2d126a2742d29a09 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 17 Jun 2014 09:34:13 -0400 Subject: [PATCH 012/103] Feature: implement mouse bindings A configured mouse binding (for example `bindsym button3 kill`) runs its command when the mouse button is pressed over parts of a container. If the binding has no modifer, it will only run when the button is clicked on the window titlebar. Otherwise if the binding has a modifier, it will run over the titlebar or any part of the contained window. fixes #558 --- docs/userguide | 34 ++++++++++++++++++++++++++++++++++ include/bindings.h | 9 +++++---- src/bindings.c | 21 ++++++++++++++------- src/click.c | 24 ++++++++++++++++++++++++ src/key_press.c | 2 +- 5 files changed, 78 insertions(+), 12 deletions(-) diff --git a/docs/userguide b/docs/userguide index 681c19f1..804877d5 100644 --- a/docs/userguide +++ b/docs/userguide @@ -394,6 +394,40 @@ umlauts or special characters 'and' having some comfortably reachable key bindings. For example, when typing, capslock+1 or capslock+2 for switching workspaces is totally convenient. Try it :-). +[[mousebindings]] + +=== Mouse bindings + +A mouse binding makes i3 execute a command upon pressing a specific mouse +button in the scope of the clicked container (see <>). You +can configure mouse bindings in a similar way to key bindings. + +*Syntax*: +---------------------------------- +bindsym [Modifiers+]button[n] command +---------------------------------- + +If the binding has no modifiers, it will only run when you click on the +titlebar of the window. Otherwise, it will run when any part of the window is +clicked. + +*Examples*: +-------------------------------- +# The middle button over a titlebar kills the window +bindsym button2 kill + +# The middle button and a modifer over any part of the window kills the window +bindsym $mod+button2 kill + +# The right button toggles floating +bindsym button3 floating toggle +bindsym $mod+button3 floating toggle + +# The side buttons move the window around +bindsym button9 move left +bindsym button8 move right +-------------------------------- + [[floating_modifier]] === The floating modifier diff --git a/include/bindings.h b/include/bindings.h index 8b73e99e..e2acc313 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -61,9 +61,10 @@ void switch_mode(const char *new_mode); void check_for_duplicate_bindings(struct context *context); /** - * Runs the given binding and handles parse errors. Returns a CommandResult for - * running the binding's command. Caller should render tree if - * needs_tree_render is true. Free with command_result_free(). + * Runs the given binding and handles parse errors. If con is passed, it will + * execute the command binding with that container selected by criteria. + * Returns a CommandResult for running the binding's command. Caller should + * render tree if needs_tree_render is true. Free with command_result_free(). * */ -CommandResult *run_binding(Binding *bind); +CommandResult *run_binding(Binding *bind, Con *con); diff --git a/src/bindings.c b/src/bindings.c index 0ad56682..a7039bf2 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -379,18 +379,25 @@ void check_for_duplicate_bindings(struct context *context) { } /* - * Runs the given binding and handles parse errors. Returns a CommandResult for - * running the binding's command. Caller should render tree if - * needs_tree_render is true. Free with command_result_free(). + * Runs the given binding and handles parse errors. If con is passed, it will + * execute the command binding with that container selected by criteria. + * Returns a CommandResult for running the binding's command. Caller should + * render tree if needs_tree_render is true. Free with command_result_free(). * */ -CommandResult *run_binding(Binding *bind) { +CommandResult *run_binding(Binding *bind, Con *con) { + char *command; + /* We need to copy the command since “reload” may be part of the command, * and then the memory that bind->command points to may not contain the * same data anymore. */ - char *command_copy = sstrdup(bind->command); - CommandResult *result = parse_command(command_copy, NULL); - free(command_copy); + if (con == NULL) + command = sstrdup(bind->command); + else + sasprintf(&command, "[con_id=\"%d\"] %s", con, bind->command); + + CommandResult *result = parse_command(command, NULL); + free(command); if (result->needs_tree_render) tree_render(); diff --git a/src/click.c b/src/click.c index ddc1119b..f501c769 100644 --- a/src/click.c +++ b/src/click.c @@ -177,6 +177,29 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod if (con->parent->type == CT_DOCKAREA) goto done; + /* if the user has bound an action to this click, it should override the + * default behavior. */ + if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) { + Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event); + /* clicks over a window decoration will always trigger the binding and + * clicks on the inside of the window will only trigger a binding if it + * has modifiers. */ + if (bind && (dest == CLICK_DECORATION || (bind->mods && dest == CLICK_INSIDE))) { + CommandResult *result = run_binding(bind, con); + + /* ASYNC_POINTER eats the event */ + xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time); + xcb_flush(conn); + + if (result->needs_tree_render) + tree_render(); + + command_result_free(result); + + return 0; + } + } + /* Any click in a workspace should focus that workspace. If the * workspace is on another output we need to do a workspace_show in * order for i3bar (and others) to notice the change in workspace. */ @@ -300,6 +323,7 @@ done: xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time); xcb_flush(conn); tree_render(); + return 0; } diff --git a/src/key_press.c b/src/key_press.c index 56021da0..95e5079e 100644 --- a/src/key_press.c +++ b/src/key_press.c @@ -30,7 +30,7 @@ void handle_key_press(xcb_key_press_event_t *event) { if (bind == NULL) return; - CommandResult *result = run_binding(bind); + CommandResult *result = run_binding(bind, NULL); if (result->needs_tree_render) tree_render(); From 25ca78bbbad2922695400437b34da44c84906203 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 19 Jun 2014 03:17:52 -0400 Subject: [PATCH 013/103] Implement the window::close event The window::close event should be emitted when a window closes. --- docs/ipc | 1 + src/tree.c | 1 + testcases/t/231-ipc-window-close.t | 52 ++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 testcases/t/231-ipc-window-close.t diff --git a/docs/ipc b/docs/ipc index 4c82d40d..3b9f40bf 100644 --- a/docs/ipc +++ b/docs/ipc @@ -717,6 +717,7 @@ This event consists of a single serialized map containing a property +change (string)+ which indicates the type of the change * +new+ - the window has become managed by i3 +* +close+ - the window has closed * +focus+ - the window has received input focus * +title+ - the window's title has changed * +fullscreen_mode+ - the window has entered or exited fullscreen mode diff --git a/src/tree.c b/src/tree.c index f22d4ba2..b54f77b1 100644 --- a/src/tree.c +++ b/src/tree.c @@ -255,6 +255,7 @@ bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent, bool * X11 Errors are returned when the window was already destroyed */ add_ignore_event(cookie.sequence, 0); } + ipc_send_window_event("close", con); FREE(con->window->class_class); FREE(con->window->class_instance); i3string_free(con->window->name); diff --git a/testcases/t/231-ipc-window-close.t b/testcases/t/231-ipc-window-close.t new file mode 100644 index 00000000..3483cf42 --- /dev/null +++ b/testcases/t/231-ipc-window-close.t @@ -0,0 +1,52 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests that the ipc close event works properly +# +# Bug still in: 4.8-7-gf4a8253 +use i3test; + +my $i3 = i3(get_socket_path()); +$i3->connect()->recv; + +my $cv; +my $t; + +sub reset_test { + $cv = AE::cv; + $t = AE::timer(0.5, 0, sub { $cv->send(0); }); +} + +reset_test; + +$i3->subscribe({ + window => sub { + my ($e) = @_; + if ($e->{change} eq 'close') { + $cv->send($e->{container}); + } + }, + })->recv; + +my $window = open_window; + +cmd 'kill'; +my $con = $cv->recv; + +ok($con, 'closing a window should send the window::close event'); +is($con->{window}, $window->{id}, 'the event should contain information about the window'); + +done_testing; From cf6cc134b8a25578aa4994143e72cde7fcc15c4b Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 19 Jun 2014 04:58:03 -0400 Subject: [PATCH 014/103] Implement the window::move event The window::move event should be emitted when the window moves position in the tree. --- docs/ipc | 1 + src/con.c | 2 + src/move.c | 3 ++ testcases/t/231-ipc-window-move.t | 61 +++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 testcases/t/231-ipc-window-move.t diff --git a/docs/ipc b/docs/ipc index 3b9f40bf..0560d4e3 100644 --- a/docs/ipc +++ b/docs/ipc @@ -721,6 +721,7 @@ This event consists of a single serialized map containing a property * +focus+ - the window has received input focus * +title+ - the window's title has changed * +fullscreen_mode+ - the window has entered or exited fullscreen mode +* +move+ - the window has changed its position in the tree Additionally a +container (object)+ field will be present, which consists of the window's parent container. Be aware that for the "new" event, the diff --git a/src/con.c b/src/con.c index ee4727b3..7934133e 100644 --- a/src/con.c +++ b/src/con.c @@ -832,6 +832,8 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool } CALL(parent, on_remove_child); + + ipc_send_window_event("move", con); } /* diff --git a/src/move.c b/src/move.c index 42510d52..44045f25 100644 --- a/src/move.c +++ b/src/move.c @@ -206,6 +206,7 @@ void tree_move(int direction) { TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused); DLOG("Swapped.\n"); + ipc_send_window_event("move", con); return; } @@ -213,6 +214,7 @@ void tree_move(int direction) { /* If we couldn't find a place to move it on this workspace, * try to move it to a workspace on a different output */ move_to_output_directed(con, direction); + ipc_send_window_event("move", con); return; } @@ -264,4 +266,5 @@ end: FREE(con->deco_render_params); tree_flatten(croot); + ipc_send_window_event("move", con); } diff --git a/testcases/t/231-ipc-window-move.t b/testcases/t/231-ipc-window-move.t new file mode 100644 index 00000000..117d27fb --- /dev/null +++ b/testcases/t/231-ipc-window-move.t @@ -0,0 +1,61 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests that the ipc window::move event works properly +# +# Bug still in: 4.8-7-gf4a8253 +use i3test; + +my $i3 = i3(get_socket_path()); +$i3->connect()->recv; + +my $cv; +my $t; + +sub reset_test { + $cv = AE::cv; + $t = AE::timer(0.5, 0, sub { $cv->send(0); }); +} + +reset_test; + +$i3->subscribe({ + window => sub { + my ($e) = @_; + if ($e->{change} eq 'move') { + $cv->send($e->{container}); + } + }, + })->recv; + +my $dummy_window = open_window; +my $window = open_window; + +cmd 'move right'; +my $con = $cv->recv; + +ok($con, 'moving a window should emit the window::move event'); +is($con->{window}, $window->{id}, 'the event should contain info about the window'); + +reset_test; + +cmd 'move to workspace ws_new'; +$con = $cv->recv; + +ok($con, 'moving a window to a different workspace should emit the window::move event'); +is($con->{window}, $window->{id}, 'the event should contain info about the window'); + +done_testing; From 3f5a0f0024b7c77fadea6431e356c0fc060e2986 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Jan 2014 08:40:03 +0100 Subject: [PATCH 015/103] Switch to xcb-xkb and libxkbcommon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes our last dependency on Xlib! :) (Okay, an Xlib dependency still comes in through other libraries that we link against, but it’s not us. Our code is simpler by this change and uses one less connection to X11.) --- common.mk | 8 +- debian/control | 3 + i3-config-wizard/i3-config-wizard.mk | 4 +- i3-config-wizard/main.c | 47 ++++++- i3bar/i3bar.mk | 4 +- i3bar/src/xcb.c | 199 +++++++++++---------------- include/handlers.h | 1 + include/i3.h | 1 + src/bindings.c | 6 +- src/config.c | 4 +- src/handlers.c | 40 ++++++ src/i3.mk | 4 +- src/main.c | 137 +++--------------- 13 files changed, 200 insertions(+), 258 deletions(-) diff --git a/common.mk b/common.mk index b086bc85..b9e15a28 100644 --- a/common.mk +++ b/common.mk @@ -92,6 +92,7 @@ else XCB_CFLAGS += $(call cflags_for_lib, xcb-util) XCB_LIBS += $(call ldflags_for_lib, xcb-util) endif +XCB_XKB_LIBS := $(call ldflags_for_lib, xcb-xkb,xcb-xkb) # XCB keyboard stuff XCB_KBD_CFLAGS := $(call cflags_for_lib, xcb-keysyms) @@ -105,9 +106,10 @@ XCB_WM_LIBS := $(call ldflags_for_lib, xcb-icccm,xcb-icccm) XCB_WM_LIBS += $(call ldflags_for_lib, xcb-xinerama,xcb-xinerama) XCB_WM_LIBS += $(call ldflags_for_lib, xcb-randr,xcb-randr) -# Xlib -X11_CFLAGS := $(call cflags_for_lib, x11) -X11_LIBS := $(call ldflags_for_lib, x11,X11) +XKB_COMMON_CFLAGS := $(call cflags_for_lib, xkbcommon,xkbcommon) +XKB_COMMON_LIBS := $(call ldflags_for_lib, xkbcommon,xkbcommon) +XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11) +XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11) # Xcursor XCURSOR_CFLAGS := $(call cflags_for_lib, xcb-cursor) diff --git a/debian/control b/debian/control index b36d5db4..9f6518e0 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,9 @@ Build-Depends: debhelper (>= 7.0.50~), libxcb-randr0-dev, libxcb-icccm4-dev, libxcb-cursor-dev, + libxcb-xkb-dev, + libxkbcommon-dev, + libxkbcommon-x11-dev, asciidoc (>= 8.4.4), xmlto, docbook-xml, diff --git a/i3-config-wizard/i3-config-wizard.mk b/i3-config-wizard/i3-config-wizard.mk index e759b4bd..1dab6452 100644 --- a/i3-config-wizard/i3-config-wizard.mk +++ b/i3-config-wizard/i3-config-wizard.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3-config-wizard i3_config_wizard_SOURCES := $(wildcard i3-config-wizard/*.c) i3_config_wizard_HEADERS := $(wildcard i3-config-wizard/*.h) -i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) -i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(X11_LIBS) $(PANGO_LIBS) +i3_config_wizard_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(PANGO_CFLAGS) $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) +i3_config_wizard_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(PANGO_LIBS) $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) i3_config_wizard_OBJECTS := $(i3_config_wizard_SOURCES:.c=.o) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c index a76c211e..c09d459d 100644 --- a/i3-config-wizard/main.c +++ b/i3-config-wizard/main.c @@ -43,6 +43,9 @@ #include #include +#include +#include + #include #include #include @@ -83,7 +86,9 @@ static xcb_pixmap_t pixmap; static xcb_gcontext_t pixmap_gc; static xcb_key_symbols_t *symbols; xcb_window_t root; -Display *dpy; +static struct xkb_keymap *xkb_keymap; +static uint8_t xkb_base_event; +static uint8_t xkb_base_error; static void finish(); @@ -250,12 +255,24 @@ static char *next_state(const cmdp_token *token) { * This reduces a lot of confusion for users who switch keyboard * layouts from qwerty to qwertz or other slight variations of * qwerty (yes, that happens quite often). */ - KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0); - if (!keysym_used_on_other_key(sym, keycode)) + const xkb_keysym_t *syms; + int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms); + if (num == 0) + errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode); + if (!keysym_used_on_other_key(syms[0], keycode)) level = 0; } - KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, level); - char *str = XKeysymToString(sym); + + const xkb_keysym_t *syms; + int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms); + if (num == 0) + errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode); + if (num > 1) + printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num); + + char str[4096]; + if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1) + errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]); const char *release = get_string("release"); char *res; char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers)); @@ -642,8 +659,14 @@ static void handle_button_press(xcb_button_press_event_t *event) { static void finish() { printf("creating \"%s\"...\n", config_path); - if (!(dpy = XOpenDisplay(NULL))) - errx(1, "Could not connect to X11"); + struct xkb_context *xkb_context; + + if ((xkb_context = xkb_context_new(0)) == NULL) + errx(1, "could not create xkbcommon context"); + + int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn); + if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) + errx(1, "xkb_x11_keymap_new_from_device failed"); FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r"); if (kc_config == NULL) @@ -797,6 +820,16 @@ int main(int argc, char *argv[]) { xcb_connection_has_error(conn)) errx(1, "Cannot open display\n"); + if (xkb_x11_setup_xkb_extension(conn, + XKB_X11_MIN_MAJOR_XKB_VERSION, + XKB_X11_MIN_MINOR_XKB_VERSION, + 0, + NULL, + NULL, + &xkb_base_event, + &xkb_base_error) != 1) + errx(EXIT_FAILURE, "Could not setup XKB extension."); + if (socket_path == NULL) socket_path = root_atom_contents("I3_SOCKET_PATH", conn, screen); diff --git a/i3bar/i3bar.mk b/i3bar/i3bar.mk index 06780250..53227a8e 100644 --- a/i3bar/i3bar.mk +++ b/i3bar/i3bar.mk @@ -4,8 +4,8 @@ CLEAN_TARGETS += clean-i3bar i3bar_SOURCES := $(wildcard i3bar/src/*.c) i3bar_HEADERS := $(wildcard i3bar/include/*.h) -i3bar_CFLAGS = $(XCB_CFLAGS) $(X11_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) -i3bar_LIBS = $(XCB_LIBS) $(X11_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) +i3bar_CFLAGS = $(XCB_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) +i3bar_LIBS = $(XCB_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(XCB_XKB_LIBS) i3bar_OBJECTS := $(i3bar_SOURCES:.c=.o) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 763942af..37a13347 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -8,6 +8,7 @@ * */ #include +#include #include #include @@ -63,8 +64,7 @@ static i3Font font; int bar_height; /* These are only relevant for XKB, which we only need for grabbing modifiers */ -Display *xkb_dpy; -int xkb_event_base; +int xkb_base; int mod_pressed = 0; /* Because the statusline is the same on all outputs, we have @@ -854,7 +854,62 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { } while ((event = xcb_poll_for_event(xcb_connection)) != NULL) { - switch (event->response_type & ~0x80) { + int type = (event->response_type & ~0x80); + + if (type == xkb_base && xkb_base > -1) { + DLOG("received an xkb event\n"); + + xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event; + if (state->xkbType == XCB_XKB_STATE_NOTIFY) { + int modstate = state->mods & config.modifier; + +#define DLOGMOD(modmask, status) \ + do { \ + switch (modmask) { \ + case ShiftMask: \ + DLOG("ShiftMask got " #status "!\n"); \ + break; \ + case ControlMask: \ + DLOG("ControlMask got " #status "!\n"); \ + break; \ + case Mod1Mask: \ + DLOG("Mod1Mask got " #status "!\n"); \ + break; \ + case Mod2Mask: \ + DLOG("Mod2Mask got " #status "!\n"); \ + break; \ + case Mod3Mask: \ + DLOG("Mod3Mask got " #status "!\n"); \ + break; \ + case Mod4Mask: \ + DLOG("Mod4Mask got " #status "!\n"); \ + break; \ + case Mod5Mask: \ + DLOG("Mod5Mask got " #status "!\n"); \ + break; \ + } \ + } while (0) + + if (modstate != mod_pressed) { + if (modstate == 0) { + DLOGMOD(config.modifier, released); + if (!activated_mode) + hide_bars(); + } else { + DLOGMOD(config.modifier, pressed); + activated_mode = false; + unhide_bars(); + } + mod_pressed = modstate; + } +#undef DLOGMOD + } + + free(event); + continue; + } + + switch (type) { case XCB_EXPOSE: /* Expose-events happen, when the window needs to be redrawn */ redraw_bars(); @@ -900,76 +955,6 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { } -/* - * We need to bind to the modifier per XKB. Sadly, XCB does not implement this - * - */ -void xkb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) { - XkbEvent ev; - int modstate = 0; - - DLOG("Got XKB-Event!\n"); - - while (XPending(xkb_dpy)) { - XNextEvent(xkb_dpy, (XEvent *)&ev); - - if (ev.type != xkb_event_base) { - ELOG("No Xkb-Event!\n"); - continue; - } - - if (ev.any.xkb_type != XkbStateNotify) { - ELOG("No State Notify!\n"); - continue; - } - - unsigned int mods = ev.state.mods; - modstate = mods & config.modifier; - } - -#define DLOGMOD(modmask, status) \ - do { \ - switch (modmask) { \ - case ShiftMask: \ - DLOG("ShiftMask got " #status "!\n"); \ - break; \ - case ControlMask: \ - DLOG("ControlMask got " #status "!\n"); \ - break; \ - case Mod1Mask: \ - DLOG("Mod1Mask got " #status "!\n"); \ - break; \ - case Mod2Mask: \ - DLOG("Mod2Mask got " #status "!\n"); \ - break; \ - case Mod3Mask: \ - DLOG("Mod3Mask got " #status "!\n"); \ - break; \ - case Mod4Mask: \ - DLOG("Mod4Mask got " #status "!\n"); \ - break; \ - case Mod5Mask: \ - DLOG("Mod5Mask got " #status "!\n"); \ - break; \ - } \ - } while (0) - - if (modstate != mod_pressed) { - if (modstate == 0) { - DLOGMOD(config.modifier, released); - if (!activated_mode) - hide_bars(); - } else { - DLOGMOD(config.modifier, pressed); - activated_mode = false; - unhide_bars(); - } - mod_pressed = modstate; - } - -#undef DLOGMOD -} - /* * Early initialization of the connection to X11: Everything which does not * depend on 'config'. @@ -1053,44 +1038,23 @@ char *init_xcb_early() { * */ void register_xkb_keyevents() { - if (xkb_dpy == NULL) { - int xkb_major, xkb_minor, xkb_errbase, xkb_err; - xkb_major = XkbMajorVersion; - xkb_minor = XkbMinorVersion; - - xkb_dpy = XkbOpenDisplay(NULL, - &xkb_event_base, - &xkb_errbase, - &xkb_major, - &xkb_minor, - &xkb_err); - - if (xkb_dpy == NULL) { - ELOG("No XKB!\n"); - exit(EXIT_FAILURE); - } - - if (fcntl(ConnectionNumber(xkb_dpy), F_SETFD, FD_CLOEXEC) == -1) { - ELOG("Could not set FD_CLOEXEC on xkbdpy: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - int i1; - if (!XkbQueryExtension(xkb_dpy, &i1, &xkb_event_base, &xkb_errbase, &xkb_major, &xkb_minor)) { - ELOG("XKB not supported by X-server!\n"); - exit(EXIT_FAILURE); - } - - if (!XkbSelectEvents(xkb_dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask)) { - ELOG("Could not grab Key!\n"); - exit(EXIT_FAILURE); - } - - xkb_io = smalloc(sizeof(ev_io)); - ev_io_init(xkb_io, &xkb_io_cb, ConnectionNumber(xkb_dpy), EV_READ); - ev_io_start(main_loop, xkb_io); - XFlush(xkb_dpy); + const xcb_query_extension_reply_t *extreply; + extreply = xcb_get_extension_data(conn, &xcb_xkb_id); + if (!extreply->present) { + ELOG("xkb is not present on this server\n"); + exit(EXIT_FAILURE); } + DLOG("initializing xcb-xkb\n"); + xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xcb_xkb_select_events(conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY, + 0, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY, + 0xff, + 0xff, + NULL); + xkb_base = extreply->first_event; } /* @@ -1098,13 +1062,14 @@ void register_xkb_keyevents() { * */ void deregister_xkb_keyevents() { - if (xkb_dpy != NULL) { - ev_io_stop(main_loop, xkb_io); - XCloseDisplay(xkb_dpy); - close(xkb_io->fd); - FREE(xkb_io); - xkb_dpy = NULL; - } + xcb_xkb_select_events(conn, + XCB_XKB_ID_USE_CORE_KBD, + 0, + 0, + 0, + 0xff, + 0xff, + NULL); } /* diff --git a/include/handlers.h b/include/handlers.h index db7d06b5..82f6b982 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -13,6 +13,7 @@ #include extern int randr_base; +extern int xkb_base; /** * Adds the given sequence to the list of events which are ignored. diff --git a/include/i3.h b/include/i3.h index c70a5c5b..5ca87541 100644 --- a/include/i3.h +++ b/include/i3.h @@ -13,6 +13,7 @@ #include #include +#include #include diff --git a/src/bindings.c b/src/bindings.c index a7039bf2..cd91a398 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -8,6 +8,8 @@ */ #include "all.h" +#include + pid_t command_error_nagbar_pid = -1; /* @@ -263,8 +265,8 @@ void translate_keysyms(void) { continue; /* We need to translate the symbol to a keycode */ - keysym = XStringToKeysym(bind->symbol); - if (keysym == NoSymbol) { + keysym = xkb_keysym_from_name(bind->symbol, XKB_KEYSYM_NO_FLAGS); + if (keysym == XKB_KEY_NoSymbol) { ELOG("Could not translate string to key symbol: \"%s\"\n", bind->symbol); continue; diff --git a/src/config.c b/src/config.c index 781ff6b8..ec084bb1 100644 --- a/src/config.c +++ b/src/config.c @@ -11,9 +11,7 @@ * */ #include "all.h" - -/* We need Xlib for XStringToKeysym */ -#include +#include char *current_configpath = NULL; Config config; diff --git a/src/handlers.c b/src/handlers.c index ba9b1211..80297c79 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -21,6 +21,8 @@ #include int randr_base = -1; +int xkb_base = -1; +int xkb_current_group; /* After mapping/unmapping windows, a notify event is generated. However, we don’t want it, since it’d trigger an infinite loop of switching between the different windows when @@ -1115,12 +1117,50 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) * */ void handle_event(int type, xcb_generic_event_t *event) { + DLOG("event type %d, xkb_base %d\n", type, xkb_base); if (randr_base > -1 && type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { handle_screen_change(event); return; } + if (xkb_base > -1 && type == xkb_base) { + DLOG("xkb event, need to handle it.\n"); + + xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event; + if (state->xkbType == XCB_XKB_MAP_NOTIFY) { + if (event_is_ignored(event->sequence, type)) { + DLOG("Ignoring map notify event for sequence %d.\n", state->sequence); + } else { + DLOG("xkb map notify, sequence %d, time %d\n", state->sequence, state->time); + add_ignore_event(event->sequence, type); + ungrab_all_keys(conn); + translate_keysyms(); + grab_all_keys(conn, false); + } + } else if (state->xkbType == XCB_XKB_STATE_NOTIFY) { + DLOG("xkb state group = %d\n", state->group); + + /* See The XKB Extension: Library Specification, section 14.1 */ + /* We check if the current group (each group contains + * two levels) has been changed. Mode_switch activates + * group XkbGroup2Index */ + if (xkb_current_group == state->group) + return; + xkb_current_group = state->group; + if (state->group == XCB_XKB_GROUP_1) { + DLOG("Mode_switch disabled\n"); + ungrab_all_keys(conn); + grab_all_keys(conn, false); + } else { + DLOG("Mode_switch enabled\n"); + grab_all_keys(conn, false); + } + } + + return; + } + switch (type) { case XCB_KEY_PRESS: case XCB_KEY_RELEASE: diff --git a/src/i3.mk b/src/i3.mk index 0325d82a..f1105a04 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -5,8 +5,8 @@ CLEAN_TARGETS += clean-i3 i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(X11_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) -i3_LIBS = $(XCB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(X11_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread +i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_LIBS = $(XKB_COMMON_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread # When using clang, we use pre-compiled headers to speed up the build. With # gcc, this actually makes the build slower. diff --git a/src/main.c b/src/main.c index 917ae1cb..ac112446 100644 --- a/src/main.c +++ b/src/main.c @@ -36,10 +36,6 @@ int listen_fds; * temporarily for drag_pointer(). */ static struct ev_check *xcb_check; -static int xkb_event_base; - -int xkb_current_group; - extern Con *focused; char **start_argv; @@ -70,9 +66,6 @@ struct ev_loop *main_loop; xcb_key_symbols_t *keysyms; -/* Those are our connections to X11 for use with libXcursor and XKB */ -Display *xlibdpy, *xkbdpy; - /* Default shmlog size if not set by user. */ const int default_shmlog_size = 25 * 1024 * 1024; @@ -94,7 +87,6 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment /* We hope that those are supported and set them to true */ bool xcursor_supported = true; -bool xkb_supported = true; /* This will be set to true when -C is used so that functions can behave * slightly differently. We don’t want i3-nagbar to be started when validating @@ -166,73 +158,6 @@ void main_set_x11_cb(bool enable) { } } -/* - * When using xmodmap to change the keyboard mapping, this event - * is only sent via XKB. Therefore, we need this special handler. - * - */ -static void xkb_got_event(EV_P_ struct ev_io *w, int revents) { - DLOG("Handling XKB event\n"); - XkbEvent ev; - - /* When using xmodmap, every change (!) gets an own event. - * Therefore, we just read all events and only handle the - * mapping_notify once. */ - bool mapping_changed = false; - while (XPending(xkbdpy)) { - XNextEvent(xkbdpy, (XEvent *)&ev); - /* While we should never receive a non-XKB event, - * better do sanity checking */ - if (ev.type != xkb_event_base) - continue; - - if (ev.any.xkb_type == XkbMapNotify) { - mapping_changed = true; - continue; - } - - if (ev.any.xkb_type != XkbStateNotify) { - ELOG("Unknown XKB event received (type %d)\n", ev.any.xkb_type); - continue; - } - - /* See The XKB Extension: Library Specification, section 14.1 */ - /* We check if the current group (each group contains - * two levels) has been changed. Mode_switch activates - * group XkbGroup2Index */ - if (xkb_current_group == ev.state.group) - continue; - - xkb_current_group = ev.state.group; - - if (ev.state.group == XkbGroup2Index) { - DLOG("Mode_switch enabled\n"); - grab_all_keys(conn, true); - } - - if (ev.state.group == XkbGroup1Index) { - DLOG("Mode_switch disabled\n"); - ungrab_all_keys(conn); - grab_all_keys(conn, false); - } - } - - if (!mapping_changed) - return; - - DLOG("Keyboard mapping changed, updating keybindings\n"); - xcb_key_symbols_free(keysyms); - keysyms = xcb_key_symbols_alloc(conn); - - xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms); - - ungrab_all_keys(conn); - DLOG("Re-grabbing...\n"); - translate_keysyms(); - grab_all_keys(conn, (xkb_current_group == XkbGroup2Index)); - DLOG("Done\n"); -} - /* * Exit handler which destroys the main_loop. Will trigger cleanup handlers. * @@ -593,21 +518,7 @@ int main(int argc, char *argv[]) { #include "atoms.xmacro" #undef xmacro - /* Initialize the Xlib connection */ - xlibdpy = xkbdpy = XOpenDisplay(NULL); - - /* Try to load the X cursors and initialize the XKB extension */ - if (xlibdpy == NULL) { - ELOG("ERROR: XOpenDisplay() failed, disabling libXcursor/XKB support\n"); - xcursor_supported = false; - xkb_supported = false; - } else if (fcntl(ConnectionNumber(xlibdpy), F_SETFD, FD_CLOEXEC) == -1) { - ELOG("Could not set FD_CLOEXEC on xkbdpy\n"); - return 1; - } else { - xcursor_load_cursors(); - /*init_xkb();*/ - } + xcursor_load_cursors(); /* Set a cursor for the root window (otherwise the root window will show no cursor until the first client is launched). */ @@ -616,27 +527,22 @@ int main(int argc, char *argv[]) { else xcb_set_root_cursor(XCURSOR_CURSOR_POINTER); - if (xkb_supported) { - int errBase, - major = XkbMajorVersion, - minor = XkbMinorVersion; - - if (fcntl(ConnectionNumber(xkbdpy), F_SETFD, FD_CLOEXEC) == -1) { - fprintf(stderr, "Could not set FD_CLOEXEC on xkbdpy\n"); - return 1; - } - - int i1; - if (!XkbQueryExtension(xkbdpy, &i1, &xkb_event_base, &errBase, &major, &minor)) { - fprintf(stderr, "XKB not supported by X-server\n"); - xkb_supported = false; - } - /* end of ugliness */ - - if (xkb_supported && !XkbSelectEvents(xkbdpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask)) { - fprintf(stderr, "Could not set XKB event mask\n"); - return 1; - } + const xcb_query_extension_reply_t *extreply; + extreply = xcb_get_extension_data(conn, &xcb_xkb_id); + if (!extreply->present) { + DLOG("xkb is not present on this server\n"); + } else { + DLOG("initializing xcb-xkb\n"); + xcb_xkb_use_extension(conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xcb_xkb_select_events(conn, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + 0, + XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + 0xff, + 0xff, + NULL); + xkb_base = extreply->first_event; } restore_connect(); @@ -770,21 +676,12 @@ int main(int argc, char *argv[]) { ewmh_update_number_of_desktops(); struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); - struct ev_io *xkb = scalloc(sizeof(struct ev_io)); xcb_check = scalloc(sizeof(struct ev_check)); struct ev_prepare *xcb_prepare = scalloc(sizeof(struct ev_prepare)); ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); ev_io_start(main_loop, xcb_watcher); - if (xkb_supported) { - ev_io_init(xkb, xkb_got_event, ConnectionNumber(xkbdpy), EV_READ); - ev_io_start(main_loop, xkb); - - /* Flush the buffer so that libev can properly get new events */ - XFlush(xkbdpy); - } - ev_check_init(xcb_check, xcb_check_cb); ev_check_start(main_loop, xcb_check); From cd06da711ccba1fc6eac925a18b6670b2cd2bccb Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 22 Jun 2014 14:42:28 +0200 Subject: [PATCH 016/103] DEPENDS: add libxkbcommon{,-x11}, update lkgv (Thanks okraits) --- DEPENDS | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/DEPENDS b/DEPENDS index 8a9f9dca..b56e2ec8 100644 --- a/DEPENDS +++ b/DEPENDS @@ -4,25 +4,27 @@ "min" means minimum required version "lkgv" means last known good version -┌─────────────┬────────┬────────┬────────────────────────────────────────┐ -│ dependency │ min. │ lkgv │ URL │ -├─────────────┼────────┼────────┼────────────────────────────────────────┤ -│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │ -│ libxcb │ 1.1.93 │ 1.7 │ http://xcb.freedesktop.org/dist/ │ -│ xcb-util │ 0.3.3 │ 0.3.8 │ http://xcb.freedesktop.org/dist/ │ -│ util-cursor³│ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │ -│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │ -│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │ -│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │ -│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │ -│ Pod::Simple²│ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/ -│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │ -│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │ -│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │ -│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification -│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │ -│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │ -└─────────────┴────────┴────────┴────────────────────────────────────────┘ +┌──────────────┬────────┬────────┬────────────────────────────────────────┐ +│ dependency │ min. │ lkgv │ URL │ +├──────────────┼────────┼────────┼────────────────────────────────────────┤ +│ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │ +│ libxcb │ 1.1.93 │ 1.10 │ http://xcb.freedesktop.org/dist/ │ +│ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │ +│ xkbcommon │ 0.2.0 │ 0.4.0 │ http://xkbcommon.org/ │ +│ xkbcommon-x11│ 0.2.0 │ 0.4.0 │ http://xkbcommon.org/ │ +│ util-cursor³ │ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │ +│ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │ +│ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │ +│ asciidoc │ 8.3.0 │ 8.6.4 │ http://www.methods.co.nz/asciidoc/ │ +│ xmlto │ 0.0.23 │ 0.0.23 │ http://www.methods.co.nz/asciidoc/ │ +│ Pod::Simple² │ 3.22 │ 3.22 │ http://search.cpan.org/~dwheeler/Pod-Simple-3.23/ +│ docbook-xml │ 4.5 │ 4.5 │ http://www.methods.co.nz/asciidoc/ │ +│ Xlib │ 1.3.3 │ 1.4.3 │ http://ftp.x.org/pub/current/src/lib/ │ +│ PCRE │ 8.12 │ 8.12 │ http://www.pcre.org/ │ +│ libsn¹ │ 0.10 │ 0.12 │ http://freedesktop.org/wiki/Software/startup-notification +│ pango │ 1.30.0 | 1.30.0 │ http://www.pango.org/ │ +│ cairo │ 1.12.2 │ 1.12.2 │ http://cairographics.org/ │ +└──────────────┴────────┴────────┴────────────────────────────────────────┘ ¹ libsn = libstartup-notification ² Pod::Simple is a Perl module required for converting the testsuite documentation to HTML. See http://michael.stapelberg.de/cpan/#Pod::Simple From c9362325454fe1fea000be277ba736bfe813df14 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 22 Jun 2014 11:12:51 -0400 Subject: [PATCH 017/103] Consistently parse workspace numbers Use ws_name_to_number() to parse workspace numbers where this transformation takes place. --- src/commands.c | 30 ++++++++---------------------- src/workspace.c | 10 +--------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/commands.c b/src/commands.c index b9c130de..a60dde62 100644 --- a/src/commands.c +++ b/src/commands.c @@ -550,12 +550,9 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) { /* get the workspace */ Con *output, *workspace = NULL; - char *endptr = NULL; - long parsed_num = strtol(which, &endptr, 10); - if (parsed_num == LONG_MIN || - parsed_num == LONG_MAX || - parsed_num < 0 || - endptr == which) { + long parsed_num = ws_name_to_number(which); + + if (parsed_num == -1) { LOG("Could not parse initial part of \"%s\" as a number.\n", which); // TODO: better error message yerror("Could not parse number"); @@ -954,16 +951,12 @@ void cmd_workspace(I3_CMD, char *which) { void cmd_workspace_number(I3_CMD, char *which) { Con *output, *workspace = NULL; - char *endptr = NULL; - long parsed_num = strtol(which, &endptr, 10); - if (parsed_num == LONG_MIN || - parsed_num == LONG_MAX || - parsed_num < 0 || - endptr == which) { + long parsed_num = ws_name_to_number(which); + + if (parsed_num == -1) { LOG("Could not parse initial part of \"%s\" as a number.\n", which); // TODO: better error message yerror("Could not parse number"); - return; } @@ -1925,15 +1918,8 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { /* Change the name and try to parse it as a number. */ FREE(workspace->name); workspace->name = sstrdup(new_name); - char *endptr = NULL; - long parsed_num = strtol(new_name, &endptr, 10); - if (parsed_num == LONG_MIN || - parsed_num == LONG_MAX || - parsed_num < 0 || - endptr == new_name) - workspace->num = -1; - else - workspace->num = parsed_num; + + workspace->num = ws_name_to_number(new_name); LOG("num = %d\n", workspace->num); /* By re-attaching, the sort order will be correct afterwards. */ diff --git a/src/workspace.c b/src/workspace.c index 41c02952..54a2bd33 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -177,15 +177,7 @@ Con *create_workspace_on_output(Output *output, Con *content) { if (!exists) { /* Set ->num to the number of the workspace, if the name actually * is a number or starts with a number */ - char *endptr = NULL; - long parsed_num = strtol(ws->name, &endptr, 10); - if (parsed_num == LONG_MIN || - parsed_num == LONG_MAX || - parsed_num < 0 || - endptr == ws->name) - ws->num = -1; - else - ws->num = parsed_num; + ws->num = ws_name_to_number(ws->name); LOG("Used number %d for workspace with name %s\n", ws->num, ws->name); break; From c2b6b06da7db49bd3ecd6fa1561ef2b407ff64a3 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 19 Jun 2014 08:09:31 -0400 Subject: [PATCH 018/103] Make command `move [direction]` work with criteria A container selected with criteria should be moved with the `move [direction]` command, instead of this command always acting on the focused container. --- include/move.h | 4 +-- src/commands.c | 44 ++++++++++++++++++----------- src/move.c | 5 ++-- testcases/t/232-cmd-move-criteria.t | 37 ++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 testcases/t/232-cmd-move-criteria.t diff --git a/include/move.h b/include/move.h index 5c8a7d20..939665ec 100644 --- a/include/move.h +++ b/include/move.h @@ -10,8 +10,8 @@ #pragma once /** - * Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT, + * Moves the given container in the given direction (TOK_LEFT, TOK_RIGHT, * TOK_UP, TOK_DOWN from cmdparse.l) * */ -void tree_move(int direction); +void tree_move(Con *con, int direction); diff --git a/src/commands.c b/src/commands.c index a60dde62..baf19893 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1545,26 +1545,36 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) { // TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking int px = atoi(move_px); - /* TODO: make 'move' work with criteria. */ - DLOG("moving in direction %s, px %s\n", direction, move_px); - if (con_is_floating(focused)) { - DLOG("floating move with %d pixels\n", px); - Rect newrect = focused->parent->rect; - if (strcmp(direction, "left") == 0) { - newrect.x -= px; - } else if (strcmp(direction, "right") == 0) { - newrect.x += px; - } else if (strcmp(direction, "up") == 0) { - newrect.y -= px; - } else if (strcmp(direction, "down") == 0) { - newrect.y += px; + owindow *current; + HANDLE_EMPTY_MATCH; + + Con *initially_focused = focused; + + TAILQ_FOREACH (current, &owindows, owindows) { + DLOG("moving in direction %s, px %s\n", direction, move_px); + if (con_is_floating(current->con)) { + DLOG("floating move with %d pixels\n", px); + Rect newrect = current->con->parent->rect; + if (strcmp(direction, "left") == 0) { + newrect.x -= px; + } else if (strcmp(direction, "right") == 0) { + newrect.x += px; + } else if (strcmp(direction, "up") == 0) { + newrect.y -= px; + } else if (strcmp(direction, "down") == 0) { + newrect.y += px; + } + floating_reposition(current->con->parent, newrect); + } else { + tree_move(current->con, (strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN)))); + cmd_output->needs_tree_render = true; } - floating_reposition(focused->parent, newrect); - } else { - tree_move((strcmp(direction, "right") == 0 ? D_RIGHT : (strcmp(direction, "left") == 0 ? D_LEFT : (strcmp(direction, "up") == 0 ? D_UP : D_DOWN)))); - cmd_output->needs_tree_render = true; } + /* the move command should not disturb focus */ + if (focused != initially_focused) + con_focus(initially_focused); + // XXX: default reply for now, make this a better reply ysuccess(true); } diff --git a/src/move.c b/src/move.c index 44045f25..9c0f310f 100644 --- a/src/move.c +++ b/src/move.c @@ -132,18 +132,17 @@ static void move_to_output_directed(Con *con, direction_t direction) { } /* - * Moves the current container in the given direction (D_LEFT, D_RIGHT, + * Moves the given container in the given direction (D_LEFT, D_RIGHT, * D_UP, D_DOWN). * */ -void tree_move(int direction) { +void tree_move(Con *con, int direction) { position_t position; Con *target; DLOG("Moving in direction %d\n", direction); /* 1: get the first parent with the same orientation */ - Con *con = focused; if (con->type == CT_WORKSPACE) { DLOG("Not moving workspace\n"); diff --git a/testcases/t/232-cmd-move-criteria.t b/testcases/t/232-cmd-move-criteria.t new file mode 100644 index 00000000..22a2eb4e --- /dev/null +++ b/testcases/t/232-cmd-move-criteria.t @@ -0,0 +1,37 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the `move [direction]` command works with criteria +# Bug still in: 4.8-16-g6888a1f +use i3test; + +my $ws = fresh_workspace; + +my $win1 = open_window; +my $win2 = open_window; +my $win3 = open_window; + +# move win1 from the left to the right +cmd '[id="' . $win1->{id} . '"] move right'; + +# now they should be switched, with win2 still being focused +my $ws_con = get_ws($ws); + +# win2 should be on the left +is($ws_con->{nodes}[0]->{window}, $win2->{id}, 'the `move [direction]` command should work with criteria'); +is($x->input_focus, $win3->{id}, 'it should not disturb focus'); + +done_testing; From babfb779d7702a52ed58aed1bd2ddbeeff879273 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 20 Jun 2014 07:44:08 -0400 Subject: [PATCH 019/103] Implement the window::floating event The window::floating event should be emitted when a window transitions to or from the floating state. --- docs/ipc | 1 + src/floating.c | 12 +++++- testcases/t/231-ipc-floating-event.t | 59 ++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 testcases/t/231-ipc-floating-event.t diff --git a/docs/ipc b/docs/ipc index 0560d4e3..db3910f3 100644 --- a/docs/ipc +++ b/docs/ipc @@ -722,6 +722,7 @@ This event consists of a single serialized map containing a property * +title+ - the window's title has changed * +fullscreen_mode+ - the window has entered or exited fullscreen mode * +move+ - the window has changed its position in the tree +* +floating+ - the window has transitioned to or from floating Additionally a +container (object)+ field will be present, which consists of the window's parent container. Be aware that for the "new" event, the diff --git a/src/floating.c b/src/floating.c index f9935952..8a2fde2c 100644 --- a/src/floating.c +++ b/src/floating.c @@ -298,16 +298,22 @@ void floating_enable(Con *con, bool automatic) { /* Check if we need to re-assign it to a different workspace because of its * coordinates and exit if that was done successfully. */ - if (floating_maybe_reassign_ws(nc)) + if (floating_maybe_reassign_ws(nc)) { + ipc_send_window_event("floating", con); return; + } /* Sanitize coordinates: Check if they are on any output */ - if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) + if (get_output_containing(nc->rect.x, nc->rect.y) != NULL) { + ipc_send_window_event("floating", con); return; + } 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); + + ipc_send_window_event("floating", con); } void floating_disable(Con *con, bool automatic) { @@ -351,6 +357,8 @@ void floating_disable(Con *con, bool automatic) { if (set_focus) con_focus(con); + + ipc_send_window_event("floating", con); } /* diff --git a/testcases/t/231-ipc-floating-event.t b/testcases/t/231-ipc-floating-event.t new file mode 100644 index 00000000..c2de64e4 --- /dev/null +++ b/testcases/t/231-ipc-floating-event.t @@ -0,0 +1,59 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the window::floating event works correctly. This event should be +# emitted when a window transitions to or from the floating state. +# Bug still in: 4.8-7-gf4a8253 +use i3test; + +my $i3 = i3(get_socket_path()); +$i3->connect->recv; + +my $cv = AnyEvent->condvar; + +$i3->subscribe({ + window => sub { + my ($event) = @_; + $cv->send($event) if $event->{change} eq 'floating'; + } + })->recv; + +my $t; +$t = AnyEvent->timer( + after => 0.5, + cb => sub { + $cv->send(0); + } +); + +my $win = open_window(); + +cmd '[id="' . $win->{id} . '"] floating enable'; +my $e = $cv->recv; + +isnt($e, 0, 'floating a container should send an ipc window event'); +is($e->{container}->{window}, $win->{id}, 'the event should contain information about the window'); +is($e->{container}->{floating}, 'user_on', 'the container should be floating'); + +$cv = AnyEvent->condvar; +cmd '[id="' . $win->{id} . '"] floating disable'; +my $e = $cv->recv; + +isnt($e, 0, 'disabling floating on a container should send an ipc window event'); +is($e->{container}->{window}, $win->{id}, 'the event should contain information about the window'); +is($e->{container}->{floating}, 'user_off', 'the container should not be floating'); + +done_testing; From d1e59a204eed49c4ee889b50000458ae6479354c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 23 Jun 2014 21:29:42 +0200 Subject: [PATCH 020/103] fix src/commands.c with clang-format --- src/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands.c b/src/commands.c index baf19893..e46a996d 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1550,7 +1550,7 @@ void cmd_move_direction(I3_CMD, char *direction, char *move_px) { Con *initially_focused = focused; - TAILQ_FOREACH (current, &owindows, owindows) { + TAILQ_FOREACH(current, &owindows, owindows) { DLOG("moving in direction %s, px %s\n", direction, move_px); if (con_is_floating(current->con)) { DLOG("floating move with %d pixels\n", px); From 4205973135f28a0ab0e25a63dbc8c6fff0d3539e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 22 Jun 2014 15:20:14 -0400 Subject: [PATCH 021/103] feature: implement ewmh desktop viewport property Set and update the _NET_DESKTOP_VIEWPORT property http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140146176862048 > _NET_DESKTOP_VIEWPORT x, y, CARDINAL[][2]/32 > Array of pairs of cardinals that define the top left corner of each > desktop's viewport. --- include/atoms.xmacro | 1 + include/ewmh.h | 6 ++++++ src/ewmh.c | 39 ++++++++++++++++++++++++++++++++++++++- src/main.c | 1 + src/workspace.c | 2 ++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index 12e9ee28..ccae87c5 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -17,6 +17,7 @@ xmacro(_NET_CLIENT_LIST) xmacro(_NET_CLIENT_LIST_STACKING) xmacro(_NET_CURRENT_DESKTOP) xmacro(_NET_NUMBER_OF_DESKTOPS) +xmacro(_NET_DESKTOP_VIEWPORT) xmacro(_NET_ACTIVE_WINDOW) xmacro(_NET_STARTUP_ID) xmacro(_NET_WORKAREA) diff --git a/include/ewmh.h b/include/ewmh.h index 38d612da..99ff655e 100644 --- a/include/ewmh.h +++ b/include/ewmh.h @@ -24,6 +24,12 @@ void ewmh_update_current_desktop(void); */ void ewmh_update_number_of_desktops(void); +/** + * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that + * define the top left corner of each desktop's viewport. + */ +void ewmh_update_desktop_viewport(void); + /** * Updates _NET_ACTIVE_WINDOW with the currently focused window. * diff --git a/src/ewmh.c b/src/ewmh.c index 3ef79937..433a202b 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -61,6 +61,43 @@ void ewmh_update_number_of_desktops(void) { A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx); } +/* + * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that + * define the top left corner of each desktop's viewport. + */ +void ewmh_update_desktop_viewport(void) { + Con *output; + int num_desktops = 0; + /* count number of desktops */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + + num_desktops++; + } + } + + uint32_t viewports[num_desktops * 2]; + + int current_position = 0; + /* fill the viewport buffer */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + + viewports[current_position++] = output->rect.x; + viewports[current_position++] = output->rect.y; + } + } + + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, + A__NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, 32, current_position, &viewports); +} + /* * Updates _NET_ACTIVE_WINDOW with the currently focused window. * @@ -159,5 +196,5 @@ void ewmh_setup_hints(void) { /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 19, supported_atoms); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 20, supported_atoms); } diff --git a/src/main.c b/src/main.c index ac112446..67024e6e 100644 --- a/src/main.c +++ b/src/main.c @@ -674,6 +674,7 @@ int main(int argc, char *argv[]) { /* Set the ewmh desktop properties. */ ewmh_update_current_desktop(); ewmh_update_number_of_desktops(); + ewmh_update_desktop_viewport(); struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); xcb_check = scalloc(sizeof(struct ev_check)); diff --git a/src/workspace.c b/src/workspace.c index 54a2bd33..2dcab4fa 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -93,6 +93,7 @@ Con *workspace_get(const char *num, bool *created) { ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); ewmh_update_number_of_desktops(); + ewmh_update_desktop_viewport(); if (created != NULL) *created = true; } else if (created != NULL) { @@ -418,6 +419,7 @@ static void _workspace_show(Con *workspace) { tree_close(old, DONT_KILL_WINDOW, false, false); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); ewmh_update_number_of_desktops(); + ewmh_update_desktop_viewport(); } } From cbd51ce6642be4e1163a64dce8ec20a454f1bf05 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 22 Jun 2014 12:24:26 -0400 Subject: [PATCH 022/103] Workspace command number selection If a `workspace {N}` or `move to workspace {N}` command is given with N as a plain number, the workspace of this number is selected for the context of the command if one exists and there is no workspace with a name that exactly matches N. --- src/commands.c | 47 ++++++++++++++++- .../t/232-cmd-workspace-number-selection.t | 52 +++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 testcases/t/232-cmd-workspace-number-selection.t diff --git a/src/commands.c b/src/commands.c index e46a996d..50180b71 100644 --- a/src/commands.c +++ b/src/commands.c @@ -513,7 +513,27 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) { LOG("should move window to workspace %s\n", name); /* get the workspace */ - Con *ws = workspace_get(name, NULL); + Con *ws = NULL; + Con *output = NULL; + + /* first look for a workspace with this name */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name)); + } + + /* if the name is plain digits, we interpret this as a "workspace number" + * command */ + if (!ws && name_is_digits(name)) { + long parsed_num = ws_name_to_number(name); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + GREP_FIRST(ws, output_get_content(output), + child->num == parsed_num); + } + } + + /* if no workspace was found, make a new one */ + if (!ws) + ws = workspace_get(name, NULL); ws = maybe_auto_back_and_forth_workspace(ws); @@ -1006,7 +1026,30 @@ void cmd_workspace_name(I3_CMD, char *name) { DLOG("should switch to workspace %s\n", name); if (maybe_back_and_forth(cmd_output, name)) return; - workspace_show_by_name(name); + + Con *ws = NULL; + Con *output = NULL; + + /* first look for a workspace with this name */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + GREP_FIRST(ws, output_get_content(output), !strcasecmp(child->name, name)); + } + + /* if the name is only digits, we interpret this as a "workspace number" + * command */ + if (!ws && name_is_digits(name)) { + long parsed_num = ws_name_to_number(name); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + GREP_FIRST(ws, output_get_content(output), + child->num == parsed_num); + } + } + + /* if no workspace was found, make a new one */ + if (!ws) + ws = workspace_get(name, NULL); + + workspace_show(ws); cmd_output->needs_tree_render = true; // XXX: default reply for now, make this a better reply diff --git a/testcases/t/232-cmd-workspace-number-selection.t b/testcases/t/232-cmd-workspace-number-selection.t new file mode 100644 index 00000000..bda05643 --- /dev/null +++ b/testcases/t/232-cmd-workspace-number-selection.t @@ -0,0 +1,52 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that `workspace {N}` acts like `workspace number {N}` when N is a plain +# digit, and likewise for `move to workspace {N}`. +# Ticket: #1238 +# Bug still in: 4.8-16-g3f5a0f0 +use i3test; + +cmd 'workspace 5:foo'; +open_window; +fresh_workspace; +cmd 'workspace 5'; + +is(focused_ws, '5:foo', + 'a command to switch to a workspace with a bare number should switch to a workspace of that number'); + +fresh_workspace; +my $win = open_window; +cmd '[id="' . $win->{id} . '"] move to workspace 5'; + +is(@{get_ws('5:foo')->{nodes}}, 2, + 'a command to move a container to a workspace with a bare number should move that container to a workspace of that number'); + +fresh_workspace; +cmd 'workspace 7'; +open_window; +cmd 'workspace 7:foo'; +$win = open_window; + +cmd 'workspace 7'; +is(focused_ws, '7', + 'a workspace with a name that is a matching plain number should be preferred when switching'); + +cmd '[id="' . $win->{id} . '"] move to workspace 7'; +is(@{get_ws('7')->{nodes}}, 2, + 'a workspace with a name that is a matching plain number should be preferred when moving'); + +done_testing; From a9c094b7313b48491c182a5da1194a0bb06747aa Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 23 Jun 2014 17:44:24 -0400 Subject: [PATCH 023/103] Implement EWMH desktop names Maintain the _NET_DESKTOP_NAMES property on the root window. http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368131760 > _NET_DESKTOP_NAMES > > _NET_DESKTOP_NAMES, UTF8_STRING[] > > The names of all virtual desktops. This is a list of NULL-terminated > strings in UTF-8 encoding [UTF8]. This property MAY be changed by a > Pager or the Window Manager at any time. --- include/atoms.xmacro | 1 + include/ewmh.h | 6 ++++++ src/commands.c | 3 +++ src/ewmh.c | 40 +++++++++++++++++++++++++++++++++++++++- src/main.c | 1 + src/workspace.c | 2 ++ 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index ccae87c5..cb928dc0 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -17,6 +17,7 @@ xmacro(_NET_CLIENT_LIST) xmacro(_NET_CLIENT_LIST_STACKING) xmacro(_NET_CURRENT_DESKTOP) xmacro(_NET_NUMBER_OF_DESKTOPS) +xmacro(_NET_DESKTOP_NAMES) xmacro(_NET_DESKTOP_VIEWPORT) xmacro(_NET_ACTIVE_WINDOW) xmacro(_NET_STARTUP_ID) diff --git a/include/ewmh.h b/include/ewmh.h index 99ff655e..3b580628 100644 --- a/include/ewmh.h +++ b/include/ewmh.h @@ -24,6 +24,12 @@ void ewmh_update_current_desktop(void); */ void ewmh_update_number_of_desktops(void); +/** + * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a + * list of NULL-terminated strings in UTF-8 encoding" + */ +void ewmh_update_desktop_names(void); + /** * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that * define the top left corner of each desktop's viewport. diff --git a/src/commands.c b/src/commands.c index 50180b71..bf9942e2 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1987,6 +1987,9 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { ysuccess(true); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); + ewmh_update_desktop_names(); + ewmh_update_desktop_viewport(); + ewmh_update_current_desktop(); } /* diff --git a/src/ewmh.c b/src/ewmh.c index 433a202b..da5dba5f 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -61,6 +61,44 @@ void ewmh_update_number_of_desktops(void) { A__NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &idx); } +/* + * Updates _NET_DESKTOP_NAMES: "The names of all virtual desktops. This is a + * list of NULL-terminated strings in UTF-8 encoding" + */ +void ewmh_update_desktop_names(void) { + Con *output; + int msg_length = 0; + + /* count the size of the property message to set */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + msg_length += strlen(ws->name) + 1; + } + } + + char desktop_names[msg_length]; + int current_position = 0; + + /* fill the buffer with the names of the i3 workspaces */ + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + + for (size_t i = 0; i < strlen(ws->name) + 1; i++) { + desktop_names[current_position++] = ws->name[i]; + } + } + } + + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, + A__NET_DESKTOP_NAMES, A_UTF8_STRING, 8, msg_length, desktop_names); +} + /* * Updates _NET_DESKTOP_VIEWPORT, which is an array of pairs of cardinals that * define the top left corner of each desktop's viewport. @@ -196,5 +234,5 @@ void ewmh_setup_hints(void) { /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 20, supported_atoms); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 21, supported_atoms); } diff --git a/src/main.c b/src/main.c index 67024e6e..77384347 100644 --- a/src/main.c +++ b/src/main.c @@ -674,6 +674,7 @@ int main(int argc, char *argv[]) { /* Set the ewmh desktop properties. */ ewmh_update_current_desktop(); ewmh_update_number_of_desktops(); + ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); struct ev_io *xcb_watcher = scalloc(sizeof(struct ev_io)); diff --git a/src/workspace.c b/src/workspace.c index 2dcab4fa..f585f2d5 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -93,6 +93,7 @@ Con *workspace_get(const char *num, bool *created) { ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); ewmh_update_number_of_desktops(); + ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); if (created != NULL) *created = true; @@ -419,6 +420,7 @@ static void _workspace_show(Con *workspace) { tree_close(old, DONT_KILL_WINDOW, false, false); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); ewmh_update_number_of_desktops(); + ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); } } From 682fb0a29153bd1fd46f20e5f29f402e986bd74e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 20 Jun 2014 08:33:02 -0400 Subject: [PATCH 024/103] Implement the window::urgent event The window::urgent event is emitted when a container becomes urgent or loses its urgent status. --- docs/ipc | 1 + src/con.c | 9 ++-- src/workspace.c | 14 +++--- testcases/t/232-ipc-window-urgent.t | 68 +++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 testcases/t/232-ipc-window-urgent.t diff --git a/docs/ipc b/docs/ipc index db3910f3..f0829dc2 100644 --- a/docs/ipc +++ b/docs/ipc @@ -723,6 +723,7 @@ This event consists of a single serialized map containing a property * +fullscreen_mode+ - the window has entered or exited fullscreen mode * +move+ - the window has changed its position in the tree * +floating+ - the window has transitioned to or from floating +* +urgent+ - the window has become urgent or lost its urgent status Additionally a +container (object)+ field will be present, which consists of the window's parent container. Be aware that for the "new" event, the diff --git a/src/con.c b/src/con.c index 7934133e..78fe5fef 100644 --- a/src/con.c +++ b/src/con.c @@ -231,6 +231,7 @@ void con_focus(Con *con) { con->urgent = false; con_update_parents_urgency(con); workspace_update_urgent_flag(con_get_workspace(con)); + ipc_send_window_event("urgent", con); } } @@ -1599,14 +1600,16 @@ void con_set_urgency(Con *con, bool urgent) { con_update_parents_urgency(con); - if (con->urgent == urgent) - LOG("Urgency flag changed to %d\n", con->urgent); - Con *ws; /* Set the urgency flag on the workspace, if a workspace could be found * (for dock clients, that is not the case). */ if ((ws = con_get_workspace(con)) != NULL) workspace_update_urgent_flag(ws); + + if (con->urgent == urgent) { + LOG("Urgency flag changed to %d\n", con->urgent); + ipc_send_window_event("urgent", con); + } } /* diff --git a/src/workspace.c b/src/workspace.c index f585f2d5..9ec664d7 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -320,11 +320,14 @@ static void workspace_reassign_sticky(Con *con) { static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents) { Con *con = w->data; - DLOG("Resetting urgency flag of con %p by timer\n", con); - con->urgent = false; - con_update_parents_urgency(con); - workspace_update_urgent_flag(con_get_workspace(con)); - tree_render(); + if (con->urgent) { + DLOG("Resetting urgency flag of con %p by timer\n", con); + con->urgent = false; + con_update_parents_urgency(con); + workspace_update_urgent_flag(con_get_workspace(con)); + ipc_send_window_event("urgent", con); + tree_render(); + } ev_timer_stop(main_loop, con->urgency_timer); FREE(con->urgency_timer); @@ -380,6 +383,7 @@ static void _workspace_show(Con *workspace) { * focus and thereby immediately destroy it */ if (next->urgent && (int)(config.workspace_urgency_timer * 1000) > 0) { /* focus for now… */ + next->urgent = false; con_focus(next); /* … but immediately reset urgency flags; they will be set to false by diff --git a/testcases/t/232-ipc-window-urgent.t b/testcases/t/232-ipc-window-urgent.t new file mode 100644 index 00000000..2ac9ecbb --- /dev/null +++ b/testcases/t/232-ipc-window-urgent.t @@ -0,0 +1,68 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the window::urgent event works correctly. The window::urgent event +# should be emitted when a window becomes urgent or loses its urgent status. +# +use i3test; + +my $config = <connect()->recv; + +my $cv; +$i3->subscribe({ + window => sub { + my ($event) = @_; + $cv->send($event) if $event->{change} eq 'urgent'; + } +})->recv; + +my $t; +$t = AnyEvent->timer( + after => 0.5, + cb => sub { + $cv->send(0); + } +); + +$cv = AnyEvent->condvar; +fresh_workspace; +my $win = open_window; +my $dummy_win = open_window; + +$win->add_hint('urgency'); +my $event = $cv->recv; + +isnt($event, 0, 'an urgent con should emit the window::urgent event'); +is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window'); +is($event->{container}->{urgent}, 1, 'the container should be urgent'); + +$cv = AnyEvent->condvar; +$win->delete_hint('urgency'); +my $event = $cv->recv; + +isnt($event, 0, 'an urgent con should emit the window::urgent event'); +is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window'); +is($event->{container}->{urgent}, 0, 'the container should not be urgent'); + +done_testing; From 14ec68526b3548551a83bba2a9142dc0bd17f19d Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 20 Jun 2014 05:19:30 -0400 Subject: [PATCH 025/103] Cross-output focus focus floating exception Focusing across outputs with `focus [direction]` should focus an existing floating con when no tiling con exists on the output in [direction]. --- src/tree.c | 7 +++ .../t/520-regress-focus-direction-floating.t | 48 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 testcases/t/520-regress-focus-direction-floating.t diff --git a/src/tree.c b/src/tree.c index b54f77b1..bbd5e6a4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -582,6 +582,13 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) return true; Con *focus = con_descend_direction(workspace, direction); + + /* special case: if there was no tiling con to focus and the workspace + * has a floating con in the focus stack, focus the top of the focus + * stack (which may be floating) */ + if (focus == workspace) + focus = con_descend_focused(workspace); + if (focus) { con_focus(focus); x_set_warp_to(&(focus->rect)); diff --git a/testcases/t/520-regress-focus-direction-floating.t b/testcases/t/520-regress-focus-direction-floating.t new file mode 100644 index 00000000..ccef49e7 --- /dev/null +++ b/testcases/t/520-regress-focus-direction-floating.t @@ -0,0 +1,48 @@ +#!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) +# +# Ensure that `focus [direction]` will focus an existing floating con when no +# tiling con exists on the output in [direction] when focusing across outputs +# Bug still in: 4.7.2-204-g893dbae +use i3test i3_autostart => 0; + +my $config = <input_focus, $win->id, + 'Focusing across outputs with `focus [direction]` should focus an existing floating con when no tiling con exists on the output in [direction].'); + +exit_gracefully($pid); + +done_testing; From 43b447855df095c56e860117d53acd0dd09ac42b Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 20 Jun 2014 07:26:36 -0400 Subject: [PATCH 026/103] Consider motif border for floating geometry When calculating the geometry of a floating window with motif hints that specify a border style, take into account that this window will have a different border style when calculating its position with floating_enable() when the window becomes managed. A nice side effect of this is that users can override motif hints with `new_float` config directives when they are specified other than `normal`. fixes #1270 --- src/manage.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/manage.c b/src/manage.c index 61ca0409..fa3a4958 100644 --- a/src/manage.c +++ b/src/manage.c @@ -436,11 +436,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (nc->geometry.width == 0) nc->geometry = (Rect) {geom->x, geom->y, geom->width, geom->height}; - if (want_floating) { - DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height); - floating_enable(nc, true); - } - if (motif_border_style != BS_NORMAL) { DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style); if (want_floating) { @@ -450,6 +445,16 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki } } + if (want_floating) { + DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height); + /* motif hints will be applied only when `new_float` is `normal` or not + * specified */ + bool automatic_border = (config.default_floating_border != BS_NORMAL && + motif_border_style == BS_NORMAL); + + floating_enable(nc, automatic_border); + } + /* to avoid getting an UnmapNotify event due to reparenting, we temporarily * declare no interest in any state change event of this window */ values[0] = XCB_NONE; From e77103012fd44f987e92cad92731108a00d859c4 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 16 Jun 2014 02:31:03 -0400 Subject: [PATCH 027/103] remove unneeded render on unmap tree_render() will call x_push_changes(), so calling it afterward is not needed. --- src/handlers.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/handlers.c b/src/handlers.c index 80297c79..d8b50997 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -489,7 +489,6 @@ static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) { tree_close(con, DONT_KILL_WINDOW, false, false); tree_render(); - x_push_changes(croot); ignore_end: /* If the client (as opposed to i3) destroyed or unmapped a window, an From ca5137eeba0e656fffd69b8af6abcf6b962d0231 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 16 Jun 2014 02:34:22 -0400 Subject: [PATCH 028/103] remove unneeded render on map request manage_window() will call tree_render() when appropriate, so pushing changes to X here is not needed. --- src/handlers.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/handlers.c b/src/handlers.c index d8b50997..4895165e 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -284,7 +284,6 @@ static void handle_map_request(xcb_map_request_event_t *event) { add_ignore_event(event->sequence, -1); manage_window(event->window, cookie, false); - x_push_changes(croot); return; } From 136b3e345bfde637d3270eafaa2d8aa3555cc3d5 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Wed, 18 Jun 2014 04:23:00 -0400 Subject: [PATCH 029/103] Handle WM_CHANGE_STATE requests for iconic state http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 > IconicState - The client's top-level window is iconic (whatever that > means for this window manager). The client can assume that its > top-level window is not viewable, its icon_window (if any) will be > viewable and, failing that, its icon_pixmap (if any) or its > WM_ICON_NAME will be displayed. For these requests, we just close the window. fixes #1279 --- include/atoms.xmacro | 1 + src/handlers.c | 15 ++++++++++ testcases/t/231-wm-change-state.t | 49 +++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 testcases/t/231-wm-change-state.t diff --git a/include/atoms.xmacro b/include/atoms.xmacro index cb928dc0..6366547c 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -37,3 +37,4 @@ xmacro(I3_PID) xmacro(_NET_REQUEST_FRAME_EXTENTS) xmacro(_NET_FRAME_EXTENTS) xmacro(_MOTIF_WM_HINTS) +xmacro(WM_CHANGE_STATE) diff --git a/src/handlers.c b/src/handlers.c index 4895165e..5d21a985 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -791,6 +791,21 @@ static void handle_client_message(xcb_client_message_event_t *event) { XCB_ATOM_CARDINAL, 32, 4, &r); xcb_flush(conn); + } else if (event->type == A_WM_CHANGE_STATE) { + /* http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 */ + Con *con = con_by_window_id(event->window); + + if (con && event->data.data32[0] == 3) { + /* this request is so we can play some animiation showing the + * window physically moving to the tray before we close it (I + * think) */ + DLOG("Client has requested iconic state. Closing this con. (con = %p)\n", con); + tree_close(con, DONT_KILL_WINDOW, false, false); + tree_render(); + } else { + DLOG("Not handling WM_CHANGE_STATE request. (window = %d, state = %d)\n", event->window, event->data.data32[0]); + } + } else { DLOG("unhandled clientmessage\n"); return; diff --git a/testcases/t/231-wm-change-state.t b/testcases/t/231-wm-change-state.t new file mode 100644 index 00000000..92992b45 --- /dev/null +++ b/testcases/t/231-wm-change-state.t @@ -0,0 +1,49 @@ +#!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) +# +# Correctly handle WM_CHANGE_STATE requests for the iconic state +# See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.4 +# Ticket: #1279 +# Bug still in: 4.8-7-gf4a8253 +use i3test; + +sub send_iconic_state_request { + my ($win) = @_; + + my $msg = pack "CCSLLLLLL", + X11::XCB::CLIENT_MESSAGE, # response_type + 32, # format + 0, # sequence + $win->id, # window + $x->atom(name => 'WM_CHANGE_STATE')->id, # message type + 3, # data32[0] + 0, # data32[1] + 0, # data32[2] + 0, # data32[3] + 0; # data32[4] + + $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg); +} + +my $ws = fresh_workspace; +my $win = open_window; + +send_iconic_state_request($win); +sync_with_i3; + +is(@{get_ws($ws)->{nodes}}, 0, 'When a window requests the iconic state, the container should be closed'); + +done_testing; From 01d8d10c57cd09c73ce45e53f5d68cdbb7de268d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 25 Jun 2014 17:57:48 +0200 Subject: [PATCH 030/103] libxkbcommon 0.4.0 is required, 0.2.0 is too old (Thanks Mii) fixes #1295 --- DEPENDS | 4 ++-- debian/control | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPENDS b/DEPENDS index b56e2ec8..2d62e748 100644 --- a/DEPENDS +++ b/DEPENDS @@ -10,8 +10,8 @@ │ pkg-config │ 0.25 │ 0.26 │ http://pkgconfig.freedesktop.org/ │ │ libxcb │ 1.1.93 │ 1.10 │ http://xcb.freedesktop.org/dist/ │ │ xcb-util │ 0.3.3 │ 0.4.1 │ http://xcb.freedesktop.org/dist/ │ -│ xkbcommon │ 0.2.0 │ 0.4.0 │ http://xkbcommon.org/ │ -│ xkbcommon-x11│ 0.2.0 │ 0.4.0 │ http://xkbcommon.org/ │ +│ xkbcommon │ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │ +│ xkbcommon-x11│ 0.4.0 │ 0.4.0 │ http://xkbcommon.org/ │ │ util-cursor³ │ 0.0.99 │ 0.0.99 │ http://xcb.freedesktop.org/dist/ │ │ libev │ 4.0 │ 4.11 │ http://libev.schmorp.de/ │ │ yajl │ 2.0.1 │ 2.0.4 │ http://lloyd.github.com/yajl/ │ diff --git a/debian/control b/debian/control index 9f6518e0..b50bb964 100644 --- a/debian/control +++ b/debian/control @@ -11,8 +11,8 @@ Build-Depends: debhelper (>= 7.0.50~), libxcb-icccm4-dev, libxcb-cursor-dev, libxcb-xkb-dev, - libxkbcommon-dev, - libxkbcommon-x11-dev, + libxkbcommon-dev (>= 0.4.0), + libxkbcommon-x11-dev (>= 0.4.0), asciidoc (>= 8.4.4), xmlto, docbook-xml, From 80007828c3046dc8617c0044f80bf1b0e8ebc3c1 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 25 Jun 2014 17:58:16 +0200 Subject: [PATCH 031/103] cleanup: src/i3.mk: add XKB_COMMON_CFLAGS when compiling i3 --- src/i3.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i3.mk b/src/i3.mk index f1105a04..8c763464 100644 --- a/src/i3.mk +++ b/src/i3.mk @@ -5,7 +5,7 @@ CLEAN_TARGETS += clean-i3 i3_SOURCES := $(filter-out $(i3_SOURCES_GENERATED),$(wildcard src/*.c)) i3_HEADERS_CMDPARSER := $(wildcard include/GENERATED_*.h) i3_HEADERS := $(filter-out $(i3_HEADERS_CMDPARSER),$(wildcard include/*.h)) -i3_CFLAGS = $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) +i3_CFLAGS = $(XKB_COMMON_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS) i3_LIBS = $(XKB_COMMON_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread # When using clang, we use pre-compiled headers to speed up the build. With From bb24db198685e7dd38736eef590539b9cc6b6891 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 30 Jun 2014 20:40:31 -0400 Subject: [PATCH 032/103] Bugfix: _NET_SUPPORTED property fixes Fix a typing mistake that may cause _NET_SUPPORTED root property to become corrupted. This bug was introduced in: Implement EWMH number of desktops property http://code.stapelberg.de/git/i3/commit/?h=next&id=b47f480728a6d053c117e4b99ee4c78d99ba6a71 Additionally adds one missing atom to _NET_SUPPORTED. fixes #1298 --- src/ewmh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ewmh.c b/src/ewmh.c index da5dba5f..1c4ac3d7 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -229,10 +229,10 @@ void ewmh_setup_hints(void) { NULL); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, child_window, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 33, 1, &child_window); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32, 1, &child_window); /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 21, supported_atoms); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 22, supported_atoms); } From 7381b50587ea7266a6bcde20285976a6c14dba42 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 1 Jul 2014 02:12:28 -0400 Subject: [PATCH 033/103] Make absolute floating move work with criteria Make commands of type `move [window|container] [to] [absolute] position [px] [px]` work with command selection criteria. fixes #1301 --- src/commands.c | 50 ++++++++++++++++++++++++++---------------- testcases/t/124-move.t | 16 ++++++++++++++ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/commands.c b/src/commands.c index 7803c984..7a650268 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1810,34 +1810,46 @@ void cmd_focus_output(I3_CMD, char *name) { void cmd_move_window_to_position(I3_CMD, char *method, char *cx, char *cy) { int x = atoi(cx); int y = atoi(cy); + bool has_error = false; - if (!con_is_floating(focused)) { - ELOG("Cannot change position. The window/container is not floating\n"); - yerror("Cannot change position. The window/container is not floating."); - return; - } + owindow *current; + HANDLE_EMPTY_MATCH; - if (strcmp(method, "absolute") == 0) { - focused->parent->rect.x = x; - focused->parent->rect.y = y; + TAILQ_FOREACH(current, &owindows, owindows) { + if (!con_is_floating(current->con)) { + ELOG("Cannot change position. The window/container is not floating\n"); - DLOG("moving to absolute position %d %d\n", x, y); - floating_maybe_reassign_ws(focused->parent); - cmd_output->needs_tree_render = true; - } + if (!has_error) { + yerror("Cannot change position of a window/container because it is not floating."); + has_error = true; + } - if (strcmp(method, "position") == 0) { - Rect newrect = focused->parent->rect; + continue; + } - DLOG("moving to position %d %d\n", x, y); - newrect.x = x; - newrect.y = y; + if (strcmp(method, "absolute") == 0) { + current->con->parent->rect.x = x; + current->con->parent->rect.y = y; - floating_reposition(focused->parent, newrect); + DLOG("moving to absolute position %d %d\n", x, y); + floating_maybe_reassign_ws(current->con->parent); + cmd_output->needs_tree_render = true; + } + + if (strcmp(method, "position") == 0) { + Rect newrect = current->con->parent->rect; + + DLOG("moving to position %d %d\n", x, y); + newrect.x = x; + newrect.y = y; + + floating_reposition(current->con->parent, newrect); + } } // XXX: default reply for now, make this a better reply - ysuccess(true); + if (!has_error) + ysuccess(true); } /* diff --git a/testcases/t/124-move.t b/testcases/t/124-move.t index 28207a3c..88a36a5b 100644 --- a/testcases/t/124-move.t +++ b/testcases/t/124-move.t @@ -245,4 +245,20 @@ my $center_y = int($x->root->rect->height/2) - int($floatcon[0]->{rect}->{height is($floatcon[0]->{rect}->{x}, $center_x, "moved to center at position $center_x x"); is($floatcon[0]->{rect}->{y}, $center_y, "moved to center at position $center_y y"); +# Make sure the command works with criteria +open_floating_window; + +@floatcon = @{get_ws($tmp)->{floating_nodes}}; + +cmd '[con_id="' . $floatcon[0]->{nodes}[0]->{id} . '"] move position 25 px 30 px'; +cmd '[con_id="' . $floatcon[1]->{nodes}[0]->{id} . '"] move position 35 px 40 px'; + +@floatcon = @{get_ws($tmp)->{floating_nodes}}; + +is($floatcon[0]->{rect}->{x}, 25, 'moved to position 25 x with criteria'); +is($floatcon[0]->{rect}->{y}, 30, 'moved to position 30 y with criteria'); + +is($floatcon[1]->{rect}->{x}, 35, 'moved to position 35 x with criteria'); +is($floatcon[1]->{rect}->{y}, 40, 'moved to position 40 y with criteria'); + done_testing; From 1c81216a72db0f8b3a93d21251ff3642f4f1d664 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 5 Jul 2014 09:57:33 -0400 Subject: [PATCH 034/103] Add testcase for EWMH desktop names Test that the EWMH specified property _NET_DESKTOP_NAMES is updated properly on the root window. We interpret this as a list of the open workspace names. This test is for this commit: Implement EWMH desktop names a9c094b7313b48491c182a5da1194a0bb06747aa --- testcases/t/234-ewmh-desktop-names.t | 72 ++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 testcases/t/234-ewmh-desktop-names.t diff --git a/testcases/t/234-ewmh-desktop-names.t b/testcases/t/234-ewmh-desktop-names.t new file mode 100644 index 00000000..570e8528 --- /dev/null +++ b/testcases/t/234-ewmh-desktop-names.t @@ -0,0 +1,72 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the EWMH specified property _NET_DESKTOP_NAMES is updated properly +# on the root window. We interpret this as a list of the open workspace names. +# Ticket: #1241 +use i3test; + +sub get_desktop_names { + my $cookie = $x->get_property( + 0, + $x->get_root_window(), + $x->atom(name => '_NET_DESKTOP_NAMES')->id, + $x->atom(name => 'UTF8_STRING')->id, + 0, + 4096, + ); + + my $reply = $x->get_property_reply($cookie->{sequence}); + + return 0 if $reply->{value_len} == 0; + + # the property is a null-delimited list of utf8 strings ;; + return split /\0/, $reply->{value}; +} + +cmd 'workspace foo'; + +my @expected_names = ('foo'); +my @desktop_names = get_desktop_names; + +is_deeply(\@desktop_names, \@expected_names, '_NET_DESKTOP_NAMES should be an array of the workspace names'); + +# open a new workspace and see that the property is updated correctly +open_window; +cmd 'workspace bar'; + +@desktop_names = get_desktop_names; +@expected_names = ('foo', 'bar'); + +is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a new workspace appears'); + +# rename the workspace and see that the property is updated correctly +cmd 'rename workspace bar to baz'; + +@desktop_names = get_desktop_names; +@expected_names = ('foo', 'baz'); + +is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a workspace is renamed'); + +# empty a workspace and see that the property is updated correctly +cmd 'workspace foo'; + +@desktop_names = get_desktop_names; +@expected_names = ('foo'); + +is_deeply(\@desktop_names, \@expected_names, 'it should be updated when a workspace is emptied'); + +done_testing; From d13730de70b1ad9e5737e6ac97e31c1544177496 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 5 Jul 2014 10:38:46 -0400 Subject: [PATCH 035/103] Add link to git repository in hacking docs --- docs/hacking-howto | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/hacking-howto b/docs/hacking-howto index f4e3f031..aca6e92b 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -951,6 +951,8 @@ For a short introduction into using git, see http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy or, for more documentation, see http://git-scm.com/documentation +You can view the git repository online at http://code.i3wm.org. + 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. From 704ccb1d25293a36623f49899b89433afbe35526 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 10 Jul 2014 22:30:45 +0200 Subject: [PATCH 036/103] tests: include error message when unable to create a FIFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have seen this message once or twice, but since the actual error message was not included, I cannot definitely say what’s going on. I think it might be a race condition where the file with the specified tmpname() already exists, but let’s be sure before we attempt to fix it. --- testcases/t/175-startup-notification.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testcases/t/175-startup-notification.t b/testcases/t/175-startup-notification.t index a85320ea..9f0c046a 100644 --- a/testcases/t/175-startup-notification.t +++ b/testcases/t/175-startup-notification.t @@ -84,7 +84,7 @@ is_num_children($first_ws, 0, 'no containers on this workspace yet'); # echo its $DESKTOP_STARTUP_ID. We (blockingly) read the variable into # $startup_id in the testcase. my $tmp = tmpnam(); -mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp"; +mkfifo($tmp, 0600) or BAIL_OUT "Could not create FIFO in $tmp: $!"; cmd qq|exec echo \$DESKTOP_STARTUP_ID >$tmp|; From b7f4ac769ba2d1a31563378e08dc58a1ab812c58 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 6 Jul 2014 17:26:16 -0400 Subject: [PATCH 037/103] Add testcase for EWMH desktop viewport Test that the EWMH specified property _NET_DESKTOP_VIEWPORT is updated properly on the root window. We interpret this as a list of x/y coordinate pairs for the upper left corner of the respective outputs of the workspaces. This test is for this commit: feature: implement ewmh desktop viewport property 4205973135f28a0ab0e25a63dbc8c6fff0d3539e --- testcases/t/521-ewmh-desktop-viewport.t | 92 +++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 testcases/t/521-ewmh-desktop-viewport.t diff --git a/testcases/t/521-ewmh-desktop-viewport.t b/testcases/t/521-ewmh-desktop-viewport.t new file mode 100644 index 00000000..07034cda --- /dev/null +++ b/testcases/t/521-ewmh-desktop-viewport.t @@ -0,0 +1,92 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the EWMH specified property _NET_DESKTOP_VIEWPORT is updated +# properly on the root window. We interpret this as a list of x/y coordinate +# pairs for the upper left corner of the respective outputs of the workspaces +# Ticket: #1241 +use i3test i3_autostart => 0; + +my $config = <get_property( + 0, + $x->get_root_window(), + $x->atom(name => '_NET_DESKTOP_VIEWPORT')->id, + $x->atom(name => 'CARDINAL')->id, + 0, + 4096 + ); + + my $reply = $x->get_property_reply($cookie->{sequence}); + + return 0 if $reply->{value_len} == 0; + + my $len = $reply->{length}; + + return unpack ("L$len", $reply->{value}); +} + +# initialize the workspaces +cmd 'workspace 1'; +cmd 'workspace 0'; + +my @expected_viewport = (0, 0, 1024, 0); +my @desktop_viewport = get_desktop_viewport; + +is_deeply(\@desktop_viewport, \@expected_viewport, + '_NET_DESKTOP_VIEWPORT should be an array of x/y coordinate pairs for the upper left corner of the respective outputs of the workspaces'); + +cmd 'workspace 0'; +open_window; +cmd 'workspace 3'; + +@expected_viewport = (0, 0, 0, 0, 1024, 0); +@desktop_viewport = get_desktop_viewport; + +is_deeply(\@desktop_viewport, \@expected_viewport, + 'it should be updated when a new workspace appears'); + +cmd 'rename workspace 3 to 2'; + +@expected_viewport = (0, 0, 0, 0, 1024, 0); +@desktop_viewport = get_desktop_viewport; + +is_deeply(\@desktop_viewport, \@expected_viewport, + 'it should stay up to date when a workspace is renamed'); + +cmd 'workspace 0'; + +@expected_viewport = (0, 0, 1024, 0); +@desktop_viewport = get_desktop_viewport; + +is_deeply(\@desktop_viewport, \@expected_viewport, + 'it should be updated when a workspace is emptied'); + +exit_gracefully($pid); + +done_testing; From 196e748e94550b0f31714517526147a47f922ee5 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 7 Jul 2014 18:42:15 -0400 Subject: [PATCH 038/103] bugfix: default floating border regression Fixes a bug where a normal floating default border is not applied when the default tiling border is set to a pixel value. This bug was introduced in this commit: 43b447855df095c56e860117d53acd0dd09ac42b Consider motif border for floating geometry Fixes a comment that claimed default floating border could override motif hints, which was never the case. fixes #1305 --- src/manage.c | 7 ++- .../t/234-regress-default-floating-border.t | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 testcases/t/234-regress-default-floating-border.t diff --git a/src/manage.c b/src/manage.c index d86eb06c..5148d3f3 100644 --- a/src/manage.c +++ b/src/manage.c @@ -447,10 +447,9 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki if (want_floating) { DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height); - /* motif hints will be applied only when `new_float` is `normal` or not - * specified */ - bool automatic_border = (config.default_floating_border != BS_NORMAL && - motif_border_style == BS_NORMAL); + /* automatically set the border to the default value if a motif border + * was not specified */ + bool automatic_border = (motif_border_style == BS_NORMAL); floating_enable(nc, automatic_border); } diff --git a/testcases/t/234-regress-default-floating-border.t b/testcases/t/234-regress-default-floating-border.t new file mode 100644 index 00000000..d5994f58 --- /dev/null +++ b/testcases/t/234-regress-default-floating-border.t @@ -0,0 +1,43 @@ +#!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) +# +# This is a regression test for a bug where a normal floating default border is +# not applied when the default tiling border is set to a pixel value. +# Ticket: #1305 +# Bug still in: 4.8-62-g7381b50 +use i3test i3_autostart => 0; + +my $config = <{floating_nodes}}; + +is($floating[0]->{nodes}[0]->{border}, 'normal', 'default floating border is `normal`'); + +exit_gracefully($pid); + +done_testing; From 0514be8d4b6a0470dff9ec9c5f00af7d63a07664 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 4 Jul 2014 05:39:07 -0400 Subject: [PATCH 039/103] Handle EWMH requests to change current desktop This request is used by pagers and bars to change the current desktop likely as a result of some user action. We interpret this as a request to focus the given workspace. for more information see: http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008 --- src/handlers.c | 32 +++++++++++++++++++++++++ testcases/t/217-NET_CURRENT_DESKTOP.t | 34 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/handlers.c b/src/handlers.c index 5d21a985..65fa46a0 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -806,6 +806,38 @@ static void handle_client_message(xcb_client_message_event_t *event) { DLOG("Not handling WM_CHANGE_STATE request. (window = %d, state = %d)\n", event->window, event->data.data32[0]); } + } else if (event->type == A__NET_CURRENT_DESKTOP) { + /* This request is used by pagers and bars to change the current + * desktop likely as a result of some user action. We interpret this as + * a request to focus the given workspace. See + * http://standards.freedesktop.org/wm-spec/latest/ar01s03.html#idm140251368135008 + * */ + Con *output; + uint32_t idx = 0; + DLOG("Request to change current desktop to index %d\n", event->data.data32[0]); + + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { + Con *ws; + TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) { + if (STARTS_WITH(ws->name, "__")) + continue; + + if (idx == event->data.data32[0]) { + /* data32[1] is a timestamp used to prevent focus race conditions */ + if (event->data.data32[1]) + last_timestamp = event->data.data32[1]; + + DLOG("Handling request to focus workspace %s\n", ws->name); + + workspace_show(ws); + tree_render(); + + return; + } + + ++idx; + } + } } else { DLOG("unhandled clientmessage\n"); return; diff --git a/testcases/t/217-NET_CURRENT_DESKTOP.t b/testcases/t/217-NET_CURRENT_DESKTOP.t index fe2ea675..7ca6fbc2 100644 --- a/testcases/t/217-NET_CURRENT_DESKTOP.t +++ b/testcases/t/217-NET_CURRENT_DESKTOP.t @@ -71,6 +71,40 @@ is(current_desktop_index, 1, "Open on 0 and view 1"); cmd 'workspace 2'; is(current_desktop_index, 2, "Open and view empty"); +######################################################### +# Test the _NET_CURRENT_DESKTOP client request +# This request is sent by pagers and bars to switch the current desktop (which +# is like an ersatz workspace) to the given index +######################################################### + +sub send_current_desktop_request { + my ($idx) = @_; + + my $msg = pack "CCSLLLLLL", + X11::XCB::CLIENT_MESSAGE, # response_type + 32, # format + 0, # sequence + 0, + $_NET_CURRENT_DESKTOP, + $idx, # data32[0] (the desktop index) + 0, # data32[1] (can be a timestamp) + 0, # data32[2] + 0, # data32[3] + 0; # data32[4] + + $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg); +} + +send_current_desktop_request(1); +is(current_desktop_index, 1, 'current desktop request switched to desktop 1'); +# note that _NET_CURRENT_DESKTOP is an index and that in this case, workspace 1 +# is at index 1 as a convenience for the test +is(focused_ws, '1', 'current desktop request switched to workspace 1'); + +send_current_desktop_request(0); +is(current_desktop_index, 0, 'current desktop request switched to desktop 0'); +is(focused_ws, '0', 'current desktop request switched to workspace 0'); + exit_gracefully($pid); done_testing; From 8e23dc881b33f1b5a434db89b804f935ff146bbb Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 4 Jul 2014 05:53:22 -0400 Subject: [PATCH 040/103] i3bar: implement custom mouse wheel commands Users can specify a command to run when a button was pressed on i3bar to override the default behavior. Currently only the mouse wheel buttons are supported. This is useful for disabling the scroll wheel action or running scripts that implement custom behavior for these buttons. Example: bar { wheel_up_cmd nop wheel_down_cmd exec ~/.i3/scripts/custom_wheel_down } fixes #1104 --- docs/userguide | 21 +++++++++++++++++++++ i3bar/include/config.h | 2 ++ i3bar/src/config.c | 14 ++++++++++++++ i3bar/src/xcb.c | 16 ++++++++++++++++ include/config.h | 8 ++++++++ include/config_directives.h | 2 ++ parser-specs/config.spec | 10 ++++++++++ src/config_directives.c | 10 ++++++++++ src/ipc.c | 10 ++++++++++ testcases/t/201-config-parser.t | 2 +- 10 files changed, 94 insertions(+), 1 deletion(-) diff --git a/docs/userguide b/docs/userguide index 804877d5..e752ec82 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1104,6 +1104,27 @@ bar { Available modifiers are Mod1-Mod5, Shift, Control (see +xmodmap(1)+). +=== Mouse button commands + +Specifies a command to run when a button was pressed on i3bar to override the +default behavior. Currently only the mouse wheel buttons are supported. This is +useful for disabling the scroll wheel action or running scripts that implement +custom behavior for these buttons. + +*Syntax*: +--------------------- +wheel_up_cmd +wheel_down_cmd +--------------------- + +*Example*: +--------------------- +bar { + wheel_up_cmd nop + wheel_down_cmd exec ~/.i3/scripts/custom_wheel_down +} +--------------------- + === Bar ID Specifies the bar ID for the configured bar instance. If this option is missing, diff --git a/i3bar/include/config.h b/i3bar/include/config.h index 730d3ef0..2c399305 100644 --- a/i3bar/include/config.h +++ b/i3bar/include/config.h @@ -24,6 +24,8 @@ typedef enum { M_DOCK = 0, typedef struct config_t { int modifier; + char *wheel_up_cmd; + char *wheel_down_cmd; position_t position; int verbose; struct xcb_color_strings_t colors; diff --git a/i3bar/src/config.c b/i3bar/src/config.c index 6f1a8b8e..bb322619 100644 --- a/i3bar/src/config.c +++ b/i3bar/src/config.c @@ -112,6 +112,20 @@ static int config_string_cb(void *params_, const unsigned char *val, size_t _len return 1; } + if (!strcmp(cur_key, "wheel_up_cmd")) { + DLOG("wheel_up_cmd = %.*s\n", len, val); + FREE(config.wheel_up_cmd); + sasprintf(&config.wheel_up_cmd, "%.*s", len, val); + return 1; + } + + if (!strcmp(cur_key, "wheel_down_cmd")) { + DLOG("wheel_down_cmd = %.*s\n", len, val); + FREE(config.wheel_down_cmd); + sasprintf(&config.wheel_down_cmd, "%.*s", len, val); + return 1; + } + if (!strcmp(cur_key, "position")) { DLOG("position = %.*s\n", len, val); config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT); diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 37a13347..5bbec13e 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -370,6 +370,14 @@ void handle_button(xcb_button_press_event_t *event) { * If there is no more workspace, don’t even send the workspace * command, otherwise (with workspace auto_back_and_forth) we’d end * up on the wrong workspace. */ + + /* If `wheel_up_cmd [COMMAND]` was specified, it should override + * the default behavior */ + if (config.wheel_up_cmd) { + i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_up_cmd); + return; + } + if (cur_ws == TAILQ_FIRST(walk->workspaces)) return; @@ -380,6 +388,14 @@ void handle_button(xcb_button_press_event_t *event) { * If there is no more workspace, don’t even send the workspace * command, otherwise (with workspace auto_back_and_forth) we’d end * up on the wrong workspace. */ + + /* if `wheel_down_cmd [COMMAND]` was specified, it should override + * the default behavior */ + if (config.wheel_down_cmd) { + i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, config.wheel_down_cmd); + return; + } + if (cur_ws == TAILQ_LAST(walk->workspaces, ws_head)) return; diff --git a/include/config.h b/include/config.h index 22110eba..b0f22417 100644 --- a/include/config.h +++ b/include/config.h @@ -261,6 +261,14 @@ struct Barconfig { M_MOD5 = 7 } modifier; + /** Command that should be run when mouse wheel up button is pressed over + * i3bar to override the default behavior. */ + char *wheel_up_cmd; + + /** Command that should be run when mouse wheel down button is pressed over + * i3bar to override the default behavior. */ + char *wheel_down_cmd; + /** Bar position (bottom by default). */ enum { P_BOTTOM = 0, P_TOP = 1 } position; diff --git a/include/config_directives.h b/include/config_directives.h index 7fdca8c8..af7b9a90 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -73,6 +73,8 @@ CFGFUN(bar_id, const char *bar_id); CFGFUN(bar_output, const char *output); CFGFUN(bar_verbose, const char *verbose); CFGFUN(bar_modifier, const char *modifier); +CFGFUN(bar_wheel_up_cmd, const char *command); +CFGFUN(bar_wheel_down_cmd, const char *command); CFGFUN(bar_position, const char *position); CFGFUN(bar_i3bar_command, const char *i3bar_command); CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text); diff --git a/parser-specs/config.spec b/parser-specs/config.spec index f1021b26..bdd03565 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -358,6 +358,8 @@ state BAR: 'hidden_state' -> BAR_HIDDEN_STATE 'id' -> BAR_ID 'modifier' -> BAR_MODIFIER + 'wheel_up_cmd' -> BAR_WHEEL_UP_CMD + 'wheel_down_cmd' -> BAR_WHEEL_DOWN_CMD 'position' -> BAR_POSITION 'output' -> BAR_OUTPUT 'tray_output' -> BAR_TRAY_OUTPUT @@ -403,6 +405,14 @@ state BAR_MODIFIER: modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Ctrl', 'Shift' -> call cfg_bar_modifier($modifier); BAR +state BAR_WHEEL_UP_CMD: + command = string + -> call cfg_bar_wheel_up_cmd($command); BAR + +state BAR_WHEEL_DOWN_CMD: + command = string + -> call cfg_bar_wheel_down_cmd($command); BAR + state BAR_POSITION: position = 'top', 'bottom' -> call cfg_bar_position($position); BAR diff --git a/src/config_directives.c b/src/config_directives.c index 6dfd369d..b5e4a32a 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -459,6 +459,16 @@ CFGFUN(bar_modifier, const char *modifier) { current_bar.modifier = M_SHIFT; } +CFGFUN(bar_wheel_up_cmd, const char *command) { + FREE(current_bar.wheel_up_cmd); + current_bar.wheel_up_cmd = sstrdup(command); +} + +CFGFUN(bar_wheel_down_cmd, const char *command) { + FREE(current_bar.wheel_down_cmd); + current_bar.wheel_down_cmd = sstrdup(command); +} + CFGFUN(bar_position, const char *position) { current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM); } diff --git a/src/ipc.c b/src/ipc.c index 8413d0a6..d6bb34a4 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -512,6 +512,16 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { break; } + if (config->wheel_up_cmd) { + ystr("wheel_up_cmd"); + ystr(config->wheel_up_cmd); + } + + if (config->wheel_down_cmd) { + ystr("wheel_down_cmd"); + ystr(config->wheel_down_cmd); + } + ystr("position"); if (config->position == P_BOTTOM) ystr("bottom"); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index 874a25ec..1153423b 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -645,7 +645,7 @@ EOT $expected = <<'EOT'; cfg_bar_output(LVDS-1) -ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}' +ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}' ERROR: CONFIG: (in file ) ERROR: CONFIG: Line 1: bar { ERROR: CONFIG: Line 2: output LVDS-1 From 6bf7f8ef78f165504ff39f8767e7e11b3fbd8e84 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 7 Jul 2014 21:01:14 -0400 Subject: [PATCH 041/103] Always explicitly set border widths on manage When a window becomes managed, explicitly set the current border width to the default instead of relying on the default value of -1 to apply the correct value. Now that there are two different kinds of default borders, a border width value of -1 is ambiguous. This can lead to different border widths effectively being applied when the container changes from tiling to floating, which is surprising behavior. This commit extends behavior introduced in this commit to normal borders: 7afe9cc78b4d22dfd5a0c5866382a0e001ea38b6 Explicitly set current border width when BS_PIXEL fixes #1304 --- src/manage.c | 7 ++---- testcases/t/228-border-widths.t | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/manage.c b/src/manage.c index 5148d3f3..b7ea5e69 100644 --- a/src/manage.c +++ b/src/manage.c @@ -454,11 +454,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki floating_enable(nc, automatic_border); } - if (nc->border_style == BS_PIXEL) { - /* if the border style is BS_PIXEL, explicitly set the border width of - * the new container */ - nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width); - } + /* explicitly set the border width to the default */ + nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width); /* to avoid getting an UnmapNotify event due to reparenting, we temporarily * declare no interest in any state change event of this window */ diff --git a/testcases/t/228-border-widths.t b/testcases/t/228-border-widths.t index e236fe60..be1e7f85 100644 --- a/testcases/t/228-border-widths.t +++ b/testcases/t/228-border-widths.t @@ -47,10 +47,12 @@ my $wscontent = get_ws($tmp); my @tiled = @{$wscontent->{nodes}}; ok(@tiled == 1, 'one tiled container opened'); +is($tiled[0]->{current_border_width}, 5, 'tiled current border width set to 5'); is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*5, 'tiled border width 5'); my @floating = @{$wscontent->{floating_nodes}}; ok(@floating == 1, 'one floating container opened'); +is($floating[0]->{nodes}[0]->{current_border_width}, 10, 'floating current border width set to 10'); is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*10, 'floating border width 10'); exit_gracefully($pid); @@ -80,12 +82,49 @@ $wscontent = get_ws($tmp); @tiled = @{$wscontent->{nodes}}; ok(@tiled == 1, 'one tiled container opened'); +is($tiled[0]->{current_border_width}, 3, 'tiled current border width set to 3'); is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*3, 'tiled border width 3'); @floating = @{$wscontent->{floating_nodes}}; ok(@floating == 1, 'one floating container opened'); +is($floating[0]->{nodes}[0]->{current_border_width}, 7, 'floating current border width set to 7'); is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*7, 'floating border width 7'); exit_gracefully($pid); +##################################################################### +# 3: make sure normal border widths work as well +##################################################################### + +$config = <{nodes}}; +ok(@tiled == 1, 'one tiled container opened'); +is($tiled[0]->{current_border_width}, 4, 'tiled current border width set to 4'); +is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*4, 'tiled border width 4'); + +@floating = @{$wscontent->{floating_nodes}}; +ok(@floating == 1, 'one floating container opened'); +is($floating[0]->{nodes}[0]->{current_border_width}, 6, 'floating current border width set to 6'); +is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*6, 'floating border width 6'); + +exit_gracefully($pid); + done_testing; From 344f6be84a9babf6b7ef06567372fd4caae25f02 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 11 Jul 2014 22:23:23 -0400 Subject: [PATCH 042/103] Handle WM_CLASS changes http://tronche.com/gui/x/icccm/sec-4.html > The WM_CLASS property (of type STRING without control characters) > contains two consecutive null-terminated strings. These specify the > Instance and Class names to be used by both the client and the window > manager for looking up resources for the application or as identifying > information. i3 processes changes to WM_CLASS by updating the cached property and running assignments. This allows the property to be used in criteria selection. fixes #1052 --- src/handlers.c | 28 ++++++++- testcases/t/235-wm-class-change-handler.t | 70 +++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 testcases/t/235-wm-class-change-handler.t diff --git a/src/handlers.c b/src/handlers.c index 65fa46a0..06878f5c 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1092,6 +1092,30 @@ static void handle_focus_in(xcb_focus_in_event_t *event) { return; } +/* + * Handles the WM_CLASS property for assignments and criteria selection. + * + */ +static bool handle_class_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, + xcb_atom_t name, xcb_get_property_reply_t *prop) { + Con *con; + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) + return false; + + if (prop == NULL) { + prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn, + false, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 32), + NULL); + + if (prop == NULL) + return false; + } + + window_update_class(con->window, prop, false); + + return true; +} + /* Returns false if the event could not be processed (e.g. the window could not * be found), true otherwise */ typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property); @@ -1109,7 +1133,8 @@ static struct property_handler_t property_handlers[] = { {0, UINT_MAX, handle_normal_hints}, {0, UINT_MAX, handle_clientleader_change}, {0, UINT_MAX, handle_transient_for}, - {0, 128, handle_windowrole_change}}; + {0, 128, handle_windowrole_change}, + {0, 128, handle_class_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -1127,6 +1152,7 @@ void property_handlers_init(void) { property_handlers[4].atom = A_WM_CLIENT_LEADER; property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR; property_handlers[6].atom = A_WM_WINDOW_ROLE; + property_handlers[7].atom = XCB_ATOM_WM_CLASS; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { diff --git a/testcases/t/235-wm-class-change-handler.t b/testcases/t/235-wm-class-change-handler.t new file mode 100644 index 00000000..e6cacded --- /dev/null +++ b/testcases/t/235-wm-class-change-handler.t @@ -0,0 +1,70 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that changes to WM_CLASS are internally processed by i3 by updating the +# cached property and running assignments. This allows the property to be used +# in criteria selection +# Ticket: #1052 +# Bug still in: 4.8-73-g6bf7f8e +use i3test i3_autostart => 0; +use X11::XCB qw(PROP_MODE_REPLACE); + +my $config = <atom(name => 'WM_CLASS'); + my $atomtype = $x->atom(name => 'STRING'); + $x->change_property( + PROP_MODE_REPLACE, + $window->id, + $atomname->id, + $atomtype->id, + 8, + length($class) + 1, + $class + ); + sync_with_i3; +} + +my $ws = fresh_workspace; + +my $win = open_window; + +change_window_class($win, "special\0Special"); + +my $con = @{get_ws_content($ws)}[0]; + +is($con->{window_properties}->{class}, 'Special', + 'The container class should be updated when a window changes class'); + +is($con->{window_properties}->{instance}, 'special', + 'The container instance should be updated when a window changes instance'); + +# The mark `special_class_mark` is added in a `for_window` assignment in the +# config for testing purposes +is($con->{mark}, 'special_class_mark', + 'A `for_window` assignment should run for a match when the window changes class'); + +exit_gracefully($pid); + +done_testing; From 0d656dd05daaa74c0b627b585cbbe8203ec17451 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 15 Jul 2014 10:35:52 +0200 Subject: [PATCH 043/103] =?UTF-8?q?don=E2=80=99t=20set=20names=20correspon?= =?UTF-8?q?ding=20to=20debug=20colors,=20remove=20=E2=80=9Csleep=201?= =?UTF-8?q?=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The names were (mostly) internal to i3 anyway. Hopefully this doesn’t break anything, but I really think this old behavior needs to go now :). --- src/con.c | 23 +---------------------- src/ipc.c | 4 +++- src/load_layout.c | 13 ++++--------- src/restore_layout.c | 5 +++-- testcases/t/122-split.t | 6 +++--- 5 files changed, 14 insertions(+), 37 deletions(-) diff --git a/src/con.c b/src/con.c index 78fe5fef..282c389f 100644 --- a/src/con.c +++ b/src/con.c @@ -13,18 +13,6 @@ */ #include "all.h" -char *colors[] = { - "#ff0000", - "#00FF00", - "#0000FF", - "#ff00ff", - "#00ffff", - "#ffff00", - "#aa0000", - "#00aa00", - "#0000aa", - "#aa00aa"}; - static void con_on_remove_child(Con *con); /* @@ -59,16 +47,7 @@ Con *con_new_skeleton(Con *parent, i3Window *window) { new->depth = window->depth; else new->depth = XCB_COPY_FROM_PARENT; - static int cnt = 0; - DLOG("opening window %d\n", cnt); - - /* TODO: remove window coloring after test-phase */ - DLOG("color %s\n", colors[cnt]); - new->name = strdup(colors[cnt]); - //uint32_t cp = get_colorpixel(colors[cnt]); - cnt++; - if ((cnt % (sizeof(colors) / sizeof(char *))) == 0) - cnt = 0; + DLOG("opening window\n"); TAILQ_INIT(&(new->floating_head)); TAILQ_INIT(&(new->nodes_head)); diff --git a/src/ipc.c b/src/ipc.c index d6bb34a4..59f0938d 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -299,8 +299,10 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr("name"); if (con->window && con->window->name) ystr(i3string_as_utf8(con->window->name)); - else + else if (con->name != NULL) ystr(con->name); + else + y(null); if (con->type == CT_WORKSPACE) { ystr("num"); diff --git a/src/load_layout.c b/src/load_layout.c index ee50660b..5fe7c84c 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -98,26 +98,21 @@ static int json_end_map(void *ctx) { * workspace called “1”. */ Con *output; Con *workspace = NULL; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name)); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name)); char *base = sstrdup(json_node->name); int cnt = 1; while (workspace != NULL) { FREE(json_node->name); asprintf(&(json_node->name), "%s_%d", base, cnt++); workspace = NULL; - TAILQ_FOREACH (output, &(croot->nodes_head), nodes) - GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name)); + TAILQ_FOREACH(output, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, json_node->name)); } free(base); /* Set num accordingly so that i3bar will properly sort it. */ json_node->num = ws_name_to_number(json_node->name); - } else { - // TODO: remove this in the “next” branch. - if (json_node->name == NULL || strcmp(json_node->name, "") == 0) { - json_node->name = sstrdup("#ff0000"); - } } LOG("attaching\n"); diff --git a/src/restore_layout.c b/src/restore_layout.c index d6d7927d..3a657c90 100644 --- a/src/restore_layout.c +++ b/src/restore_layout.c @@ -197,8 +197,9 @@ static void open_placeholder_window(Con *con) { /* Set the same name as was stored in the layout file. While perhaps * slightly confusing in the first instant, this brings additional * clarity to which placeholder is waiting for which actual window. */ - xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder, - A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name); + if (con->name != NULL) + xcb_change_property(restore_conn, XCB_PROP_MODE_REPLACE, placeholder, + A__NET_WM_NAME, A_UTF8_STRING, 8, strlen(con->name), con->name); DLOG("Created placeholder window 0x%08x for leaf container %p / %s\n", placeholder, con, con->name); diff --git a/testcases/t/122-split.t b/testcases/t/122-split.t index 6afdd806..e9d06938 100644 --- a/testcases/t/122-split.t +++ b/testcases/t/122-split.t @@ -48,7 +48,7 @@ sub verify_split_layout { is(@{$first->{nodes}}, 0, 'first container has no children'); is(@{$second->{nodes}}, 0, 'second container has no children (yet)'); - my $old_name = $second->{name}; + my $old_id = $second->{id}; cmd $args{split_command}; cmd 'open'; @@ -60,10 +60,10 @@ sub verify_split_layout { $second = $content->[1]; is(@{$first->{nodes}}, 0, 'first container has no children'); - isnt($second->{name}, $old_name, 'second container was replaced'); + isnt($second->{id}, $old_id, 'second container was replaced'); is($second->{layout}, 'splith', 'orientation is horizontal'); is(@{$second->{nodes}}, 2, 'second container has 2 children'); - is($second->{nodes}->[0]->{name}, $old_name, 'found old second container'); + is($second->{nodes}->[0]->{id}, $old_id, 'found old second container'); } verify_split_layout(split_command => 'split h'); From 8e8afb2609845a808fcc2d72eb93fb44dd570c7e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 20 Jul 2014 23:49:08 +0200 Subject: [PATCH 044/103] Remove TODO for cycling through the floating z-index. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I think this is not a good feature to have. It shouldn’t be necessary for a tiling window manager that explicitly discourages the use of floating windows. fixes #1324 --- src/tree.c | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/tree.c b/src/tree.c index bbd5e6a4..6e289790 100644 --- a/src/tree.c +++ b/src/tree.c @@ -599,33 +599,30 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) Con *parent = con->parent; if (con->type == CT_FLOATING_CON) { - /* left/right focuses the previous/next floating container */ - if (orientation == HORIZ) { - Con *next; - if (way == 'n') - next = TAILQ_NEXT(con, floating_windows); - else - next = TAILQ_PREV(con, floating_head, floating_windows); - - /* If there is no next/previous container, wrap */ - if (!next) { - if (way == 'n') - next = TAILQ_FIRST(&(parent->floating_head)); - else - next = TAILQ_LAST(&(parent->floating_head), floating_head); - } - - /* Still no next/previous container? bail out */ - if (!next) - return false; - - con_focus(con_descend_focused(next)); - return true; - } else { - /* up/down cycles through the Z-index */ - /* TODO: implement cycling through the z-index */ + if (orientation != HORIZ) return false; + + /* left/right focuses the previous/next floating container */ + Con *next; + if (way == 'n') + next = TAILQ_NEXT(con, floating_windows); + else + next = TAILQ_PREV(con, floating_head, floating_windows); + + /* If there is no next/previous container, wrap */ + if (!next) { + if (way == 'n') + next = TAILQ_FIRST(&(parent->floating_head)); + else + next = TAILQ_LAST(&(parent->floating_head), floating_head); } + + /* Still no next/previous container? bail out */ + if (!next) + return false; + + con_focus(con_descend_focused(next)); + return true; } /* If the orientation does not match or there is no other con to focus, we From 1ee6bf10774a31f9f684f0b83cc7a231ac4ffe39 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 5 Aug 2014 11:49:16 +0200 Subject: [PATCH 045/103] make AnyEvent::I3 dependency versioned (Thanks sur5r) --- DEPENDS | 6 +++--- debian/control | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPENDS b/DEPENDS index 2d62e748..d5dae142 100644 --- a/DEPENDS +++ b/DEPENDS @@ -37,6 +37,6 @@ i3-migrate-config-to-v4 and i3-dmenu-desktop are implemented in Perl, but have no dependencies besides Perl 5.10. - i3-save-tree is also implemented in Perl and needs AnyEvent::I3 and JSON::XS. - While i3-save-tree is not required for running i3 itself, it is strongly - recommended to provide it in distribution packages. + i3-save-tree is also implemented in Perl and needs AnyEvent::I3 (>= 0.12) and + JSON::XS. While i3-save-tree is not required for running i3 itself, it is + strongly recommended to provide it in distribution packages. diff --git a/debian/control b/debian/control index b50bb964..cc9660f7 100644 --- a/debian/control +++ b/debian/control @@ -41,7 +41,7 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils Provides: x-window-manager Suggests: rxvt-unicode | x-terminal-emulator -Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl, libjson-xs-perl +Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl Description: improved dynamic tiling window manager Key features of i3 are good documentation, reasonable defaults (changeable in a simple configuration file) and good multi-monitor support. The user From 034c82c0aceaeac0a286d0042c04a6d4953f96b1 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 27 Jul 2014 22:32:50 -0400 Subject: [PATCH 046/103] Raise floating windows on `focus [direction]` Raise a window when cycling focus between floating windows with `focus [direction]` command so that newly focused windows are rendered on top of other windows. This is done by placing the window last in the floating nodes of the parent and reordering the stack so the relative order is preserved. fixes #1322 --- src/tree.c | 8 +++++ testcases/t/236-floating-focus-raise.t | 44 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 testcases/t/236-floating-focus-raise.t diff --git a/src/tree.c b/src/tree.c index 6e289790..a6b15122 100644 --- a/src/tree.c +++ b/src/tree.c @@ -621,6 +621,14 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap) if (!next) return false; + /* Raise the floating window on top of other windows preserving + * relative stack order */ + while (TAILQ_LAST(&(parent->floating_head), floating_head) != next) { + Con *last = TAILQ_LAST(&(parent->floating_head), floating_head); + TAILQ_REMOVE(&(parent->floating_head), last, floating_windows); + TAILQ_INSERT_HEAD(&(parent->floating_head), last, floating_windows); + } + con_focus(con_descend_focused(next)); return true; } diff --git a/testcases/t/236-floating-focus-raise.t b/testcases/t/236-floating-focus-raise.t new file mode 100644 index 00000000..4be87137 --- /dev/null +++ b/testcases/t/236-floating-focus-raise.t @@ -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) +# +# Test that focusing floating windows with the command `focus [direction]` +# promotes the focused window to the top of the rendering stack. +# Ticket: #1322 +# Bug still in: 4.8-88-gcc09348 +use i3test; + +my $ws = fresh_workspace; + +my $win1 = open_floating_window; +my $win2 = open_floating_window; +my $win3 = open_floating_window; + +# it's a good idea to do this a few times because of the implementation +for my $i (1 .. 3) { + cmd 'focus left'; + my $ws_con = get_ws($ws); + is($ws_con->{floating_nodes}[-1]->{nodes}[0]->{id}, get_focused($ws), + "focus left put the focused window on top of the floating windows (try $i)"); +} + +for my $i (1 .. 3) { + cmd 'focus right'; + my $ws_con = get_ws($ws); + is($ws_con->{floating_nodes}[-1]->{nodes}[0]->{id}, get_focused($ws), + "focus right put the focused window on top of the floating windows (try $i)"); +} + +done_testing; From 294d52e0180ea81bd18606fa604486026855691a Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 8 Aug 2014 12:22:41 +0200 Subject: [PATCH 047/103] t/*ewmh*: fix flakiness by syncing --- testcases/t/234-ewmh-desktop-names.t | 3 +++ testcases/t/521-ewmh-desktop-viewport.t | 3 +++ 2 files changed, 6 insertions(+) diff --git a/testcases/t/234-ewmh-desktop-names.t b/testcases/t/234-ewmh-desktop-names.t index 570e8528..d95965c4 100644 --- a/testcases/t/234-ewmh-desktop-names.t +++ b/testcases/t/234-ewmh-desktop-names.t @@ -20,6 +20,9 @@ use i3test; sub get_desktop_names { + # Make sure that i3 pushed its changes to X11 before querying. + sync_with_i3; + my $cookie = $x->get_property( 0, $x->get_root_window(), diff --git a/testcases/t/521-ewmh-desktop-viewport.t b/testcases/t/521-ewmh-desktop-viewport.t index 07034cda..9e36090c 100644 --- a/testcases/t/521-ewmh-desktop-viewport.t +++ b/testcases/t/521-ewmh-desktop-viewport.t @@ -33,6 +33,9 @@ EOT my $pid = launch_with_config($config); sub get_desktop_viewport { + # Make sure that i3 pushed its changes to X11 before querying. + sync_with_i3; + my $cookie = $x->get_property( 0, $x->get_root_window(), From 80db544e26659c35b04da8e839d401b7e60a7d83 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 11 Aug 2014 09:45:35 +0200 Subject: [PATCH 048/103] retina support: convert logical to physical pixels for default_border_width --- src/config_directives.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/config_directives.c b/src/config_directives.c index b5e4a32a..690e08bf 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -271,13 +271,15 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width) } if (strcmp(windowtype, "new_window") == 0) { - DLOG("default tiled border style = %d and border width = %d\n", border_style, border_width); + DLOG("default tiled border style = %d and border width = %d (%d physical px)\n", + border_style, border_width, logical_px(border_width)); config.default_border = border_style; - config.default_border_width = border_width; + config.default_border_width = logical_px(border_width); } else { - DLOG("default floating border style = %d and border width = %d\n", border_style, border_width); + DLOG("default floating border style = %d and border width = %d (%d physical px)\n", + border_style, border_width, logical_px(border_width)); config.default_floating_border = border_style; - config.default_floating_border_width = border_width; + config.default_floating_border_width = logical_px(border_width); } } From 5762456d895acd16546c38f75ca34e435dccb980 Mon Sep 17 00:00:00 2001 From: Mats Date: Fri, 1 Aug 2014 20:07:04 +0200 Subject: [PATCH 049/103] Align lower line of bar decoration to border width In normal border style, two horizontal lines are drawn to outline the bar decoration. The lower line leaves a gap to the left and right to align with the border. This gap was hardcoded to 2 pixels. Now it matches the current border width, if any. In stacked layout, only the bottommost bar is aligned. In tabbed layout, no gaps are left in order to close the outline and make the notion of a tab more clear. --- src/x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/x.c b/src/x.c index e3fdde19..ff0a2295 100644 --- a/src/x.c +++ b/src/x.c @@ -473,12 +473,12 @@ void x_draw_decoration(Con *con) { /* 5: draw two unconnected horizontal lines in border color */ xcb_change_gc(conn, parent->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->border}); Rect *dr = &(con->deco_rect); - int deco_diff_l = 2; - int deco_diff_r = 2; - if (parent->layout == L_TABBED) { - if (TAILQ_PREV(con, nodes_head, nodes) != NULL) + adjacent_t borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; + int deco_diff_l = borders_to_hide & ADJ_LEFT_SCREEN_EDGE ? 0 : con->current_border_width; + int deco_diff_r = borders_to_hide & ADJ_RIGHT_SCREEN_EDGE ? 0 : con-> current_border_width; + if (parent->layout == L_TABBED || + (parent->layout == L_STACKED && TAILQ_NEXT(con, nodes) != NULL)) { deco_diff_l = 0; - if (TAILQ_NEXT(con, nodes) != NULL) deco_diff_r = 0; } xcb_segment_t segments[] = { From 33d1d5d3c61a2136eb4b42ffd29870fd68d2d766 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 23 Aug 2014 16:02:30 +0200 Subject: [PATCH 050/103] Treat everything up to 120 dpi as 96 dpi (Thanks jefvel) See the comment in the source for rationale. --- libi3/dpi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libi3/dpi.c b/libi3/dpi.c index 090a6560..37d5b215 100644 --- a/libi3/dpi.c +++ b/libi3/dpi.c @@ -1,3 +1,10 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved dynamic tiling window manager + * © 2009-2014 Michael Stapelberg and contributors (see also: LICENSE) + * + */ #include "libi3.h" #include @@ -12,5 +19,13 @@ extern xcb_screen_t *root_screen; int logical_px(const int logical) { const int dpi = (double)root_screen->height_in_pixels * 25.4 / (double)root_screen->height_in_millimeters; + /* There are many misconfigurations out there, i.e. systems with screens + * whose dpi is in fact higher than 96 dpi, but not significantly higher, + * so software was never adapted. We could tell people to reconfigure their + * systems to 96 dpi in order to get the behavior they expect/are used to, + * but since we can easily detect this case in code, let’s do it for them. + */ + if ((dpi / 96.0) < 1.25) + return logical; return ceil((dpi / 96.0) * logical); } From beba1633ac67b6cfa911b1a2c162ea928359d885 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 25 Aug 2014 17:55:59 +0200 Subject: [PATCH 051/103] Parse tray_output as a word, not string (Thanks Peter) This makes it robust against trailing whitespace. fixes #1352 --- parser-specs/config.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser-specs/config.spec b/parser-specs/config.spec index bdd03565..4025665d 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -422,7 +422,7 @@ state BAR_OUTPUT: -> call cfg_bar_output($output); BAR state BAR_TRAY_OUTPUT: - output = string + output = word -> call cfg_bar_tray_output($output); BAR state BAR_FONT: From 9058fc44e6e3e483473380bcede88f92c5b7c3c6 Mon Sep 17 00:00:00 2001 From: aszlig Date: Sat, 2 Aug 2014 07:01:15 +0200 Subject: [PATCH 052/103] Allow to validate the config file without X. We're going to call parse_configuration() very early if -C is given on the command line. Instead of the previous "only_check_config", which has been a global variable, we now simply pass use_nagbar as false if we're just validating. This causes the whole parsing to run without X and of course without starting nagbar and displaying the errors to standard out/error instead. The return code of parse_configuration() is now a boolean which represents whether an error occured during parsing and the programs exit code is returned accordingly. Although the config parser still has a lot of side-effects, we now can parse without the need to have an XCB connection. A nicer implementation would be to just set the new font and load it just after we're done parsing, but to ensure we don't break functionality we just load a dummy FONT_TYPE_NONE if XCB isn't available. The main reason for going this route is that it's a bit difficult to test fonts in a distribution agnostic way without bundling fonts with i3 (or Xdummy to be more exact). Signed-off-by: aszlig --- include/config.h | 14 +++++++ include/config_parser.h | 7 +++- libi3/font.c | 6 +++ src/config.c | 13 +++++-- src/config_parser.c | 8 +++- src/main.c | 16 +++----- testcases/t/235-check-config-no-x.t | 60 +++++++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 testcases/t/235-check-config-no-x.t diff --git a/include/config.h b/include/config.h index b0f22417..dea26d96 100644 --- a/include/config.h +++ b/include/config.h @@ -326,6 +326,20 @@ struct Barconfig { TAILQ_ENTRY(Barconfig) configs; }; +/** + * Finds the configuration file to use (either the one specified by + * override_configpath), the user’s one or the system default) and calls + * parse_file(). + * + * If you specify override_configpath, only this path is used to look for a + * configuration file. + * + * If use_nagbar is false, don't try to start i3-nagbar but log the errors to + * stdout/stderr instead. + * + */ +bool parse_configuration(const char *override_configpath, bool use_nagbar); + /** * Reads the configuration from ~/.i3/config or /etc/i3/config if not found. * diff --git a/include/config_parser.h b/include/config_parser.h index e18e5cf2..9fc3bf2f 100644 --- a/include/config_parser.h +++ b/include/config_parser.h @@ -33,7 +33,10 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context); /** * Parses the given file by first replacing the variables, then calling - * parse_config and possibly launching i3-nagbar. + * parse_config and launching i3-nagbar if use_nagbar is true. + * + * The return value is a boolean indicating whether there were errors during + * parsing. * */ -void parse_file(const char *f); +bool parse_file(const char *f, bool use_nagbar); diff --git a/libi3/font.c b/libi3/font.c index fc868e60..7670335f 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -167,6 +167,12 @@ i3Font load_font(const char *pattern, const bool fallback) { i3Font font; font.type = FONT_TYPE_NONE; + /* No XCB connction, return early because we're just validating the + * configuration file. */ + if (conn == NULL) { + return font; + } + #if PANGO_SUPPORT /* Try to load a pango font if specified */ if (strlen(pattern) > strlen("pango:") && !strncmp(pattern, "pango:", strlen("pango:"))) { diff --git a/src/config.c b/src/config.c index ec084bb1..5089ef22 100644 --- a/src/config.c +++ b/src/config.c @@ -112,12 +112,19 @@ static char *get_config_path(const char *override_configpath) { * parse_file(). * */ -static void parse_configuration(const char *override_configpath) { +bool parse_configuration(const char *override_configpath, bool use_nagbar) { char *path = get_config_path(override_configpath); LOG("Parsing configfile %s\n", path); FREE(current_configpath); current_configpath = path; - parse_file(path); + + /* initialize default bindings if we're just validating the config file */ + if (!use_nagbar && bindings == NULL) { + bindings = scalloc(sizeof(struct bindings_head)); + TAILQ_INIT(bindings); + } + + return parse_file(path, use_nagbar); } /* @@ -260,7 +267,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, if (config.workspace_urgency_timer == 0) config.workspace_urgency_timer = 0.5; - parse_configuration(override_configpath); + parse_configuration(override_configpath, true); if (reload) { translate_keysyms(); diff --git a/src/config_parser.c b/src/config_parser.c index ee02c393..24cebcec 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -840,7 +840,7 @@ static char *migrate_config(char *input, off_t size) { * parse_config and possibly launching i3-nagbar. * */ -void parse_file(const char *f) { +bool parse_file(const char *f, bool use_nagbar) { SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables); int fd, ret, read_bytes = 0; struct stat stbuf; @@ -1000,7 +1000,7 @@ void parse_file(const char *f) { check_for_duplicate_bindings(context); - if (context->has_errors || context->has_warnings) { + if (use_nagbar && (context->has_errors || context->has_warnings)) { ELOG("FYI: You are using i3 version " I3_VERSION "\n"); if (version == 3) ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n"); @@ -1030,6 +1030,8 @@ void parse_file(const char *f) { free(pageraction); } + bool has_errors = context->has_errors; + FREE(context->line_copy); free(context); free(new); @@ -1042,6 +1044,8 @@ void parse_file(const char *f) { SLIST_REMOVE_HEAD(&variables, variables); FREE(current); } + + return !has_errors; } #endif diff --git a/src/main.c b/src/main.c index 9c086235..b696e031 100644 --- a/src/main.c +++ b/src/main.c @@ -88,11 +88,6 @@ struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignment /* We hope that those are supported and set them to true */ bool xcursor_supported = true; -/* This will be set to true when -C is used so that functions can behave - * slightly differently. We don’t want i3-nagbar to be started when validating - * the config, for example. */ -bool only_check_config = false; - /* * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb. * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop @@ -201,6 +196,7 @@ int main(int argc, char *argv[]) { bool force_xinerama = false; char *fake_outputs = NULL; bool disable_signalhandler = false; + bool only_check_config = false; static struct option long_options[] = { {"no-autostart", no_argument, 0, 'a'}, {"config", required_argument, 0, 'c'}, @@ -366,10 +362,14 @@ int main(int argc, char *argv[]) { } } + if (only_check_config) { + exit(parse_configuration(override_configpath, false) ? 0 : 1); + } + /* If the user passes more arguments, we act like i3-msg would: Just send * the arguments as an IPC message to i3. This allows for nice semantic * commands such as 'i3 border none'. */ - if (!only_check_config && optind < argc) { + if (optind < argc) { /* We enable verbose mode so that the user knows what’s going on. * This should make it easier to find mistakes when the user passes * arguments by mistake. */ @@ -492,10 +492,6 @@ int main(int argc, char *argv[]) { xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root); load_configuration(conn, override_configpath, false); - if (only_check_config) { - LOG("Done checking configuration file. Exiting.\n"); - exit(0); - } if (config.ipc_socket_path == NULL) { /* Fall back to a file name in /tmp/ based on the PID */ diff --git a/testcases/t/235-check-config-no-x.t b/testcases/t/235-check-config-no-x.t new file mode 100644 index 00000000..614d6b3a --- /dev/null +++ b/testcases/t/235-check-config-no-x.t @@ -0,0 +1,60 @@ +#!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) +# +# Check whether the -C option works without a display and doesn't +# accidentally start the nagbar. +# +use i3test i3_autostart => 0; +use File::Temp qw(tempfile); + +sub check_config { + my ($config) = @_; + my ($fh, $tmpfile) = tempfile(UNLINK => 1); + print $fh $config; + my $output = qx(DISPLAY= ../i3 -C -c $tmpfile 2>&1); + my $retval = $?; + $fh->flush; + close($fh); + return ($retval >> 8, $output); +} + +################################################################################ +# 1: test with a bogus configuration file +################################################################################ + +my $cfg = < Date: Tue, 26 Aug 2014 10:00:14 +0200 Subject: [PATCH 053/103] Properly handle windows unsetting WM_TRANSIENT_FOR (Thanks Janus) fixes #1351 --- src/ipc.c | 5 +++++ src/render.c | 2 ++ src/window.c | 8 +++++--- testcases/t/114-client-leader.t | 21 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/ipc.c b/src/ipc.c index 59f0938d..03b3d5ad 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -339,6 +339,11 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { ystr(i3string_as_utf8(con->window->name)); } + ystr("transient_for"); + if (con->window->transient_for == XCB_NONE) + y(null); + else y(integer, con->window->transient_for); + y(map_close); } diff --git a/src/render.c b/src/render.c index ed35eb0c..3cc50063 100644 --- a/src/render.c +++ b/src/render.c @@ -288,6 +288,8 @@ void render_con(Con *con, bool render_fullscreen) { while (transient_con != NULL && transient_con->window != NULL && transient_con->window->transient_for != XCB_NONE) { + DLOG("transient_con = 0x%08x, transient_con->window->transient_for = 0x%08x, fullscreen_id = 0x%08x\n", + transient_con->window->id, transient_con->window->transient_for, fullscreen->window->id); if (transient_con->window->transient_for == fullscreen->window->id) { is_transient_for = true; break; diff --git a/src/window.c b/src/window.c index 538f4629..e406752a 100644 --- a/src/window.c +++ b/src/window.c @@ -125,7 +125,8 @@ void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop, bo */ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) { if (prop == NULL || xcb_get_property_value_length(prop) == 0) { - DLOG("CLIENT_LEADER not set.\n"); + DLOG("CLIENT_LEADER not set on window 0x%08x.\n", win->id); + win->leader = XCB_NONE; FREE(prop); return; } @@ -149,7 +150,8 @@ void window_update_leader(i3Window *win, xcb_get_property_reply_t *prop) { */ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) { if (prop == NULL || xcb_get_property_value_length(prop) == 0) { - DLOG("TRANSIENT_FOR not set.\n"); + DLOG("TRANSIENT_FOR not set on window 0x%08x.\n", win->id); + win->transient_for = XCB_NONE; FREE(prop); return; } @@ -160,7 +162,7 @@ void window_update_transient_for(i3Window *win, xcb_get_property_reply_t *prop) return; } - DLOG("Transient for changed to %08x\n", transient_for); + DLOG("Transient for changed to 0x%08x (window 0x%08x)\n", transient_for, win->id); win->transient_for = transient_for; diff --git a/testcases/t/114-client-leader.t b/testcases/t/114-client-leader.t index 63e92c3c..1efe34bf 100644 --- a/testcases/t/114-client-leader.t +++ b/testcases/t/114-client-leader.t @@ -99,4 +99,25 @@ is($x->input_focus, $child->id, "Child window focused"); } +################################################################################ +# Verify that transient_for can be set and unset. +################################################################################ + +$tmp = fresh_workspace; + +$fwindow = open_window({ dont_map => 1 }); +$fwindow->transient_for($right); +$fwindow->map; + +my $floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0]; +is($floating_con->{window_properties}->{transient_for}, $right->id, 'WM_TRANSIENT_FOR properly parsed'); + +$x->delete_property($fwindow->id, $x->atom(name => 'WM_TRANSIENT_FOR')->id); +$x->flush; + +sync_with_i3; + +$floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0]; +is($floating_con->{window_properties}->{transient_for}, undef, 'WM_TRANSIENT_FOR properly removed'); + done_testing; From 8870edc2ca9c187d0358f11c13eb2dc775302dce Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 20 Sep 2014 17:21:08 -0400 Subject: [PATCH 054/103] Do not resend focus on click MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not set focused_id to XCB_NONE in route click to force resend focus, in some cases to an already focused window. Sending focus again on click is not necessary and may cause problems with certain wine or mono apps. Resending focus makes combo boxes not work in Office 2010. This effectively reverts commit 250577da, so in case this commit causes any problems with Eclipse to resurface, we’ll revert this commit in favor of 250577da (the Eclipse-related bug fix). --- src/click.c | 1 - src/x.c | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/click.c b/src/click.c index f501c769..5c36aeb0 100644 --- a/src/click.c +++ b/src/click.c @@ -214,7 +214,6 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod if (ws != focused_workspace) workspace_show(ws); - focused_id = XCB_NONE; /* get the floating con */ Con *floatingcon = con_inside_floating(con); diff --git a/src/x.c b/src/x.c index ff0a2295..ae97ef0d 100644 --- a/src/x.c +++ b/src/x.c @@ -15,9 +15,9 @@ /* Stores the X11 window ID of the currently focused window */ xcb_window_t focused_id = XCB_NONE; -/* Because 'focused_id' might be reset to force input focus (after click to - * raise), we separately keep track of the X11 window ID to be able to always - * tell whether the focused window actually changed. */ +/* Because 'focused_id' might be reset to force input focus, we separately keep + * track of the X11 window ID to be able to always tell whether the focused + * window actually changed. */ static xcb_window_t last_focused = XCB_NONE; /* Stores coordinates to warp mouse pointer to if set */ From fd8a2b0e5114afea50dad4589d7668fc054ea78e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 22 Sep 2014 01:09:25 -0400 Subject: [PATCH 055/103] IPC: set ws reply "num" member to -1 when named When a named workspace (i.e., a workspace that has a name that does not begin with text that can be parsed as an integer greater than or equal to zero) is represented by the ipc as a workspace json object such as can be queried with `i3-msg -t get_workspaces`, set the num property to -1 instead of json null. This is for convenience of ipc consumers using type-constrained languages such as C which have difficulty cleanly expressing nullable integers. fixes #1368 --- docs/ipc | 2 +- src/ipc.c | 5 +---- testcases/t/139-ws-numbers.t | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/ipc b/docs/ipc index f0829dc2..ce38a546 100644 --- a/docs/ipc +++ b/docs/ipc @@ -156,7 +156,7 @@ following properties: num (integer):: The logical number of the workspace. Corresponds to the command - to switch to this workspace. + to switch to this workspace. For named workspaces, this will be -1. name (string):: The name of this workspace (by default num+1), as changed by the user. Encoded in UTF-8. diff --git a/src/ipc.c b/src/ipc.c index 03b3d5ad..6dab654c 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -617,10 +617,7 @@ IPC_HANDLER(get_workspaces) { y(map_open); ystr("num"); - if (ws->num == -1) - y(null); - else - y(integer, ws->num); + y(integer, ws->num); ystr("name"); ystr(ws->name); diff --git a/testcases/t/139-ws-numbers.t b/testcases/t/139-ws-numbers.t index 6829a147..f76ee04b 100644 --- a/testcases/t/139-ws-numbers.t +++ b/testcases/t/139-ws-numbers.t @@ -24,7 +24,7 @@ sub check_order { my ($msg) = @_; my @ws = @{$i3->get_workspaces->recv}; - my @nums = map { $_->{num} } grep { defined($_->{num}) } @ws; + my @nums = map { $_->{num} } grep { $_->{num} != -1 } @ws; my @sorted = sort @nums; is_deeply(\@nums, \@sorted, $msg); From 7fe55090ec4821101754a4a4eaf77cd00022c805 Mon Sep 17 00:00:00 2001 From: Mats Date: Thu, 25 Sep 2014 19:45:15 +0200 Subject: [PATCH 056/103] Don't draw borders wider than actual width Rectangles passed to function xcb_poly_fill_rectangle are of type xcb_rectangle_t and defined as: struct xcb_rectangle_t { int16_t x; int16_t y; uint16_t width; uint16_t height; } The rectangles for the right and lower border had a width and height, respectively, greater than the actual border width. Furthermore, offset the bottom border to not overlap with the right one and, for the top border, use r->width instead of con->rect.width as with the other borders. --- src/x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/x.c b/src/x.c index ae97ef0d..6d836a7b 100644 --- a/src/x.c +++ b/src/x.c @@ -430,16 +430,16 @@ void x_draw_decoration(Con *con) { xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &leftline); } if (!(borders_to_hide & ADJ_RIGHT_SCREEN_EDGE)) { - xcb_rectangle_t rightline = {r->width + br.width + br.x, 0, r->width, r->height}; + xcb_rectangle_t rightline = {r->width + (br.width + br.x), 0, -(br.width + br.x), r->height}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &rightline); } if (!(borders_to_hide & ADJ_LOWER_SCREEN_EDGE)) { - xcb_rectangle_t bottomline = {0, r->height + br.height + br.y, r->width, r->height}; + xcb_rectangle_t bottomline = {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); } /* 1pixel border needs an additional line at the top */ if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { - xcb_rectangle_t topline = {br.x, 0, con->rect.width + br.width + br.x, br.y}; + xcb_rectangle_t topline = {br.x, 0, r->width + br.width, br.y}; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline); } @@ -453,10 +453,10 @@ void x_draw_decoration(Con *con) { xcb_change_gc(conn, con->pm_gc, XCB_GC_FOREGROUND, (uint32_t[]) {p->color->indicator}); if (p->parent_layout == L_SPLITH) xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) { - {r->width + br.width + br.x, br.y, r->width, r->height + br.height}}); + {r->width + (br.width + br.x), br.y, -(br.width + br.x), r->height + br.height}}); else if (p->parent_layout == L_SPLITV) xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, (xcb_rectangle_t[]) { - {br.x, r->height + br.height + br.y, r->width - (2 * br.x), r->height}}); + {br.x, r->height + (br.height + br.y), r->width + br.width, -(br.height + br.y)}}); } } From ea2552e8524fd2064d4ed26d89ddeba49af3d717 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 1 Oct 2014 22:50:48 +0200 Subject: [PATCH 057/103] Bugfix: use the command parser to properly extract workspace names fixes #1377 --- include/commands_parser.h | 8 +++ src/commands_parser.c | 99 +++++++++++++++++------------ src/workspace.c | 14 ++-- testcases/t/172-start-on-named-ws.t | 18 ++++++ 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/include/commands_parser.h b/include/commands_parser.h index 6e531e9b..cfa44dd5 100644 --- a/include/commands_parser.h +++ b/include/commands_parser.h @@ -44,6 +44,14 @@ struct CommandResult { bool needs_tree_render; }; +/** + * Parses a string (or word, if as_word is true). Extracted out of + * parse_command so that it can be used in src/workspace.c for interpreting + * workspace commands. + * + */ +char *parse_string(const char **walk, bool as_word); + /** * Parses and executes the given command. If a caller-allocated yajl_gen is * passed, a json reply will be generated in the format specified by the ipc diff --git a/src/commands_parser.c b/src/commands_parser.c index 31729676..f325a048 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -204,6 +204,61 @@ static void next_state(const cmdp_token *token) { } } +/* + * Parses a string (or word, if as_word is true). Extracted out of + * parse_command so that it can be used in src/workspace.c for interpreting + * workspace commands. + * + */ +char *parse_string(const char **walk, bool as_word) { + const char *beginning = *walk; + /* Handle quoted strings (or words). */ + if (**walk == '"') { + beginning++; + (*walk)++; + while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\')) + (*walk)++; + } else { + if (!as_word) { + /* For a string (starting with 's'), the delimiters are + * comma (,) and semicolon (;) which introduce a new + * operation or command, respectively. Also, newlines + * end a command. */ + while (**walk != ';' && **walk != ',' && + **walk != '\0' && **walk != '\r' && + **walk != '\n') + (*walk)++; + } else { + /* For a word, the delimiters are white space (' ' or + * '\t'), closing square bracket (]), comma (,) and + * semicolon (;). */ + while (**walk != ' ' && **walk != '\t' && + **walk != ']' && **walk != ',' && + **walk != ';' && **walk != '\r' && + **walk != '\n' && **walk != '\0') + (*walk)++; + } + } + if (*walk == beginning) + return NULL; + + char *str = scalloc(*walk - beginning + 1); + /* We copy manually to handle escaping of characters. */ + int inpos, outpos; + for (inpos = 0, outpos = 0; + inpos < (*walk - beginning); + inpos++, outpos++) { + /* We only handle escaped double quotes to not break + * backwards compatibility with people using \w in + * regular expressions etc. */ + if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"') + inpos++; + str[outpos] = beginning[inpos]; + } + + return str; +} + /* * Parses and executes the given command. If a caller-allocated yajl_gen is * passed, a json reply will be generated in the format specified by the ipc @@ -262,48 +317,8 @@ CommandResult *parse_command(const char *input, yajl_gen gen) { if (strcmp(token->name, "string") == 0 || strcmp(token->name, "word") == 0) { - const char *beginning = walk; - /* Handle quoted strings (or words). */ - if (*walk == '"') { - beginning++; - walk++; - while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\')) - walk++; - } else { - if (token->name[0] == 's') { - /* For a string (starting with 's'), the delimiters are - * comma (,) and semicolon (;) which introduce a new - * operation or command, respectively. Also, newlines - * end a command. */ - while (*walk != ';' && *walk != ',' && - *walk != '\0' && *walk != '\r' && - *walk != '\n') - walk++; - } else { - /* For a word, the delimiters are white space (' ' or - * '\t'), closing square bracket (]), comma (,) and - * semicolon (;). */ - while (*walk != ' ' && *walk != '\t' && - *walk != ']' && *walk != ',' && - *walk != ';' && *walk != '\r' && - *walk != '\n' && *walk != '\0') - walk++; - } - } - if (walk != beginning) { - char *str = scalloc(walk - beginning + 1); - /* We copy manually to handle escaping of characters. */ - int inpos, outpos; - for (inpos = 0, outpos = 0; - inpos < (walk - beginning); - inpos++, outpos++) { - /* We only handle escaped double quotes to not break - * backwards compatibility with people using \w in - * regular expressions etc. */ - if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"') - inpos++; - str[outpos] = beginning[inpos]; - } + char *str = parse_string(&walk, (token->name[0] != 's')); + if (str != NULL) { if (token->identifier) push_string(token->identifier, str); /* If we are at the end of a quoted string, skip the ending diff --git a/src/workspace.c b/src/workspace.c index 9ec664d7..1bb619c3 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -126,7 +126,7 @@ Con *create_workspace_on_output(Output *output, Con *content) { strncasecmp(bind->command, "workspace", strlen("workspace")) != 0) continue; DLOG("relevant command = %s\n", bind->command); - char *target = bind->command + strlen("workspace "); + const char *target = bind->command + strlen("workspace "); while ((*target == ' ' || *target == '\t') && target != '\0') target++; /* We check if this is the workspace @@ -142,16 +142,16 @@ Con *create_workspace_on_output(Output *output, Con *content) { strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 || strncasecmp(target, "current", strlen("current")) == 0) continue; - if (*target == '"') - target++; - if (strncasecmp(target, "__", strlen("__")) == 0) { + char *target_name = parse_string(&target, false); + if (target_name == NULL) + continue; + if (strncasecmp(target_name, "__", strlen("__")) == 0) { LOG("Cannot create workspace \"%s\". Names starting with __ are i3-internal.\n", target); + free(target_name); continue; } FREE(ws->name); - ws->name = strdup(target); - if (ws->name[strlen(ws->name) - 1] == '"') - ws->name[strlen(ws->name) - 1] = '\0'; + ws->name = target_name; DLOG("trying name *%s*\n", ws->name); /* Ensure that this workspace is not assigned to a different output — diff --git a/testcases/t/172-start-on-named-ws.t b/testcases/t/172-start-on-named-ws.t index 09b708be..8b00abac 100644 --- a/testcases/t/172-start-on-named-ws.t +++ b/testcases/t/172-start-on-named-ws.t @@ -88,4 +88,22 @@ is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3 without whitespace'); exit_gracefully($pid); +################################################################################ +# 5: now with a binding that contains multiple commands +################################################################################ + +$config = < Date: Wed, 1 Oct 2014 19:26:52 -0400 Subject: [PATCH 058/103] Testcases: Use Xephyr instead of XDummy Replace the XDummy script with Xephyr. This is done because of some changes in the Xorg server that make XDummy difficult to use. Rename library internal variables and function names to replace "xdummy" with "xserver" to show this change (except for renaming the package and lib file for better git history). Rename the switch `--keep-xdummy-output` to `--keep-xserver-output`. This switch should now be rarely used because Xephyr requires less set up. Replace "xdummy" with "xephyr" in comments and utility help information. Update docs to show the new dependency. fixes #1367 --- docs/testsuite | 20 +- testcases/.gitignore | 1 - testcases/Xdummy | 1930 ----------------------------- testcases/complete-run.pl | 26 +- testcases/lib/StartXDummy.pm | 41 +- testcases/t/112-floating-resize.t | 2 +- 6 files changed, 43 insertions(+), 1977 deletions(-) delete mode 100755 testcases/Xdummy diff --git a/docs/testsuite b/docs/testsuite index 6c3a36d9..61a72219 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -74,6 +74,9 @@ client, simply called +cpan+. It comes with every Perl installation and can be used to install the testsuite. Many users prefer to use the more modern +cpanminus+ instead, though (because it asks no questions and just works): +The tests additionally require +Xephyr(1)+ to run a nested X server. Install ++xserver-xephyr+ on Debian or +xorg-xserver-xephyr+ on Arch Linux. + .Installing testsuite dependencies using cpanminus (preferred) -------------------------------------------------------------------------------- $ cd ~/i3/testcases @@ -102,7 +105,7 @@ more testcases. Also, it takes care of starting up a separate instance of i3 with an appropriate configuration file and creates a folder for each run containing the appropriate i3 logfile for each testcase. The latest folder can always be found under the symlink +latest/+. Unless told differently, it will -run the tests on a separate X server instance (using the Xdummy script). +run the tests on a separate X server instance (using Xephyr). .Example invocation of complete-run.pl+ --------------------------------------- @@ -146,12 +149,11 @@ $ less latest/i3-log-for-04-floating.t If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this: --------------------------------------------------- -$ ./complete-run.pl --parallel=1 --keep-xdummy-output +$ ./complete-run.pl --parallel=1 --keep-xserver-output --------------------------------------------------- -One common cause of failures is not having the X dummy server module -installed. Under Debian and Ubuntu this is the package -+xserver-xorg-video-dummy+. +This will show the output of Xephyr, which is the X server implementation we +use for testing. ==== IPC interface @@ -175,10 +177,9 @@ manager. === Filesystem structure In the git root of i3, the testcases live in the folder +testcases+. This -folder contains the +complete-run.pl+ and +Xdummy+ scripts and a base -configuration file which will be used for the tests. The different testcases -(their file extension is .t, not .pl) themselves can be found in the -conventionally named subfolder +t+: +folder contains the +complete-run.pl+ and a base configuration file which will +be used for the tests. The different testcases (their file extension is .t, not +.pl) themselves can be found in the conventionally named subfolder +t+: .Filesystem structure -------------------------------------------- @@ -197,7 +198,6 @@ conventionally named subfolder +t+: │   │   ├── omitted for brevity │   │   ├── ... │   │   └── 74-regress-focus-toggle.t -│   └── Xdummy -------------------------------------------- == Anatomy of a testcase diff --git a/testcases/.gitignore b/testcases/.gitignore index 294f0dae..c11c5563 100644 --- a/testcases/.gitignore +++ b/testcases/.gitignore @@ -8,4 +8,3 @@ inc META.yml i3-cfg-for-* - -Xdummy.so diff --git a/testcases/Xdummy b/testcases/Xdummy deleted file mode 100755 index 638a7b3b..00000000 --- a/testcases/Xdummy +++ /dev/null @@ -1,1930 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------- -# Copyright (C) 2005-2010 Karl J. Runge -# All rights reserved. -# -# This file is part of Xdummy. -# -# Xdummy is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or (at -# your option) any later version. -# -# Xdummy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Xdummy; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA -# or see . -# ---------------------------------------------------------------------- -# -# -# Xdummy: an LD_PRELOAD hack to run a stock Xorg(1) or XFree86(1) server -# with the "dummy" video driver to make it avoid Linux VT switching, etc. -# -# Run "Xdummy -help" for more info. -# -install="" -uninstall="" -runit=1 -prconf="" -notweak="" -root="" -nosudo="" -xserver="" -geom="" -nomodelines="" -depth="" -debug="" -strace="" -cmdline_config="" - -PATH=$PATH:/bin:/usr/bin -export PATH - -program=`basename "$0"` - -help () { - ${PAGER:-more} << END -$program: - - A hack to run a stock Xorg(1) or XFree86(1) X server with the "dummy" - (RAM-only framebuffer) video driver such that it AVOIDS the Linux VT - switching, opening device files in /dev, keyboard and mouse conflicts, - and other problems associated with the normal use of "dummy". - - In other words, it tries to make Xorg/XFree86 with the "dummy" - device driver act more like Xvfb(1). - - The primary motivation for the Xdummy script is to provide a virtual X - server for x11vnc but with more features than Xvfb (or Xvnc); however - it could be used for other reasons (e.g. better automated testing - than with Xvfb.) One nice thing is the dummy server supports RANDR - dynamic resizing while Xvfb does not. - - So, for example, x11vnc+Xdummy terminal services are a little better - than x11vnc+Xvfb. - - To achieve this, while running the real Xserver $program intercepts - system and library calls via the LD_PRELOAD method and modifies - the behavior to make it work correctly (e.g. avoid the VT stuff.) - LD_PRELOAD tricks are usually "clever hacks" and so might not work - in all situations or break when something changes. - - WARNING: Take care in using Xdummy, although it never has it is - possible that it could damage hardware. One can use the -prconf - option to have it print out the xorg.conf config that it would use - and then inspect it carefully before actually using it. - - This program no longer needs to be run as root as of 12/2009. - However, if there are problems for certain situations (usually older - servers) it may perform better if run as root (use the -root option.) - When running as root remember the previous paragraph and that Xdummy - comes without any warranty. - - gcc/cc and other build tools are required for this script to be able - to compile the LD_PRELOAD shared object. Be sure they are installed - on the system. See -install and -uninstall described below. - - Your Linux distribution may not install the dummy driver by default, - e.g: - - /usr/lib/xorg/modules/drivers/dummy_drv.so - - some have it in a package named xserver-xorg-video-dummy you that - need to install. - -Usage: - - $program <${program}-args> - - (actually, the arguments can be supplied in any order.) - -Examples: - - $program -install - - $program :1 - - $program -debug :1 - - $program -tmpdir ~/mytmp :1 -nolisten tcp - -startx example: - - startx -e bash -- $program :2 -depth 16 - - (if startx needs to be run as root, you can su(1) to a normal - user in the bash shell and then launch ~/.xinitrc or ~/.xsession, - gnome-session, startkde, startxfce4, etc.) - -xdm example: - - xdm -config /usr/local/dummy/xdm-config -nodaemon - - where the xdm-config file has line: - - DisplayManager.servers: /usr/local/dummy/Xservers - - and /usr/local/dummy/Xservers has lines: - - :1 local /usr/local/dummy/Xdummy :1 -debug - :2 local /usr/local/dummy/Xdummy :2 -debug - - (-debug is optional) - -gdm/kdm example: - - TBD. - -Root permission and x11vnc: - - Update: as of 12/2009 this program no longer must be run as root. - So try it as non-root before running it as root and/or the - following schemes. - - In some circumstances X server program may need to be run as root. - If so, one could run x11vnc as root with -unixpw (it switches - to the user that logs in) and that may be OK, some other ideas: - - - add this to sudo via visudo: - - ALL ALL = NOPASSWD: /usr/local/bin/Xdummy - - - use this little suid wrapper: -/* - * xdummy.c - * - cc -o ./xdummy xdummy.c - sudo cp ./xdummy /usr/local/bin/xdummy - sudo chown root:root /usr/local/bin/xdummy - sudo chmod u+s /usr/local/bin/xdummy - * - */ -#include -#include -#include -#include - -int main (int argc, char *argv[]) { - extern char **environ; - char str[100]; - sprintf(str, "XDUMMY_UID=%d", (int) getuid()); - putenv(str); - setuid(0); - setgid(0); - execv("/usr/local/bin/Xdummy", argv); - exit(1); - return 1; -} - - -Options: - - ${program}-args: - - -install Compile the LD_PRELOAD shared object and install it - next to the $program script file as: - - $0.so - - When that file exists it is used as the LD_PRELOAD - shared object without recompiling. Otherwise, - each time $program is run the LD_PRELOAD shared - object is compiled as a file in /tmp (or -tmpdir) - - If you set the environment variable - INTERPOSE_GETUID=1 when building, then when - $program is run as an ordinary user, the shared - object will interpose getuid() calls and pretend - to be root. Otherwise it doesn't pretend to - be root. - - You can also set the CFLAGS environment variable - to anything else you want on the compile cmdline. - - -uninstall Remove the file: - - $0.so - - The LD_PRELOAD shared object will then be compiled - each time this program is run. - - The X server is not started under -install, -uninstall, or -prconf. - - - :N The DISPLAY (e.g. :15) is often the first - argument. It is passed to the real X server and - also used by the Xdummy script as an identifier. - - -geom geom1[,geom2...] Take the geometry (e.g. 1024x768) or list - of geometries and insert them into the Screen - section of the tweaked X server config file. - Use this to have a different geometry than the - one(s) in the system config file. - - The option -geometry can be used instead of -geom; - x11vnc calls Xdummy and Xvfb this way. - - -nomodelines When you specify -geom/-geometry, $program will - create Modelines for each geometry and put them - in the Monitor section. If you do not want this - then supply -nomodelines. - - -depth n Use pixel color depth n (e.g. 8, 16, or 24). This - makes sure the X config file has a Screen.Display - subsection of this depth. Note this option is - ALSO passed to the X server. - - -DEPTH n Same as -depth, except not passed to X server. - - -tmpdir dir Specify a temporary directory, owned by you and - only writable by you. This is used in place of - /tmp/Xdummy.\$USER/.. to place the $program.so - shared object, tweaked config files, etc. - - -nonroot Run in non-root mode (working 12/2009, now default) - - -root Run as root (may still be needed in some - environments.) Same as XDUMMY_RUN_AS_ROOT=1. - - -nosudo Do not try to use sudo(1) when re-running as root, - use su(1) instead. - - -xserver path Specify the path to the Xserver to use. Default - is to try "Xorg" first and then "XFree86". If - those are not in \$PATH, it tries these locations: - /usr/bin/Xorg - /usr/X11R6/bin/Xorg - /usr/X11R6/bin/XFree86 - - -n Do not run the command to start the X server, - just show the command that $program would run. - The LD_PRELOAD shared object will be built, - if needed. Also note any XDUMMY* environment - variables that need to be set. - - -prconf Print, to stdout, the tweaked Xorg/XFree86 - config file (-config and -xf86config server - options, respectively.) The Xserver is not - started. - - -notweak Do not tweak (modify) the Xorg/XFree86 config file - (system or server command line) at all. The -geom - and similar config file modifications are ignored. - - It is up to you to make sure it is a working - config file (e.g. "dummy" driver, etc.) - Perhaps you want to use a file based on the - -prconf output. - - -debug Extra debugging output. - - -strace strace(1) the Xserver process (for troubleshooting.) - -ltrace ltrace(1) instead of strace (can be slow.) - - -h, -help Print out this help. - - - Xserver-args: - - Most of the Xorg and XFree86 options will work and are simply - passed along if you supply them. Important ones that may be - supplied if missing: - - :N X Display number for server to use. - - vtNN Linux virtual terminal (VT) to use (a VT is currently - still used, just not switched to and from.) - - -config file Driver "dummy" tweaked config file, a - -xf86config file number of settings are tweaked besides Driver. - - If -config/-xf86config is not given, the system one - (e.g. /etc/X11/xorg.conf) is used. If the system one cannot be - found, a built-in one is used. Any settings in the config file - that are not consistent with "dummy" mode will be overwritten - (unless -notweak is specified.) - - Use -config xdummy-builtin to force usage of the builtin config. - - If "file" is only a basename (e.g. "xorg.dummy.conf") with no /'s, - then no tweaking of it is done: the X server will look for that - basename via its normal search algorithm. If the found file does - not refer to the "dummy" driver, etc, then the X server will fail. - -Notes: - - The Xorg/XFree86 "dummy" driver is currently undocumented. It works - well in this mode, but it is evidently not intended for end-users. - So it could be removed or broken at any time. - - If the display Xserver-arg (e.g. :1) is not given, or ":" is given - that indicates $program should try to find a free one (based on - tcp ports.) - - If the display virtual terminal, VT, (e.g. vt9) is not given that - indicates $program should try to find a free one (or guess a high one.) - - This program is not completely secure WRT files in /tmp (but it tries - to a good degree.) Better is to use the -tmpdir option to supply a - directory only writable by you. Even better is to get rid of users - on the local machine you do not trust :-) - - Set XDUMMY_SET_XV=1 to turn on debugging output for this script. - -END -} - -warn() { - echo "$*" 1>&2 -} - -if [ "X$XDUMMY_SET_XV" != "X" ]; then - set -xv -fi - -if [ "X$XDUMMY_UID" = "X" ]; then - XDUMMY_UID=`id -u` - export XDUMMY_UID -fi -if [ "X$XDUMMY_UID" = "X0" ]; then - if [ "X$SUDO_UID" != "X" ]; then - XDUMMY_UID=$SUDO_UID - export XDUMMY_UID - fi -fi - -# check if root=1 first: -# -if [ "X$XDUMMY_RUN_AS_ROOT" = "X1" ]; then - root=1 -fi -for arg in $* -do - if [ "X$arg" = "X-nonroot" ]; then - root="" - elif [ "X$arg" = "X-root" ]; then - root=1 - fi -done - -# See if it really needs to be run as root: -# -if [ "X$XDUMMY_SU_EXEC" = "X" -a "X$root" = "X1" -a "X`id -u`" != "X0" ]; then - # this is to prevent infinite loop in case su/sudo doesn't work: - XDUMMY_SU_EXEC=1 - export XDUMMY_SU_EXEC - - dosu=1 - nosudo="" - - for arg in $* - do - if [ "X$arg" = "X-nonroot" ]; then - dosu="" - elif [ "X$arg" = "X-nosudo" ]; then - nosudo="1" - elif [ "X$arg" = "X-help" ]; then - dosu="" - elif [ "X$arg" = "X-h" ]; then - dosu="" - elif [ "X$arg" = "X-install" ]; then - dosu="" - elif [ "X$arg" = "X-uninstall" ]; then - dosu="" - elif [ "X$arg" = "X-n" ]; then - dosu="" - elif [ "X$arg" = "X-prconf" ]; then - dosu="" - fi - done - if [ $dosu ]; then - # we need to restart it with su/sudo: - if type sudo > /dev/null 2>&1; then - : - else - nosudo=1 - fi - if [ "X$nosudo" = "X" ]; then - warn "$program: supply the sudo password to restart as root:" - if [ "X$XDUMMY_UID" != "X" ]; then - exec sudo $0 -uid $XDUMMY_UID "$@" - else - exec sudo $0 "$@" - fi - else - warn "$program: supply the root password to restart as root:" - if [ "X$XDUMMY_UID" != "X" ]; then - exec su -c "$0 -uid $XDUMMY_UID $*" - else - exec su -c "$0 $*" - fi - fi - # DONE: - exit - fi -fi - -# This will hold the X display, e.g. :20 -# -disp="" -args="" -cmdline_config="" - -# Process Xdummy args: -# -while [ "X$1" != "X" ] -do - if [ "X$1" = "X-config" -o "X$1" = "X-xf86config" ]; then - cmdline_config="$2" - fi - case $1 in - ":"*) disp=$1 - ;; - "-install") install=1; runit="" - ;; - "-uninstall") uninstall=1; runit="" - ;; - "-n") runit="" - ;; - "-no") runit="" - ;; - "-norun") runit="" - ;; - "-prconf") prconf=1; runit="" - ;; - "-notweak") notweak=1 - ;; - "-noconf") notweak=1 - ;; - "-nonroot") root="" - ;; - "-root") root=1 - ;; - "-nosudo") nosudo=1 - ;; - "-xserver") xserver="$2"; shift - ;; - "-uid") XDUMMY_UID="$2"; shift - export XDUMMY_UID - ;; - "-geom") geom="$2"; shift - ;; - "-geometry") geom="$2"; shift - ;; - "-nomodelines") nomodelines=1 - ;; - "-depth") depth="$2"; args="$args -depth $2"; - shift - ;; - "-DEPTH") depth="$2"; shift - ;; - "-tmpdir") XDUMMY_TMPDIR="$2"; shift - ;; - "-debug") debug=1 - ;; - "-nodebug") debug="" - ;; - "-strace") strace=1 - ;; - "-ltrace") strace=2 - ;; - "-h") help; exit 0 - ;; - "-help") help; exit 0 - ;; - *) args="$args $1" - ;; - esac - shift -done - -# Try to get a username for use in our tmp directory, etc. -# -user="" -if [ X`id -u` = "X0" ]; then - user=root # this will also be used below for id=0 -elif [ "X$USER" != "X" ]; then - user=$USER -elif [ "X$LOGNAME" != "X" ]; then - user=$LOGNAME -fi - -# Keep trying... -# -if [ "X$user" = "X" ]; then - user=`whoami 2>/dev/null` -fi -if [ "X$user" = "X" ]; then - user=`basename "$HOME"` -fi -if [ "X$user" = "X" -o "X$user" = "X." ]; then - user="u$$" -fi - -if [ "X$debug" = "X1" -a "X$runit" != "X" ]; then - echo "" - echo "/usr/bin/env:" - env | egrep -v '^(LS_COLORS|TERMCAP)' | sort - echo "" -fi - -# Function to compile the LD_PRELOAD shared object: -# -make_so() { - # extract code embedded in this script into a tmp C file: - n1=`grep -n '^#code_begin' $0 | head -1 | awk -F: '{print $1}'` - n2=`grep -n '^#code_end' $0 | head -1 | awk -F: '{print $1}'` - n1=`expr $n1 + 1` - dn=`expr $n2 - $n1` - - tmp=$tdir/Xdummy.$RANDOM$$.c - rm -f $tmp - if [ -e $tmp -o -h $tmp ]; then - warn "$tmp still exists." - exit 1 - fi - touch $tmp || exit 1 - tail -n +$n1 $0 | head -n $dn > $tmp - - # compile it to Xdummy.so: - if [ -f "$SO" ]; then - mv $SO $SO.$$ - rm -f $SO.$$ - fi - rm -f $SO - touch $SO - if [ ! -f "$SO" ]; then - SO=$tdir/Xdummy.$user.so - warn "warning switching LD_PRELOAD shared object to: $SO" - fi - - if [ -f "$SO" ]; then - mv $SO $SO.$$ - rm -f $SO.$$ - fi - rm -f $SO - - # we assume gcc: - if [ "X$INTERPOSE_GETUID" = "X1" ]; then - CFLAGS="$CFLAGS -DINTERPOSE_GETUID" - fi - echo "$program:" cc -shared -fPIC $CFLAGS -o $SO $tmp - cc -shared -fPIC $CFLAGS -o $SO $tmp - rc=$? - rm -f $tmp - if [ $rc != 0 ]; then - warn "$program: cannot build $SO" - exit 1 - fi - if [ "X$debug" != "X" -o "X$install" != "X" ]; then - warn "$program: created $SO" - ls -l "$SO" - fi -} - -# Set tdir to tmp dir for make_so(): -if [ "X$XDUMMY_TMPDIR" != "X" ]; then - tdir=$XDUMMY_TMPDIR - mkdir -p $tdir -else - tdir="/tmp" -fi - -# Handle -install/-uninstall case: -SO=$0.so -if [ "X$install" != "X" -o "X$uninstall" != "X" ]; then - if [ -e "$SO" -o -h "$SO" ]; then - warn "$program: removing $SO" - fi - if [ -f "$SO" ]; then - mv $SO $SO.$$ - rm -f $SO.$$ - fi - rm -f $SO - if [ -e "$SO" -o -h "$SO" ]; then - warn "warning: $SO still exists." - exit 1 - fi - if [ $install ]; then - make_so - if [ ! -f "$SO" ]; then - exit 1 - fi - fi - exit 0 -fi - -# We need a tmp directory for the .so, tweaked config file, and for -# redirecting filenames we cannot create (under -nonroot) -# -tack="" -if [ "X$XDUMMY_TMPDIR" = "X" ]; then - XDUMMY_TMPDIR="/tmp/Xdummy.$user" - - # try to tack on a unique subdir (display number or pid) - # to allow multiple instances - # - if [ "X$disp" != "X" ]; then - t0=$disp - else - t0=$1 - fi - tack=`echo "$t0" | sed -e 's/^.*://'` - if echo "$tack" | grep '^[0-9][0-9]*$' > /dev/null; then - : - else - tack=$$ - fi - if [ "X$tack" != "X" ]; then - XDUMMY_TMPDIR="$XDUMMY_TMPDIR/$tack" - fi -fi - -tmp=$XDUMMY_TMPDIR -if echo "$tmp" | grep '^/tmp' > /dev/null; then - if [ "X$tmp" != "X/tmp" -a "X$tmp" != "X/tmp/" ]; then - # clean this subdir of /tmp out, otherwise leave it... - rm -rf $XDUMMY_TMPDIR - if [ -e $XDUMMY_TMPDIR ]; then - warn "$XDUMMY_TMPDIR still exists" - exit 1 - fi - fi -fi - -mkdir -p $XDUMMY_TMPDIR -chmod 700 $XDUMMY_TMPDIR -if [ "X$tack" != "X" ]; then - chmod 700 `dirname "$XDUMMY_TMPDIR"` 2>/dev/null -fi - -# See if we can write something there: -# -tfile="$XDUMMY_TMPDIR/test.file" -touch $tfile -if [ ! -f "$tfile" ]; then - XDUMMY_TMPDIR="/tmp/Xdummy.$$.$USER" - warn "warning: setting tmpdir to $XDUMMY_TMPDIR ..." - rm -rf $XDUMMY_TMPDIR || exit 1 - mkdir -p $XDUMMY_TMPDIR || exit 1 -fi -rm -f $tfile - -export XDUMMY_TMPDIR - -# Compile the LD_PRELOAD shared object if needed (needs XDUMMY_TMPDIR) -# -if [ ! -f "$SO" ]; then - SO="$XDUMMY_TMPDIR/Xdummy.so" - make_so -fi - -# Decide which X server to use: -# -if [ "X$xserver" = "X" ]; then - if type Xorg >/dev/null 2>&1; then - xserver="Xorg" - elif type XFree86 >/dev/null 2>&1; then - xserver="XFree86" - elif -x /usr/bin/Xorg; then - xserver="/usr/bin/Xorg" - elif -x /usr/X11R6/bin/Xorg; then - xserver="/usr/X11R6/bin/Xorg" - elif -x /usr/X11R6/bin/XFree86; then - xserver="/usr/X11R6/bin/XFree86" - fi - if [ "X$xserver" = "X" ]; then - # just let it fail below. - xserver="/usr/bin/Xorg" - warn "$program: cannot locate a stock Xserver... assuming $xserver" - fi -fi - -# See if the binary is suid or not readable under -nonroot mode: -# -if [ "X$BASH_VERSION" != "X" ]; then - xserver_path=`type -p $xserver 2>/dev/null` -else - xserver_path=`type $xserver 2>/dev/null | awk '{print $NF}'` -fi -if [ -e "$xserver_path" -a "X$root" = "X" -a "X$runit" != "X" ]; then - if [ ! -r $xserver_path -o -u $xserver_path -o -g $xserver_path ]; then - # XXX not quite correct with rm -rf $XDUMMY_TMPDIR ... - # we keep on a filesystem we know root can write to. - base=`basename "$xserver_path"` - new="/tmp/$base.$user.bin" - if [ -e $new ]; then - snew=`ls -l $new | awk '{print $5}' | grep '^[0-9][0-9]*$'` - sold=`ls -l $xserver_path | awk '{print $5}' | grep '^[0-9][0-9]*$'` - if [ "X$snew" != "X" -a "X$sold" != "X" -a "X$sold" != "X$snew" ]; then - warn "removing different sized copy:" - ls -l $new $xserver_path - rm -f $new - fi - fi - if [ ! -e $new -o ! -s $new ]; then - rm -f $new - touch $new || exit 1 - chmod 700 $new || exit 1 - if [ ! -r $xserver_path ]; then - warn "" - warn "NEED TO COPY UNREADABLE $xserver_path to $new as root:" - warn "" - ls -l $xserver_path 1>&2 - warn "" - warn "This only needs to be done once:" - warn " cat $xserver_path > $new" - warn "" - nos=$nosudo - if type sudo > /dev/null 2>&1; then - : - else - nos=1 - fi - if [ "X$nos" = "X1" ]; then - warn "Please supply root passwd to 'su -c'" - su -c "cat $xserver_path > $new" - else - warn "Please supply the sudo passwd if asked:" - sudo /bin/sh -c "cat $xserver_path > $new" - fi - else - warn "" - warn "COPYING SETUID $xserver_path to $new" - warn "" - ls -l $xserver_path 1>&2 - warn "" - cat $xserver_path > $new - fi - ls -l $new - if [ -s $new ]; then - : - else - rm -f $new - ls -l $new - exit 1 - fi - warn "" - warn "Please restart Xdummy now." - exit 0 - fi - if [ ! -O $new ]; then - warn "file \"$new\" not owned by us!" - ls -l $new - exit 1 - fi - xserver=$new - fi -fi - -# Work out display: -# -if [ "X$disp" != "X" ]; then - : -elif [ "X$1" != "X" ]; then - if echo "$1" | grep '^:[0-9]' > /dev/null; then - disp=$1 - shift - elif [ "X$1" = "X:" ]; then - # ":" means for us to find one. - shift - fi -fi -if [ "X$disp" = "X" -o "X$disp" = "X:" ]; then - # try to find an open display port: - # (tcp outdated...) - ports=`netstat -ant | grep LISTEN | awk '{print $4}' | sed -e 's/^.*://'` - n=0 - while [ $n -le 20 ] - do - port=`printf "60%02d" $n` - if echo "$ports" | grep "^${port}\$" > /dev/null; then - : - else - disp=":$n" - warn "$program: auto-selected DISPLAY $disp" - break - fi - n=`expr $n + 1` - done -fi - -# Work out which vt to use, try to find/guess an open one if necessary. -# -vt="" -for arg in $* -do - if echo "$arg" | grep '^vt' > /dev/null; then - vt=$arg - break - fi -done -if [ "X$vt" = "X" ]; then - if [ "X$user" = "Xroot" ]; then - # root can user fuser(1) to see if it is in use: - if type fuser >/dev/null 2>&1; then - # try /dev/tty17 thru /dev/tty32 - n=17 - while [ $n -le 32 ] - do - dev="/dev/tty$n" - if fuser $dev >/dev/null 2>&1; then - : - else - vt="vt$n" - warn "$program: auto-selected VT $vt => $dev" - break - fi - n=`expr $n + 1` - done - fi - fi - if [ "X$vt" = "X" ]; then - # take a wild guess... - vt=vt16 - warn "$program: selected fallback VT $vt" - fi -else - vt="" -fi - -# Decide flavor of Xserver: -# -stype=`basename "$xserver"` -if echo "$stype" | grep -i xfree86 > /dev/null; then - stype=xfree86 -else - stype=xorg -fi - -tweak_config() { - in="$1" - config2="$XDUMMY_TMPDIR/xdummy_modified_xconfig.conf" - if [ "X$disp" != "X" ]; then - d=`echo "$disp" | sed -e 's,/,,g' -e 's/:/_/g'` - config2="$config2$d" - fi - - # perl script to tweak the config file... add/delete options, etc. - # - env XDUMMY_GEOM=$geom \ - XDUMMY_DEPTH=$depth \ - XDUMMY_NOMODELINES=$nomodelines \ - perl > $config2 < $in -e ' - $n = 0; - $geom = $ENV{XDUMMY_GEOM}; - $depth = $ENV{XDUMMY_DEPTH}; - $nomodelines = $ENV{XDUMMY_NOMODELINES}; - $mode_str = ""; - $videoram = "24000"; - $HorizSync = "30.0 - 130.0"; - $VertRefresh = "50.0 - 250.0"; - if ($geom ne "") { - my $tmp = ""; - foreach $g (split(/,/, $geom)) { - $tmp .= "\"$g\" "; - if (!$nomodelines && $g =~ /(\d+)x(\d+)/) { - my $w = $1; - my $h = $2; - $mode_str .= " Modeline \"$g\" "; - my $dot = sprintf("%.2f", $w * $h * 70 * 1.e-6); - $mode_str .= $dot; - $mode_str .= " " . $w; - $mode_str .= " " . int(1.02 * $w); - $mode_str .= " " . int(1.10 * $w); - $mode_str .= " " . int(1.20 * $w); - $mode_str .= " " . $h; - $mode_str .= " " . int($h + 1); - $mode_str .= " " . int($h + 3); - $mode_str .= " " . int($h + 20); - $mode_str .= "\n"; - } - } - $tmp =~ s/\s*$//; - $geom = $tmp; - } - while (<>) { - if ($ENV{XDUMMY_NOTWEAK}) { - print $_; - next; - } - $n++; - if (/^\s*#/) { - # pass comments straight thru - print; - next; - } - if (/^\s*Section\s+(\S+)/i) { - # start of Section - $sect = $1; - $sect =~ s/\W//g; - $sect =~ y/A-Z/a-z/; - $sects{$sect} = 1; - print; - next; - } - if (/^\s*EndSection/i) { - # end of Section - if ($sect eq "serverflags") { - if (!$got_DontVTSwitch) { - print " ##Xdummy:##\n"; - print " Option \"DontVTSwitch\" \"true\"\n"; - } - if (!$got_AllowMouseOpenFail) { - print " ##Xdummy:##\n"; - print " Option \"AllowMouseOpenFail\" \"true\"\n"; - } - if (!$got_PciForceNone) { - print " ##Xdummy:##\n"; - print " Option \"PciForceNone\" \"true\"\n"; - } - } elsif ($sect eq "device") { - if (!$got_Driver) { - print " ##Xdummy:##\n"; - print " Driver \"dummy\"\n"; - } - if (!$got_VideoRam) { - print " ##Xdummy:##\n"; - print " VideoRam $videoram\n"; - } - } elsif ($sect eq "screen") { - if ($depth ne "" && !got_DefaultDepth) { - print " ##Xdummy:##\n"; - print " DefaultDepth $depth\n"; - } - if ($got_Monitor eq "") { - print " ##Xdummy:##\n"; - print " Monitor \"Monitor0\"\n"; - } - } elsif ($sect eq "monitor") { - if (!got_HorizSync) { - print " ##Xdummy:##\n"; - print " HorizSync $HorizSync\n"; - } - if (!got_VertRefresh) { - print " ##Xdummy:##\n"; - print " VertRefresh $VertRefresh\n"; - } - if (!$nomodelines) { - print " ##Xdummy:##\n"; - print $mode_str; - } - } - $sect = ""; - print; - next; - } - - if (/^\s*SubSection\s+(\S+)/i) { - # start of Section - $subsect = $1; - $subsect =~ s/\W//g; - $subsect =~ y/A-Z/a-z/; - $subsects{$subsect} = 1; - if ($sect eq "screen" && $subsect eq "display") { - $got_Modes = 0; - } - print; - next; - } - if (/^\s*EndSubSection/i) { - # end of SubSection - if ($sect eq "screen") { - if ($subsect eq "display") { - if ($depth ne "" && !$set_Depth) { - print " ##Xdummy:##\n"; - print " Depth\t$depth\n"; - } - if ($geom ne "" && ! $got_Modes) { - print " ##Xdummy:##\n"; - print " Modes\t$geom\n"; - } - } - } - $subsect = ""; - print; - next; - } - - $l = $_; - $l =~ s/#.*$//; - if ($sect eq "serverflags") { - if ($l =~ /^\s*Option.*DontVTSwitch/i) { - $_ =~ s/false/true/ig; - $got_DontVTSwitch = 1; - } - if ($l =~ /^\s*Option.*AllowMouseOpenFail/i) { - $_ =~ s/false/true/ig; - $got_AllowMouseOpenFail = 1; - } - if ($l =~ /^\s*Option.*PciForceNone/i) { - $_ =~ s/false/true/ig; - $got_PciForceNone= 1; - } - } - if ($sect eq "module") { - if ($l =~ /^\s*Load.*\b(dri|fbdevhw)\b/i) { - $_ = "##Xdummy## $_"; - } - } - if ($sect eq "monitor") { - if ($l =~ /^\s*HorizSync/i) { - $got_HorizSync = 1; - } - if ($l =~ /^\s*VertRefresh/i) { - $got_VertRefresh = 1; - } - } - if ($sect eq "device") { - if ($l =~ /^(\s*Driver)\b/i) { - $_ = "$1 \"dummy\"\n"; - $got_Driver = 1; - } - if ($l =~ /^\s*VideoRam/i) { - $got_VideoRam= 1; - } - } - if ($sect eq "inputdevice") { - if ($l =~ /^\s*Option.*\bDevice\b/i) { - print " ##Xdummy:##\n"; - $_ = " Option \"Device\" \"/dev/dilbert$n\"\n"; - } - } - if ($sect eq "screen") { - if ($l =~ /^\s*DefaultDepth\s+(\d+)/i) { - if ($depth ne "") { - print " ##Xdummy:##\n"; - $_ = " DefaultDepth\t$depth\n"; - } - $got_DefaultDepth = 1; - } - if ($l =~ /^\s*Monitor\s+(\S+)/i) { - $got_Monitor = $1; - $got_Monitor =~ s/"//g; - } - if ($subsect eq "display") { - if ($geom ne "") { - if ($l =~ /^(\s*Modes)\b/i) { - print " ##Xdummy:##\n"; - $_ = "$1 $geom\n"; - $got_Modes = 1; - } - } - if ($l =~ /^\s*Depth\s+(\d+)/i) { - my $d = $1; - if (!$set_Depth && $depth ne "") { - $set_Depth = 1; - if ($depth != $d) { - print " ##Xdummy:##\n"; - $_ = " Depth\t$depth\n"; - } - } - } - } - } - print; - } - if ($ENV{XDUMMY_NOTWEAK}) { - exit; - } - # create any crucial sections that are missing: - if (! exists($sects{serverflags})) { - print "\n##Xdummy:##\n"; - print "Section \"ServerFlags\"\n"; - print " Option \"DontVTSwitch\" \"true\"\n"; - print " Option \"AllowMouseOpenFail\" \"true\"\n"; - print " Option \"PciForceNone\" \"true\"\n"; - print "EndSection\n"; - } - if (! exists($sects{device})) { - print "\n##Xdummy:##\n"; - print "Section \"Device\"\n"; - print " Identifier \"Videocard0\"\n"; - print " Driver \"dummy\"\n"; - print " VideoRam $videoram\n"; - print "EndSection\n"; - } - if (! exists($sects{monitor})) { - print "\n##Xdummy:##\n"; - print "Section \"Monitor\"\n"; - print " Identifier \"Monitor0\"\n"; - print " HorizSync $HorizSync\n"; - print " VertRefresh $VertRefresh\n"; - print "EndSection\n"; - } - if (! exists($sects{screen})) { - print "\n##Xdummy:##\n"; - print "Section \"Screen\"\n"; - print " Identifier \"Screen0\"\n"; - print " Device \"Videocard0\"\n"; - if ($got_Monitor ne "") { - print " Monitor \"$got_Monitor\"\n"; - } else { - print " Monitor \"Monitor0\"\n"; - } - if ($depth ne "") { - print " DefaultDepth $depth\n"; - } else { - print " DefaultDepth 24\n"; - } - print " SubSection \"Display\"\n"; - print " Viewport 0 0\n"; - print " Depth 24\n"; - if ($got_Modes) { - ; - } elsif ($geom ne "") { - print " Modes $geom\n"; - } else { - print " Modes \"1280x1024\" \"1024x768\" \"800x600\"\n"; - } - print " EndSubSection\n"; - print "EndSection\n"; - } -'; -} - -# Work out config file and tweak it. -# -if [ "X$cmdline_config" = "X" ]; then - : -elif [ "X$cmdline_config" = "Xxdummy-builtin" ]; then - : -elif echo "$cmdline_config" | grep '/' > /dev/null; then - : -else - # ignore basename only case (let server handle it) - cmdline_config="" - notweak=1 -fi - -config=$cmdline_config - -if [ "X$notweak" = "X1" -a "X$root" = "X" -a -f "$cmdline_config" ]; then - # if not root we need to copy (but not tweak) the specified config. - XDUMMY_NOTWEAK=1 - export XDUMMY_NOTWEAK - notweak="" -fi - -if [ ! $notweak ]; then - # tweaked config will be put in $config2: - config2="" - if [ "X$config" = "X" ]; then - # use the default one: - if [ "X$stype" = "Xxorg" ]; then - config=/etc/X11/xorg.conf - else - if [ -f "/etc/X11/XF86Config-4" ]; then - config="/etc/X11/XF86Config-4" - else - config="/etc/X11/XF86Config" - fi - fi - if [ ! -f "$config" ]; then - for c in /etc/X11/xorg.conf /etc/X11/XF86Config-4 /etc/X11/XF86Config - do - if [ -f $c ]; then - config=$c - break - fi - done - fi - fi - - if [ "X$config" = "Xxdummy-builtin" ]; then - config="" - fi - - if [ ! -f "$config" ]; then - config="$XDUMMY_TMPDIR/xorg.conf" - warn "$program: using minimal built-in xorg.conf settings." - cat > $config < /dev/null; then - so=`echo "$so" | sed -e "s,^\.,$pwd,"` - fi - if echo "$so" | grep '/' > /dev/null; then - : - else - so="$pwd/$so" - fi - warn "env LD_PRELOAD=$so $xserver $disp $args $vt" - warn "" - if [ ! $runit ]; then - exit 0 - fi -fi - -if [ $strace ]; then - if [ "X$strace" = "X2" ]; then - ltrace -f env LD_PRELOAD=$SO $xserver $disp $args $vt - else - strace -f env LD_PRELOAD=$SO $xserver $disp $args $vt - fi -else - exec env LD_PRELOAD=$SO $xserver $disp $args $vt -fi - -exit $? - -######################################################################### - -code() { -#code_begin -#include -#define O_ACCMODE 0003 -#define O_RDONLY 00 -#define O_WRONLY 01 -#define O_RDWR 02 -#define O_CREAT 0100 /* not fcntl */ -#define O_EXCL 0200 /* not fcntl */ -#define O_NOCTTY 0400 /* not fcntl */ -#define O_TRUNC 01000 /* not fcntl */ -#define O_APPEND 02000 -#define O_NONBLOCK 04000 -#define O_NDELAY O_NONBLOCK -#define O_SYNC 010000 -#define O_FSYNC O_SYNC -#define O_ASYNC 020000 - -#include -#include -#include - -#include -#include - -#define __USE_GNU -#include - -static char tmpdir[4096]; -static char str1[4096]; -static char str2[4096]; - -static char devs[256][1024]; -static int debug = -1; -static int root = -1; -static int changed_uid = 0; -static int saw_fonts = 0; -static int saw_lib_modules = 0; - -static time_t start = 0; - -void check_debug(void) { - if (debug < 0) { - if (getenv("XDUMMY_DEBUG") != NULL) { - debug = 1; - } else { - debug = 0; - } - /* prevent other processes using the preload: */ - putenv("LD_PRELOAD="); - } -} -void check_root(void) { - if (root < 0) { - /* script tells us if we are root */ - if (getenv("XDUMMY_ROOT") != NULL) { - root = 1; - } else { - root = 0; - } - } -} - -void check_uid(void) { - if (start == 0) { - start = time(NULL); - if (debug) fprintf(stderr, "START: %u\n", (unsigned int) start); - return; - } else if (changed_uid == 0) { - if (saw_fonts || time(NULL) > start + 20) { - if (getenv("XDUMMY_UID")) { - int uid = atoi(getenv("XDUMMY_UID")); - if (debug) fprintf(stderr, "SETREUID: %d saw_fonts=%d\n", uid, saw_fonts); - if (uid >= 0) { - /* this will simply fail in -nonroot mode: */ - setreuid(uid, -1); - } - } - changed_uid = 1; - } - } -} - -#define CHECKIT if (debug < 0) check_debug(); \ - if (root < 0) check_root(); \ - check_uid(); - -static void set_tmpdir(void) { - char *s; - static int didset = 0; - if (didset) { - return; - } - s = getenv("XDUMMY_TMPDIR"); - if (! s) { - s = "/tmp"; - } - tmpdir[0] = '\0'; - strcat(tmpdir, s); - strcat(tmpdir, "/"); - didset = 1; -} - -static char *tmpdir_path(const char *path) { - char *str; - set_tmpdir(); - strcpy(str2, path); - str = str2; - while (*str) { - if (*str == '/') { - *str = '_'; - } - str++; - } - strcpy(str1, tmpdir); - strcat(str1, str2); - return str1; -} - -int open(const char *pathname, int flags, unsigned short mode) { - int fd; - char *store_dev = NULL; - static int (*real_open)(const char *, int , unsigned short) = NULL; - - CHECKIT - if (! real_open) { - real_open = (int (*)(const char *, int , unsigned short)) - dlsym(RTLD_NEXT, "open"); - } - - if (strstr(pathname, "lib/modules/")) { - /* not currently used. */ - saw_lib_modules = 1; - } - - if (!root) { - if (strstr(pathname, "/dev/") == pathname) { - store_dev = strdup(pathname); - } - if (strstr(pathname, "/dev/tty") == pathname && strcmp(pathname, "/dev/tty")) { - pathname = tmpdir_path(pathname); - if (debug) fprintf(stderr, "OPEN: %s -> %s (as FIFO)\n", store_dev, pathname); - /* we make it a FIFO so ioctl on it does not fail */ - unlink(pathname); - mkfifo(pathname, 0666); - } else if (0) { - /* we used to handle more /dev files ... */ - fd = real_open(pathname, O_WRONLY|O_CREAT, 0777); - close(fd); - } - } - - fd = real_open(pathname, flags, mode); - - if (debug) fprintf(stderr, "OPEN: %s %d %d fd=%d\n", pathname, flags, mode, fd); - - if (! root) { - if (store_dev) { - if (fd < 256) { - strcpy(devs[fd], store_dev); - } - free(store_dev); - } - } - - return(fd); -} - -int open64(const char *pathname, int flags, unsigned short mode) { - int fd; - - CHECKIT - if (debug) fprintf(stderr, "OPEN64: %s %d %d\n", pathname, flags, mode); - - fd = open(pathname, flags, mode); - return(fd); -} - -int rename(const char *oldpath, const char *newpath) { - static int (*real_rename)(const char *, const char *) = NULL; - - CHECKIT - if (! real_rename) { - real_rename = (int (*)(const char *, const char *)) - dlsym(RTLD_NEXT, "rename"); - } - - if (debug) fprintf(stderr, "RENAME: %s %s\n", oldpath, newpath); - - if (root) { - return(real_rename(oldpath, newpath)); - } - - if (strstr(oldpath, "/var/log") == oldpath) { - if (debug) fprintf(stderr, "RENAME: returning 0\n"); - return 0; - } - return(real_rename(oldpath, newpath)); -} - -FILE *fopen(const char *pathname, const char *mode) { - static FILE* (*real_fopen)(const char *, const char *) = NULL; - char *str; - - if (! saw_fonts) { - if (strstr(pathname, "/fonts/")) { - if (strstr(pathname, "fonts.dir")) { - saw_fonts = 1; - } else if (strstr(pathname, "fonts.alias")) { - saw_fonts = 1; - } - } - } - - CHECKIT - if (! real_fopen) { - real_fopen = (FILE* (*)(const char *, const char *)) - dlsym(RTLD_NEXT, "fopen"); - } - - if (debug) fprintf(stderr, "FOPEN: %s %s\n", pathname, mode); - - if (strstr(pathname, "xdummy_modified_xconfig.conf")) { - /* make our config appear to be in /etc/X11, etc. */ - char *q = strrchr(pathname, '/'); - if (q != NULL && getenv("XDUMMY_TMPDIR") != NULL) { - strcpy(str1, getenv("XDUMMY_TMPDIR")); - strcat(str1, q); - if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str1); - pathname = str1; - } - } - - if (root) { - return(real_fopen(pathname, mode)); - } - - str = (char *) pathname; - if (strstr(pathname, "/var/log") == pathname) { - str = tmpdir_path(pathname); - if (debug) fprintf(stderr, "FOPEN: %s -> %s\n", pathname, str); - } - return(real_fopen(str, mode)); -} - - -#define RETURN0 if (debug) \ - {fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return 0; -#define RETURN1 if (debug) \ - {fprintf(stderr, "IOCTL: covered %d 0x%x\n", fd, req);} return -1; - -int ioctl(int fd, int req, void *ptr) { - static int closed_xf86Info_consoleFd = 0; - static int (*real_ioctl)(int, int , void *) = NULL; - - CHECKIT - if (! real_ioctl) { - real_ioctl = (int (*)(int, int , void *)) - dlsym(RTLD_NEXT, "open"); - } - if (debug) fprintf(stderr, "IOCTL: %d 0x%x %p\n", fd, req, ptr); - - /* based on xorg-x11-6.8.1-dualhead.patch */ - if (req == VT_GETMODE) { - /* close(xf86Info.consoleFd) */ - if (0 && ! closed_xf86Info_consoleFd) { - /* I think better not to close it... */ - close(fd); - closed_xf86Info_consoleFd = 1; - } - RETURN0 - } else if (req == VT_SETMODE) { - RETURN0 - } else if (req == VT_GETSTATE) { - RETURN0 - } else if (req == KDSETMODE) { - RETURN0 - } else if (req == KDSETLED) { - RETURN0 - } else if (req == KDGKBMODE) { - RETURN0 - } else if (req == KDSKBMODE) { - RETURN0 - } else if (req == VT_ACTIVATE) { - RETURN0 - } else if (req == VT_WAITACTIVE) { - RETURN0 - } else if (req == VT_RELDISP) { - if (ptr == (void *) 1) { - RETURN1 - } else if (ptr == (void *) VT_ACKACQ) { - RETURN0 - } - } - - return(real_ioctl(fd, req, ptr)); -} - -typedef void (*sighandler_t)(int); -#define SIGUSR1 10 -#define SIG_DFL ((sighandler_t)0) - -sighandler_t signal(int signum, sighandler_t handler) { - static sighandler_t (*real_signal)(int, sighandler_t) = NULL; - - CHECKIT - if (! real_signal) { - real_signal = (sighandler_t (*)(int, sighandler_t)) - dlsym(RTLD_NEXT, "signal"); - } - - if (debug) fprintf(stderr, "SIGNAL: %d %p\n", signum, handler); - - if (signum == SIGUSR1) { - if (debug) fprintf(stderr, "SIGNAL: skip SIGUSR1\n"); - return SIG_DFL; - } - - return(real_signal(signum, handler)); -} - -int close(int fd) { - static int (*real_close)(int) = NULL; - - CHECKIT - if (! real_close) { - real_close = (int (*)(int)) dlsym(RTLD_NEXT, "close"); - } - - if (debug) fprintf(stderr, "CLOSE: %d\n", fd); - if (!root) { - if (fd < 256) { - devs[fd][0] = '\0'; - } - } - return(real_close(fd)); -} - -struct stat { - int foo; -}; - -int stat(const char *path, struct stat *buf) { - static int (*real_stat)(const char *, struct stat *) = NULL; - - CHECKIT - if (! real_stat) { - real_stat = (int (*)(const char *, struct stat *)) - dlsym(RTLD_NEXT, "stat"); - } - - if (debug) fprintf(stderr, "STAT: %s\n", path); - - return(real_stat(path, buf)); -} - -int stat64(const char *path, struct stat *buf) { - static int (*real_stat64)(const char *, struct stat *) = NULL; - - CHECKIT - if (! real_stat64) { - real_stat64 = (int (*)(const char *, struct stat *)) - dlsym(RTLD_NEXT, "stat64"); - } - - if (debug) fprintf(stderr, "STAT64: %s\n", path); - - return(real_stat64(path, buf)); -} - -int chown(const char *path, uid_t owner, gid_t group) { - static int (*real_chown)(const char *, uid_t, gid_t) = NULL; - - CHECKIT - if (! real_chown) { - real_chown = (int (*)(const char *, uid_t, gid_t)) - dlsym(RTLD_NEXT, "chown"); - } - - if (root) { - return(real_chown(path, owner, group)); - } - - if (debug) fprintf(stderr, "CHOWN: %s %d %d\n", path, owner, group); - - if (strstr(path, "/dev") == path) { - if (debug) fprintf(stderr, "CHOWN: return 0\n"); - return 0; - } - - return(real_chown(path, owner, group)); -} - -extern int *__errno_location (void); -#ifndef ENODEV -#define ENODEV 19 -#endif - -int ioperm(unsigned long from, unsigned long num, int turn_on) { - static int (*real_ioperm)(unsigned long, unsigned long, int) = NULL; - - CHECKIT - if (! real_ioperm) { - real_ioperm = (int (*)(unsigned long, unsigned long, int)) - dlsym(RTLD_NEXT, "ioperm"); - } - if (debug) fprintf(stderr, "IOPERM: %d %d %d\n", (int) from, (int) num, turn_on); - if (root) { - return(real_ioperm(from, num, turn_on)); - } - if (from == 0 && num == 1024 && turn_on == 1) { - /* we want xf86EnableIO to fail */ - if (debug) fprintf(stderr, "IOPERM: setting ENODEV.\n"); - *__errno_location() = ENODEV; - return -1; - } - return 0; -} - -int iopl(int level) { - static int (*real_iopl)(int) = NULL; - - CHECKIT - if (! real_iopl) { - real_iopl = (int (*)(int)) dlsym(RTLD_NEXT, "iopl"); - } - if (debug) fprintf(stderr, "IOPL: %d\n", level); - if (root) { - return(real_iopl(level)); - } - return 0; -} - -#ifdef INTERPOSE_GETUID - -/* - * we got things to work w/o pretending to be root. - * so we no longer interpose getuid(), etc. - */ - -uid_t getuid(void) { - static uid_t (*real_getuid)(void) = NULL; - CHECKIT - if (! real_getuid) { - real_getuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid"); - } - if (root) { - return(real_getuid()); - } - if (debug) fprintf(stderr, "GETUID: 0\n"); - return 0; -} -uid_t geteuid(void) { - static uid_t (*real_geteuid)(void) = NULL; - CHECKIT - if (! real_geteuid) { - real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid"); - } - if (root) { - return(real_geteuid()); - } - if (debug) fprintf(stderr, "GETEUID: 0\n"); - return 0; -} -uid_t geteuid_kludge1(void) { - static uid_t (*real_geteuid)(void) = NULL; - CHECKIT - if (! real_geteuid) { - real_geteuid = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid"); - } - if (debug) fprintf(stderr, "GETEUID: 0 saw_libmodules=%d\n", saw_lib_modules); - if (root && !saw_lib_modules) { - return(real_geteuid()); - } else { - saw_lib_modules = 0; - return 0; - } -} - -uid_t getuid32(void) { - static uid_t (*real_getuid32)(void) = NULL; - CHECKIT - if (! real_getuid32) { - real_getuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "getuid32"); - } - if (root) { - return(real_getuid32()); - } - if (debug) fprintf(stderr, "GETUID32: 0\n"); - return 0; -} -uid_t geteuid32(void) { - static uid_t (*real_geteuid32)(void) = NULL; - CHECKIT - if (! real_geteuid32) { - real_geteuid32 = (uid_t (*)(void)) dlsym(RTLD_NEXT, "geteuid32"); - } - if (root) { - return(real_geteuid32()); - } - if (debug) fprintf(stderr, "GETEUID32: 0\n"); - return 0; -} - -gid_t getgid(void) { - static gid_t (*real_getgid)(void) = NULL; - CHECKIT - if (! real_getgid) { - real_getgid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid"); - } - if (root) { - return(real_getgid()); - } - if (debug) fprintf(stderr, "GETGID: 0\n"); - return 0; -} -gid_t getegid(void) { - static gid_t (*real_getegid)(void) = NULL; - CHECKIT - if (! real_getegid) { - real_getegid = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid"); - } - if (root) { - return(real_getegid()); - } - if (debug) fprintf(stderr, "GETEGID: 0\n"); - return 0; -} -gid_t getgid32(void) { - static gid_t (*real_getgid32)(void) = NULL; - CHECKIT - if (! real_getgid32) { - real_getgid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getgid32"); - } - if (root) { - return(real_getgid32()); - } - if (debug) fprintf(stderr, "GETGID32: 0\n"); - return 0; -} -gid_t getegid32(void) { - static gid_t (*real_getegid32)(void) = NULL; - CHECKIT - if (! real_getegid32) { - real_getegid32 = (gid_t (*)(void)) dlsym(RTLD_NEXT, "getegid32"); - } - if (root) { - return(real_getegid32()); - } - if (debug) fprintf(stderr, "GETEGID32: 0\n"); - return 0; -} -#endif - -#if 0 -/* maybe we need to interpose on strcmp someday... here is the template */ -int strcmp(const char *s1, const char *s2) { - static int (*real_strcmp)(const char *, const char *) = NULL; - CHECKIT - if (! real_strcmp) { - real_strcmp = (int (*)(const char *, const char *)) dlsym(RTLD_NEXT, "strcmp"); - } - if (debug) fprintf(stderr, "STRCMP: '%s' '%s'\n", s1, s2); - return(real_strcmp(s1, s2)); -} -#endif - -#code_end -} diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index 7ca89016..9592c767 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -43,7 +43,7 @@ sub Log { say $log "@_" } my %timings; my $help = 0; -# Number of tests to run in parallel. Important to know how many Xdummy +# Number of tests to run in parallel. Important to know how many Xephyr # instances we need to start (unless @displays are given). Defaults to # num_cores * 2. my $parallel = undef; @@ -55,11 +55,11 @@ my %options = ( coverage => 0, restart => 0, ); -my $keep_xdummy_output = 0; +my $keep_xserver_output = 0; my $result = GetOptions( "coverage-testing" => \$options{coverage}, - "keep-xdummy-output" => \$keep_xdummy_output, + "keep-xserver-output" => \$keep_xserver_output, "valgrind" => \$options{valgrind}, "strace" => \$options{strace}, "xtrace" => \$options{xtrace}, @@ -86,6 +86,9 @@ foreach my $binary (@binaries) { die "$binary is not an executable" unless -x $binary; } +qx(Xephyr -help 2>&1); +die "Xephyr was not found in your path. Please install Xephyr (xserver-xephyr on Debian)." if $?; + @displays = split(/,/, join(',', @displays)); @displays = map { s/ //g; $_ } @displays; @@ -97,9 +100,9 @@ my @testfiles = @ARGV; my $numtests = scalar @testfiles; -# No displays specified, let’s start some Xdummy instances. +# No displays specified, let’s start some Xephyr instances. if (@displays == 0) { - @displays = start_xdummy($parallel, $numtests, $keep_xdummy_output); + @displays = start_xserver($parallel, $numtests, $keep_xserver_output); } # 1: create an output directory for this test-run @@ -115,8 +118,7 @@ symlink("$outdir", "latest") or die "Could not symlink latest to $outdir"; # connect to all displays for two reasons: # 1: check if the display actually works # 2: keep the connection open so that i3 is not the only client. this prevents -# the X server from exiting (Xdummy will restart it, but not quick enough -# sometimes) +# the X server from exiting my @single_worker; for my $display (@displays) { my $screen; @@ -346,7 +348,7 @@ complete-run.pl [files...] =head1 EXAMPLE -To run the whole testsuite on a reasonable number of Xdummy instances (your +To run the whole testsuite on a reasonable number of Xephyr instances (your running X11 will not be touched), run: ./complete-run.pl @@ -365,11 +367,11 @@ will parallelize the tests: # Run tests on the second X server ./complete-run.pl -d :1 - # Run four tests in parallel on some Xdummy servers + # Run four tests in parallel on some Xephyr servers ./complete-run.pl -d :1,:2,:3,:4 Note that it is not necessary to specify this anymore. If omitted, -complete-run.pl will start (num_cores * 2) Xdummy instances. +complete-run.pl will start (num_cores * 2) Xephyr instances. =item B<--valgrind> @@ -392,8 +394,8 @@ Exits i3 cleanly (instead of kill -9) to make coverage testing work properly. =item B<--parallel> -Number of Xdummy instances to start (if you don’t 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 Xdummy instance + # Run all tests on a single Xephyr instance ./complete-run.pl -p 1 diff --git a/testcases/lib/StartXDummy.pm b/testcases/lib/StartXDummy.pm index 592feb85..444f630a 100644 --- a/testcases/lib/StartXDummy.pm +++ b/testcases/lib/StartXDummy.pm @@ -7,7 +7,7 @@ use Exporter 'import'; use Time::HiRes qw(sleep); use v5.10; -our @EXPORT = qw(start_xdummy); +our @EXPORT = qw(start_xserver); my @pids; my $x_socketpath = '/tmp/.X11-unix/X'; @@ -19,15 +19,15 @@ sub slurp { <$fh>; } -# forks an Xdummy or Xdmx process +# forks an X server process sub fork_xserver { - my $keep_xdummy_output = shift; + my $keep_xserver_output = shift; my $displaynum = shift; my $pid = fork(); die "Could not fork: $!" unless defined($pid); if ($pid == 0) { - # Child, close stdout/stderr, then start Xdummy. - if (!$keep_xdummy_output) { + # Child, close stdout/stderr, then start Xephyr + if (!$keep_xserver_output) { close STDOUT; close STDERR; } @@ -60,16 +60,17 @@ sub wait_for_x { } } -=head2 start_xdummy($parallel) +=head2 start_xserver($parallel) -Starts C<$parallel> (or number of cores * 2 if undef) Xdummy processes (see -the file ./Xdummy) and returns two arrayrefs: a list of X11 display numbers to -the Xdummy processes and a list of PIDs of the processes. +Starts C<$parallel> (or number of cores * 2 if undef) Xephyr processes (see +http://www.freedesktop.org/wiki/Software/Xephyr/) and returns two arrayrefs: a +list of X11 display numbers to the Xephyr processes and a list of PIDs of the +processes. =cut -sub start_xdummy { - my ($parallel, $numtests, $keep_xdummy_output) = @_; +sub start_xserver { + my ($parallel, $numtests, $keep_xserver_output) = @_; my @displays = (); my @childpids = (); @@ -78,11 +79,8 @@ sub start_xdummy { my $child = waitpid -1, POSIX::WNOHANG; @pids = grep { $_ != $child } @pids; return unless @pids == 0; - print STDERR "All Xdummy processes died.\n"; - print STDERR "Use ./complete-run.pl --parallel 1 --keep-xdummy-output\n"; - print STDERR ""; - print STDERR "A frequent cause for this is missing the DUMMY Xorg module,\n"; - print STDERR "package xserver-xorg-video-dummy on Debian.\n"; + print STDERR "All X server processes died.\n"; + print STDERR "Use ./complete-run.pl --parallel 1 --keep-xserver-output\n"; exit 1; }; @@ -104,16 +102,13 @@ sub start_xdummy { my ($displaynum) = map { /(\d+)$/ } reverse sort glob($x_socketpath . '*'); $displaynum++; - say "Starting $parallel Xdummy instances, starting at :$displaynum..."; + say "Starting $parallel Xephyr instances, starting at :$displaynum..."; my @sockets_waiting; for (1 .. $parallel) { - # We use -config /dev/null to prevent Xdummy from using the system - # Xorg configuration. The tests should be independant from the - # actual system X configuration. - my $socket = fork_xserver($keep_xdummy_output, $displaynum, - './Xdummy', ":$displaynum", '-config', '/dev/null', - '-configdir', '/dev/null', '-nolisten', 'tcp'); + my $socket = fork_xserver($keep_xserver_output, $displaynum, + 'Xephyr', ":$displaynum", '-screen', '1280x800', + '-nolisten', 'tcp'); push(@displays, ":$displaynum"); push(@sockets_waiting, $socket); $displaynum++; diff --git a/testcases/t/112-floating-resize.t b/testcases/t/112-floating-resize.t index ec690b5e..a7390051 100644 --- a/testcases/t/112-floating-resize.t +++ b/testcases/t/112-floating-resize.t @@ -69,7 +69,7 @@ cmd 'border 1pixel'; test_resize; ################################################################################ -# Check if we can position a floating window out of bounds. The XDummy screen +# Check if we can position a floating window out of bounds. The Xephyr screen # is 1280x1024, so x=2864, y=893 is out of bounds. ################################################################################ From 3cf413492fffe3c831ef5cd1371734f74239dc4d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 2 Oct 2014 23:07:50 +0200 Subject: [PATCH 059/103] t/114-client-leader: fix race by waiting until the window is mapped --- testcases/t/114-client-leader.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testcases/t/114-client-leader.t b/testcases/t/114-client-leader.t index 1efe34bf..ec8bff26 100644 --- a/testcases/t/114-client-leader.t +++ b/testcases/t/114-client-leader.t @@ -109,6 +109,8 @@ $fwindow = open_window({ dont_map => 1 }); $fwindow->transient_for($right); $fwindow->map; +wait_for_map($fwindow); + my $floating_con = get_ws($tmp)->{floating_nodes}[0]->{nodes}[0]; is($floating_con->{window_properties}->{transient_for}, $right->id, 'WM_TRANSIENT_FOR properly parsed'); From fbaf0844261b70ab00d71fb1cd856e730f6fe5fd Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 2 Oct 2014 19:04:53 -0400 Subject: [PATCH 060/103] Implement the ipc 'binding' event The binding event will be triggered when a binding is run as a result of some a user action. The binding event has the following properties: change: (str) Currently this will only be "run" but may be expanded in the future. Included for consistency with other events. binding: (map) the serialized binding The "binding" member will have these properties: input_type: (str) either "keyboard" or "mouse" input_code: (int) the xcb keycode of the keyboard binding if it was provided or the mouse button if it is a mouse binding. symbol: (str) the string representation of the input code command: (str) the bound command mods: (list of str) a list of the modifiers that were pressed as string symbols fixes #1210 --- docs/ipc | 46 ++++++++++++++- include/i3/ipc.h | 3 + include/ipc.h | 5 ++ src/bindings.c | 2 +- src/ipc.c | 81 +++++++++++++++++++++++++++ testcases/t/238-ipc-binding-event.t | 86 +++++++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 testcases/t/238-ipc-binding-event.t diff --git a/docs/ipc b/docs/ipc index ce38a546..c95bcd57 100644 --- a/docs/ipc +++ b/docs/ipc @@ -1,7 +1,7 @@ IPC interface (interprocess communication) ========================================== Michael Stapelberg -February 2014 +October 2014 This document describes how to interface with i3 from a separate process. This is useful for example to remote-control i3 (to write test cases for example) or @@ -638,6 +638,9 @@ window (3):: barconfig_update (4):: Sent when the hidden_state or mode field in the barconfig of any bar instance was updated and when the config is reloaded. +binding (5):: + Sent when a configured command binding is triggered with the keyboard or + mouse *Example:* -------------------------------------------------------------------- @@ -749,6 +752,47 @@ This event consists of a single serialized map reporting on options from the barconfig of the specified bar_id that were updated in i3. This event is the same as a +GET_BAR_CONFIG+ reply for the bar with the given id. +=== binding event + +This event consists of a single serialized map reporting on the details of a +binding that ran a command because of user input. The +change (sring)+ field +indicates what sort of binding event was triggered (right now it will always be ++"run"+ but may be expanded in the future). + +The +binding (object)+ field contains details about the binding that was run: + +command (string):: + The i3 command that is configured to run for this binding. +mods (array of strings):: + The modifier keys that were configured with this binding. +input_code (integer):: + If the binding was configured with +bindcode+, this will be the key code + that was given for the binding. If the binding is a mouse binding, it will be + the number of the mouse button that was pressed. Otherwise it will be 0. +symbol (string):: + If this is a keyboard binding that was configured with +bindsym+, this + field will contain the given symbol. +input_type (string):: + This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was + a keyboard or a mouse binding. + +*Example:* +--------------------------- +{ + "change": "run", + "binding": { + "command": "nop", + "mods": [ + "shift", + "ctrl" + ], + "input_code": 0, + "symbol": "t", + "input_type": "keyboard" + } +} +--------------------------- + == See also (existing libraries) [[libraries]] diff --git a/include/i3/ipc.h b/include/i3/ipc.h index 9562a209..f1b50dec 100644 --- a/include/i3/ipc.h +++ b/include/i3/ipc.h @@ -100,3 +100,6 @@ typedef struct i3_ipc_header { /** Bar config update will be triggered to update the bar config */ #define I3_IPC_EVENT_BARCONFIG_UPDATE (I3_IPC_EVENT_MASK | 4) + +/** The binding event will be triggered when bindings run */ +#define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5) diff --git a/include/ipc.h b/include/ipc.h index e9358ee5..77dcad21 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -105,3 +105,8 @@ void ipc_send_window_event(const char *property, Con *con); * For the barconfig update events, we send the serialized barconfig. */ void ipc_send_barconfig_update_event(Barconfig *barconfig); + +/** + * For the binding events, we send the serialized binding struct. + */ +void ipc_send_binding_event(const char *event_type, Binding *bind); diff --git a/src/bindings.c b/src/bindings.c index cd91a398..b75c6357 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -423,7 +423,7 @@ CommandResult *run_binding(Binding *bind, Con *con) { free(pageraction); } - /* TODO: emit event for running a binding */ + ipc_send_binding_event("run", bind); return result; } diff --git a/src/ipc.c b/src/ipc.c index 6dab654c..e93fe567 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -151,6 +151,57 @@ static void dump_rect(yajl_gen gen, const char *name, Rect r) { y(map_close); } +static void dump_binding(yajl_gen gen, Binding *bind) { + y(map_open); + ystr("input_code"); + y(integer, bind->keycode); + + ystr("input_type"); + ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse")); + + ystr("symbol"); + ystr(bind->symbol); + + ystr("command"); + ystr(bind->command); + + ystr("mods"); + y(array_open); + for (int i = 0; i < 8; i++) { + if (bind->mods & (1 << i)) { + switch (1 << i) { + case XCB_MOD_MASK_SHIFT: + ystr("shift"); + break; + case XCB_MOD_MASK_LOCK: + ystr("lock"); + break; + case XCB_MOD_MASK_CONTROL: + ystr("ctrl"); + break; + case XCB_MOD_MASK_1: + ystr("Mod1"); + break; + case XCB_MOD_MASK_2: + ystr("Mod2"); + break; + case XCB_MOD_MASK_3: + ystr("Mod3"); + break; + case XCB_MOD_MASK_4: + ystr("Mod4"); + break; + case XCB_MOD_MASK_5: + ystr("Mod5"); + break; + } + } + } + y(array_close); + + y(map_close); +} + void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(map_open); ystr("id"); @@ -1146,3 +1197,33 @@ void ipc_send_barconfig_update_event(Barconfig *barconfig) { y(free); setlocale(LC_NUMERIC, ""); } + +/* + * For the binding events, we send the serialized binding struct. + */ +void ipc_send_binding_event(const char *event_type, Binding *bind) { + DLOG("Issue IPC binding %s event (sym = %s, code = %d)\n", event_type, bind->symbol, bind->keycode); + + setlocale(LC_NUMERIC, "C"); + + yajl_gen gen = ygenalloc(); + + y(map_open); + + ystr("change"); + ystr(event_type); + + ystr("binding"); + dump_binding(gen, bind); + + y(map_close); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + + ipc_send_event("binding", I3_IPC_EVENT_BINDING, (const char *)payload); + + y(free); + setlocale(LC_NUMERIC, ""); +} diff --git a/testcases/t/238-ipc-binding-event.t b/testcases/t/238-ipc-binding-event.t new file mode 100644 index 00000000..a968bd1a --- /dev/null +++ b/testcases/t/238-ipc-binding-event.t @@ -0,0 +1,86 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the binding event works properly +# Ticket: #1210 +use i3test i3_autostart => 0; + +my $keysym = 't'; +my $command = 'nop'; +my @mods = ('Shift', 'Ctrl'); +my $binding_symbol = join("+", @mods) . "+$keysym"; + +my $config = < /dev/null); + + skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?; + + my $pid = launch_with_config($config); + + my $i3 = i3(get_socket_path()); + $i3->connect->recv; + + my $cv = AE::cv; + my $timer = AE::timer 0.5, 0, sub { $cv->send(0); }; + + $i3->subscribe({ + binding => sub { + $cv->send(shift); + } + })->recv; + + qx(xdotool key $binding_symbol); + + my $e = $cv->recv; + + does_i3_live; + + diag "Event:\n", Dumper($e); + + ok($e, + 'the binding event should emit when user input triggers an i3 binding event'); + + is($e->{change}, 'run', + 'the `change` field should indicate this binding has run'); + + ok($e->{binding}, + 'the `binding` field should be a hash that contains information about the binding'); + + is($e->{binding}->{input_type}, 'keyboard', + 'the input_type field should be the input type of the binding (keyboard or mouse)'); + + note 'the `mods` field should contain the symbols for the modifiers of the binding'; + foreach (@mods) { + ok(grep(/$_/i, @{$e->{binding}->{mods}}), "`mods` contains the modifier $_"); + } + + is($e->{binding}->{command}, $command, + 'the `command` field should contain the command the binding ran'); + + is($e->{binding}->{input_code}, 0, + 'the input_code should be the specified code if the key was bound with bindcode, and otherwise zero'); + + exit_gracefully($pid); + +} +done_testing; From 3e841ac5b6a68766478e9d1538bfc4a62416b8ee Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 3 Oct 2014 09:40:22 +0200 Subject: [PATCH 061/103] =?UTF-8?q?check=20for=20AnyEvent::I3=20=E2=89=A5?= =?UTF-8?q?=200.16=20in=20the=20binding=20event=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- testcases/t/238-ipc-binding-event.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testcases/t/238-ipc-binding-event.t b/testcases/t/238-ipc-binding-event.t index a968bd1a..6931fe20 100644 --- a/testcases/t/238-ipc-binding-event.t +++ b/testcases/t/238-ipc-binding-event.t @@ -35,6 +35,8 @@ SKIP: { skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?; + skip "AnyEvent::I3 too old (need >= 0.16)", 1 if $AnyEvent::I3::VERSION < 0.16; + my $pid = launch_with_config($config); my $i3 = i3(get_socket_path()); From 6ba7728136c9abb7e5c54a24b81aefcf97bb9b7e Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 5 Oct 2014 14:50:30 -0400 Subject: [PATCH 062/103] Bugfix: check symbol for NULL in binding json dump When dumping a binding, as is done during the binding event, check symbol for NULL. If it is, dump json null. This prevents a crash when running a binding that was configured with bindcode. fixes #1379 --- docs/ipc | 4 ++-- src/ipc.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/ipc b/docs/ipc index c95bcd57..3cf66368 100644 --- a/docs/ipc +++ b/docs/ipc @@ -769,9 +769,9 @@ input_code (integer):: If the binding was configured with +bindcode+, this will be the key code that was given for the binding. If the binding is a mouse binding, it will be the number of the mouse button that was pressed. Otherwise it will be 0. -symbol (string):: +symbol (string or null):: If this is a keyboard binding that was configured with +bindsym+, this - field will contain the given symbol. + field will contain the given symbol. Otherwise it will be +null+. input_type (string):: This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was a keyboard or a mouse binding. diff --git a/src/ipc.c b/src/ipc.c index e93fe567..4c39a04c 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -160,7 +160,10 @@ static void dump_binding(yajl_gen gen, Binding *bind) { ystr((const char*)(bind->input_type == B_KEYBOARD ? "keyboard" : "mouse")); ystr("symbol"); - ystr(bind->symbol); + if (bind->symbol == NULL) + y(null); + else + ystr(bind->symbol); ystr("command"); ystr(bind->command); From ecca0baff3c631e2468713e4aecf04b11eb43ef0 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 4 Oct 2014 15:01:22 -0400 Subject: [PATCH 063/103] Testcases: rename StartXDummy to StartXServer Rename the package StartXDummy to StartXServer in the testcases library because XDummy is no longer used. No logic changes. --- testcases/complete-run.pl | 6 +++--- testcases/lib/{StartXDummy.pm => StartXServer.pm} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename testcases/lib/{StartXDummy.pm => StartXServer.pm} (99%) diff --git a/testcases/complete-run.pl b/testcases/complete-run.pl index 9592c767..61f2ef52 100755 --- a/testcases/complete-run.pl +++ b/testcases/complete-run.pl @@ -19,7 +19,7 @@ use Time::HiRes qw(time); use IO::Handle; # these are shipped with the testsuite use lib qw(lib); -use StartXDummy; +use StartXServer; use StatusLine; use TestWorker; # the following modules are not shipped with Perl @@ -133,7 +133,7 @@ for my $display (@displays) { # Read previous timing information, if available. We will be able to roughly # predict the test duration and schedule a good order for the tests. -my $timingsjson = StartXDummy::slurp('.last_run_timings.json'); +my $timingsjson = StartXServer::slurp('.last_run_timings.json'); %timings = %{decode_json($timingsjson)} if length($timingsjson) > 0; # Re-order the files so that those which took the longest time in the previous @@ -222,7 +222,7 @@ printf("\t%s with %.2f seconds\n", $_, $timings{$_}) if ($numtests == 1) { say ''; say 'Test output:'; - say StartXDummy::slurp($logfile); + say StartXServer::slurp($logfile); } END { cleanup() } diff --git a/testcases/lib/StartXDummy.pm b/testcases/lib/StartXServer.pm similarity index 99% rename from testcases/lib/StartXDummy.pm rename to testcases/lib/StartXServer.pm index 444f630a..032f58c6 100644 --- a/testcases/lib/StartXDummy.pm +++ b/testcases/lib/StartXServer.pm @@ -1,4 +1,4 @@ -package StartXDummy; +package StartXServer; # vim:ts=4:sw=4:expandtab use strict; From 1d60e48c73407c215b35a97668d104c11cb12166 Mon Sep 17 00:00:00 2001 From: Mats Date: Wed, 22 Oct 2014 14:13:08 +0200 Subject: [PATCH 064/103] Makefile.PL: Add Inline::C dependency Inline::C is no longer shipped with Inline. --- testcases/Makefile.PL | 1 + 1 file changed, 1 insertion(+) diff --git a/testcases/Makefile.PL b/testcases/Makefile.PL index 6bc80d8f..f2c15013 100755 --- a/testcases/Makefile.PL +++ b/testcases/Makefile.PL @@ -11,6 +11,7 @@ WriteMakefile( 'AnyEvent::I3' => '0.15', 'X11::XCB' => '0.09', 'Inline' => 0, + 'Inline::C' => 0, 'ExtUtils::PkgConfig' => 0, 'Test::More' => '0.94', 'IPC::Run' => 0, From a66c6b8b6d45ef61f0e879fef2de9b5c1a02f4a5 Mon Sep 17 00:00:00 2001 From: Mats Date: Wed, 22 Oct 2014 14:08:30 +0200 Subject: [PATCH 065/103] t/234-layout-restore-output: Remove unused import List::MoreUtils is imported but not used. --- testcases/t/234-layout-restore-output.t | 1 - 1 file changed, 1 deletion(-) diff --git a/testcases/t/234-layout-restore-output.t b/testcases/t/234-layout-restore-output.t index b75f4be5..12d04b87 100644 --- a/testcases/t/234-layout-restore-output.t +++ b/testcases/t/234-layout-restore-output.t @@ -19,7 +19,6 @@ # Bug still in: 4.8-26-gf96ec19 use i3test; use File::Temp qw(tempfile); -use List::MoreUtils qw(uniq); use IO::Handle; my $ws = fresh_workspace; From 90bed2a183c9c6df1f3f3360b2c9e486c4a0ca77 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 23 Oct 2014 23:05:01 +0200 Subject: [PATCH 066/103] Use command -v (built-in) instead of which(1) (Thanks val) fixes #1380 --- i3-sensible-editor | 2 +- i3-sensible-pager | 2 +- i3-sensible-terminal | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/i3-sensible-editor b/i3-sensible-editor index b3afceb7..4e7456b7 100755 --- a/i3-sensible-editor +++ b/i3-sensible-editor @@ -10,7 +10,7 @@ # Hopefully one of these is installed (no flamewars about preference please!): for editor in $VISUAL $EDITOR nano vim vi emacs pico qe mg jed gedit mc-edit; do - if which $editor > /dev/null 2>&1; then + if command -v $editor > /dev/null 2>&1; then exec $editor "$@" fi done diff --git a/i3-sensible-pager b/i3-sensible-pager index df463251..5ad78606 100755 --- a/i3-sensible-pager +++ b/i3-sensible-pager @@ -12,7 +12,7 @@ # We don't use 'more' because it will exit if the file is too short. # Worst case scenario we'll open the file in your editor. for pager in $PAGER less most w3m i3-sensible-editor; do - if which $pager > /dev/null 2>&1; then + if command -v $pager > /dev/null 2>&1; then exec $pager "$@" fi done diff --git a/i3-sensible-terminal b/i3-sensible-terminal index fddefae1..485fc16d 100755 --- a/i3-sensible-terminal +++ b/i3-sensible-terminal @@ -9,7 +9,7 @@ # distribution-specific mechanism to find the preferred terminal emulator. On # Debian, there is the x-terminal-emulator symlink for example. for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do - if which $terminal > /dev/null 2>&1; then + if command -v $terminal > /dev/null 2>&1; then exec $terminal "$@" fi done From 7b00114f7ea870162149d6ee3e19b23597945612 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 23 Oct 2014 23:18:15 +0200 Subject: [PATCH 067/103] set DesktopNames (which gdm uses) (Thanks Yves-Alexis) --- i3.xsession.desktop | 1 + 1 file changed, 1 insertion(+) diff --git a/i3.xsession.desktop b/i3.xsession.desktop index d1e3dc2d..d1340053 100644 --- a/i3.xsession.desktop +++ b/i3.xsession.desktop @@ -5,3 +5,4 @@ Exec=i3 TryExec=i3 Type=Application X-LightDM-DesktopName=i3 +DesktopNames=i3 From 012553040974ded6a650218ab5c098738b20dfe4 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 6 Nov 2014 19:56:32 +0100 Subject: [PATCH 068/103] merge the formerly debian-specific x-terminal-emulator patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I think instead of maintaining distribution-specific patches forever, it’s a bit easier to have them applied upstream, especially since they don’t break anything. fixes #876 --- .../patches/manpage-x-terminal-emulator.patch | 20 --------------- debian/patches/series | 2 -- debian/patches/use-x-terminal-emulator.patch | 25 ------------------- i3-sensible-terminal | 8 +++--- man/i3-sensible-terminal.man | 1 + 5 files changed, 5 insertions(+), 51 deletions(-) delete mode 100644 debian/patches/manpage-x-terminal-emulator.patch delete mode 100644 debian/patches/series delete mode 100644 debian/patches/use-x-terminal-emulator.patch diff --git a/debian/patches/manpage-x-terminal-emulator.patch b/debian/patches/manpage-x-terminal-emulator.patch deleted file mode 100644 index 17b8ee4c..00000000 --- a/debian/patches/manpage-x-terminal-emulator.patch +++ /dev/null @@ -1,20 +0,0 @@ -Description: list x-terminal-emulator as one of i3-sensible-terminal’s choices -Author: Michael Stapelberg -Origin: vendor -Forwarded: not-needed -Last-Update: 2011-12-28 - ---- - -Index: i3-4.1.1/man/i3-sensible-terminal.man -=================================================================== ---- i3-4.1.1.orig/man/i3-sensible-terminal.man 2011-12-28 23:56:55.487581000 +0100 -+++ i3-4.1.1/man/i3-sensible-terminal.man 2011-12-28 23:57:06.725802633 +0100 -@@ -22,6 +22,7 @@ - It tries to start one of the following (in that order): - - * $TERMINAL (this is a non-standard variable) -+* x-terminal-emulator - * urxvt - * rxvt - * terminator diff --git a/debian/patches/series b/debian/patches/series deleted file mode 100644 index 08d60ae0..00000000 --- a/debian/patches/series +++ /dev/null @@ -1,2 +0,0 @@ -use-x-terminal-emulator.patch -manpage-x-terminal-emulator.patch diff --git a/debian/patches/use-x-terminal-emulator.patch b/debian/patches/use-x-terminal-emulator.patch deleted file mode 100644 index 96161624..00000000 --- a/debian/patches/use-x-terminal-emulator.patch +++ /dev/null @@ -1,25 +0,0 @@ -Description: i3-sensible-terminal: try x-terminal-emulator first -Author: Michael Stapelberg -Origin: vendor -Forwarded: not-needed -Last-Update: 2012-09-19 - ---- - -Index: i3-4.3/i3-sensible-terminal -=================================================================== ---- i3-4.3.orig/i3-sensible-terminal 2012-09-19 18:08:09.000000000 +0200 -+++ i3-4.3/i3-sensible-terminal 2012-09-19 18:32:06.393883488 +0200 -@@ -4,11 +4,7 @@ - # - # This script tries to exec a terminal emulator by trying some known terminal - # emulators. --# --# Distributions/packagers should enhance this script with a --# distribution-specific mechanism to find the preferred terminal emulator. On --# Debian, there is the x-terminal-emulator symlink for example. --for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do -+for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do - if which $terminal > /dev/null 2>&1; then - exec $terminal "$@" - fi diff --git a/i3-sensible-terminal b/i3-sensible-terminal index 485fc16d..d56efa70 100755 --- a/i3-sensible-terminal +++ b/i3-sensible-terminal @@ -5,10 +5,10 @@ # This script tries to exec a terminal emulator by trying some known terminal # emulators. # -# Distributions/packagers should enhance this script with a -# distribution-specific mechanism to find the preferred terminal emulator. On -# Debian, there is the x-terminal-emulator symlink for example. -for terminal in $TERMINAL urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do +# We welcome patches that add distribution-specific mechanisms to find the +# preferred terminal emulator. On Debian, there is the x-terminal-emulator +# symlink for example. +for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do if command -v $terminal > /dev/null 2>&1; then exec $terminal "$@" fi diff --git a/man/i3-sensible-terminal.man b/man/i3-sensible-terminal.man index 49d8831c..d1ad5198 100644 --- a/man/i3-sensible-terminal.man +++ b/man/i3-sensible-terminal.man @@ -22,6 +22,7 @@ is appropriate for the distribution. It tries to start one of the following (in that order): * $TERMINAL (this is a non-standard variable) +* x-terminal-emulator (only present on Debian and derivatives) * urxvt * rxvt * terminator From bb1f857b00e2e6a0fc6bb7d4b768e9f2ae37c730 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Mon, 10 Nov 2014 00:04:47 -0500 Subject: [PATCH 069/103] bugfix: copy binding before run Copy the binding struct before running it and use this copy to emit the binding event. This fixes a crash when the command `reload` is used in a binding when the binding event is emitted. --- include/bindings.h | 5 +++ src/bindings.c | 35 ++++++++++++++++-- src/config.c | 4 +-- testcases/t/238-regress-reload-bindsym.t | 45 ++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 testcases/t/238-regress-reload-bindsym.t diff --git a/include/bindings.h b/include/bindings.h index e2acc313..02c3f190 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -60,6 +60,11 @@ void switch_mode(const char *new_mode); */ void check_for_duplicate_bindings(struct context *context); +/** + * Frees the binding. If bind is null, it simply returns. + */ +void binding_free(Binding *bind); + /** * Runs the given binding and handles parse errors. If con is passed, it will * execute the command binding with that container selected by criteria. diff --git a/src/bindings.c b/src/bindings.c index b75c6357..0bda6019 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -380,6 +380,33 @@ void check_for_duplicate_bindings(struct context *context) { } } +/* + * Creates a dynamically allocated copy of bind. + */ +static Binding *binding_copy(Binding *bind) { + Binding *ret = smalloc(sizeof(Binding)); + *ret = *bind; + ret->symbol = strdup(bind->symbol); + ret->command = strdup(bind->command); + ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes); + memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes); + return ret; +} + +/* + * Frees the binding. If bind is null, it simply returns. + */ +void binding_free(Binding *bind) { + if (bind == NULL) { + return; + } + + FREE(bind->symbol); + FREE(bind->translated_to); + FREE(bind->command); + FREE(bind); +} + /* * Runs the given binding and handles parse errors. If con is passed, it will * execute the command binding with that container selected by criteria. @@ -390,14 +417,15 @@ void check_for_duplicate_bindings(struct context *context) { CommandResult *run_binding(Binding *bind, Con *con) { char *command; - /* We need to copy the command since “reload” may be part of the command, - * and then the memory that bind->command points to may not contain the + /* We need to copy the binding and command since “reload” may be part of + * the command, and then the memory that bind points to may not contain the * same data anymore. */ if (con == NULL) command = sstrdup(bind->command); else sasprintf(&command, "[con_id=\"%d\"] %s", con, bind->command); + Binding *bind_cp = binding_copy(bind); CommandResult *result = parse_command(command, NULL); free(command); @@ -423,7 +451,8 @@ CommandResult *run_binding(Binding *bind, Con *con) { free(pageraction); } - ipc_send_binding_event("run", bind); + ipc_send_binding_event("run", bind_cp); + binding_free(bind_cp); return result; } diff --git a/src/config.c b/src/config.c index 5089ef22..b41f0e1b 100644 --- a/src/config.c +++ b/src/config.c @@ -147,9 +147,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, while (!TAILQ_EMPTY(bindings)) { bind = TAILQ_FIRST(bindings); TAILQ_REMOVE(bindings, bind, bindings); - FREE(bind->translated_to); - FREE(bind->command); - FREE(bind); + binding_free(bind); } FREE(bindings); SLIST_REMOVE(&modes, mode, Mode, modes); diff --git a/testcases/t/238-regress-reload-bindsym.t b/testcases/t/238-regress-reload-bindsym.t new file mode 100644 index 00000000..6d5d12c3 --- /dev/null +++ b/testcases/t/238-regress-reload-bindsym.t @@ -0,0 +1,45 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Test that the binding event works properly +# Ticket: #1210 +use i3test i3_autostart => 0; + +my $config = < /dev/null); + + skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?; + + my $pid = launch_with_config($config); + + my $i3 = i3(get_socket_path()); + $i3->connect->recv; + + qx(xdotool key r); + + does_i3_live; + + exit_gracefully($pid); + +} +done_testing; From 8d031bfbf8a556ac664d15e7779d22a19882e32d Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sun, 16 Nov 2014 21:10:48 -0500 Subject: [PATCH 070/103] Handle _NET_CLOSE_WINDOW client message requests > Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW client > message request to the root window. We interpret this message as a request to close the con for the given window. See: http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896 fixes #1396 --- include/atoms.xmacro | 1 + src/ewmh.c | 2 +- src/handlers.c | 18 ++++++++ testcases/t/239-net-close-window-request.t | 49 ++++++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 testcases/t/239-net-close-window-request.t diff --git a/include/atoms.xmacro b/include/atoms.xmacro index 6366547c..2755d6cb 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -20,6 +20,7 @@ xmacro(_NET_NUMBER_OF_DESKTOPS) xmacro(_NET_DESKTOP_NAMES) xmacro(_NET_DESKTOP_VIEWPORT) xmacro(_NET_ACTIVE_WINDOW) +xmacro(_NET_CLOSE_WINDOW) xmacro(_NET_STARTUP_ID) xmacro(_NET_WORKAREA) xmacro(WM_PROTOCOLS) diff --git a/src/ewmh.c b/src/ewmh.c index 1c4ac3d7..0746a5e1 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -234,5 +234,5 @@ void ewmh_setup_hints(void) { /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 22, supported_atoms); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 23, supported_atoms); } diff --git a/src/handlers.c b/src/handlers.c index 06878f5c..569a8ec3 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -838,6 +838,24 @@ static void handle_client_message(xcb_client_message_event_t *event) { ++idx; } } + } else if (event->type == A__NET_CLOSE_WINDOW) { + /* + * Pagers wanting to close a window MUST send a _NET_CLOSE_WINDOW + * client message request to the root window. + * http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896 + */ + Con *con = con_by_window_id(event->window); + if (con) { + DLOG("Handling _NET_CLOSE_WINDOW request (con = %p)\n", con); + + if (event->data.data32[0]) + last_timestamp = event->data.data32[0]; + + tree_close(con, KILL_WINDOW, false, false); + tree_render(); + } else { + DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window); + } } else { DLOG("unhandled clientmessage\n"); return; diff --git a/testcases/t/239-net-close-window-request.t b/testcases/t/239-net-close-window-request.t new file mode 100644 index 00000000..20c3f843 --- /dev/null +++ b/testcases/t/239-net-close-window-request.t @@ -0,0 +1,49 @@ +#!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 _NET_CLOSE_WINDOW requests to close a window. +# See http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472668896 +# Ticket: #1396 +# Bug still in: 4.8-116-gbb1f857 +use i3test; + +sub send_close_window_request { + my ($win) = @_; + + my $msg = pack "CCSLLLLLL", + X11::XCB::CLIENT_MESSAGE, # response_type + 32, # format + 0, # sequence + $win->id, # window + $x->atom(name => '_NET_CLOSE_WINDOW')->id, # message type + 0, # data32[0] + 0, # data32[1] + 0, # data32[2] + 0, # data32[3] + 0; # data32[4] + + $x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg); +} + +my $ws = fresh_workspace; +my $win = open_window; + +send_close_window_request($win); +sync_with_i3; + +is(@{get_ws($ws)->{nodes}}, 0, 'When a pager sends a _NET_CLOSE_WINDOW request for a window, the container should be closed'); + +done_testing; From ef9b081a3eed6d8ffabff07ba81cb696c4ed4685 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 18 Nov 2014 08:27:53 +0100 Subject: [PATCH 071/103] Bugfix: check if values are non-NULL before copying (Thanks xeen) fixes #1397 --- src/bindings.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bindings.c b/src/bindings.c index 0bda6019..70d274a7 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -386,10 +386,14 @@ void check_for_duplicate_bindings(struct context *context) { static Binding *binding_copy(Binding *bind) { Binding *ret = smalloc(sizeof(Binding)); *ret = *bind; - ret->symbol = strdup(bind->symbol); - ret->command = strdup(bind->command); - ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes); - memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes); + if (bind->symbol != NULL) + ret->symbol = strdup(bind->symbol); + if (bind->command != NULL) + ret->command = strdup(bind->command); + if (bind->translated_to != NULL) { + ret->translated_to = smalloc(sizeof(xcb_keycode_t) * bind->number_keycodes); + memcpy(ret->translated_to, bind->translated_to, sizeof(xcb_keycode_t) * bind->number_keycodes); + } return ret; } From 105db325dcff935c0e4d8ac14eb37c99dd5e124f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 23 Nov 2014 00:35:57 +0100 Subject: [PATCH 072/103] Revert "Bugfix: Set input focus with last timestamp" This reverts commit 9cee8dac5e9057bf2cb72edc3cf398fdc08b8d73. fixes #1383 --- src/x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x.c b/src/x.c index 6d836a7b..b39c19d0 100644 --- a/src/x.c +++ b/src/x.c @@ -1023,7 +1023,7 @@ void x_push_changes(Con *con) { values[0] = CHILD_EVENT_MASK & ~(XCB_EVENT_MASK_FOCUS_CHANGE); xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values); } - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, last_timestamp); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, to_focus, XCB_CURRENT_TIME); if (focused->window != NULL) { values[0] = CHILD_EVENT_MASK; xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values); @@ -1041,7 +1041,7 @@ void x_push_changes(Con *con) { if (focused_id == XCB_NONE) { DLOG("Still no window focused, better set focus to the root window\n"); - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, last_timestamp); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME); ewmh_update_active_window(XCB_WINDOW_NONE); focused_id = root; } From 9cbae6bb4f44ef26eaf544e92459039e204b2865 Mon Sep 17 00:00:00 2001 From: smlb Date: Sun, 23 Nov 2014 17:51:57 +0100 Subject: [PATCH 073/103] Added termite to default terminals --- i3-sensible-terminal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3-sensible-terminal b/i3-sensible-terminal index d56efa70..06c96354 100755 --- a/i3-sensible-terminal +++ b/i3-sensible-terminal @@ -8,7 +8,7 @@ # We welcome patches that add distribution-specific mechanisms to find the # preferred terminal emulator. On Debian, there is the x-terminal-emulator # symlink for example. -for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal; do +for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite; do if command -v $terminal > /dev/null 2>&1; then exec $terminal "$@" fi From 58c65a64fe279258d374aa83ca3871aeacd66adb Mon Sep 17 00:00:00 2001 From: cornerman Date: Sun, 16 Nov 2014 22:05:51 +0100 Subject: [PATCH 074/103] add deco_rect property to con in ipc response --- docs/ipc | 4 ++++ src/ipc.c | 1 + src/load_layout.c | 20 ++++++++++++-------- testcases/t/116-nestedcons.t | 1 + 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/ipc b/docs/ipc index 3cf66368..55177f27 100644 --- a/docs/ipc +++ b/docs/ipc @@ -316,6 +316,10 @@ window_rect (map):: So, when using the +default+ layout, you will have a 2 pixel border on each side, making the window_rect +{ "x": 2, "y": 0, "width": 632, "height": 366 }+ (for example). +deco_rect (map):: + The coordinates of the *window decoration* inside its container. These + coordinates are relative to the container and do not include the actual + client window. geometry (map):: The original geometry the window specified when i3 mapped it. Used when switching a window to floating mode, for example. diff --git a/src/ipc.c b/src/ipc.c index 4c39a04c..c70ec323 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -347,6 +347,7 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { y(integer, con->current_border_width); dump_rect(gen, "rect", con->rect); + dump_rect(gen, "deco_rect", con->deco_rect); dump_rect(gen, "window_rect", con->window_rect); dump_rect(gen, "geometry", con->geometry); diff --git a/src/load_layout.c b/src/load_layout.c index 5fe7c84c..3835589f 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -24,6 +24,7 @@ static Con *json_node; static Con *to_focus; static bool parsing_swallows; static bool parsing_rect; +static bool parsing_deco_rect; static bool parsing_window_rect; static bool parsing_geometry; static bool parsing_focus; @@ -47,7 +48,7 @@ static int json_start_map(void *ctx) { match_init(current_swallow); TAILQ_INSERT_TAIL(&(json_node->swallow_head), current_swallow, matches); } else { - if (!parsing_rect && !parsing_window_rect && !parsing_geometry) { + if (!parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) { if (last_key && strcasecmp(last_key, "floating_nodes") == 0) { DLOG("New floating_node\n"); Con *ws = con_get_workspace(json_node); @@ -68,7 +69,7 @@ static int json_start_map(void *ctx) { static int json_end_map(void *ctx) { LOG("end of map\n"); - if (!parsing_swallows && !parsing_rect && !parsing_window_rect && !parsing_geometry) { + if (!parsing_swallows && !parsing_rect && !parsing_deco_rect && !parsing_window_rect && !parsing_geometry) { /* Set a few default values to simplify manually crafted layout files. */ if (json_node->layout == L_DEFAULT) { DLOG("Setting layout = L_SPLITH\n"); @@ -121,12 +122,11 @@ static int json_end_map(void *ctx) { x_con_init(json_node, json_node->depth); json_node = json_node->parent; } - if (parsing_rect) - parsing_rect = false; - if (parsing_window_rect) - parsing_window_rect = false; - if (parsing_geometry) - parsing_geometry = false; + + parsing_rect = false; + parsing_deco_rect = false; + parsing_window_rect = false; + parsing_geometry = false; return 1; } @@ -175,6 +175,9 @@ static int json_key(void *ctx, const unsigned char *val, size_t len) { if (strcasecmp(last_key, "rect") == 0) parsing_rect = true; + if (strcasecmp(last_key, "deco_rect") == 0) + parsing_deco_rect = true; + if (strcasecmp(last_key, "window_rect") == 0) parsing_window_rect = true; @@ -532,6 +535,7 @@ void tree_append_json(Con *con, const char *filename, char **errormsg) { to_focus = NULL; parsing_swallows = false; parsing_rect = false; + parsing_deco_rect = false; parsing_window_rect = false; parsing_geometry = false; parsing_focus = false; diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index eb2fe144..d9ff1c39 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -54,6 +54,7 @@ my $expected = { type => 'root', id => $ignore, rect => $ignore, + deco_rect => $ignore, window_rect => $ignore, geometry => $ignore, swallows => $ignore, From bf1d0c9335cb2bf893b9b08daf8fd69f52b5bce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Tue, 2 Dec 2014 21:38:30 +0100 Subject: [PATCH 075/103] ensure align = left is the default and update documentation accordingly fixes #1403 --- docs/i3bar-protocol | 2 +- i3bar/include/common.h | 1 + i3bar/src/child.c | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/i3bar-protocol b/docs/i3bar-protocol index 92763154..0ca2fd82 100644 --- a/docs/i3bar-protocol +++ b/docs/i3bar-protocol @@ -148,7 +148,7 @@ min_width:: when you want to set a sensible minimum width regardless of which font you are using, and at what particular size. align:: - Align text on the +center+ (default), +right+ or +left+ of the block, when + Align text on the +center+, +right+ or +left+ (default) of the block, when the minimum width of the latter, specified by the +min_width+ key, is not reached. name and instance:: diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 31ac5161..e8b6be0a 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -27,6 +27,7 @@ struct rect_t { }; typedef enum { + /* First value to make it the default. */ ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 0bbf2a46..4b09daa9 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -188,12 +188,12 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) { sasprintf(&(ctx->block.color), "%.*s", len, val); } if (strcasecmp(ctx->last_map_key, "align") == 0) { - if (len == strlen("left") && !strncmp((const char *)val, "left", strlen("left"))) { - ctx->block.align = ALIGN_LEFT; + if (len == strlen("center") && !strncmp((const char *)val, "center", strlen("center"))) { + ctx->block.align = ALIGN_CENTER; } else if (len == strlen("right") && !strncmp((const char *)val, "right", strlen("right"))) { ctx->block.align = ALIGN_RIGHT; } else { - ctx->block.align = ALIGN_CENTER; + ctx->block.align = ALIGN_LEFT; } } else if (strcasecmp(ctx->last_map_key, "min_width") == 0) { i3String *text = i3string_from_utf8_with_length((const char *)val, len); From bfd60ec6199d581f5bd17176ddff99aead5f7a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Wed, 3 Dec 2014 20:11:05 +0100 Subject: [PATCH 076/103] Improve error message when a full_text property is missing --- i3bar/src/child.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 4b09daa9..8d5e999e 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -233,7 +233,7 @@ static int stdin_end_map(void *context) { /* Ensure we have a full_text set, so that when it is missing (or null), * i3bar doesn’t crash and the user gets an annoying message. */ if (!new_block->full_text) - new_block->full_text = i3string_from_utf8("SPEC VIOLATION (null)"); + new_block->full_text = i3string_from_utf8("SPEC VIOLATION: full_text is NULL!"); if (new_block->urgent) ctx->has_urgent = true; TAILQ_INSERT_TAIL(&statusline_head, new_block, blocks); From 8ea67d04882a2c6e16db24dacbe3587a9b64af3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Tue, 2 Dec 2014 18:22:11 +0100 Subject: [PATCH 077/103] i3bar: Respect the urgency flag on status blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …by drawing urgent blocks with the same settings as an urgent workspace. (Also use logical_px() and bar_height.) fixes #682 --- i3bar/src/xcb.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 95aa748f..30dd837d 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -156,7 +156,7 @@ void refresh_statusline(void) { /* If this is not the last block, add some pixels for a separator. */ if (TAILQ_NEXT(block, blocks) != NULL) - block->width += block->sep_block_width; + statusline_width += block->sep_block_width; statusline_width += block->width + block->x_offset + block->x_append; } @@ -168,7 +168,7 @@ void refresh_statusline(void) { realloc_sl_buffer(); /* Clear the statusline pixmap. */ - xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, font.height + logical_px(5)}; + xcb_rectangle_t rect = {0, 0, root_screen->width_in_pixels, bar_height}; xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_clear, 1, &rect); /* Draw the text of each block. */ @@ -176,11 +176,30 @@ void refresh_statusline(void) { TAILQ_FOREACH(block, &statusline_head, blocks) { if (i3string_get_num_bytes(block->full_text) == 0) continue; + uint32_t fg_color; - uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg); - set_font_colors(statusline_ctx, colorpixel, colors.bar_bg); - draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width); - x += block->width + block->x_offset + block->x_append; + /* If this block is urgent, draw it with the defined color and border. */ + if (block->urgent) { + fg_color = colors.urgent_ws_fg; + + uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND; + + /* Draw the background */ + uint32_t bg_color = colors.urgent_ws_bg; + uint32_t bg_values[] = { bg_color, bg_color }; + xcb_change_gc(xcb_connection, statusline_ctx, mask, bg_values); + + /* The urgent background “overshoots” by 2 px so that the text that + * is printed onto it will not be look so cut off. */ + xcb_rectangle_t bg_rect = { x - logical_px(2), 0, block->width + logical_px(4), bar_height }; + xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect); + } else { + fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg); + } + + set_font_colors(statusline_ctx, fg_color, colors.bar_bg); + draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width); + x += block->width + block->sep_block_width + block->x_offset + block->x_append; if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) { /* This is not the last block, draw a separator. */ @@ -190,8 +209,8 @@ void refresh_statusline(void) { xcb_change_gc(xcb_connection, statusline_ctx, mask, values); xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm, statusline_ctx, 2, - (xcb_point_t[]) {{x - sep_offset, 2}, - {x - sep_offset, font.height - 2}}); + (xcb_point_t[]) { { x - sep_offset, logical_px(4) }, + { x - sep_offset, bar_height - logical_px(4) } }); } } } @@ -1703,18 +1722,18 @@ void draw_bars(bool unhide) { /* We assume the tray icons are quadratic (we use the font * *height* as *width* of the icons) because we configured them * like this. */ - traypx += font.height + 2; + traypx += font.height + logical_px(2); } /* Add 2px of padding if there are any tray icons */ if (traypx > 0) - traypx += 2; + traypx += logical_px(2); xcb_copy_area(xcb_connection, statusline_pm, outputs_walk->buffer, outputs_walk->bargc, - MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + 4)), 0, - MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - 4)), 3, - MIN(outputs_walk->rect.w - traypx - 4, (int)statusline_width), font.height + 2); + MAX(0, (int16_t)(statusline_width - outputs_walk->rect.w + logical_px(4))), 0, + MAX(0, (int16_t)(outputs_walk->rect.w - statusline_width - traypx - logical_px(4))), 0, + MIN(outputs_walk->rect.w - traypx - logical_px(4), (int)statusline_width), bar_height); } if (!config.disable_ws) { From dc351fb291011b923678786a440fe6c5cc438892 Mon Sep 17 00:00:00 2001 From: Mats Date: Sat, 29 Nov 2014 18:22:56 +0100 Subject: [PATCH 078/103] Prevent workspace change during global fullscreen While in global fullscreen, the workspace could be changed leaving the fullscreen container still visible on top but losing its focus. --- src/commands.c | 24 ++++++++++++++++++++++++ testcases/t/100-fullscreen.t | 20 ++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/commands.c b/src/commands.c index 95dcb7bb..48df5e3d 100644 --- a/src/commands.c +++ b/src/commands.c @@ -958,6 +958,12 @@ void cmd_workspace(I3_CMD, char *which) { DLOG("which=%s\n", which); + if (con_get_fullscreen_con(croot, CF_GLOBAL)) { + LOG("Cannot switch workspace while in global fullscreen\n"); + ysuccess(false); + return; + } + if (strcmp(which, "next") == 0) ws = workspace_next(); else if (strcmp(which, "prev") == 0) @@ -986,6 +992,12 @@ void cmd_workspace(I3_CMD, char *which) { void cmd_workspace_number(I3_CMD, char *which) { Con *output, *workspace = NULL; + if (con_get_fullscreen_con(croot, CF_GLOBAL)) { + LOG("Cannot switch workspace while in global fullscreen\n"); + ysuccess(false); + return; + } + long parsed_num = ws_name_to_number(which); if (parsed_num == -1) { @@ -1020,6 +1032,12 @@ void cmd_workspace_number(I3_CMD, char *which) { * */ void cmd_workspace_back_and_forth(I3_CMD) { + if (con_get_fullscreen_con(croot, CF_GLOBAL)) { + LOG("Cannot switch workspace while in global fullscreen\n"); + ysuccess(false); + return; + } + workspace_back_and_forth(); cmd_output->needs_tree_render = true; @@ -1038,6 +1056,12 @@ void cmd_workspace_name(I3_CMD, char *name) { return; } + if (con_get_fullscreen_con(croot, CF_GLOBAL)) { + LOG("Cannot switch workspace while in global fullscreen\n"); + ysuccess(false); + return; + } + DLOG("should switch to workspace %s\n", name); if (maybe_back_and_forth(cmd_output, name)) return; diff --git a/testcases/t/100-fullscreen.t b/testcases/t/100-fullscreen.t index 206116ee..bb9403b4 100644 --- a/testcases/t/100-fullscreen.t +++ b/testcases/t/100-fullscreen.t @@ -191,6 +191,26 @@ is($x->input_focus, $window->id, 'fullscreen window focused'); cmd 'focus left'; is($x->input_focus, $window->id, 'fullscreen window still focused'); +################################################################################ +# Verify that changing workspace while in global fullscreen does not work. +################################################################################ + +$tmp = fresh_workspace; +$window = open_window; + +cmd 'fullscreen global'; +is($x->input_focus, $window->id, 'window focused'); +is(focused_ws(), $tmp, 'workspace selected'); + +$other = get_unused_workspace; +cmd "workspace $other"; +is($x->input_focus, $window->id, 'window still focused'); +is(focused_ws(), $tmp, 'workspace still selected'); + +# leave global fullscreen so that is does not interfere with the other tests +$window->fullscreen(0); +sync_with_i3; + ################################################################################ # Verify that fullscreening a window on a second workspace and moving it onto # the first workspace unfullscreens the first window. From e59a76e456b87ded59f4983ea2882b468f3df5ac Mon Sep 17 00:00:00 2001 From: Mats Date: Sun, 26 Oct 2014 19:33:09 +0100 Subject: [PATCH 079/103] Extend the fullscreen command Rather than just toggling the fullscreen modes, allow to set them directly with: fullscreen enable|toggle [global] fullscreen disable For compatibility, retain the previous command and its toggling behavior: fullscreen [global] fixes #1120 --- docs/userguide | 16 +++-- i3.config | 2 +- i3.config.keycodes | 2 +- include/commands.h | 4 +- include/con.h | 12 ++++ man/i3.man | 2 +- parser-specs/commands.spec | 22 +++++- src/commands.c | 18 +++-- src/con.c | 111 +++++++++++++++++++++++------- testcases/i3-test.config | 2 +- testcases/t/100-fullscreen.t | 127 +++++++++++++++++++++++++++++++++++ 11 files changed, 273 insertions(+), 45 deletions(-) diff --git a/docs/userguide b/docs/userguide index e752ec82..5f00fd6a 100644 --- a/docs/userguide +++ b/docs/userguide @@ -91,7 +91,7 @@ To display a window in fullscreen mode or to go out of fullscreen mode again, press +$mod+f+. There is also a global fullscreen mode in i3 in which the client will span all -available outputs (the command is +fullscreen global+). +available outputs (the command is +fullscreen toggle global+). === Opening other applications @@ -367,7 +367,7 @@ bindcode [--release] [Modifiers+]keycode command *Examples*: -------------------------------- # Fullscreen -bindsym $mod+f fullscreen +bindsym $mod+f fullscreen toggle # Restart bindsym $mod+Shift+r restart @@ -1502,9 +1502,13 @@ Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+ or +layout splith+ to change the current container layout to splith/splitv, stacking, tabbed layout, splitv or splith, respectively. -To make the current window (!) fullscreen, use +fullscreen+, to make -it floating (or tiling again) use +floating enable+ respectively +floating disable+ -(or +floating toggle+): +To make the current window (!) fullscreen, use +fullscreen enable+ (or ++fullscreen enable global+ for the global mode), to leave either fullscreen +mode use +fullscreen disable+, and to toggle between these two states use ++fullscreen toggle+ (or +fullscreen toggle global+). + +Likewise, to make the current window floating (or tiling again) use +floating +enable+ respectively +floating disable+ (or +floating toggle+): *Syntax*: -------------- @@ -1525,7 +1529,7 @@ bindsym $mod+x layout toggle bindsym $mod+x layout toggle all # Toggle fullscreen -bindsym $mod+f fullscreen +bindsym $mod+f fullscreen toggle # Toggle floating/tiling bindsym $mod+t floating toggle diff --git a/i3.config b/i3.config index 30b3f6a8..d7e96fe9 100644 --- a/i3.config +++ b/i3.config @@ -75,7 +75,7 @@ bindsym Mod1+h split h bindsym Mod1+v split v # enter fullscreen mode for the focused container -bindsym Mod1+f fullscreen +bindsym Mod1+f fullscreen toggle # change container layout (stacked, tabbed, toggle split) bindsym Mod1+s layout stacking diff --git a/i3.config.keycodes b/i3.config.keycodes index 27398515..93528a54 100644 --- a/i3.config.keycodes +++ b/i3.config.keycodes @@ -69,7 +69,7 @@ bindcode $mod+43 split h bindcode $mod+55 split v # enter fullscreen mode for the focused container -bindcode $mod+41 fullscreen +bindcode $mod+41 fullscreen toggle # change container layout (stacked, tabbed, toggle split) bindcode $mod+39 layout stacking diff --git a/include/commands.h b/include/commands.h index cb687890..780a9e8e 100644 --- a/include/commands.h +++ b/include/commands.h @@ -187,10 +187,10 @@ void cmd_focus_level(I3_CMD, char *level); void cmd_focus(I3_CMD); /** - * Implementation of 'fullscreen [global]'. + * Implementation of 'fullscreen [enable|disable|toggle] [global]'. * */ -void cmd_fullscreen(I3_CMD, char *fullscreen_mode); +void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode); /** * Implementation of 'move [ [px]]'. diff --git a/include/con.h b/include/con.h index 184bc918..5d761f68 100644 --- a/include/con.h +++ b/include/con.h @@ -172,6 +172,18 @@ void con_fix_percent(Con *con); */ void con_toggle_fullscreen(Con *con, int fullscreen_mode); +/** + * Enables fullscreen mode for the given container, if necessary. + * + */ +void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode); + +/** + * Disables fullscreen mode for the given container, if necessary. + * + */ +void con_disable_fullscreen(Con *con); + /** * Moves the given container to the currently focused container on the given * workspace. diff --git a/man/i3.man b/man/i3.man index ea232fcf..203b42ee 100644 --- a/man/i3.man +++ b/man/i3.man @@ -230,7 +230,7 @@ bindsym Mod1+h split h bindsym Mod1+v split v # enter fullscreen mode for the focused container -bindsym Mod1+f fullscreen +bindsym Mod1+f fullscreen toggle # change container layout (stacked, tabbed, default) bindsym Mod1+s layout stacking diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index e3da62c9..82348df7 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -156,12 +156,28 @@ state KILL: end -> call cmd_kill($kill_mode) +# fullscreen enable|toggle [global] +# fullscreen disable # fullscreen [global] state FULLSCREEN: - fullscreen_mode = 'global' - -> call cmd_fullscreen($fullscreen_mode) + action = 'disable' + -> call cmd_fullscreen($action, "output") + action = 'enable', 'toggle' + -> FULLSCREEN_MODE + action = '' + -> FULLSCREEN_COMPAT + +state FULLSCREEN_MODE: + mode = 'global' + -> call cmd_fullscreen($action, $mode) end - -> call cmd_fullscreen($fullscreen_mode) + -> call cmd_fullscreen($action, "output") + +state FULLSCREEN_COMPAT: + mode = 'global' + -> call cmd_fullscreen("toggle", $mode) + end + -> call cmd_fullscreen("toggle", "output") # split v|h|vertical|horizontal state SPLIT: diff --git a/src/commands.c b/src/commands.c index 48df5e3d..e87617c9 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1598,20 +1598,26 @@ void cmd_focus(I3_CMD) { } /* - * Implementation of 'fullscreen [global]'. + * Implementation of 'fullscreen enable|toggle [global]' and + * 'fullscreen disable' * */ -void cmd_fullscreen(I3_CMD, char *fullscreen_mode) { - if (fullscreen_mode == NULL) - fullscreen_mode = "output"; - DLOG("toggling fullscreen, mode = %s\n", fullscreen_mode); +void cmd_fullscreen(I3_CMD, char *action, char *fullscreen_mode) { + fullscreen_mode_t mode = strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT; + DLOG("%s fullscreen, mode = %s\n", action, fullscreen_mode); owindow *current; HANDLE_EMPTY_MATCH; TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); - con_toggle_fullscreen(current->con, (strcmp(fullscreen_mode, "global") == 0 ? CF_GLOBAL : CF_OUTPUT)); + if (strcmp(action, "toggle") == 0) { + con_toggle_fullscreen(current->con, mode); + } else if (strcmp(action, "enable") == 0) { + con_enable_fullscreen(current->con, mode); + } else if (strcmp(action, "disable") == 0) { + con_disable_fullscreen(current->con); + } } cmd_output->needs_tree_render = true; diff --git a/src/con.c b/src/con.c index 6c7b2d96..3293a9fd 100644 --- a/src/con.c +++ b/src/con.c @@ -565,37 +565,27 @@ void con_fix_percent(Con *con) { * */ void con_toggle_fullscreen(Con *con, int fullscreen_mode) { - Con *workspace, *fullscreen; - if (con->type == CT_WORKSPACE) { DLOG("You cannot make a workspace fullscreen.\n"); return; } DLOG("toggling fullscreen for %p / %s\n", con, con->name); - if (con->fullscreen_mode == CF_NONE) { - /* 1: check if there already is a fullscreen con */ - if (fullscreen_mode == CF_GLOBAL) - fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL); - else { - workspace = con_get_workspace(con); - fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT); - } - if (fullscreen != NULL) { - /* Disable fullscreen for the currently fullscreened - * container and enable it for the one the user wants - * to have in fullscreen mode. */ - LOG("Disabling fullscreen for (%p/%s) upon user request\n", - fullscreen, fullscreen->name); - fullscreen->fullscreen_mode = CF_NONE; - } - /* 2: enable fullscreen */ - con->fullscreen_mode = fullscreen_mode; - } else { - /* 1: disable fullscreen */ - con->fullscreen_mode = CF_NONE; - } + if (con->fullscreen_mode == CF_NONE) + con_enable_fullscreen(con, fullscreen_mode); + else + con_disable_fullscreen(con); +} + +/* + * Sets the specified fullscreen mode for the given container, sends the + * “fullscreen_mode” event and changes the XCB fullscreen property of the + * container’s window, if any. + * + */ +static void con_set_fullscreen_mode(Con *con, fullscreen_mode_t fullscreen_mode) { + con->fullscreen_mode = fullscreen_mode; DLOG("mode now: %d\n", con->fullscreen_mode); @@ -618,6 +608,79 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) { A__NET_WM_STATE, XCB_ATOM_ATOM, 32, num, values); } +/* + * Enables fullscreen mode for the given container, if necessary. + * + * If the container’s mode is already CF_OUTPUT or CF_GLOBAL, the container is + * kept fullscreen but its mode is set to CF_GLOBAL and CF_OUTPUT, + * respectively. + * + * Other fullscreen containers will be disabled first, if they hide the new + * one. + * + */ +void con_enable_fullscreen(Con *con, fullscreen_mode_t fullscreen_mode) { + if (con->type == CT_WORKSPACE) { + DLOG("You cannot make a workspace fullscreen.\n"); + return; + } + + assert(fullscreen_mode == CF_GLOBAL || fullscreen_mode == CF_OUTPUT); + + if (fullscreen_mode == CF_GLOBAL) + DLOG("enabling global fullscreen for %p / %s\n", con, con->name); + else + DLOG("enabling fullscreen for %p / %s\n", con, con->name); + + if (con->fullscreen_mode == fullscreen_mode) { + DLOG("fullscreen already enabled for %p / %s\n", con, con->name); + return; + } + + Con *con_ws = con_get_workspace(con); + + /* Disable any fullscreen container that would conflict the new one. */ + Con *fullscreen = con_get_fullscreen_con(croot, CF_GLOBAL); + if (fullscreen == NULL) + fullscreen = con_get_fullscreen_con(con_ws, CF_OUTPUT); + if (fullscreen != NULL) + con_disable_fullscreen(fullscreen); + + /* Set focus to new fullscreen container. Unless in global fullscreen mode + * and on another workspace restore focus afterwards. + * Switch to the container’s workspace if mode is global. */ + Con *cur_ws = con_get_workspace(focused); + Con *old_focused = focused; + if (fullscreen_mode == CF_GLOBAL && cur_ws != con_ws) + workspace_show(con_ws); + con_focus(con); + if (fullscreen_mode != CF_GLOBAL && cur_ws != con_ws) + con_focus(old_focused); + + con_set_fullscreen_mode(con, fullscreen_mode); +} + +/* + * Disables fullscreen mode for the given container regardless of the mode, if + * necessary. + * + */ +void con_disable_fullscreen(Con *con) { + if (con->type == CT_WORKSPACE) { + DLOG("You cannot make a workspace fullscreen.\n"); + return; + } + + DLOG("disabling fullscreen for %p / %s\n", con, con->name); + + if (con->fullscreen_mode == CF_NONE) { + DLOG("fullscreen already disabled for %p / %s\n", con, con->name); + return; + } + + con_set_fullscreen_mode(con, CF_NONE); +} + /* * Moves the given container to the currently focused container on the given * workspace. diff --git a/testcases/i3-test.config b/testcases/i3-test.config index 513dda36..9d0b7d66 100644 --- a/testcases/i3-test.config +++ b/testcases/i3-test.config @@ -20,7 +20,7 @@ bindsym Mod1+h split h bindsym Mod1+v split v # Fullscreen (Mod1+f) -bindsym Mod1+f fullscreen +bindsym Mod1+f fullscreen toggle # Stacking (Mod1+s) bindsym Mod1+s layout stacking diff --git a/testcases/t/100-fullscreen.t b/testcases/t/100-fullscreen.t index bb9403b4..458fff93 100644 --- a/testcases/t/100-fullscreen.t +++ b/testcases/t/100-fullscreen.t @@ -255,4 +255,131 @@ $swindow = open_window({ is(fullscreen_windows($tmp), 1, 'one fullscreen window on ws'); is($x->input_focus, $swindow->id, 'fullscreen window focused'); +################################################################################ +# Verify that command ‘fullscreen enable’ works and is idempotent. +################################################################################ + +$tmp = fresh_workspace; + +$window = open_window; +is($x->input_focus, $window->id, 'window focused'); +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +cmd 'fullscreen enable'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace'); + +cmd 'fullscreen enable'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 1, 'still one fullscreen window on workspace'); + +$window->fullscreen(0); +sync_with_i3; +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +################################################################################ +# Verify that command ‘fullscreen enable global’ works and is idempotent. +################################################################################ + +$tmp = fresh_workspace; + +$window = open_window; +is($x->input_focus, $window->id, 'window focused'); +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +cmd 'fullscreen enable global'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace'); + +cmd 'fullscreen enable global'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 1, 'still one fullscreen window on workspace'); + +$window->fullscreen(0); +sync_with_i3; +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +################################################################################ +# Verify that command ‘fullscreen disable’ works and is idempotent. +################################################################################ + +$tmp = fresh_workspace; + +$window = open_window; +is($x->input_focus, $window->id, 'window focused'); +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +$window->fullscreen(1); +sync_with_i3; +is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace'); + +cmd 'fullscreen disable'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +cmd 'fullscreen disable'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 0, 'still no fullscreen window on workspace'); + +################################################################################ +# Verify that command ‘fullscreen toggle’ works. +################################################################################ + +$tmp = fresh_workspace; + +$window = open_window; +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +cmd 'fullscreen toggle'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace'); + +cmd 'fullscreen toggle'; +is($x->input_focus, $window->id, 'window still focused'); +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +################################################################################ +# Verify that a window’s fullscreen is disabled when another one is enabled +# on the same workspace. The new fullscreen window should be focused. +################################################################################ + +$tmp = fresh_workspace; + +$window = open_window; +$other = open_window; + +is($x->input_focus, $other->id, 'other window focused'); +is(fullscreen_windows($tmp), 0, 'no fullscreen window on workspace'); + +cmd 'fullscreen enable'; +is($x->input_focus, $other->id, 'other window focused'); +is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace'); + +cmd '[id="' . $window->id . '"] fullscreen enable'; +is($x->input_focus, $window->id, 'window focused'); +is(fullscreen_windows($tmp), 1, 'one fullscreen window on workspace'); + +################################################################################ +# Verify that when a global fullscreen is enabled the window is focused and +# its workspace is selected, so that disabling the fullscreen keeps the window +# focused and visible. +################################################################################ + +$tmp = fresh_workspace; + +$window = open_window; + +is($x->input_focus, $window->id, 'window focused'); + +cmd 'workspace ' . get_unused_workspace; +isnt($x->input_focus, $window->id, 'window not focused'); +isnt(focused_ws(), $tmp, 'workspace not selected'); + +cmd '[id="' . $window->id . '"] fullscreen enable global'; +is($x->input_focus, $window->id, 'window focused'); + +cmd 'fullscreen disable'; +is($x->input_focus, $window->id, 'window still focused'); +is(focused_ws(), $tmp, 'workspace selected'); + done_testing; From 04fa40d3e5e11be98a89d5d97c395f95a6d08e37 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 13 Dec 2014 22:33:42 +0100 Subject: [PATCH 080/103] docs/ipc: use an actual event type (thanks Adaephon) --- docs/ipc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ipc b/docs/ipc index 55177f27..4a2b1df9 100644 --- a/docs/ipc +++ b/docs/ipc @@ -617,7 +617,7 @@ you can register to an event. *Example:* --------------------------------- type: SUBSCRIBE -payload: [ "workspace", "focus" ] +payload: [ "workspace", "output" ] --------------------------------- From 823b46a544679c55319af1afe000b67c74f47dcd Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Fri, 19 Dec 2014 23:43:47 -0500 Subject: [PATCH 081/103] Include workspace con in workspace event Send the affected workspace in the "current" property for each workspace event for any type of workspace event that affects a particular workspace. fixes #1411 --- docs/ipc | 17 ++++++++------- include/ipc.h | 14 +++++++++---- src/commands.c | 10 ++++----- src/con.c | 10 ++++++++- src/ipc.c | 29 +++++++++++++++++++------- src/move.c | 2 +- src/workspace.c | 17 +++++++++++---- testcases/t/115-ipc-workspaces.t | 30 +++++++++++++++++---------- testcases/t/227-ipc-workspace-empty.t | 2 ++ 9 files changed, 90 insertions(+), 41 deletions(-) diff --git a/docs/ipc b/docs/ipc index 4a2b1df9..ff7c8aae 100644 --- a/docs/ipc +++ b/docs/ipc @@ -668,15 +668,16 @@ if ($is_event) { This event consists of a single serialized map containing a property +change (string)+ which indicates the type of the change ("focus", "init", -"empty", "urgent"). +"empty", "urgent"). A +current (object)+ property will be present with the +affected workspace whenever the type of event affects a workspace (otherwise, +it will be +null). -Moreover, when the change is "focus", an +old (object)+ and a +current -(object)+ properties will be present with the previous and current -workspace respectively. When the first switch occurs (when i3 focuses -the workspace visible at the beginning) there is no previous -workspace, and the +old+ property will be set to +null+. Also note -that if the previous is empty it will get destroyed when switching, -but will still be present in the "old" property. +When the change is "focus", an +old (object)+ property will be present with the +previous workspace. When the first switch occurs (when i3 focuses the +workspace visible at the beginning) there is no previous workspace, and the ++old+ property will be set to +null+. Also note that if the previous is empty +it will get destroyed when switching, but will still be present in the "old" +property. *Example:* --------------------- diff --git a/include/ipc.h b/include/ipc.h index 77dcad21..96a60a1f 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -89,11 +89,17 @@ void ipc_shutdown(void); void dump_node(yajl_gen gen, Con *con, bool inplace_restart); /** - * For the workspace "focus" event we send, along the usual "change" field, - * also the current and previous workspace, in "current" and "old" - * respectively. + * Generates a json workspace event. Returns a dynamically allocated yajl + * generator. Free with yajl_gen_free(). */ -void ipc_send_workspace_focus_event(Con *current, Con *old); +yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old); + +/** + * For the workspace events we send, along with the usual "change" field, also + * the workspace container in "current". For focus events, we send the + * previously focused workspace in "old". + */ +void ipc_send_workspace_event(const char *change, Con *current, Con *old); /** * For the window events we send, along the usual "change" field, diff --git a/src/commands.c b/src/commands.c index e87617c9..498c25c8 100644 --- a/src/commands.c +++ b/src/commands.c @@ -944,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) { restore_open_placeholder_windows(parent); if (content == JSON_CONTENT_WORKSPACE) - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"restored\"}"); + ipc_send_workspace_event("restored", parent, NULL); cmd_output->needs_tree_render = true; } @@ -1313,7 +1313,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { create_workspace_on_output(current_output, ws->parent); /* notify the IPC listeners */ - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); + ipc_send_workspace_event("init", ws, NULL); } DLOG("Detaching\n"); @@ -1334,7 +1334,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows) floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect)); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}"); + ipc_send_workspace_event("move", ws, NULL); if (workspace_was_visible) { /* Focus the moved workspace on the destination output. */ workspace_show(ws); @@ -1761,7 +1761,7 @@ void cmd_reload(I3_CMD) { load_configuration(conn, NULL, true); x_set_i3_atoms(); /* Send an IPC event just in case the ws names have changed */ - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); + ipc_send_workspace_event("reload", NULL, NULL); /* Send an update event for the barconfig just in case it has changed */ update_barconfig(); @@ -2040,7 +2040,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { cmd_output->needs_tree_render = true; ysuccess(true); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); + ipc_send_workspace_event("rename", workspace, NULL); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); ewmh_update_current_desktop(); diff --git a/src/con.c b/src/con.c index 3293a9fd..38ea0585 100644 --- a/src/con.c +++ b/src/con.c @@ -12,6 +12,7 @@ * */ #include "all.h" +#include "yajl_utils.h" static void con_on_remove_child(Con *con); @@ -1435,8 +1436,15 @@ static void con_on_remove_child(Con *con) { if (con->type == CT_WORKSPACE) { if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) { LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name); + yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL); tree_close(con, DONT_KILL_WINDOW, false, false); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload); + + y(free); } return; } diff --git a/src/ipc.c b/src/ipc.c index c70ec323..7dbb6632 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1120,21 +1120,23 @@ int ipc_create_socket(const char *filename) { } /* - * For the workspace "focus" event we send, along the usual "change" field, - * also the current and previous workspace, in "current" and "old" - * respectively. + * Generates a json workspace event. Returns a dynamically allocated yajl + * generator. Free with yajl_gen_free(). */ -void ipc_send_workspace_focus_event(Con *current, Con *old) { +yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) { setlocale(LC_NUMERIC, "C"); yajl_gen gen = ygenalloc(); y(map_open); ystr("change"); - ystr("focus"); + ystr(change); ystr("current"); - dump_node(gen, current, false); + if (current == NULL) + y(null); + else + dump_node(gen, current, false); ystr("old"); if (old == NULL) @@ -1144,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) { y(map_close); + setlocale(LC_NUMERIC, ""); + + return gen; +} + +/* + * For the workspace events we send, along with the usual "change" field, also + * the workspace container in "current". For focus events, we send the + * previously focused workspace in "old". + */ +void ipc_send_workspace_event(const char *change, Con *current, Con *old) { + yajl_gen gen = ipc_marshal_workspace_event(change, current, old); + const unsigned char *payload; ylength length; y(get_buf, &payload, &length); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload); + y(free); - setlocale(LC_NUMERIC, ""); } /** diff --git a/src/move.c b/src/move.c index 9c0f310f..1999a1fe 100644 --- a/src/move.c +++ b/src/move.c @@ -128,7 +128,7 @@ static void move_to_output_directed(Con *con, direction_t direction) { tree_flatten(croot); - ipc_send_workspace_focus_event(ws, old_ws); + ipc_send_workspace_event("focus", ws, old_ws); } /* diff --git a/src/workspace.c b/src/workspace.c index 1bb619c3..a3056633 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -11,6 +11,7 @@ * */ #include "all.h" +#include "yajl_utils.h" /* Stores a copy of the name of the last used workspace for the workspace * back-and-forth switching. */ @@ -91,7 +92,7 @@ Con *workspace_get(const char *num, bool *created) { con_attach(workspace, content, false); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); + ipc_send_workspace_event("init", workspace, NULL); ewmh_update_number_of_desktops(); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); @@ -409,7 +410,7 @@ static void _workspace_show(Con *workspace) { } else con_focus(next); - ipc_send_workspace_focus_event(workspace, current); + ipc_send_workspace_event("focus", workspace, current); DLOG("old = %p / %s\n", old, (old ? old->name : "(null)")); /* Close old workspace if necessary. This must be done *after* doing @@ -421,8 +422,16 @@ static void _workspace_show(Con *workspace) { /* check if this workspace is currently visible */ if (!workspace_is_visible(old)) { LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name); + yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL); tree_close(old, DONT_KILL_WINDOW, false, false); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload); + + y(free); + ewmh_update_number_of_desktops(); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); @@ -766,7 +775,7 @@ void workspace_update_urgent_flag(Con *ws) { DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent); if (old_flag != ws->urgent) - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}"); + ipc_send_workspace_event("urgent", ws, NULL); } /* diff --git a/testcases/t/115-ipc-workspaces.t b/testcases/t/115-ipc-workspaces.t index 29837430..2bcc6d60 100644 --- a/testcases/t/115-ipc-workspaces.t +++ b/testcases/t/115-ipc-workspaces.t @@ -23,7 +23,7 @@ $i3->connect()->recv; # Workspaces requests and events ################################ -my $focused = get_ws(focused_ws()); +my $old_ws = get_ws(focused_ws()); # Events @@ -36,15 +36,11 @@ $i3->subscribe({ workspace => sub { my ($event) = @_; if ($event->{change} eq 'init') { - $init->send(1); + $init->send($event); } elsif ($event->{change} eq 'focus') { - # Check that we have the old and new workspace - $focus->send( - $event->{current}->{name} == '2' && - $event->{old}->{name} == $focused->{name} - ); + $focus->send($event); } elsif ($event->{change} eq 'empty') { - $empty->send(1); + $empty->send($event); } } })->recv; @@ -61,8 +57,20 @@ $t = AnyEvent->timer( } ); -ok($init->recv, 'Workspace "init" event received'); -ok($focus->recv, 'Workspace "focus" event received'); -ok($empty->recv, 'Workspace "empty" event received'); +my $init_event = $init->recv; +my $focus_event = $focus->recv; +my $empty_event = $empty->recv; + +my $current_ws = get_ws(focused_ws()); + +ok($init_event, 'workspace "init" event received'); +is($init_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the initted workspace con'); + +ok($focus_event, 'workspace "focus" event received'); +is($focus_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con'); +is($focus_event->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last'); + +ok($empty_event, 'workspace "empty" event received'); +is($empty_event->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con'); done_testing; diff --git a/testcases/t/227-ipc-workspace-empty.t b/testcases/t/227-ipc-workspace-empty.t index 185910e8..0c67423e 100644 --- a/testcases/t/227-ipc-workspace-empty.t +++ b/testcases/t/227-ipc-workspace-empty.t @@ -50,6 +50,7 @@ subtest 'Workspace empty event upon switch', sub { my $event = $cond->recv; is($event->{change}, 'empty', '"Empty" event received upon workspace switch'); + is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con'); }; ################################################################################ @@ -116,6 +117,7 @@ subtest 'Workspace empty event upon window close', sub { my $event = $cond->recv; is($event->{change}, 'empty', '"Empty" event received upon window close'); + is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con'); }; } From 69dd8ce3984063fbc8bf5f8884690a16748be9f6 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 20 Dec 2014 19:17:14 -0500 Subject: [PATCH 082/103] Fix start_application() doc about which shell is used Since this commit: f691a55923850a4d315450925fc98733d07b69c9 the shell that is used is the system's bourne shell (/bin/sh) and the env variable SHELL is not considered. No logic changes. --- include/startup.h | 4 ++-- src/startup.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/startup.h b/include/startup.h index 9cbdf17b..2f28baa7 100644 --- a/include/startup.h +++ b/include/startup.h @@ -21,8 +21,8 @@ * (immediately), the application is reparented to init (process-id 1), which * correctly handles childs, so we don’t have to do it :-). * - * The shell is determined by looking for the SHELL environment variable. If - * it does not exist, /bin/sh is used. + * The shell used to start applications is the system's bourne shell (i.e., + * /bin/sh). * * The no_startup_id flag determines whether a startup notification context * (and ID) should be created, which is the default and encouraged behavior. diff --git a/src/startup.c b/src/startup.c index d6fe6d40..ebe8c1d9 100644 --- a/src/startup.c +++ b/src/startup.c @@ -123,8 +123,8 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) { * the application is reparented to init (process-id 1), which correctly handles * childs, so we don’t have to do it :-). * - * The shell is determined by looking for the SHELL environment variable. If it - * does not exist, /bin/sh is used. + * The shell used to start applications is the system's bourne shell (i.e., + * /bin/sh). * * The no_startup_id flag determines whether a startup notification context * (and ID) should be created, which is the default and encouraged behavior. From 13850a55b0145ba646a7dda4f2155905571b9e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Wed, 24 Dec 2014 15:45:12 -0500 Subject: [PATCH 083/103] Inset the urgent background of a status block for consistency with workspace buttons. fixes #1423 --- i3bar/src/xcb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 30dd837d..179f8274 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -191,7 +191,7 @@ void refresh_statusline(void) { /* The urgent background “overshoots” by 2 px so that the text that * is printed onto it will not be look so cut off. */ - xcb_rectangle_t bg_rect = { x - logical_px(2), 0, block->width + logical_px(4), bar_height }; + xcb_rectangle_t bg_rect = { x - logical_px(2), logical_px(1), block->width + logical_px(4), bar_height - logical_px(2) }; xcb_poly_fill_rectangle(xcb_connection, statusline_pm, statusline_ctx, 1, &bg_rect); } else { fg_color = (block->color ? get_colorpixel(block->color) : colors.bar_fg); From 94808019277991a404a0a39e2a27ca1574072f3c Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 23 Jan 2015 17:07:31 +0100 Subject: [PATCH 084/103] =?UTF-8?q?don=E2=80=99t=20fix=20coordinates/chang?= =?UTF-8?q?e=20focus/warp=20when=20source=5Foutput=20was=20scratchpad?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/con.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/con.c b/src/con.c index 38ea0585..524008ce 100644 --- a/src/con.c +++ b/src/con.c @@ -771,7 +771,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool next = ws; } - if (source_output != dest_output) { + if (source_output != dest_output && !con_is_internal(source_output)) { /* Take the relative coordinates of the current output, then add them * to the coordinate space of the correct output */ if (fix_coordinates && con->type == CT_FLOATING_CON) { From 073ecdd7114a2ce680d4000d4172de19e12e0563 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 23 Jan 2015 17:11:05 +0100 Subject: [PATCH 085/103] =?UTF-8?q?Revert=20"don=E2=80=99t=20fix=20coordin?= =?UTF-8?q?ates/change=20focus/warp=20when=20source=5Foutput=20was=20scrat?= =?UTF-8?q?chpad"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 94808019277991a404a0a39e2a27ca1574072f3c. This causes focus issues. --- src/con.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/con.c b/src/con.c index 524008ce..38ea0585 100644 --- a/src/con.c +++ b/src/con.c @@ -771,7 +771,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool next = ws; } - if (source_output != dest_output && !con_is_internal(source_output)) { + if (source_output != dest_output) { /* Take the relative coordinates of the current output, then add them * to the coordinate space of the correct output */ if (fix_coordinates && con->type == CT_FLOATING_CON) { From 407e9cf7450755737507352972b67e2108f35d45 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 24 Jan 2015 21:53:10 -0500 Subject: [PATCH 086/103] Add lxterminal to i3-sensible-terminal fixes #1436 --- i3-sensible-terminal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i3-sensible-terminal b/i3-sensible-terminal index 06c96354..747a9280 100755 --- a/i3-sensible-terminal +++ b/i3-sensible-terminal @@ -8,7 +8,7 @@ # We welcome patches that add distribution-specific mechanisms to find the # preferred terminal emulator. On Debian, there is the x-terminal-emulator # symlink for example. -for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite; do +for terminal in $TERMINAL x-terminal-emulator urxvt rxvt terminator Eterm aterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal; do if command -v $terminal > /dev/null 2>&1; then exec $terminal "$@" fi From 29a832a160a839fe1b1199c57b3078bc323eacaf Mon Sep 17 00:00:00 2001 From: Marein Konings Date: Sun, 18 Jan 2015 20:48:34 +0100 Subject: [PATCH 087/103] Add 'mark' as allowed key to i3-save-tree output --- i3-save-tree | 1 + 1 file changed, 1 insertion(+) diff --git a/i3-save-tree b/i3-save-tree index add4c8c1..c64fc72a 100755 --- a/i3-save-tree +++ b/i3-save-tree @@ -92,6 +92,7 @@ my %allowed_keys = map { ($_, 1) } qw( name geometry window_properties + mark ); sub strip_containers { From f28ce227e354230da4b33c58d53373a71e67b08d Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Fri, 9 Jan 2015 01:18:23 +0300 Subject: [PATCH 088/103] i3bar: suspend the child when bars are fully obscured --- i3bar/include/outputs.h | 1 + i3bar/src/xcb.c | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index e0beae89..bd41e776 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -40,6 +40,7 @@ struct i3_output { char* name; /* Name of the output */ bool active; /* If the output is active */ bool primary; /* If it is the primary output */ + bool visible; /* If the bar is visible on this output */ int ws; /* The number of the currently visible ws */ rect rect; /* The rect (relative to the root-win) */ diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 179f8274..6f5f0ca2 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -481,6 +481,39 @@ void handle_button(xcb_button_press_event_t *event) { free(buffer); } +/* + * Handle visibility notifications: when none of the bars are visible, e.g. + * if windows are in full-screen on each output, suspend the child process. + * + */ +static void handle_visibility_notify(xcb_visibility_notify_event_t *event) { + bool visible = (event->state != XCB_VISIBILITY_FULLY_OBSCURED); + int num_visible = 0; + i3_output *output; + + SLIST_FOREACH (output, outputs, slist) { + if (!output->active) { + continue; + } + if (output->bar == event->window) { + if (output->visible == visible) { + return; + } + output->visible = visible; + } + num_visible += output->visible; + } + + if (num_visible == 0) { + stop_child(); + } else if (num_visible == visible) { + /* Wake the child only when transitioning from 0 to 1 visible bar. + * We cannot transition from 0 to 2 or more visible bars at once since + * visibility events are delivered to each window separately */ + cont_child(); + } +} + /* * Adjusts the size of the tray window and alignment of the tray clients by * configuring their respective x coordinates. To be called when mapping or @@ -945,6 +978,10 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { } switch (type) { + case XCB_VISIBILITY_NOTIFY: + /* Visibility change: a bar is [un]obscured by other window */ + handle_visibility_notify((xcb_visibility_notify_event_t *)event); + break; case XCB_EXPOSE: /* Expose-events happen, when the window needs to be redrawn */ redraw_bars(); @@ -1461,6 +1498,12 @@ void reconfig_windows(bool redraw_bars) { * */ values[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; + if (config.hide_on_modifier == M_DOCK) { + /* If the bar is normally visible, catch visibility change events to suspend + * the status process when the bar is obscured by full-screened windows. */ + values[2] |= XCB_EVENT_MASK_VISIBILITY_CHANGE; + walk->visible = true; + } if (!config.disable_ws) { values[2] |= XCB_EVENT_MASK_BUTTON_PRESS; } From 74b69d6d02391d9e8beb2c8585bad2c552c731f4 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 29 Jan 2015 20:52:52 -0500 Subject: [PATCH 089/103] Add mouse binding pointer position configuration Add the `--whole-window` switch for mouse bindings. This switch controls what part of the container the pointer must be over to trigger a mouse binding. The default is to only trigger mouse bindings over the titlebars. With this switch, a mouse binding will be triggered over the main part of the window as well. This is a breaking change to the previous behavior, which would trigger a mouse binding with a modifier over any part of the window. fixes #1429 --- docs/userguide | 10 +++++----- include/bindings.h | 2 +- include/config_directives.h | 4 ++-- include/data.h | 5 +++++ parser-specs/config.spec | 10 ++++++++-- src/bindings.c | 3 ++- src/click.c | 6 +++--- src/config_directives.c | 8 ++++---- testcases/t/201-config-parser.t | 8 ++++---- 9 files changed, 34 insertions(+), 22 deletions(-) diff --git a/docs/userguide b/docs/userguide index 5f00fd6a..d6afa334 100644 --- a/docs/userguide +++ b/docs/userguide @@ -404,12 +404,12 @@ can configure mouse bindings in a similar way to key bindings. *Syntax*: ---------------------------------- -bindsym [Modifiers+]button[n] command +bindsym [--whole-window] [Modifiers+]button[n] command ---------------------------------- -If the binding has no modifiers, it will only run when you click on the -titlebar of the window. Otherwise, it will run when any part of the window is -clicked. +By default, the binding will only run when you click on the titlebar of the +window. If the +--whole-window+ flag is given, it will run when any part of the +window is clicked. *Examples*: -------------------------------- @@ -417,7 +417,7 @@ clicked. bindsym button2 kill # The middle button and a modifer over any part of the window kills the window -bindsym $mod+button2 kill +bindsym --whole-window $mod+button2 kill # The right button toggles floating bindsym button3 floating toggle diff --git a/include/bindings.h b/include/bindings.h index 02c3f190..19345f8c 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -24,7 +24,7 @@ const char *DEFAULT_BINDING_MODE; * */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, - const char *release, const char *command, const char *mode); + const char *release, const char *whole_window, const char *command, const char *mode); /** * Grab the bound keys (tell X to send us keypress events for those keycodes) diff --git a/include/config_directives.h b/include/config_directives.h index af7b9a90..0f1a6620 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -61,10 +61,10 @@ CFGFUN(color_single, const char *colorclass, const char *color); CFGFUN(floating_modifier, const char *modifiers); CFGFUN(new_window, const char *windowtype, const char *border, const long width); CFGFUN(workspace, const char *workspace, const char *output); -CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command); +CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command); CFGFUN(enter_mode, const char *mode); -CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command); +CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command); CFGFUN(bar_font, const char *font); CFGFUN(bar_mode, const char *mode); diff --git a/include/data.h b/include/data.h index 1a67685d..8f2c197d 100644 --- a/include/data.h +++ b/include/data.h @@ -255,6 +255,11 @@ struct Binding { B_UPON_KEYRELEASE_IGNORE_MODS = 2, } release; + /** If this is true for a mouse binding, the binding should be executed + * when the button is pressed over any part of the window, not just the + * title bar (default). */ + bool whole_window; + uint32_t number_keycodes; /** Keycode to bind */ diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 4025665d..95c206fe 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -278,6 +278,8 @@ state FONT: state BINDING: release = '--release' -> + whole_window = '--whole-window' + -> modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod' -> '+' @@ -288,8 +290,10 @@ state BINDING: state BINDCOMMAND: release = '--release' -> + whole_window = '--whole-window' + -> command = string - -> call cfg_binding($bindtype, $modifiers, $key, $release, $command) + -> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command) ################################################################################ # Mode configuration @@ -333,8 +337,10 @@ state MODE_BINDING: state MODE_BINDCOMMAND: release = '--release' -> + whole_window = '--whole-window' + -> command = string - -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $command); MODE + -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $whole_window, $command); MODE ################################################################################ # Bar configuration (i3bar) diff --git a/src/bindings.c b/src/bindings.c index 70d274a7..8f9767e6 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -49,10 +49,11 @@ static struct Mode *mode_from_name(const char *name) { * */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, - const char *release, const char *command, const char *modename) { + const char *release, const char *whole_window, const char *command, const char *modename) { Binding *new_binding = scalloc(sizeof(Binding)); DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release); new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS); + new_binding->whole_window = (whole_window != NULL); if (strcmp(bindtype, "bindsym") == 0) { new_binding->input_type = (strncasecmp(input_code, "button", (sizeof("button") - 1)) == 0 ? B_MOUSE diff --git a/src/click.c b/src/click.c index 5c36aeb0..86a63ea6 100644 --- a/src/click.c +++ b/src/click.c @@ -182,9 +182,9 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) { Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event); /* clicks over a window decoration will always trigger the binding and - * clicks on the inside of the window will only trigger a binding if it - * has modifiers. */ - if (bind && (dest == CLICK_DECORATION || (bind->mods && dest == CLICK_INSIDE))) { + * clicks on the inside of the window will only trigger a binding if + * the --whole-window flag was given for the binding. */ + if (bind && (dest == CLICK_DECORATION || bind->whole_window)) { CommandResult *result = run_binding(bind, con); /* ASYNC_POINTER eats the event */ diff --git a/src/config_directives.c b/src/config_directives.c index 690e08bf..e8fdfe77 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -171,8 +171,8 @@ CFGFUN(font, const char *font) { font_pattern = sstrdup(font); } -CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) { - configure_binding(bindtype, modifiers, key, release, command, DEFAULT_BINDING_MODE); +CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) { + configure_binding(bindtype, modifiers, key, release, whole_window, command, DEFAULT_BINDING_MODE); } /******************************************************************************* @@ -181,8 +181,8 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co static char *current_mode; -CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) { - configure_binding(bindtype, modifiers, key, release, command, current_mode); +CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) { + configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode); } CFGFUN(enter_mode, const char *modename) { diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index 1153423b..86ef731f 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -50,9 +50,9 @@ EOT my $expected = <<'EOT'; cfg_enter_mode(meh) -cfg_mode_binding(bindsym, Mod1,Shift, x, (null), resize grow) -cfg_mode_binding(bindcode, Mod1, 44, (null), resize shrink) -cfg_mode_binding(bindsym, Mod1, x, --release, exec foo) +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) EOT is(parser_calls($config), @@ -618,7 +618,7 @@ EOT $expected = <<'EOT'; cfg_enter_mode(yo) -cfg_mode_binding(bindsym, (null), x, (null), resize shrink left) +cfg_mode_binding(bindsym, (null), x, (null), (null), resize shrink left) ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'bindsym', 'bindcode', 'bind', '}' ERROR: CONFIG: (in file ) ERROR: CONFIG: Line 1: mode "yo" { From 72b3c9f4c9a3356d97e3029e492b8cc437af4137 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 4 Feb 2015 19:52:40 +0100 Subject: [PATCH 090/103] cking-howto: update links --- docs/hacking-howto | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/hacking-howto b/docs/hacking-howto index aca6e92b..a591047e 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -951,17 +951,15 @@ For a short introduction into using git, see http://web.archive.org/web/20121024222556/http://www.spheredev.org/wiki/Git_for_the_lazy or, for more documentation, see http://git-scm.com/documentation -You can view the git repository online at http://code.i3wm.org. - 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. When working on bugfixes, please make sure you mention that you are working on -it in the corresponding bugreport at http://bugs.i3wm.org/. In case there is no -bugreport yet, please create one. +it in the corresponding bugreport at https://github.com/i3/i3/issues In case +there is no bugreport yet, please create one. -After you are done, please submit your work for review at http://cr.i3wm.org/ +After you are done, please submit your work for review 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 From 663235c714762c069e660ffe14a8334f73365c1b Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 4 Feb 2015 19:54:07 +0100 Subject: [PATCH 091/103] docs/debugging: use logs.i3wm.org --- docs/debugging | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/debugging b/docs/debugging index 9dec3056..1253b0c5 100644 --- a/docs/debugging +++ b/docs/debugging @@ -72,15 +72,17 @@ i3-msg 'debuglog on; shmlog on; reload' No matter whether i3 misbehaved in some way without crashing or whether it just crashed, the logfile provides all information necessary to debug the problem. -To save a compressed version of the logfile (suitable for attaching it to a -bugreport), use: --------------------------------------------------------------------- -DISPLAY=:0 i3-dump-log | bzip2 -c > /tmp/i3.log.bz2 --------------------------------------------------------------------- +To upload a compressed version of the logfile (for a bugreport), use: +------------------------------------------------------------------------------ +DISPLAY=:0 i3-dump-log | bzip2 -c | curl --data-binary @- http://logs.i3wm.org +------------------------------------------------------------------------------ This command does not depend on i3 (it also works while i3 displays the crash dialog), but it requires a working X11 connection. +After running it, you will get a URL to the logfile. Please include that URL in +your bug report. + == On crashes: Obtaining a backtrace When i3 crashes, it will display a dialog stating “i3 just crashed”, offering From d75f80402af73808b0acaacadb89e2227f2fdde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ingo=20B=C3=BCrk?= Date: Mon, 9 Feb 2015 20:13:02 +0100 Subject: [PATCH 092/103] Correctly calculate clicks on i3bar status blocks fixes #1458 --- i3bar/src/xcb.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index 6f5f0ca2..cbab7423 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -117,6 +117,12 @@ int _xcb_request_failed(xcb_void_cookie_t cookie, char *err_msg, int line) { return 0; } +uint32_t get_sep_offset(struct status_block *block) { + if (!block->no_separator && block->sep_block_width > 0) + return block->sep_block_width / 2 + block->sep_block_width % 2; + return 0; +} + /* * Redraws the statusline to the buffer * @@ -148,8 +154,8 @@ void refresh_statusline(void) { block->x_offset = padding_width; break; case ALIGN_CENTER: - block->x_offset = padding_width / logical_px(2); - block->x_append = padding_width / logical_px(2) + padding_width % logical_px(2); + block->x_offset = padding_width / 2; + block->x_append = padding_width / 2 + padding_width % 2; break; } } @@ -201,9 +207,9 @@ void refresh_statusline(void) { draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 3, block->width); x += block->width + block->sep_block_width + block->x_offset + block->x_append; - if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) { + uint32_t sep_offset = get_sep_offset(block); + if (TAILQ_NEXT(block, blocks) != NULL && sep_offset > 0) { /* This is not the last block, draw a separator. */ - uint32_t sep_offset = block->sep_block_width / 2 + block->sep_block_width % 2; uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH; uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)}; xcb_change_gc(xcb_connection, statusline_ctx, mask, values); @@ -362,22 +368,31 @@ void handle_button(xcb_button_press_event_t *event) { continue; tray_width += (font.height + logical_px(2)); } + if (tray_width > 0) + tray_width += logical_px(2); int block_x = 0, last_block_x; - int offset = (walk->rect.w - (statusline_width + tray_width)) - logical_px(10); + int offset = walk->rect.w - statusline_width - tray_width - logical_px(4); x = original_x - offset; if (x >= 0) { struct status_block *block; + int sep_offset_remainder = 0; + + TAILQ_FOREACH (block, &statusline_head, blocks) { + if (i3string_get_num_bytes(block->full_text) == 0) + continue; - TAILQ_FOREACH(block, &statusline_head, blocks) { last_block_x = block_x; - block_x += block->width + block->x_offset + block->x_append; + block_x += block->width + block->x_offset + block->x_append + + get_sep_offset(block) + sep_offset_remainder; if (x <= block_x && x >= last_block_x) { send_block_clicked(event->detail, block->name, block->instance, event->root_x, event->root_y); return; } + + sep_offset_remainder = block->sep_block_width - get_sep_offset(block); } } x = original_x; From d38d2dc3e3454642f8ceaaadeaa0fd8d0b88feea Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Feb 2015 14:26:22 -0500 Subject: [PATCH 093/103] Update bar font config on reload Dynamically update the font when the `reload` command is called by reloading the font with `xcb_init_late` and adjusting the size of the bar by updating the output dimensions with a call to the ipc. --- i3bar/src/ipc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/i3bar/src/ipc.c b/i3bar/src/ipc.c index 3eb50beb..e1b343e5 100644 --- a/i3bar/src/ipc.c +++ b/i3bar/src/ipc.c @@ -159,6 +159,9 @@ void got_bar_config_update(char *event) { if (found_id == NULL) return; + /* reconfigure the bar based on the current outputs */ + i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL); + free_colors(&(config.colors)); /* update the configuration with the received settings */ @@ -169,6 +172,8 @@ void got_bar_config_update(char *event) { reconfig_windows(true); } + /* update fonts and colors */ + init_xcb_late(config.fontname); init_colors(&(config.colors)); realloc_sl_buffer(); From e91a9174e24d8fe0616115d777f8ee641e50a782 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Feb 2015 15:11:40 -0500 Subject: [PATCH 094/103] libi3: free previous font on font load When loading a new font with `load_font`, free the previously loaded font with `free_font`. If no font is loaded, `free_font` will simply return (instead of crashing because of a double free). --- include/libi3.h | 6 ++++-- libi3/font.c | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/libi3.h b/include/libi3.h index 7c0ead32..7a2bdf58 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -290,7 +290,8 @@ uint32_t get_mod_mask_for(uint32_t keysym, /** * Loads a font for usage, also getting its height. If fallback is true, - * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. + * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any + * font was previously loaded, it will be freed. * */ i3Font load_font(const char *pattern, const bool fallback); @@ -302,7 +303,8 @@ i3Font load_font(const char *pattern, const bool fallback); void set_font(i3Font *font); /** - * Frees the resources taken by the current font. + * Frees the resources taken by the current font. If no font was previously + * loaded, it simply returns. * */ void free_font(void); diff --git a/libi3/font.c b/libi3/font.c index 7670335f..a338f975 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -160,10 +160,14 @@ static int predict_text_width_pango(const char *text, size_t text_len) { /* * Loads a font for usage, also getting its metrics. If fallback is true, - * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. + * the fonts 'fixed' or '-misc-*' will be loaded instead of exiting. If any + * font was previously loaded, it will be freed. * */ i3Font load_font(const char *pattern, const bool fallback) { + /* if any font was previously loaded, free it now */ + free_font(); + i3Font font; font.type = FONT_TYPE_NONE; @@ -257,10 +261,15 @@ void set_font(i3Font *font) { } /* - * Frees the resources taken by the current font. + * Frees the resources taken by the current font. If no font was previously + * loaded, it simply returns. * */ void free_font(void) { + /* if there is no saved font, simply return */ + if (savedFont == NULL) + return; + free(savedFont->pattern); switch (savedFont->type) { case FONT_TYPE_NONE: @@ -283,6 +292,8 @@ void free_font(void) { assert(false); break; } + + savedFont = NULL; } /* From c815fc798d530aaaac1ab3402ccd47defb7bcc0f Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Tue, 10 Feb 2015 17:46:02 -0500 Subject: [PATCH 095/103] Handle button release events This enables the --release switch on mouse button bindings. --- docs/userguide | 7 ++++--- src/click.c | 14 ++++++++++---- src/handlers.c | 1 + 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/userguide b/docs/userguide index d6afa334..4b4dacc8 100644 --- a/docs/userguide +++ b/docs/userguide @@ -404,17 +404,18 @@ can configure mouse bindings in a similar way to key bindings. *Syntax*: ---------------------------------- -bindsym [--whole-window] [Modifiers+]button[n] command +bindsym [--release] [--whole-window] [Modifiers+]button[n] command ---------------------------------- By default, the binding will only run when you click on the titlebar of the window. If the +--whole-window+ flag is given, it will run when any part of the -window is clicked. +window is clicked. If the +--release+ flag is given, it will run when the mouse +button is released. *Examples*: -------------------------------- # The middle button over a titlebar kills the window -bindsym button2 kill +bindsym --release button2 kill # The middle button and a modifer over any part of the window kills the window bindsym --whole-window $mod+button2 kill diff --git a/src/click.c b/src/click.c index 86a63ea6..51ffcf3a 100644 --- a/src/click.c +++ b/src/click.c @@ -200,6 +200,11 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod } } + /* There is no default behavior for button release events so we are done. */ + if (event->response_type == XCB_BUTTON_RELEASE) { + goto done; + } + /* Any click in a workspace should focus that workspace. If the * workspace is on another output we need to do a workspace_show in * order for i3bar (and others) to notice the change in workspace. */ @@ -336,9 +341,10 @@ done: */ int handle_button_press(xcb_button_press_event_t *event) { Con *con; - DLOG("Button %d pressed on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n", - event->state, event->event, event->child, event->event_x, event->event_y, - event->root_x, event->root_y); + DLOG("Button %d %s on window 0x%08x (child 0x%08x) at (%d, %d) (root %d, %d)\n", + event->state, (event->response_type == XCB_BUTTON_PRESS ? "press" : "release"), + event->event, event->child, event->event_x, event->event_y, event->root_x, + event->root_y); last_timestamp = event->time; @@ -351,7 +357,7 @@ int handle_button_press(xcb_button_press_event_t *event) { if (!(con = con_by_frame_id(event->event))) { /* If the root window is clicked, find the relevant output from the * click coordinates and focus the output's active workspace. */ - if (event->event == root) { + if (event->event == root && event->response_type == XCB_BUTTON_PRESS) { Con *output, *ws; TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { if (con_is_internal(output) || diff --git a/src/handlers.c b/src/handlers.c index 569a8ec3..7ebb2ccb 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1257,6 +1257,7 @@ void handle_event(int type, xcb_generic_event_t *event) { break; case XCB_BUTTON_PRESS: + case XCB_BUTTON_RELEASE: handle_button_press((xcb_button_press_event_t *)event); break; From 1c5ab5fa36ce82b8cbb7e33dbeb350e57f927d03 Mon Sep 17 00:00:00 2001 From: Lukas K Date: Tue, 27 Jan 2015 22:58:48 +0100 Subject: [PATCH 096/103] Support _NET_WM_MOVERESIZE Add support for the _NET_WM_MOVERESIZE client message. This message enables clients to initiate window moving or resizing. Toolkits like Gtk3 use this message when the user drags a client-side decorated window by its title bar. When Gtk detects that the window manager does not support this client message, it uses a slow fallback implementation. fixes #1432 --- include/atoms.xmacro | 1 + src/ewmh.c | 3 ++- src/handlers.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/atoms.xmacro b/include/atoms.xmacro index 2755d6cb..1f28c014 100644 --- a/include/atoms.xmacro +++ b/include/atoms.xmacro @@ -1,6 +1,7 @@ xmacro(_NET_SUPPORTED) xmacro(_NET_SUPPORTING_WM_CHECK) xmacro(_NET_WM_NAME) +xmacro(_NET_WM_MOVERESIZE) xmacro(_NET_WM_STATE_FULLSCREEN) xmacro(_NET_WM_STATE_DEMANDS_ATTENTION) xmacro(_NET_WM_STATE_MODAL) diff --git a/src/ewmh.c b/src/ewmh.c index 0746a5e1..844a0db9 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -234,5 +234,6 @@ void ewmh_setup_hints(void) { /* I’m not entirely sure if we need to keep _NET_WM_NAME on root. */ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_WM_NAME, A_UTF8_STRING, 8, strlen("i3"), "i3"); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 23, supported_atoms); + /* only send the first 24 atoms (last one is _NET_CLOSE_WINDOW) increment that number when adding supported atoms */ + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A__NET_SUPPORTED, XCB_ATOM_ATOM, 32, 24, supported_atoms); } diff --git a/src/handlers.c b/src/handlers.c index 569a8ec3..27e899ea 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -651,6 +651,19 @@ static void handle_expose_event(xcb_expose_event_t *event) { return; } +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ +#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ + /* * Handle client messages (EWMH) * @@ -856,6 +869,33 @@ static void handle_client_message(xcb_client_message_event_t *event) { } else { DLOG("Couldn't find con for _NET_CLOSE_WINDOW request. (window = %d)\n", event->window); } + } else if (event->type == A__NET_WM_MOVERESIZE) { + /* + * Client-side decorated Gtk3 windows emit this signal when being + * dragged by their GtkHeaderBar + */ + Con *con = con_by_window_id(event->window); + if (!con || !con_is_floating(con)) { + DLOG("Couldn't find con for _NET_WM_MOVERESIZE request, or con not floating (window = %d)\n", event->window); + return; + } + DLOG("Handling _NET_WM_MOVERESIZE request (con = %p)\n", con); + uint32_t direction = event->data.data32[2]; + uint32_t x_root = event->data.data32[0]; + uint32_t y_root = event->data.data32[1]; + /* construct fake xcb_button_press_event_t */ + xcb_button_press_event_t fake = { + .root_x = x_root, + .root_y = y_root, + .event_x = x_root - (con->rect.x), + .event_y = y_root - (con->rect.y)}; + if (direction == _NET_WM_MOVERESIZE_MOVE) { + floating_drag_window(con->parent, &fake); + } else if (direction >= _NET_WM_MOVERESIZE_SIZE_TOPLEFT && direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) { + floating_resize_window(con->parent, FALSE, &fake); + } else { + DLOG("_NET_WM_MOVERESIZE direction %d not implemented\n", direction); + } } else { DLOG("unhandled clientmessage\n"); return; From 74d4e84fa3228decbd0c71529d61ea764b41ba62 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 11 Feb 2015 09:15:01 +0100 Subject: [PATCH 097/103] add a CONTRIBUTING file See https://github.com/blog/1184-contributing-guidelines --- CONTRIBUTING.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..8fff92bd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,19 @@ +# i3status/i3lock bugreports/feature requests + +Note that i3status and i3lock related bugreports and feature requests should be +filed in the corresponding repositories, i.e. https://github.com/i3/i3status +and https://github.com/i3/i3lock + +# i3 bugreports/feature requests + +1. Read http://i3wm.org/docs/debugging.html +2. Make sure you include a link to your logfile in your report (section 3). +3. Make sure you include the i3 version number in your report (section 1). + +# Pull requests + +* Before sending a pull request for new features, please check with us that the + feature is something we want to see in i3 by opening an issue which has + “feature request” or “enhancement” in its title. +* Use `clang-format` to format your code. +* Run the testsuite, see http://i3wm.org/docs/testsuite.html From fbe25297b7921c390bcdac846710667fc04569f8 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 11 Feb 2015 21:13:19 +0100 Subject: [PATCH 098/103] Properly invalidate rendering cache when updating orientation (Thanks hercek) fixes #1445 --- include/con.h | 6 ++++++ src/con.c | 2 +- src/tree.c | 3 +-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/con.h b/include/con.h index 5d761f68..b025adab 100644 --- a/include/con.h +++ b/include/con.h @@ -356,3 +356,9 @@ void con_set_urgency(Con *con, bool urgent); * */ char *con_get_tree_representation(Con *con); + +/** + * force parent split containers to be redrawn + * + */ +void con_force_split_parents_redraw(Con *con); diff --git a/src/con.c b/src/con.c index 38ea0585..4957c303 100644 --- a/src/con.c +++ b/src/con.c @@ -20,7 +20,7 @@ static void con_on_remove_child(Con *con); * force parent split containers to be redrawn * */ -static void con_force_split_parents_redraw(Con *con) { +void con_force_split_parents_redraw(Con *con) { Con *parent = con; while (parent && parent->type != CT_WORKSPACE && parent->type != CT_DOCKAREA) { diff --git a/src/tree.c b/src/tree.c index a6b15122..e99c5063 100644 --- a/src/tree.c +++ b/src/tree.c @@ -407,8 +407,7 @@ void tree_split(Con *con, orientation_t orientation) { Con *parent = con->parent; /* Force re-rendering to make the indicator border visible. */ - FREE(con->deco_render_params); - FREE(parent->deco_render_params); + con_force_split_parents_redraw(con); /* if we are in a container whose parent contains only one * child (its split functionality is unused so far), we just change the From 726bb97ce3fba333979908616c370778f570884d Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Thu, 12 Feb 2015 08:59:33 +0100 Subject: [PATCH 099/103] CONTRIBUTING: document people should use the `next` branch --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fff92bd..7a5e0c5f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,5 +15,6 @@ and https://github.com/i3/i3lock * Before sending a pull request for new features, please check with us that the feature is something we want to see in i3 by opening an issue which has “feature request” or “enhancement” in its title. +* Use the `next` branch for developing and sending your pull request. * Use `clang-format` to format your code. * Run the testsuite, see http://i3wm.org/docs/testsuite.html From e18e2b9f988f42b0a858b634fa997289418e544a Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Thu, 12 Feb 2015 14:45:34 -0500 Subject: [PATCH 100/103] i3bar: use Pango markup Parse text within workspace buttons and the i3bar statusline as Pango markup. This lets people specify things like font weight, text color, background color, font size, and font family in the text of i3bar. fixes #1468 --- docs/i3bar-protocol | 3 ++- docs/userguide | 5 +++++ i3bar/src/child.c | 6 +++--- i3bar/src/workspaces.c | 6 +++--- include/libi3.h | 18 ++++++++++++++++++ libi3/font.c | 24 +++++++++++++++++------- libi3/string.c | 35 +++++++++++++++++++++++++++++++++++ 7 files changed, 83 insertions(+), 14 deletions(-) diff --git a/docs/i3bar-protocol b/docs/i3bar-protocol index 0ca2fd82..8fd51ae9 100644 --- a/docs/i3bar-protocol +++ b/docs/i3bar-protocol @@ -119,7 +119,8 @@ click_events:: full_text:: The most simple block you can think of is one which just includes the only required key, the +full_text+ key. i3bar will display the string - value and that’s it. + value parsed as + https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup]. short_text:: Where appropriate, the +short_text+ (string) entry should also be provided. It will be used in case the status line needs to be shortened diff --git a/docs/userguide b/docs/userguide index 4b4dacc8..45b05a06 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1625,6 +1625,10 @@ container to the next/previous workspace and +move container to workspace curren See <> for how to move a container/workspace to a different RandR output. +Workspace names are parsed as +https://developer.gnome.org/pango/stable/PangoMarkupFormat.html[Pango markup] +by i3bar. + [[back_and_forth]] To switch back to the previously focused workspace, use +workspace back_and_forth+; likewise, you can move containers to the previously focused @@ -1646,6 +1650,7 @@ move [window|container] [to] workspace ------------------------- bindsym $mod+1 workspace 1 bindsym $mod+2 workspace 2 +bindsym $mod+3 workspace 3:vim ... bindsym $mod+Shift+1 move container to workspace 1 diff --git a/i3bar/src/child.c b/i3bar/src/child.c index d0f0c5fb..2f7dd76e 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -182,7 +182,7 @@ static int stdin_boolean(void *context, int val) { static int stdin_string(void *context, const unsigned char *val, size_t len) { parser_ctx *ctx = context; if (strcasecmp(ctx->last_map_key, "full_text") == 0) { - ctx->block.full_text = i3string_from_utf8_with_length((const char *)val, len); + ctx->block.full_text = i3string_from_markup_with_length((const char *)val, len); } if (strcasecmp(ctx->last_map_key, "color") == 0) { sasprintf(&(ctx->block.color), "%.*s", len, val); @@ -196,7 +196,7 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) { ctx->block.align = ALIGN_LEFT; } } else if (strcasecmp(ctx->last_map_key, "min_width") == 0) { - i3String *text = i3string_from_utf8_with_length((const char *)val, len); + i3String *text = i3string_from_markup_with_length((const char *)val, len); ctx->block.min_width = (uint32_t)predict_text_width(text); i3string_free(text); } @@ -304,7 +304,7 @@ static void read_flat_input(char *buffer, int length) { buffer[length - 1] = '\0'; else buffer[length] = '\0'; - first->full_text = i3string_from_utf8(buffer); + first->full_text = i3string_from_markup(buffer); } static bool read_json_input(unsigned char *input, int length) { diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index e8184d40..45b511ff 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -123,12 +123,12 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t /* Offset may be equal to length, in which case display the number */ params->workspaces_walk->name = (offset < len - ? i3string_from_utf8_with_length(ws_name + offset, len - offset) - : i3string_from_utf8(ws_num)); + ? i3string_from_markup_with_length(ws_name + offset, len - offset) + : i3string_from_markup(ws_num)); } else { /* Default case: just save the name */ - params->workspaces_walk->name = i3string_from_utf8_with_length(ws_name, len); + params->workspaces_walk->name = i3string_from_markup_with_length(ws_name, len); } /* Save its rendered width */ diff --git a/include/libi3.h b/include/libi3.h index 7a2bdf58..c1a11dfc 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -141,6 +141,12 @@ int sasprintf(char **strp, const char *fmt, ...); */ i3String *i3string_from_utf8(const char *from_utf8); +/** + * Build an i3String from an UTF-8 encoded string in Pango markup. + * + */ +i3String *i3string_from_markup(const char *from_markup); + /** * Build an i3String from an UTF-8 encoded string with fixed length. * To be used when no proper NUL-terminaison is available. @@ -149,6 +155,13 @@ i3String *i3string_from_utf8(const char *from_utf8); */ i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes); +/** + * Build an i3String from an UTF-8 encoded string in Pango markup with fixed + * length. + * + */ +i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes); + /** * Build an i3String from an UCS-2 encoded string. * Returns the newly-allocated i3String. @@ -193,6 +206,11 @@ const xcb_char2b_t *i3string_as_ucs2(i3String *str); */ size_t i3string_get_num_bytes(i3String *str); +/** + * Whether the given i3String is in Pango markup. + */ +bool i3string_is_markup(i3String *str); + /** * Returns the number of glyphs in an i3String. * diff --git a/libi3/font.c b/libi3/font.c index a338f975..f2a7e1fb 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -102,7 +102,8 @@ static bool load_pango_font(i3Font *font, const char *desc) { * */ static void draw_text_pango(const char *text, size_t text_len, - xcb_drawable_t drawable, int x, int y, int max_width) { + xcb_drawable_t drawable, int x, int y, + int max_width, bool is_markup) { /* Create the Pango layout */ /* root_visual_type is cached in load_pango_font */ cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, @@ -116,7 +117,10 @@ static void draw_text_pango(const char *text, size_t text_len, pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - pango_layout_set_text(layout, text, text_len); + if (is_markup) + pango_layout_set_markup(layout, text, text_len); + else + pango_layout_set_text(layout, text, text_len); /* Do the drawing */ cairo_set_source_rgb(cr, pango_font_red, pango_font_green, pango_font_blue); @@ -135,7 +139,7 @@ static void draw_text_pango(const char *text, size_t text_len, * Calculate the text width using Pango rendering. * */ -static int predict_text_width_pango(const char *text, size_t text_len) { +static int predict_text_width_pango(const char *text, size_t text_len, bool is_markup) { /* Create a dummy Pango layout */ /* root_visual_type is cached in load_pango_font */ cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1); @@ -145,7 +149,12 @@ static int predict_text_width_pango(const char *text, size_t text_len) { /* Get the font width */ gint width; pango_layout_set_font_description(layout, savedFont->specific.pango_desc); - pango_layout_set_text(layout, text, text_len); + + if (is_markup) + pango_layout_set_markup(layout, text, text_len); + else + pango_layout_set_text(layout, text, text_len); + pango_cairo_update_layout(cr, layout); pango_layout_get_pixel_size(layout, &width, NULL); @@ -383,7 +392,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable, case FONT_TYPE_PANGO: /* Render the text using Pango */ draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), - drawable, x, y, max_width); + drawable, x, y, max_width, i3string_is_markup(text)); return; #endif default: @@ -422,7 +431,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, case FONT_TYPE_PANGO: /* Render the text using Pango */ draw_text_pango(text, strlen(text), - drawable, x, y, max_width); + drawable, x, y, max_width, false); return; #endif default: @@ -518,7 +527,8 @@ int predict_text_width(i3String *text) { #if PANGO_SUPPORT case FONT_TYPE_PANGO: /* Calculate extents using Pango */ - return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text)); + return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), + i3string_is_markup(text)); #endif default: assert(false); diff --git a/libi3/string.c b/libi3/string.c index 009312d6..afeca974 100644 --- a/libi3/string.c +++ b/libi3/string.c @@ -20,6 +20,7 @@ struct _i3String { xcb_char2b_t *ucs2; size_t num_glyphs; size_t num_bytes; + bool is_markup; }; /* @@ -39,6 +40,19 @@ i3String *i3string_from_utf8(const char *from_utf8) { return str; } +/* + * Build an i3String from an UTF-8 encoded string in Pango markup. + * + */ +i3String *i3string_from_markup(const char *from_markup) { + i3String *str = i3string_from_utf8(from_markup); + + /* Set the markup flag */ + str->is_markup = true; + + return str; +} + /* * Build an i3String from an UTF-8 encoded string with fixed length. * To be used when no proper NUL-terminaison is available. @@ -59,6 +73,20 @@ i3String *i3string_from_utf8_with_length(const char *from_utf8, size_t num_bytes return str; } +/* + * Build an i3String from an UTF-8 encoded string in Pango markup with fixed + * length. + * + */ +i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_bytes) { + i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes); + + /* set the markup flag */ + str->is_markup = true; + + return str; +} + /* * Build an i3String from an UCS-2 encoded string. * Returns the newly-allocated i3String. @@ -133,6 +161,13 @@ size_t i3string_get_num_bytes(i3String *str) { return str->num_bytes; } +/* + * Whether the given i3String is in Pango markup. + */ +bool i3string_is_markup(i3String *str) { + return str->is_markup; +} + /* * Returns the number of glyphs in an i3String. * From bb6823f5c770e35ef7c75dc443662aa5fb5fd77f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 15 Feb 2015 13:42:29 +0100 Subject: [PATCH 101/103] debian: add changelog entry for 4.8-2 --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index 5e3d174b..02fb0e53 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,14 @@ i3-wm (4.8.1-1) unstable; urgency=medium -- Michael Stapelberg Sun, 15 Jun 2014 19:37:32 +0200 +i3-wm (4.8-2) unstable; urgency=medium + + * Backport two bugfixes: + - backport-dpi-fix.patch (Closes: #778460) + - backport-i3bar-tray-fix.patch (Closes: #778461) + + -- Michael Stapelberg Sun, 15 Feb 2015 13:24:42 +0100 + i3-wm (4.8-1) unstable; urgency=medium * New upstream release. From 05de5201c006043ecd3d5a816d8835f7ae9d9157 Mon Sep 17 00:00:00 2001 From: Tony Crisci Date: Sat, 21 Feb 2015 16:36:24 -0500 Subject: [PATCH 102/103] Docs: add hint to use xvfb-run Document that Xvfb can be used to run tests without an X server, and be used to significantly speed up tests on machines with slow video cards. --- docs/testsuite | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/testsuite b/docs/testsuite index 61a72219..29a35218 100644 --- a/docs/testsuite +++ b/docs/testsuite @@ -107,6 +107,11 @@ containing the appropriate i3 logfile for each testcase. The latest folder can always be found under the symlink +latest/+. Unless told differently, it will run the tests on a separate X server instance (using Xephyr). +Xephyr will open a window where you can inspect the running test. You can run +the tests without an X session with Xvfb, such as with +xvfb-run +./complete-run+. This will also speed up the tests signficantly especially on +machines without a powerful video card. + .Example invocation of complete-run.pl+ --------------------------------------- $ cd ~/i3/testcases From 55ba1c5e56ccddc901a8afd653fa1710035a2884 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 28 Feb 2015 14:36:22 +0100 Subject: [PATCH 103/103] release i3 v4.9 --- RELEASE-NOTES-4.8 | 138 ---------------------------------------------- RELEASE-NOTES-4.9 | 124 +++++++++++++++++++++++++++++++++++++++++ man/asciidoc.conf | 2 +- 3 files changed, 125 insertions(+), 139 deletions(-) delete mode 100644 RELEASE-NOTES-4.8 create mode 100644 RELEASE-NOTES-4.9 diff --git a/RELEASE-NOTES-4.8 b/RELEASE-NOTES-4.8 deleted file mode 100644 index f0f5923f..00000000 --- a/RELEASE-NOTES-4.8 +++ /dev/null @@ -1,138 +0,0 @@ - - ┌──────────────────────────────┐ - │ Release notes for i3 v4.8 │ - └──────────────────────────────┘ - -This is i3 v4.8. This version is considered stable. All users of i3 are -strongly encouraged to upgrade. - -The biggest new feature certainly is layout saving/restoring. See -http://i3wm.org/docs/layout-saving.html for more details. tl;dr: export your -current layout as JSON file, load it into new i3 sessions, get placeholder -windows that will be replaced by the actual apps once you start them. - -Also very important for owners of HiDPI/“retina” displays is that i3 will now -respect your configured DPI and scale up its UI elements accordingly. Use -“xrandr --dpi 184” to set your dpi to 184, in case your setup does not figure -it out automatically. To get properly scaling fonts, we also changed the -default font from a bitmap font to a pango font (“DejaVu Sans Mono 8”). - -Multiple changes improve the compatibility of i3 with other software, e.g. -java-based software (focus handling, once again) or external pagers (we now -provide _NET_CLIENT_LIST and let pager applications change workspaces). - -For packagers, another change is that yajl ≥ 2.0 is now required for compiling -i3. This should not be a problem for anyone, as that version is pretty old by -now. - -For contributors, note that we have starting formatting the source code with -clang-format-3.5. This means that there will no longer be a need to argue about -coding style when discussing patches :). - - ┌────────────────────────────┐ - │ Changes in v4.8 │ - └────────────────────────────┘ - - • docs/ipc: reformat/update list of ipc libraries - • docs/ipc: fix current_workspace outputs reply member - • docs/ipc: update ipc COMMAND reply docs - • docs/userguide: fix multiple typos - • docs/debugging: use bzip2 - • docs/debugging: explain how to enable logging on the fly - • docs/debugging: merge the debug symbols/backtrace section - • docs/debugging: recommend i3 --moreversion - • man/i3-nagbar.man: update manpage to document all options - • i3bar: Amend status line error 127 message - • i3bar: don’t kill watcher on EOF, leads to better error messages - • i3bar: send mouse wheel events to child too - • i3bar: do click handling and tray padding retina-correctly - • i3bar: render separators render-correctly - • i3bar: reinit colors on barconfig update - • i3bar: Don't start child unless status_command - • i3bar: implement custom workspace numbers config - • resize floating windows when right-clicking the decoration - • enable shmlog when invoked as i3-with-shmlog - • Disable pointer warps when focus_follows_mouse is disabled - • Movement into a branch considers movement direction - • set ewmh desktop properties on startup - • handle ButtonPress events with child != XCB_NONE - • implement layout restoring - • only LOG() the DPI when it changes, DLOG() it otherwise - • send IPC window events for focus and title changes - • these types of windows are now floating by default: - dialog, utility, toolbar and splash windows, modal windows, windows with an - equal minimum and maximum size - • send last event timestamp with WM_TAKE_FOCUS message - • maintain the _NET_CLIENT_LIST property - • don’t set input focus _and_ send WM_TAKE_FOCUS - • respect CFLAGS in linking command - • fix parallel make - • reset SIGPIPE handler before executing a command - • render default window border width retina-correctly - • draw workspace buttons and padded text blocks retina-correctly - • render resize windows retina-correctly - • delegate click handling to dock clients - • send complete config on barconfig_update - • implement the window::fullscreen_mode ipc event - • make all workspaces starting with "__" internal - • improve error messages for i3-internal workspace names - • allow _NET_ACTIVE_WINDOW requests to switch workspaces if they indicate - that they are a pager (following the spec) - • workspace assignments by number - • add configuration option for disabling mouse warping - • set _NET_ACTIVE_WINDOW to None when none has focus - • set X-LightDM-DesktopName in i3.xsession.desktop to fix autostart on Ubuntu - • don’t ELOG ipc EOF - • replace all printf()s with D?LOG - • delete ipc socket when exiting, cleanup tmpdir - • default config: switch to DejaVu Sans Mono 8 as default font - • cleanup tmpdir when restarting and not using XDG_RUNTIME_DIR - • Snap pointer to resize bar on drag resize - • Size resizebar according to container size - • Fix clang -Wextra except -Wunused-parameter - • Respect Motif hint for window decorations - - ┌────────────────────────────┐ - │ Bugfixes │ - └────────────────────────────┘ - - • create con pixmaps when not needed - • i3bar: fix resource leak: statusline_ctx needs to be freed first - • tree_split should not split floating cons - • fix memory leak with ipc_receive_message - • fix invalid reads by setting con->window to NULL in tree_close - • fix memory leak when closing windows - • fix memory leak when matching window by criteria - • fix memory leak when matching window by con_id - • ignore dock clients in the resize command - • clear wm_size_hints if they are not set - • resize window check should check for NULL - • fix window event crash with no window - • i3-dmenu-desktop: also quote the %c field code - • new_window and new_float can now be used simultaneously with different - border widths - • fix crash when using multiple for_window statements that move windows - • Set input focus with last timestamp - • handle windows whose WM_TRANSIENT_FOR points to themselve - • don’t overwrite the original size of floating windows when changing border - • don’t errnously render floating fullscreen windows during restart - • ensure floating windows don’t drop out of fullscreen when restarting - • don’t overwrite the window’s geometry after restartingnext - • i3bar: Set `mapped` flag on trayclient creation - • i3bar: don't show "EOF" status line error - - ┌────────────────────────────┐ - │ Thanks! │ - └────────────────────────────┘ - -Thanks for testing, bugfixes, discussions and everything I forgot go out to: - -Aleksi Blinnikka, Alexander Berntsen, Alexander Kedrik, Antonio, Arun -Persaud, Atte Peltomaki, bo, Campbell Barton, chris, David Coppa, eeemsi, -Holger Langenau, Jean-Philippe Ouellet, Jens, jeroentbt, Jonas Maaskola, -Julian Ospald, Kernc, Koston, lasers, lkraav, Marcin, Marco Hunsicker, -Marcus Crestani, Matthias Thubauville, Maxime, Michael Stapelberg, Peter -Boström, Petr Písař, Quentin Glidic, Steve Jones, TonyC, Tony Crisci, -Vivien Didelot, Wieland Hoffmann, x33a, xeen - --- Michael Stapelberg, 2014-06-15 diff --git a/RELEASE-NOTES-4.9 b/RELEASE-NOTES-4.9 new file mode 100644 index 00000000..9012646a --- /dev/null +++ b/RELEASE-NOTES-4.9 @@ -0,0 +1,124 @@ + + ┌──────────────────────────────┐ + │ Release notes for i3 v4.9 │ + └──────────────────────────────┘ + +This is i3 v4.9. This version is considered stable. All users of i3 are +strongly encouraged to upgrade. + +Notable new features include mouse button bindings and improved EWMH +compatibility, meaning more external pager programs work with i3 now. + +Aside from that, this release contains plenty of bugfixes and little +enhancements. + +The new dependency on libxkbcommon ≥ 0.4.0 is notable for distribution +packages. This dependency allowed us to drop our last direct dependency +on Xlib :). + +It’s also worth mentioning that all i3 repositories are now on GitHub, see +http://thread.gmane.org/gmane.comp.window-managers.i3.general/1666 for the +announcement. + + ┌────────────────────────────┐ + │ Changes in v4.9 │ + └────────────────────────────┘ + + • docs/ipc: use an actual event type + • docs/debugging: use logs.i3wm.org + • docs/testsuite: add hint to use xvfb-run + • testcases: use Xephyr instead of XDummy + • i3-sensible-*: use command -v (built-in) instead of which(1) + • i3.xsession.desktop: set DesktopNames (which gdm uses) + • i3-save-tree: interpret commandline parameters as utf-8 + • i3-save-tree: add 'mark' as allowed key to i3-save-tree output + • i3bar-protocol: ensure align = left is the default + • i3bar: implement custom mouse wheel commands + • i3bar: improve error message when a full_text property is missing + • i3bar: respect the urgency flag on status blocks + • i3bar: inset the urgent background of a status block for consistency with + workspace buttons + • i3bar: suspend the child when bars are fully obscured + • i3bar: use Pango markup + • ipc: implement the window::close event + • ipc: implement the window::move event + • ipc: implement the window::floating event + • ipc: implement the window::urgent event + • ipc: set ws reply "num" member to -1 when named + • ipc: add deco_rect property to con in ipc response + • ipc: include workspace con in workspace event + • ewmh: implement property _NET_NUMBER_OF_DESKTOPS + • ewmh: implement property _NET_DESKTOP_VIEWPORT + • ewmh: implement property _NET_DESKTOP_NAMES + • ewmh: handle _NET_CURRENT_DESKTOP requests + • ewmh: handle _NET_CLOSE_WINDOW requests + • ewmh: handle _NET_WM_MOVERESIZE requests + • implement mouse bindings (e.g. bindsym button3 kill) + • add mouse binding --whole-window flag + • add mouse binding --release flag + • switch to xcb-xkb and libxkbcommon, removing our last direct Xlib dep + • make “move [direction]” work with criteria + • make “move to position” work with criteria + • “workspace ” and “move to workspace ” now look for a workspace + starting with number (unless there is a workspace exactly matching that + number). I.e., “workspace 4” will go to a workspace called “4: www” unless + you have a workspace “4” + • “focus ” now focuses floating containers when there are no + tiling containers on the destination output + • take the motif border into account when calculating floating window + geometry + • revert “Disable pointer warps when focus_follows_mouse is disabled” as it + was unexpected by a number of users. Sorry for the back-and-forth + • handle WM_CLASS changes + • raise floating windows on “focus ” + • align lower line of bar decoration to border width + • parse tray_output as a word, not string + • allow to validate the config file without X + • do not resend focus on click, fixes compatibility problems with some wine + or mono apps (e.g. Office 2010) + • don't draw borders wider than actual width + • prevent workspace change during global fullscreen + • extend the fullscreen command (fullscreen [global]) + • fix start_application() doc about which shell is used + + ┌────────────────────────────┐ + │ Bugfixes │ + └────────────────────────────┘ + + • i3-dmenu-desktop: quote path + • i3bar: fix a double free when changing color configuration + • i3bar: render bars after the first chunk of JSON + • i3bar: add a sync call to confirm reparents before exiting (fixes tray + restart issues) + • i3bar: correctly calculate clicks on i3bar status blocks + • i3bar: make click events on status blocks work with 'workspace_buttons no' + • retina support: convert logical to physical pixels for default_border_width + • retina support: treat everything up to 120 dpi as 96 dpi + • don’t set input focus if not accepted (fixes problems with xfce4-notifyd) + • don’t focus unmapped container on manage + • create the directory for storing the restart state + • avoid changing border width when changing containers from tiling to + floating + • layout saving: properly restore workspace containers + • rerender the decoration when the container requires a pixmap and doesn’t + have one + • don’t set focus in con_set_layout() on invisible workspaces + • properly handle windows unsetting WM_TRANSIENT_FOR + • use the command parser to properly extract workspace names + • copy binding before run (fixes reloads) + • revert "Bugfix: Set input focus with last timestamp" + • render floating windows during global fullscreen + • actually parse client.placeholder + + ┌────────────────────────────┐ + │ Thanks! │ + └────────────────────────────┘ + +Thanks for testing, bugfixes, discussions and everything I forgot go out to: + + Alexander Monakov, aszlig, cornerman, dmurph, Mats, dsargrad, hercek, hjem, + Ingo, Ingo Bürk, Janus, javier, jefvel, Lukas K, Marein Konings, Mats, + Michael Stapelberg, Mii, nikolaus, okraits, Peter, smlb, sur5r, Tony Crisci, + val, vals, xeen, Yves-Alexis + +-- Michael Stapelberg, 2015-02-28 diff --git a/man/asciidoc.conf b/man/asciidoc.conf index 84b6d52c..f358e81b 100644 --- a/man/asciidoc.conf +++ b/man/asciidoc.conf @@ -7,7 +7,7 @@ template::[header-declarations] {mantitle} {manvolnum} i3 -4.8 +4.9 i3 Manual