]> git.sur5r.net Git - i3/i3/blobdiff - src/cfgparse.y
Merge branch 'fix-assign-and-move'
[i3/i3] / src / cfgparse.y
index 0d2c69771814c2ad0ced926efe26d12648e1b6a2..2a22aae4ef6bef8197ceb65121bff3dd43bb569b 100644 (file)
@@ -3,6 +3,8 @@
  * vim:ts=4:sw=4:expandtab
  *
  */
+#undef I3__FILE__
+#define I3__FILE__ "cfgparse.y"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -11,6 +13,8 @@
 
 #include "all.h"
 
+bool force_old_config_parser = false;
+
 static pid_t configerror_pid = -1;
 
 static Match current_match;
@@ -19,6 +23,9 @@ static Barconfig current_bar;
  * store this in a separate variable because in the i3 config struct we just
  * store the i3Font. */
 static char *font_pattern;
+/* The path to the temporary script files used by i3-nagbar. We need to keep
+ * them around to delete the files in the i3-nagbar SIGCHLD handler. */
+static char *edit_script_path, *pager_script_path;
 
 typedef struct yy_buffer_state *YY_BUFFER_STATE;
 extern int yylex(struct context *context);
@@ -51,7 +58,7 @@ void yyerror(const char *error_message) {
     ELOG("\n");
 }
 
