]> git.sur5r.net Git - i3/i3/commitdiff
Introduce support for specifying variables from X resources. (#2286)
authorIngo Bürk <admin@airblader.de>
Sun, 8 May 2016 10:55:27 +0000 (12:55 +0200)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Sun, 8 May 2016 10:55:27 +0000 (12:55 +0200)
This patch introduces a new 'set_from_resource' config directive which
allows defining a variable by retrieving its value from the X resource
database. This avoids having to configure a color scheme in multiple
files. The directive takes an additional fallback value which is used
in case the resource cannot be found or during config validation where
no X connection is available.

Furthermore, this patch includes the following changes:
- If the same variable is defined twice, we now properly overwrite the
  value of the assignment rather than inserting two variable definitions
  with the same key.
- We now depend on xcb-util-xrm to query the resource.
- Increase the buffer size for variable / resource assignments.

fixes #2130

12 files changed:
DEPENDS
common.mk
debian/control
docs/userguide
include/config_parser.h
parser-specs/config.spec
src/config_parser.c
src/i3.mk
testcases/t/201-config-parser.t
testcases/t/532-xresources.t [new file with mode: 0644]
travis/travis-base-ubuntu-386.Dockerfile
travis/travis-base-ubuntu.Dockerfile

diff --git a/DEPENDS b/DEPENDS
index fad11da54e9dcd12578e0483651533662818b71b..227aaec373770f2c8c68c714a413773499bbee78 100644 (file)
--- a/DEPENDS
+++ b/DEPENDS
@@ -4,28 +4,29 @@
    "min" means minimum required version
    "lkgv" means last known good version
 
    "min" means minimum required version
    "lkgv" means last known good version
 
-┌──────────────┬────────┬────────┬────────────────────────────────────────┐
-│ dependency   │ min.   │ lkgv   │ URL                                    │
-├──────────────┼────────┼────────┼────────────────────────────────────────┤
-│ pkg-config   │ 0.25   │ 0.28   │ http://pkgconfig.freedesktop.org/      │
-│ libxcb       │ 1.1.93 │ 1.11   │ http://xcb.freedesktop.org/dist/       │
-│ xcb-util     │ 0.3.3  │ 0.4.1  │ http://xcb.freedesktop.org/dist/       │
-│ xkbcommon    │ 0.4.0  │ 0.5.0  │ http://xkbcommon.org/                  │
-│ xkbcommon-x11│ 0.4.0  │ 0.5.0  │ http://xkbcommon.org/                  │
-│ util-cursor³⁴│ 0.0.99 │ 0.1.2  │ http://xcb.freedesktop.org/dist/       │
-│ util-wm⁴     │ 0.3.8  │ 0.3.8  │ http://xcb.freedesktop.org/dist/       │
-│ util-keysyms⁴│ 0.3.8  │ 0.4.0  │ http://xcb.freedesktop.org/dist/       │
-│ libev        │ 4.0    │ 4.19   │ http://libev.schmorp.de/               │
-│ yajl         │ 2.0.1  │ 2.1.0  │ http://lloyd.github.com/yajl/          │
-│ asciidoc     │ 8.3.0  │ 8.6.8  │ 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/     │
-│ PCRE         │ 8.12   │ 8.35   │ http://www.pcre.org/                   │
-│ libsn¹       │ 0.10   │ 0.12   │ http://freedesktop.org/wiki/Software/startup-notification
-│ pango        │ 1.30.0 | 1.36.8 │ http://www.pango.org/                  │
-│ cairo        │ 1.14.4 │ 1.14.4 │ http://cairographics.org/              │
-└──────────────┴────────┴────────┴────────────────────────────────────────┘
+┌──────────────┬────────┬────────┬───────────────────────────────────────────────────────────┐
+│ dependency   │ min.   │ lkgv   │ URL                                                       │
+├──────────────┼────────┼────────┼───────────────────────────────────────────────────────────┤
+│ pkg-config   │ 0.25   │ 0.28   │ http://pkgconfig.freedesktop.org/                         │
+│ libxcb       │ 1.1.93 │ 1.11   │ http://xcb.freedesktop.org/dist/                          │
+│ xcb-util     │ 0.3.3  │ 0.4.1  │ http://xcb.freedesktop.org/dist/                          │
+│ xkbcommon    │ 0.4.0  │ 0.5.0  │ http://xkbcommon.org/                                     │
+│ xkbcommon-x11│ 0.4.0  │ 0.5.0  │ http://xkbcommon.org/                                     │
+│ util-cursor³⁴│ 0.0.99 │ 0.1.2  │ http://xcb.freedesktop.org/dist/                          │
+│ util-wm⁴     │ 0.3.8  │ 0.3.8  │ http://xcb.freedesktop.org/dist/                          │
+│ util-keysyms⁴│ 0.3.8  │ 0.4.0  │ http://xcb.freedesktop.org/dist/                          │
+│ util-xrm⁴    │ 1.0.0  │ 1.0.0  │ https://github.com/Airblader/xcb-util-xrm                 │
+│ libev        │ 4.0    │ 4.19   │ http://libev.schmorp.de/                                  │
+│ yajl         │ 2.0.1  │ 2.1.0  │ http://lloyd.github.com/yajl/                             │
+│ asciidoc     │ 8.3.0  │ 8.6.8  │ 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/                        │
+│ PCRE         │ 8.12   │ 8.35   │ http://www.pcre.org/                                      │
+│ libsn¹       │ 0.10   │ 0.12   │ http://freedesktop.org/wiki/Software/startup-notification │
+│ pango        │ 1.30.0 | 1.36.8 │ http://www.pango.org/                                     │
+│ cairo        │ 1.14.4 │ 1.14.4 │ 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
  ¹ 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
index 1e738b045530eca66c5ee62df869bdc6b7024fe4..0215d35b1375584ac6d797252a1c374aed73001c 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -126,6 +126,10 @@ 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)
 
 XKB_COMMON_X11_CFLAGS := $(call cflags_for_lib, xkbcommon-x11,xkbcommon-x11)
 XKB_COMMON_X11_LIBS := $(call ldflags_for_lib, xkbcommon-x11,xkbcommon-x11)
 
+# XCB xrm
+XCB_XRM_CFLAGS := $(call cflags_for_lib, xcb-xrm)
+XCB_XRM_LIBS   := $(call ldflags_for_lib, xcb-xrm,xcb-xrm)
+
 # yajl
 YAJL_CFLAGS := $(call cflags_for_lib, yajl)
 YAJL_LIBS   := $(call ldflags_for_lib, yajl,yajl)
 # yajl
 YAJL_CFLAGS := $(call cflags_for_lib, yajl)
 YAJL_LIBS   := $(call ldflags_for_lib, yajl,yajl)
index 577bb4d4de269471cda506053ae2f24554ad8990..6b1530b8ccfe3443f68c008c0f3086a519225303 100644 (file)
@@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 9),
                libxcb-randr0-dev,
                libxcb-icccm4-dev,
                libxcb-cursor-dev,
                libxcb-randr0-dev,
                libxcb-icccm4-dev,
                libxcb-cursor-dev,
+               libxcb-xrm-dev,
                libxcb-xkb-dev,
                libxkbcommon-dev (>= 0.4.0),
                libxkbcommon-x11-dev (>= 0.4.0),
                libxcb-xkb-dev,
                libxkbcommon-dev (>= 0.4.0),
                libxkbcommon-x11-dev (>= 0.4.0),
index d0ac2ddb7184f6ec1ee326a9eccf8a600ebffdca..d9cfc642b99b47d04f4633a3a4cc76f880e1d94e 100644 (file)
@@ -700,6 +700,38 @@ absolutely no plans to change this. If you need a more dynamic configuration
 you should create a little script which generates a configuration file and run
 it before starting i3 (for example in your +~/.xsession+ file).
 
 you should create a little script which generates a configuration file and run
 it before starting i3 (for example in your +~/.xsession+ file).
 
+Also see <<xresources>> to learn how to create variables based on resources
+loaded from the X resource database.
+
+[[xresources]]
+=== X resources
+
+<<variables>> can also be created using a value configured in the X resource
+database. This is useful, for example, to avoid configuring color values within
+the i3 configuration. Instead, the values can be configured, once, in the X
+resource database to achieve an easily maintainable, consistent color theme
+across many X applications.
+
+Defining a resource will load this resource from the resource database and
+assign its value to the specified variable. A fallback must be specified in
+case the resource cannot be loaded from the database.
+
+*Syntax*:
+----------------------------------------------------
+set_from_resource $<name> <resource_name> <fallback>
+----------------------------------------------------
+
+*Example*:
+----------------------------------------------------------------------------
+# The ~/.Xresources should contain a line such as
+#     *color0: #121212
+# and must be loaded properly, e.g., by using
+#     xrdb ~/.Xresources
+# This value is picked up on by other applications (e.g., the URxvt terminal
+# emulator) and can be used in i3 like this:
+set_from_resource $black i3wm.color0 #000000
+----------------------------------------------------------------------------
+
 [[assign_workspace]]
 === Automatically putting clients on specific workspaces
 
 [[assign_workspace]]
 === Automatically putting clients on specific workspaces
 
index 9c23a11df56be737c559087da84a81bc471cd96c..2ba79a68b8fa0f338a70b7f0919061f77fe839e1 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <yajl/yajl_gen.h>
 
 
 #include <yajl/yajl_gen.h>
 
+SLIST_HEAD(variables_head, Variable);
 extern pid_t config_error_nagbar_pid;
 
 /*
 extern pid_t config_error_nagbar_pid;
 
 /*
index ef3bc2e0e4eeb69834057385d4fd03fb1646400c..24f40802b8dac4e8e6edab16bd6beffe5389d860 100644 (file)
@@ -18,6 +18,7 @@ state INITIAL:
   error ->
   '#'                                      -> IGNORE_LINE
   'set'                                    -> IGNORE_LINE
   error ->
   '#'                                      -> IGNORE_LINE
   'set'                                    -> IGNORE_LINE
+  'set_from_resource'                      -> IGNORE_LINE
   bindtype = 'bindsym', 'bindcode', 'bind' -> BINDING
   'bar'                                    -> BARBRACE
   'font'                                   -> FONT
   bindtype = 'bindsym', 'bindcode', 'bind' -> BINDING
   'bar'                                    -> BARBRACE
   'font'                                   -> FONT
index b197eb223e981186982391d6d6607a1cff728857..5b5e236303626796e4baa2d7287a085ef099e3b5 100644 (file)
@@ -35,6 +35,7 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <xcb/xcb_xrm.h>
 
 #include "all.h"
 
 
 #include "all.h"
 
@@ -42,6 +43,8 @@
 #define y(x, ...) yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__)
 #define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str))
 
 #define y(x, ...) yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__)
 #define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str))
 
+xcb_xrm_database_t *database = NULL;
+
 #ifndef TEST_PARSER
 pid_t config_error_nagbar_pid = -1;
 static struct context *context;
 #ifndef TEST_PARSER
 pid_t config_error_nagbar_pid = -1;
 static struct context *context;
@@ -811,18 +814,80 @@ void start_config_error_nagbar(const char *configpath, bool has_errors) {
     free(pageraction);
 }
 
     free(pageraction);
 }
 
+/*
+ * Inserts or updates a variable assignment depending on whether it already exists.
+ *
+ */
+static void upsert_variable(struct variables_head *variables, char *key, char *value) {
+    struct Variable *current;
+    SLIST_FOREACH(current, variables, variables) {
+        if (strcmp(current->key, key) != 0) {
+            continue;
+        }
+
+        DLOG("Updated variable: %s = %s -> %s\n", key, current->value, value);
+        FREE(current->value);
+        current->value = sstrdup(value);
+        return;
+    }
+
+    DLOG("Defined new variable: %s = %s\n", key, value);
+    struct Variable *new = scalloc(1, sizeof(struct Variable));
+    struct Variable *test = NULL, *loc = NULL;
+    new->key = sstrdup(key);
+    new->value = sstrdup(value);
+    /* ensure that the correct variable is matched in case of one being
+     * the prefix of another */
+    SLIST_FOREACH(test, variables, variables) {
+        if (strlen(new->key) >= strlen(test->key))
+            break;
+        loc = test;
+    }
+
+    if (loc == NULL) {
+        SLIST_INSERT_HEAD(variables, new, variables);
+    } else {
+        SLIST_INSERT_AFTER(loc, new, variables);
+    }
+}
+
+static char *get_resource(char *name) {
+    if (conn == NULL) {
+        return NULL;
+    }
+
+    /* Load the resource database lazily. */
+    if (database == NULL) {
+        database = xcb_xrm_database_from_default(conn);
+
+        if (database == NULL) {
+            ELOG("Failed to open the resource database.\n");
+
+            /* Load an empty database so we don't keep trying to load the
+             * default database over and over again. */
+            database = xcb_xrm_database_from_string("");
+
+            return NULL;
+        }
+    }
+
+    char *resource;
+    xcb_xrm_resource_get_string(database, name, NULL, &resource);
+    return resource;
+}
+
 /*
  * Parses the given file by first replacing the variables, then calling
  * parse_config and possibly launching i3-nagbar.
  *
  */
 bool parse_file(const char *f, bool use_nagbar) {
 /*
  * Parses the given file by first replacing the variables, then calling
  * parse_config and possibly launching i3-nagbar.
  *
  */
 bool parse_file(const char *f, bool use_nagbar) {
-    SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
+    struct variables_head variables = SLIST_HEAD_INITIALIZER(&variables);
     int fd;
     struct stat stbuf;
     char *buf;
     FILE *fstr;
     int fd;
     struct stat stbuf;
     char *buf;
     FILE *fstr;
-    char buffer[4096], key[512], value[512], *continuation = NULL;
+    char buffer[4096], key[512], value[4096], *continuation = NULL;
 
     if ((fd = open(f, O_RDONLY)) == -1)
         die("Could not open configuration file: %s\n", strerror(errno));
 
     if ((fd = open(f, O_RDONLY)) == -1)
         die("Could not open configuration file: %s\n", strerror(errno));
@@ -848,8 +913,9 @@ bool parse_file(const char *f, bool use_nagbar) {
         }
 
         /* sscanf implicitly strips whitespace. */
         }
 
         /* sscanf implicitly strips whitespace. */
-        const bool skip_line = (sscanf(buffer, "%511s %511[^\n]", key, value) < 1 || strlen(key) < 3);
+        const bool skip_line = (sscanf(buffer, "%511s %4095[^\n]", key, value) < 1 || strlen(key) < 3);
         const bool comment = (key[0] == '#');
         const bool comment = (key[0] == '#');
+        value[4095] = '\n';
 
         continuation = strstr(buffer, "\\\n");
         if (continuation) {
 
         continuation = strstr(buffer, "\\\n");
         if (continuation) {
@@ -868,49 +934,55 @@ bool parse_file(const char *f, bool use_nagbar) {
         }
 
         if (strcasecmp(key, "set") == 0) {
         }
 
         if (strcasecmp(key, "set") == 0) {
-            if (value[0] != '$') {
+            char v_key[512];
+            char v_value[4096];
+
+            if (sscanf(value, "%511s %4095[^\n]", v_key, v_value) < 1) {
+                ELOG("Failed to parse variable specification '%s', skipping it.\n", value);
+                continue;
+            }
+
+            if (v_key[0] != '$') {
                 ELOG("Malformed variable assignment, name has to start with $\n");
                 continue;
             }
 
                 ELOG("Malformed variable assignment, name has to start with $\n");
                 continue;
             }
 
-            /* get key/value for this variable */
-            char *v_key = value, *v_value;
-            if (strstr(value, " ") == NULL && strstr(value, "\t") == NULL) {
-                ELOG("Malformed variable assignment, need a value\n");
+            upsert_variable(&variables, v_key, v_value);
+            continue;
+        } else if (strcasecmp(key, "set_from_resource") == 0) {
+            char res_name[512];
+            char v_key[512];
+            char fallback[4096];
+
+            if (sscanf(value, "%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) {
+                ELOG("Failed to parse resource specification '%s', skipping it.\n", value);
                 continue;
             }
 
                 continue;
             }
 
-            if (!(v_value = strstr(value, " ")))
-                v_value = strstr(value, "\t");
-
-            *(v_value++) = '\0';
-            while (*v_value == '\t' || *v_value == ' ')
-                v_value++;
-
-            struct Variable *new = scalloc(1, sizeof(struct Variable));
-            struct Variable *test = NULL, *loc = NULL;
-            new->key = sstrdup(v_key);
-            new->value = sstrdup(v_value);
-            /* ensure that the correct variable is matched in case of one being
-             * the prefix of another */
-            SLIST_FOREACH(test, &variables, variables) {
-                if (strlen(new->key) >= strlen(test->key))
-                    break;
-                loc = test;
+            if (v_key[0] != '$') {
+                ELOG("Malformed variable assignment, name has to start with $\n");
+                continue;
             }
 
             }
 
-            if (loc == NULL) {
-                SLIST_INSERT_HEAD(&variables, new, variables);
-            } else {
-                SLIST_INSERT_AFTER(loc, new, variables);
+            char *res_value = get_resource(res_name);
+            if (res_value == NULL) {
+                DLOG("Could not get resource '%s', using fallback '%s'.\n", res_name, fallback);
+                res_value = sstrdup(fallback);
             }
 
             }
 
-            DLOG("Got new variable %s = %s\n", v_key, v_value);
+            upsert_variable(&variables, v_key, res_value);
+            FREE(res_value);
             continue;
         }
     }
     fclose(fstr);
 
             continue;
         }
     }
     fclose(fstr);
 
+    if (database != NULL) {
+        xcb_xrm_database_free(database);
+        /* Explicitly set the database to NULL again in case the config gets reloaded. */
+        database = NULL;
+    }
+
     /* For every custom variable, see how often it occurs in the file and
      * how much extra bytes it requires when replaced. */
     struct Variable *current, *nearest;
     /* For every custom variable, see how often it occurs in the file and
      * how much extra bytes it requires when replaced. */
     struct Variable *current, *nearest;
index ed8e3ae905e9114af330110d81a6de90f3f3aaaf..6cb4abf4ea8b7d2138bc9c84216b30eaa9fe3e38 100644 (file)
--- 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_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             = $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCB_CURSOR_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
-i3_LIBS               = $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCB_CURSOR_LIBS) $(PANGO_LIBS) $(YAJL_LIBS) $(LIBEV_LIBS) $(PCRE_LIBS) $(LIBSN_LIBS) -lm -lpthread
+i3_CFLAGS             = $(XKB_COMMON_CFLAGS) $(XKB_COMMON_X11_CFLAGS) $(XCB_CFLAGS) $(XCB_KBD_CFLAGS) $(XCB_WM_CFLAGS) $(XCB_CURSOR_CFLAGS) $(XCB_XRM_CFLAGS) $(PANGO_CFLAGS) $(YAJL_CFLAGS) $(LIBEV_CFLAGS) $(PCRE_CFLAGS) $(LIBSN_CFLAGS)
+i3_LIBS               = $(XKB_COMMON_LIBS) $(XKB_COMMON_X11_LIBS) $(XCB_LIBS) $(XCB_XKB_LIBS) $(XCB_KBD_LIBS) $(XCB_WM_LIBS) $(XCB_CURSOR_LIBS) $(XCB_XRM_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.
 
 # When using clang, we use pre-compiled headers to speed up the build. With
 # gcc, this actually makes the build slower.
index e8835005821d99ed06275f787e445e037a7d28f0..bcccc5a2e5fdf1f2f5db0d77506b091db44abf4e 100644 (file)
@@ -441,7 +441,7 @@ client.focused          #4c7899 #285577 #ffffff #2e9ef4
 EOT
 
 my $expected_all_tokens = <<'EOT';
 EOT
 
 my $expected_all_tokens = <<'EOT';
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'set_from_resource', 'bindsym', 'bindcode', 'bind', 'bar', 'font', 'mode', 'floating_minimum_size', 'floating_maximum_size', 'floating_modifier', 'default_orientation', 'workspace_layout', 'new_window', 'new_float', 'hide_edge_borders', 'for_window', 'assign', 'no_focus', 'focus_follows_mouse', 'mouse_warping', 'force_focus_wrapping', 'force_xinerama', 'force-xinerama', 'workspace_auto_back_and_forth', 'fake_outputs', 'fake-outputs', 'force_display_urgency_hint', 'focus_on_window_activation', 'show_marks', 'workspace', 'ipc_socket', 'ipc-socket', 'restart_state', 'popup_during_fullscreen', 'exec_always', 'exec', 'client.background', 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent', 'client.placeholder'
 EOT
 
 my $expected_end = <<'EOT';
 EOT
 
 my $expected_end = <<'EOT';
diff --git a/testcases/t/532-xresources.t b/testcases/t/532-xresources.t
new file mode 100644 (file)
index 0000000..438fad2
--- /dev/null
@@ -0,0 +1,64 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Tests for using X resources in the config.
+# Ticket: #2130
+use i3test i3_autostart => 0;
+use X11::XCB qw(PROP_MODE_REPLACE);
+
+sub get_marks {
+    return i3(get_socket_path())->get_marks->recv;
+}
+
+my $config = <<EOT;
+# i3 config file (v4)
+font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+
+# This isn't necessarily what X resources are intended for, but it'll do the
+# job for the test.
+set_from_resource \$mark i3wm.mark none
+for_window [class=worksforme] mark \$mark
+
+set_from_resource \$othermark i3wm.doesnotexist none
+for_window [class=doesnotworkforme] mark \$othermark
+
+EOT
+
+$x->change_property(
+    PROP_MODE_REPLACE,
+    $x->get_root_window(),
+    $x->atom(name => 'RESOURCE_MANAGER')->id,
+    $x->atom(name => 'STRING')->id,
+    32,
+    length('*mark: works'),
+    '*mark: works');
+$x->flush;
+
+my $pid = launch_with_config($config);
+
+open_window(wm_class => 'worksforme');
+sync_with_i3;
+is_deeply(get_marks(), [ 'works' ], 'the resource has loaded correctly');
+
+cmd 'kill';
+
+open_window(wm_class => 'doesnotworkforme');
+sync_with_i3;
+is_deeply(get_marks(), [ 'none' ], 'the resource fallback was used');
+
+exit_gracefully($pid);
+
+done_testing;
index b1ee1c0c0e240054ffc1f24d80f292c63ade05f9..ae6b7c82d884eac9c00e8e699a6911539c543f16 100644 (file)
@@ -27,3 +27,14 @@ COPY debian/control /usr/src/i3-debian-packaging/control
 RUN linux32 apt-get update && \
     DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \
     rm -rf /var/lib/apt/lists/*
 RUN linux32 apt-get update && \
     DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \
     rm -rf /var/lib/apt/lists/*
+
+# Install xcb-util-xrm. This is a workaround until it is available in the
+# distribution packages.
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xutils-dev
+RUN git clone --recursive https://github.com/Airblader/xcb-util-xrm.git && \
+    cd xcb-util-xrm && \
+    ./autogen.sh --prefix=/usr && \
+    make && \
+    make install && \
+    cd -
index e76229eb5b3207c1b159e247ff10828150b0a053..62b81283923914664e474563dd2ba6725de3258a 100644 (file)
@@ -28,3 +28,14 @@ COPY debian/control /usr/src/i3-debian-packaging/control
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \
     rm -rf /var/lib/apt/lists/*
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive mk-build-deps --install --remove --tool 'apt-get --no-install-recommends -y' /usr/src/i3-debian-packaging/control && \
     rm -rf /var/lib/apt/lists/*
+
+# Install xcb-util-xrm. This is a workaround until it is available in the
+# distribution packages.
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xutils-dev
+RUN git clone --recursive https://github.com/Airblader/xcb-util-xrm.git && \
+    cd xcb-util-xrm && \
+    ./autogen.sh --prefix=/usr && \
+    make && \
+    make install && \
+    cd -