-int yywrap() {
+int yywrap(void) {
     return 1;
 }
 
@@ -103,7 +110,10 @@ static int detect_version(char *buf) {
                 strncasecmp(bind, "focus down", strlen("focus down")) == 0 ||
                 strncasecmp(bind, "border normal", strlen("border normal")) == 0 ||
                 strncasecmp(bind, "border 1pixel", strlen("border 1pixel")) == 0 ||
-                strncasecmp(bind, "border borderless", strlen("border borderless")) == 0) {
+                strncasecmp(bind, "border pixel", strlen("border pixel")) == 0 ||
+                strncasecmp(bind, "border borderless", strlen("border borderless")) == 0 ||
+                strncasecmp(bind, "--no-startup-id", strlen("--no-startup-id")) == 0 ||
+                strncasecmp(bind, "bar", strlen("bar")) == 0) {
                 printf("deciding for version 4 due to this line: %.*s\n", (int)(walk-line), line);
                 return 4;
             }
@@ -231,6 +241,12 @@ static char *migrate_config(char *input, off_t size) {
  */
 static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
     ev_child_stop(EV_A_ watcher);
+
+    if (unlink(edit_script_path) != 0)
+        warn("Could not delete temporary i3-nagbar script %s", edit_script_path);
+    if (unlink(pager_script_path) != 0)
+        warn("Could not delete temporary i3-nagbar script %s", pager_script_path);
+
     if (!WIFEXITED(watcher->rstatus)) {
         fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
         return;
@@ -262,6 +278,23 @@ static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
 }
 #endif
 
+/*
+ * Writes the given command as a shell script to path.
+ * Returns true unless something went wrong.
+ *
+ */
+static bool write_nagbar_script(const char *path, const char *command) {
+    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
+    if (fd == -1) {
+        warn("Could not create temporary script to store the nagbar command");
+        return false;
+    }
+    write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n"));
+    write(fd, command, strlen(command));
+    close(fd);
+    return true;
+}
+
 /*
  * Starts an i3-nagbar process which alerts the user that his configuration
  * file contains one or more errors. Also offers two buttons: One to launch an
@@ -274,6 +307,18 @@ static void start_configerror_nagbar(const char *config_path) {
         return;
 
     fprintf(stderr, "Starting i3-nagbar due to configuration errors\n");
+
+    /* We need to create a custom script containing our actual command
+     * since not every terminal emulator which is contained in
+     * i3-sensible-terminal supports -e with multiple arguments (and not
+     * all of them support -e with one quoted argument either).
+     *
+     * NB: The paths need to be unique, that is, don’t assume users close
+     * their nagbars at any point in time (and they still need to work).
+     * */
+    edit_script_path = get_process_filename("nagbar-cfgerror-edit");
+    pager_script_path = get_process_filename("nagbar-cfgerror-pager");
+
     configerror_pid = fork();
     if (configerror_pid == -1) {
         warn("Could not fork()");
@@ -282,10 +327,17 @@ static void start_configerror_nagbar(const char *config_path) {
 
     /* child */
     if (configerror_pid == 0) {
+        char *edit_command, *pager_command;
+        sasprintf(&edit_command, "i3-sensible-editor \"%s\" && i3-msg reload\n", config_path);
+        sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename);
+        if (!write_nagbar_script(edit_script_path, edit_command) ||
+            !write_nagbar_script(pager_script_path, pager_command))
+            return;
+
         char *editaction,
              *pageraction;
-        sasprintf(&editaction, "i3-sensible-terminal -e sh -c \"i3-sensible-editor \\\"%s\\\" && i3-msg reload\"", config_path);
-        sasprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename);
+        sasprintf(&editaction, "i3-sensible-terminal -e \"%s\"", edit_script_path);
+        sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path);
         char *argv[] = {
             NULL, /* will be replaced by the executable path */
             "-t",
@@ -382,8 +434,10 @@ static void check_for_duplicate_bindings(struct context *context) {
             /* Check if the keycodes or modifiers are different. If so, they
              * can't be duplicate */
             if (bind->keycode != current->keycode ||
-                bind->mods != current->mods)
+                bind->mods != current->mods ||
+                bind->release != current->release)
                 continue;
+
             context->has_errors = true;
             if (current->keycode != 0) {
                 ELOG("Duplicate keybinding in config file:\n  modmask %d with keycode %d, command \"%s\"\n",
@@ -477,6 +531,8 @@ void parse_file(const char *f) {
                 v_value = strstr(value, "\t");
 
             *(v_value++) = '\0';
+            while (*v_value == '\t' || *v_value == ' ')
+                v_value++;
 
             struct Variable *new = scalloc(sizeof(struct Variable));
             new->key = sstrdup(v_key);
@@ -550,15 +606,15 @@ void parse_file(const char *f) {
         /* We need to convert this v3 configuration */
         char *converted = migrate_config(new, stbuf.st_size);
         if (converted != NULL) {
-            printf("\n");
-            printf("****************************************************************\n");
-            printf("NOTE: Automatically converted configuration file from v3 to v4.\n");
-            printf("\n");
-            printf("Please convert your config file to v4. You can use this command:\n");
-            printf("    mv %s %s.O\n", f, f);
-            printf("    i3-migrate-config-to-v4 %s.O > %s\n", f, f);
-            printf("****************************************************************\n");
-            printf("\n");
+            ELOG("\n");
+            ELOG("****************************************************************\n");
+            ELOG("NOTE: Automatically converted configuration file from v3 to v4.\n");
+            ELOG("\n");
+            ELOG("Please convert your config file to v4. You can use this command:\n");
+            ELOG("    mv %s %s.O\n", f, f);
+            ELOG("    i3-migrate-config-to-v4 %s.O > %s\n", f, f);
+            ELOG("****************************************************************\n");
+            ELOG("\n");
             free(new);
             new = converted;
         } else {
@@ -571,15 +627,20 @@ void parse_file(const char *f) {
         }
     }
 
-    /* now lex/parse it */
-    yy_scan_string(new);
 
     context = scalloc(sizeof(struct context));
     context->filename = f;
 
-    if (yyparse() != 0) {
-        fprintf(stderr, "Could not parse configfile\n");
-        exit(1);
+    if (force_old_config_parser) {
+        /* now lex/parse it */
+        yy_scan_string(new);
+        if (yyparse() != 0) {
+            fprintf(stderr, "Could not parse configfile\n");
+            exit(1);
+        }
+    } else {
+        struct ConfigResult *config_output = parse_config(new, context);
+        yajl_gen_free(config_output->json_gen);
     }
 
     check_for_duplicate_bindings(context);
@@ -609,10 +670,14 @@ void parse_file(const char *f) {
     }
 
     if (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");
         start_configerror_nagbar(f);
     }
 
-    yylex_destroy();
+    if (force_old_config_parser)
+        yylex_destroy();
     FREE(context->line_copy);
     free(context);
     FREE(font_pattern);
@@ -657,6 +722,8 @@ void parse_file(const char *f) {
 %token                  TOKCONTROL                  "control"
 %token                  TOKSHIFT                    "shift"
 %token                  TOKFLOATING_MODIFIER        "floating_modifier"
+%token                  TOKFLOATING_MAXIMUM_SIZE    "floating_maximum_size"
+%token                  TOKFLOATING_MINIMUM_SIZE    "floating_minimum_size"
 %token  <string>        QUOTEDSTRING                "<quoted string>"
 %token                  TOKWORKSPACE                "workspace"
 %token                  TOKOUTPUT                   "output"
@@ -670,6 +737,7 @@ void parse_file(const char *f) {
 %token  <color>         TOKCOLOR
 %token                  TOKARROW                    "→"
 %token                  TOKMODE                     "mode"
+%token                  TOK_TIME_MS                 "ms"
 %token                  TOK_BAR                     "bar"
 %token                  TOK_ORIENTATION             "default_orientation"
 %token                  TOK_HORIZ                   "horizontal"
@@ -680,11 +748,16 @@ void parse_file(const char *f) {
 %token                  TOKNEWFLOAT                 "new_float"
 %token                  TOK_NORMAL                  "normal"
 %token                  TOK_NONE                    "none"
+%token                  TOK_PIXEL                   "pixel"
 %token                  TOK_1PIXEL                  "1pixel"
+%token                  TOK_HIDE_EDGE_BORDERS       "hide_edge_borders"
+%token                  TOK_BOTH                    "both"
 %token                  TOKFOCUSFOLLOWSMOUSE        "focus_follows_mouse"
 %token                  TOK_FORCE_FOCUS_WRAPPING    "force_focus_wrapping"
 %token                  TOK_FORCE_XINERAMA          "force_xinerama"
+%token                  TOK_FAKE_OUTPUTS            "fake_outputs"
 %token                  TOK_WORKSPACE_AUTO_BAF      "workspace_auto_back_and_forth"
+%token                  TOK_WORKSPACE_URGENCY_TIMER "force_display_urgency_hint"
 %token                  TOKWORKSPACEBAR             "workspace_bar"
 %token                  TOK_DEFAULT                 "default"
 %token                  TOK_STACKING                "stacking"
@@ -701,10 +774,19 @@ void parse_file(const char *f) {
 %token                  TOK_BAR_MODE                "mode (bar)"
 %token                  TOK_BAR_HIDE                "hide"
 %token                  TOK_BAR_DOCK                "dock"
+%token                  TOK_BAR_MODIFIER            "modifier (bar)"
+%token                  TOK_BAR_CONTROL             "shift (bar)"
+%token                  TOK_BAR_SHIFT               "control (bar)"
+%token                  TOK_BAR_MOD1                "Mod1"
+%token                  TOK_BAR_MOD2                "Mod2"
+%token                  TOK_BAR_MOD3                "Mod3"
+%token                  TOK_BAR_MOD4                "Mod4"
+%token                  TOK_BAR_MOD5                "Mod5"
 %token                  TOK_BAR_POSITION            "position"
 %token                  TOK_BAR_BOTTOM              "bottom"
 %token                  TOK_BAR_TOP                 "top"
 %token                  TOK_BAR_STATUS_COMMAND      "status_command"
+%token                  TOK_BAR_I3BAR_COMMAND       "i3bar_command"
 %token                  TOK_BAR_FONT                "font (bar)"
 %token                  TOK_BAR_WORKSPACE_BUTTONS   "workspace_buttons"
 %token                  TOK_BAR_VERBOSE             "verbose"
@@ -716,6 +798,7 @@ void parse_file(const char *f) {
 %token                  TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
 %token                  TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
 %token                  TOK_NO_STARTUP_ID           "--no-startup-id"
+%token                  TOK_RELEASE                 "--release"
 
 %token              TOK_MARK            "mark"
 %token              TOK_CLASS           "class"
@@ -724,6 +807,7 @@ void parse_file(const char *f) {
 %token              TOK_ID              "id"
 %token              TOK_CON_ID          "con_id"
 %token              TOK_TITLE           "title"
+%token              TOK_URGENT          "urgent"
 
 %type   <binding>       binding
 %type   <binding>       bindcode
@@ -734,15 +818,21 @@ void parse_file(const char *f) {
 %type   <number>        layout_mode
 %type   <number>        border_style
 %type   <number>        new_window
+%type   <number>        hide_edge_borders
+%type   <number>        edge_hiding_mode
 %type   <number>        new_float
 %type   <number>        colorpixel
 %type   <number>        bool
 %type   <number>        popup_setting
 %type   <number>        bar_position_position
 %type   <number>        bar_mode_mode
+%type   <number>        bar_modifier_modifier
 %type   <number>        optional_no_startup_id
+%type   <number>        optional_border_width
+%type   <number>        optional_release
 %type   <string>        command
 %type   <string>        word_or_number
+%type   <string>        duration
 %type   <string>        qstring_or_number
 %type   <string>        optional_workspace_name
 %type   <string>        workspace_name
@@ -760,14 +850,19 @@ line:
     | for_window
     | mode
     | bar
+    | floating_maximum_size
+    | floating_minimum_size
     | floating_modifier
     | orientation
     | workspace_layout
     | new_window
     | new_float
+    | hide_edge_borders
     | focus_follows_mouse
     | force_focus_wrapping
     | force_xinerama
+    | fake_outputs
+    | force_display_urgency_hint
     | workspace_back_and_forth
     | workspace_bar
     | workspace
@@ -805,33 +900,40 @@ binding:
     ;
 
 bindcode:
-    binding_modifiers NUMBER command
+    optional_release binding_modifiers NUMBER command
     {
-        printf("\tFound keycode binding mod%d with key %d and command %s\n", $1, $2, $3);
+        DLOG("bindcode: release = %d, mod = %d, key = %d, command = %s\n", $1, $2, $3, $4);
         Binding *new = scalloc(sizeof(Binding));
 
-        new->keycode = $2;
-        new->mods = $1;
-        new->command = $3;
+        new->release = $1;
+        new->keycode = $3;
+        new->mods = $2;
+        new->command = $4;
 
         $$ = new;
     }
     ;
 
 bindsym:
-    binding_modifiers word_or_number command
+    optional_release binding_modifiers word_or_number command
     {
-        printf("\tFound keysym binding mod%d with key %s and command %s\n", $1, $2, $3);
+        DLOG("bindsym: release = %d, mod = %d, key = %s, command = %s\n", $1, $2, $3, $4);
         Binding *new = scalloc(sizeof(Binding));
 
-        new->symbol = $2;
-        new->mods = $1;
-        new->command = $3;
+        new->release = $1;
+        new->symbol = $3;
+        new->mods = $2;
+        new->command = $4;
 
         $$ = new;
     }
     ;
 
+optional_release:
+    /* empty */ { $$ = B_UPON_KEYPRESS; }
+    | TOK_RELEASE  { $$ = B_UPON_KEYRELEASE; }
+    ;
+
 for_window:
     TOK_FOR_WINDOW match command
     {
@@ -936,6 +1038,20 @@ criterion:
         current_match.title = regex_new($3);
         free($3);
     }
+    | TOK_URGENT '=' STR
+    {
+        printf("criteria: urgent = %s\n", $3);
+        if (strcasecmp($3, "latest") == 0 ||
+            strcasecmp($3, "newest") == 0 ||
+            strcasecmp($3, "recent") == 0 ||
+            strcasecmp($3, "last") == 0) {
+            current_match.urgent = U_LATEST;
+        } else if (strcasecmp($3, "oldest") == 0 ||
+                   strcasecmp($3, "first") == 0) {
+            current_match.urgent = U_OLDEST;
+        }
+        free($3);
+    }
     ;
 
 qstring_or_number:
@@ -951,6 +1067,11 @@ word_or_number:
     }
     ;
 
+duration:
+    NUMBER { sasprintf(&$$, "%d", $1); }
+    | NUMBER TOK_TIME_MS { sasprintf(&$$, "%d", $1); }
+    ;
+
 mode:
     TOKMODE QUOTEDSTRING '{' modelines '}'
     {
@@ -1029,10 +1150,12 @@ barlines:
 barline:
     comment
     | bar_status_command
+    | bar_i3bar_command
     | bar_output
     | bar_tray_output
     | bar_position
     | bar_mode
+    | bar_modifier
     | bar_font
     | bar_workspace_buttons
     | bar_verbose
@@ -1055,6 +1178,15 @@ bar_status_command:
     }
     ;
 
+bar_i3bar_command:
+    TOK_BAR_I3BAR_COMMAND STR
+    {
+        DLOG("should add i3bar_command %s\n", $2);
+        FREE(current_bar.i3bar_command);
+        current_bar.i3bar_command = $2;
+    }
+    ;
+
 bar_output:
     TOK_BAR_OUTPUT STR
     {
@@ -1101,6 +1233,23 @@ bar_mode_mode:
     | TOK_BAR_DOCK { $$ = M_DOCK; }
     ;
 
+bar_modifier:
+    TOK_BAR_MODIFIER bar_modifier_modifier
+    {
+        DLOG("modifier %d\n", $2);
+        current_bar.modifier = $2;
+    };
+
+bar_modifier_modifier:
+    TOK_BAR_CONTROL { $$ = M_CONTROL; }
+    | TOK_BAR_SHIFT { $$ = M_SHIFT; }
+    | TOK_BAR_MOD1  { $$ = M_MOD1; }
+    | TOK_BAR_MOD2  { $$ = M_MOD2; }
+    | TOK_BAR_MOD3  { $$ = M_MOD3; }
+    | TOK_BAR_MOD4  { $$ = M_MOD4; }
+    | TOK_BAR_MOD5  { $$ = M_MOD5; }
+    ;
+
 bar_font:
     TOK_BAR_FONT STR
     {
@@ -1165,36 +1314,90 @@ bar_color_statusline:
 bar_color_focused_workspace:
     TOK_BAR_COLOR_FOCUSED_WORKSPACE HEXCOLOR HEXCOLOR
     {
-        DLOG("focused_ws = %s and %s\n", $2, $3);
+        /* Old syntax: text / background */
+        DLOG("focused_ws = %s, %s (old)\n", $2, $3);
+        current_bar.colors.focused_workspace_bg = $3;
         current_bar.colors.focused_workspace_text = $2;
+    }
+    | TOK_BAR_COLOR_FOCUSED_WORKSPACE HEXCOLOR HEXCOLOR HEXCOLOR
+    {
+        /* New syntax: border / background / text */
+        DLOG("focused_ws = %s, %s and %s\n", $2, $3, $4);
+        current_bar.colors.focused_workspace_border = $2;
         current_bar.colors.focused_workspace_bg = $3;
+        current_bar.colors.focused_workspace_text = $4;
     }
     ;
 
 bar_color_active_workspace:
     TOK_BAR_COLOR_ACTIVE_WORKSPACE HEXCOLOR HEXCOLOR
     {
-        DLOG("active_ws = %s and %s\n", $2, $3);
+        /* Old syntax: text / background */
+        DLOG("active_ws = %s, %s (old)\n", $2, $3);
+        current_bar.colors.active_workspace_bg = $3;
         current_bar.colors.active_workspace_text = $2;
+    }
+    | TOK_BAR_COLOR_ACTIVE_WORKSPACE HEXCOLOR HEXCOLOR HEXCOLOR
+    {
+        /* New syntax: border / background / text */
+        DLOG("active_ws = %s, %s and %s\n", $2, $3, $4);
+        current_bar.colors.active_workspace_border = $2;
         current_bar.colors.active_workspace_bg = $3;
+        current_bar.colors.active_workspace_text = $4;
     }
     ;
 
 bar_color_inactive_workspace:
     TOK_BAR_COLOR_INACTIVE_WORKSPACE HEXCOLOR HEXCOLOR
     {
-        DLOG("inactive_ws = %s and %s\n", $2, $3);
+        /* Old syntax: text / background */
+        DLOG("inactive_ws = %s, %s (old)\n", $2, $3);
+        current_bar.colors.inactive_workspace_bg = $3;
         current_bar.colors.inactive_workspace_text = $2;
+    }
+    | TOK_BAR_COLOR_INACTIVE_WORKSPACE HEXCOLOR HEXCOLOR HEXCOLOR
+    {
+        DLOG("inactive_ws = %s, %s and %s\n", $2, $3, $4);
+        current_bar.colors.inactive_workspace_border = $2;
         current_bar.colors.inactive_workspace_bg = $3;
+        current_bar.colors.inactive_workspace_text = $4;
     }
     ;
 
 bar_color_urgent_workspace:
     TOK_BAR_COLOR_URGENT_WORKSPACE HEXCOLOR HEXCOLOR
     {
-        DLOG("urgent_ws = %s and %s\n", $2, $3);
+        /* Old syntax: text / background */
+        DLOG("urgent_ws = %s, %s (old)\n", $2, $3);
+        current_bar.colors.urgent_workspace_bg = $3;
         current_bar.colors.urgent_workspace_text = $2;
+    }
+    | TOK_BAR_COLOR_URGENT_WORKSPACE HEXCOLOR HEXCOLOR HEXCOLOR
+    {
+        DLOG("urgent_ws = %s, %s and %s\n", $2, $3, $4);
+        current_bar.colors.urgent_workspace_border = $2;
         current_bar.colors.urgent_workspace_bg = $3;
+        current_bar.colors.urgent_workspace_text = $4;
+    }
+    ;
+
+floating_maximum_size:
+    TOKFLOATING_MAXIMUM_SIZE NUMBER WORD NUMBER
+    {
+        printf("floating_maximum_width = %d\n", $2);
+        printf("floating_maximum_height = %d\n", $4);
+        config.floating_maximum_width = $2;
+        config.floating_maximum_height = $4;
+    }
+    ;
+
+floating_minimum_size:
+    TOKFLOATING_MINIMUM_SIZE NUMBER WORD NUMBER
+    {
+        printf("floating_minimum_width = %d\n", $2);
+        printf("floating_minimum_height = %d\n", $4);
+        config.floating_minimum_width = $2;
+        config.floating_minimum_height = $4;
     }
     ;
 
@@ -1288,9 +1491,27 @@ new_float:
     ;
 
 border_style:
-    TOK_NORMAL      { $$ = BS_NORMAL; }
-    | TOK_NONE      { $$ = BS_NONE; }
-    | TOK_1PIXEL    { $$ = BS_1PIXEL; }
+    TOK_NORMAL optional_border_width
+    {
+        /* FIXME: the whole border_style thing actually screws up when new_float is used because it overwrites earlier values :-/ */
+        config.default_border_width = $2;
+        $$ = BS_NORMAL;
+    }
+    | TOK_1PIXEL
+    {
+        config.default_border_width = 1;
+        $$ = BS_PIXEL;
+    }
+    | TOK_NONE
+    {
+        config.default_border_width = 0;
+        $$ = BS_NONE;
+    }
+    | TOK_PIXEL optional_border_width
+    {
+        config.default_border_width = $2;
+        $$ = BS_PIXEL;
+    }
     ;
 
 bool:
@@ -1309,6 +1530,22 @@ bool:
     }
     ;
 
+hide_edge_borders:
+    TOK_HIDE_EDGE_BORDERS edge_hiding_mode
+    {
+        DLOG("hide edge borders = %d\n", $2);
+        config.hide_edge_borders = $2;
+    }
+    ;
+
+edge_hiding_mode:
+    TOK_NONE        { $$ = ADJ_NONE; }
+    | TOK_VERT      { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; }
+    | TOK_HORIZ     { $$ = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; }
+    | TOK_BOTH      { $$ = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; }
+    | bool          { $$ = ($1 ? ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE : ADJ_NONE); }
+    ;
+
 focus_follows_mouse:
     TOKFOCUSFOLLOWSMOUSE bool
     {
@@ -1333,6 +1570,14 @@ force_xinerama:
     }
     ;
 
+fake_outputs:
+    TOK_FAKE_OUTPUTS STR
+    {
+        DLOG("fake outputs = %s\n", $2);
+        config.fake_outputs = $2;
+    }
+    ;
+
 workspace_back_and_forth:
     TOK_WORKSPACE_AUTO_BAF bool
     {
@@ -1341,6 +1586,14 @@ workspace_back_and_forth:
     }
     ;
 
+force_display_urgency_hint:
+    TOK_WORKSPACE_URGENCY_TIMER duration
+    {
+        DLOG("workspace urgency_timer = %f\n", atoi($2) / 1000.0);
+        config.workspace_urgency_timer = atoi($2) / 1000.0;
+    }
+    ;
+
 workspace_bar:
     TOKWORKSPACEBAR bool
     {
@@ -1529,6 +1782,11 @@ exec_always:
     }
     ;
 
+optional_border_width:
+    /* empty */ { $$ = 2; } // 2 pixels is the default value for any type of border
+    | NUMBER  { $$ = $1; }
+    ;
+
 optional_no_startup_id:
     /* empty */ { $$ = false; }
     | TOK_NO_STARTUP_ID  { $$ = true; }
@@ -1570,6 +1828,15 @@ color:
         dest->background = $3;
         dest->text = $4;
     }
+    | TOKCOLOR colorpixel colorpixel colorpixel colorpixel
+    {
+        struct Colortriple *dest = $1;
+
+        dest->border = $2;
+        dest->background = $3;
+        dest->text = $4;
+        dest->indicator = $5;
+    }
     ;
 
 colorpixel: