]> git.sur5r.net Git - i3/i3/blobdiff - src/cfgparse.y
Create different IDs for each bar (+test)
[i3/i3] / src / cfgparse.y
index f8e84ae9b3b8eeaf1f4fe707d27b9229a229c42d..2c796af129e63bbce2d7bfa807915f9e7dd34d43 100644 (file)
@@ -14,6 +14,7 @@
 static pid_t configerror_pid = -1;
 
 static Match current_match;
+static Barconfig current_bar;
 
 typedef struct yy_buffer_state *YY_BUFFER_STATE;
 extern int yylex(struct context *context);
@@ -240,6 +241,23 @@ static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
     configerror_pid = -1;
 }
 
+/* We need ev >= 4 for the following code. Since it is not *that* important (it
+ * only makes sure that there are no i3-nagbar instances left behind) we still
+ * support old systems with libev 3. */
+#if EV_VERSION_MAJOR >= 4
+/*
+ * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
+ * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
+ *
+ */
+static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
+    if (configerror_pid != -1) {
+        LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", configerror_pid);
+        kill(configerror_pid, SIGKILL);
+    }
+}
+#endif
+
 /*
  * 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
@@ -259,9 +277,9 @@ static void start_configerror_nagbar(const char *config_path) {
     if (configerror_pid == 0) {
         char *editaction,
              *pageraction;
-        if (asprintf(&editaction, TERM_EMU " -e sh -c \"${EDITOR:-vi} \"%s\" && i3-msg reload\"", config_path) == -1)
+        if (asprintf(&editaction, "i3-sensible-terminal -e sh -c \"i3-sensible-editor \\\"%s\\\" && i3-msg reload\"", config_path) == -1)
             exit(1);
-        if (asprintf(&pageraction, TERM_EMU " -e sh -c \"${PAGER:-less} \"%s\"\"", errorfilename) == -1)
+        if (asprintf(&pageraction, "i3-sensible-terminal -e i3-sensible-pager \"%s\"", errorfilename) == -1)
             exit(1);
         char *argv[] = {
             NULL, /* will be replaced by the executable path */
@@ -283,6 +301,17 @@ static void start_configerror_nagbar(const char *config_path) {
     ev_child *child = smalloc(sizeof(ev_child));
     ev_child_init(child, &nagbar_exited, configerror_pid, 0);
     ev_child_start(main_loop, child);
+
+/* We need ev >= 4 for the following code. Since it is not *that* important (it
+ * only makes sure that there are no i3-nagbar instances left behind) we still
+ * support old systems with libev 3. */
+#if EV_VERSION_MAJOR >= 4
+    /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
+     * still running) */
+    ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
+    ev_cleanup_init(cleanup, nagbar_cleanup);
+    ev_cleanup_start(main_loop, cleanup);
+#endif
 }
 
 /*
@@ -311,6 +340,53 @@ void kill_configerror_nagbar(bool wait_for_it) {
     waitpid(configerror_pid, NULL, 0);
 }
 
+/*
+ * Checks for duplicate key bindings (the same keycode or keysym is configured
+ * more than once). If a duplicate binding is found, a message is printed to
+ * stderr and the has_errors variable is set to true, which will start
+ * i3-nagbar.
+ *
+ */
+static void check_for_duplicate_bindings(struct context *context) {
+    Binding *bind, *current;
+    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)
+                break;
+
+            /* Check if one is using keysym while the other is using bindsym.
+             * If so, skip. */
+            /* XXX: It should be checked at a later place (when translating the
+             * keysym to keycodes) if there are any duplicates */
+            if ((bind->symbol == NULL && current->symbol != NULL) ||
+                (bind->symbol != NULL && current->symbol == NULL))
+                continue;
+
+            /* If bind is NULL, current has to be NULL, too (see above).
+             * If the keycodes differ, it can't be a duplicate. */
+            if (bind->symbol != NULL &&
+                strcasecmp(bind->symbol, current->symbol) != 0)
+                continue;
+
+            /* Check if the keycodes or modifiers are different. If so, they
+             * can't be duplicate */
+            if (bind->keycode != current->keycode ||
+                bind->mods != current->mods)
+                continue;
+            context->has_errors = true;
+            if (current->keycode != 0) {
+                ELOG("Duplicate keybinding in config file:\n  modmask %d with keycode %d, command \"%s\"\n",
+                     current->mods, current->keycode, current->command);
+            } else {
+                ELOG("Duplicate keybinding in config file:\n  modmask %d with keysym %s, command \"%s\"\n",
+                     current->mods, current->symbol, current->command);
+            }
+        }
+    }
+}
+
 void parse_file(const char *f) {
     SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
     int fd, ret, read_bytes = 0;
@@ -390,7 +466,8 @@ void parse_file(const char *f) {
         int extra = (strlen(current->value) - strlen(current->key));
         char *next;
         for (next = bufcopy;
-             (next = strcasestr(bufcopy + (next - bufcopy), current->key)) != NULL;
+             next < (bufcopy + stbuf.st_size) &&
+             (next = strcasestr(next, current->key)) != NULL;
              next += strlen(current->key)) {
             *next = '_';
             extra_bytes += extra;
@@ -471,6 +548,8 @@ void parse_file(const char *f) {
         exit(1);
     }
 
+    check_for_duplicate_bindings(context);
+
     if (context->has_errors) {
         start_configerror_nagbar(f);
     }
@@ -509,6 +588,7 @@ void parse_file(const char *f) {
 %token  <string>        STR                         "<string>"
 %token  <string>        STR_NG                      "<string (non-greedy)>"
 %token  <string>        HEX                         "<hex>"
+%token  <string>        HEXCOLOR                    "#<hex>"
 %token  <string>        OUTPUT                      "<RandR output>"
 %token                  TOKBINDCODE
 %token                  TOKTERMINAL
@@ -532,17 +612,21 @@ void parse_file(const char *f) {
 %token  <color>         TOKCOLOR
 %token                  TOKARROW                    "→"
 %token                  TOKMODE                     "mode"
+%token                  TOK_BAR                     "bar"
 %token                  TOK_ORIENTATION             "default_orientation"
 %token                  TOK_HORIZ                   "horizontal"
 %token                  TOK_VERT                    "vertical"
 %token                  TOK_AUTO                    "auto"
 %token                  TOK_WORKSPACE_LAYOUT        "workspace_layout"
 %token                  TOKNEWWINDOW                "new_window"
+%token                  TOKNEWFLOAT                 "new_float"
 %token                  TOK_NORMAL                  "normal"
 %token                  TOK_NONE                    "none"
 %token                  TOK_1PIXEL                  "1pixel"
 %token                  TOKFOCUSFOLLOWSMOUSE        "focus_follows_mouse"
 %token                  TOK_FORCE_FOCUS_WRAPPING    "force_focus_wrapping"
+%token                  TOK_FORCE_XINERAMA          "force_xinerama"
+%token                  TOK_WORKSPACE_AUTO_BAF      "workspace_auto_back_and_forth"
 %token                  TOKWORKSPACEBAR             "workspace_bar"
 %token                  TOK_DEFAULT                 "default"
 %token                  TOK_STACKING                "stacking"
@@ -553,9 +637,31 @@ void parse_file(const char *f) {
 %token                  TOK_LEAVE_FULLSCREEN        "leave_fullscreen"
 %token                  TOK_FOR_WINDOW              "for_window"
 
+%token                  TOK_BAR_OUTPUT              "output (bar)"
+%token                  TOK_BAR_TRAY_OUTPUT         "tray_output"
+%token                  TOK_BAR_SOCKET_PATH         "socket_path"
+%token                  TOK_BAR_MODE                "mode"
+%token                  TOK_BAR_HIDE                "hide"
+%token                  TOK_BAR_DOCK                "dock"
+%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_FONT                "font"
+%token                  TOK_BAR_WORKSPACE_BUTTONS   "workspace_buttons"
+%token                  TOK_BAR_VERBOSE             "verbose"
+%token                  TOK_BAR_COLORS              "colors"
+%token                  TOK_BAR_COLOR_BACKGROUND    "background"
+%token                  TOK_BAR_COLOR_STATUSLINE    "statusline"
+%token                  TOK_BAR_COLOR_FOCUSED_WORKSPACE "focused_workspace"
+%token                  TOK_BAR_COLOR_ACTIVE_WORKSPACE "active_workspace"
+%token                  TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
+%token                  TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
+
 %token              TOK_MARK            "mark"
 %token              TOK_CLASS           "class"
 %token              TOK_INSTANCE        "instance"
+%token              TOK_WINDOW_ROLE     "window_role"
 %token              TOK_ID              "id"
 %token              TOK_CON_ID          "con_id"
 %token              TOK_TITLE           "title"
@@ -569,9 +675,12 @@ void parse_file(const char *f) {
 %type   <number>        layout_mode
 %type   <number>        border_style
 %type   <number>        new_window
+%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   <string>        command
 %type   <string>        word_or_number
 %type   <string>        optional_workspace_name
@@ -589,12 +698,16 @@ line:
     bindline
     | for_window
     | mode
+    | bar
     | floating_modifier
     | orientation
     | workspace_layout
     | new_window
+    | new_float
     | focus_follows_mouse
     | force_focus_wrapping
+    | force_xinerama
+    | workspace_back_and_forth
     | workspace_bar
     | workspace
     | assign
@@ -705,12 +818,20 @@ criterion:
     TOK_CLASS '=' STR
     {
         printf("criteria: class = %s\n", $3);
-        current_match.class = $3;
+        current_match.class = regex_new($3);
+        free($3);
     }
     | TOK_INSTANCE '=' STR
     {
         printf("criteria: instance = %s\n", $3);
-        current_match.instance = $3;
+        current_match.instance = regex_new($3);
+        free($3);
+    }
+    | TOK_WINDOW_ROLE '=' STR
+    {
+        printf("criteria: window_role = %s\n", $3);
+        current_match.role = regex_new($3);
+        free($3);
     }
     | TOK_CON_ID '=' STR
     {
@@ -745,12 +866,14 @@ criterion:
     | TOK_MARK '=' STR
     {
         printf("criteria: mark = %s\n", $3);
-        current_match.mark = $3;
+        current_match.mark = regex_new($3);
+        free($3);
     }
     | TOK_TITLE '=' STR
     {
         printf("criteria: title = %s\n", $3);
-        current_match.title = $3;
+        current_match.title = regex_new($3);
+        free($3);
     }
     ;
 
@@ -806,6 +929,207 @@ modeline:
     }
     ;
 
+bar:
+    TOK_BAR '{' barlines '}'
+    {
+        printf("\t new bar configuration finished, saving.\n");
+        /* Generate a unique ID for this bar */
+        current_bar.id = sstrdup("bar-XXXXXX");
+        /* This works similar to mktemp in that it replaces the last six X with
+         * random letters, but without the restriction that the given buffer
+         * has to contain a valid path name. */
+        char *x = current_bar.id + strlen("bar-");
+        while (*x != '\0') {
+            *(x++) = (rand() % 26) + 'a';
+        }
+
+        /* Copy the current (static) structure into a dynamically allocated
+         * one, then cleanup our static one. */
+        Barconfig *bar_config = scalloc(sizeof(Barconfig));
+        memcpy(bar_config, &current_bar, sizeof(Barconfig));
+        TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs);
+
+        memset(&current_bar, '\0', sizeof(Barconfig));
+    }
+    ;
+
+barlines:
+    /* empty */
+    | barlines barline
+    ;
+
+barline:
+    comment
+    | bar_status_command
+    | bar_output
+    | bar_tray_output
+    | bar_position
+    | bar_mode
+    | bar_font
+    | bar_workspace_buttons
+    | bar_verbose
+    | bar_socket_path
+    | bar_colors
+    | bar_color_background
+    | bar_color_statusline
+    | bar_color_focused_workspace
+    | bar_color_active_workspace
+    | bar_color_inactive_workspace
+    | bar_color_urgent_workspace
+    ;
+
+bar_status_command:
+    TOK_BAR_STATUS_COMMAND STR
+    {
+        DLOG("should add status command %s\n", $2);
+        FREE(current_bar.status_command);
+        current_bar.status_command = $2;
+    }
+    ;
+
+bar_output:
+    TOK_BAR_OUTPUT STR
+    {
+        DLOG("bar output %s\n", $2);
+        int new_outputs = current_bar.num_outputs + 1;
+        current_bar.outputs = srealloc(current_bar.outputs, sizeof(char*) * new_outputs);
+        current_bar.outputs[current_bar.num_outputs] = $2;
+        current_bar.num_outputs = new_outputs;
+    }
+    ;
+
+bar_tray_output:
+    TOK_BAR_TRAY_OUTPUT STR
+    {
+        DLOG("tray %s\n", $2);
+        FREE(current_bar.tray_output);
+        current_bar.tray_output = $2;
+    }
+    ;
+
+bar_position:
+    TOK_BAR_POSITION bar_position_position
+    {
+        DLOG("position %d\n", $2);
+        current_bar.position = $2;
+    }
+    ;
+
+bar_position_position:
+    TOK_BAR_TOP      { $$ = P_TOP; }
+    | TOK_BAR_BOTTOM { $$ = P_BOTTOM; }
+    ;
+
+bar_mode:
+    TOK_BAR_MODE bar_mode_mode
+    {
+        DLOG("mode %d\n", $2);
+        current_bar.mode = $2;
+    }
+    ;
+
+bar_mode_mode:
+    TOK_BAR_HIDE   { $$ = M_HIDE; }
+    | TOK_BAR_DOCK { $$ = M_DOCK; }
+    ;
+
+bar_font:
+    TOK_BAR_FONT STR
+    {
+        DLOG("font %s\n", $2);
+        FREE(current_bar.font);
+        current_bar.font = $2;
+    }
+    ;
+
+bar_workspace_buttons:
+    TOK_BAR_WORKSPACE_BUTTONS bool
+    {
+        DLOG("workspace_buttons = %d\n", $2);
+        /* We store this inverted to make the default setting right when
+         * initializing the struct with zero. */
+        current_bar.hide_workspace_buttons = !($2);
+    }
+    ;
+
+bar_verbose:
+    TOK_BAR_VERBOSE bool
+    {
+        DLOG("verbose = %d\n", $2);
+        current_bar.verbose = $2;
+    }
+    ;
+
+bar_socket_path:
+    TOK_BAR_SOCKET_PATH STR
+    {
+        DLOG("socket_path = %s\n", $2);
+        FREE(current_bar.socket_path);
+        current_bar.socket_path = $2;
+    }
+    ;
+
+bar_colors:
+    TOK_BAR_COLORS '{' barlines '}'
+    {
+        /* At the moment, the TOK_BAR_COLORS token is only to make the config
+         * friendlier for humans. We might change this in the future if it gets
+         * more complex. */
+    }
+    ;
+
+bar_color_background:
+    TOK_BAR_COLOR_BACKGROUND HEXCOLOR
+    {
+        DLOG("background = %s\n", $2);
+        current_bar.colors.background = $2;
+    }
+    ;
+
+bar_color_statusline:
+    TOK_BAR_COLOR_STATUSLINE HEXCOLOR
+    {
+        DLOG("statusline = %s\n", $2);
+        current_bar.colors.statusline = $2;
+    }
+    ;
+
+bar_color_focused_workspace:
+    TOK_BAR_COLOR_FOCUSED_WORKSPACE HEXCOLOR HEXCOLOR
+    {
+        DLOG("focused_ws = %s and %s\n", $2, $3);
+        current_bar.colors.focused_workspace_text = $2;
+        current_bar.colors.focused_workspace_bg = $3;
+    }
+    ;
+
+bar_color_active_workspace:
+    TOK_BAR_COLOR_ACTIVE_WORKSPACE HEXCOLOR HEXCOLOR
+    {
+        DLOG("active_ws = %s and %s\n", $2, $3);
+        current_bar.colors.active_workspace_text = $2;
+        current_bar.colors.active_workspace_bg = $3;
+    }
+    ;
+
+bar_color_inactive_workspace:
+    TOK_BAR_COLOR_INACTIVE_WORKSPACE HEXCOLOR HEXCOLOR
+    {
+        DLOG("inactive_ws = %s and %s\n", $2, $3);
+        current_bar.colors.inactive_workspace_text = $2;
+        current_bar.colors.inactive_workspace_bg = $3;
+    }
+    ;
+
+bar_color_urgent_workspace:
+    TOK_BAR_COLOR_URGENT_WORKSPACE HEXCOLOR HEXCOLOR
+    {
+        DLOG("urgent_ws = %s and %s\n", $2, $3);
+        current_bar.colors.urgent_workspace_text = $2;
+        current_bar.colors.urgent_workspace_bg = $3;
+    }
+    ;
+
 floating_modifier:
     TOKFLOATING_MODIFIER binding_modifiers
     {
@@ -887,6 +1211,14 @@ new_window:
     }
     ;
 
+new_float:
+    TOKNEWFLOAT border_style
+    {
+       DLOG("new floating windows should start with border style %d\n", $2);
+       config.default_floating_border = $2;
+    }
+    ;
+
 border_style:
     TOK_NORMAL      { $$ = BS_NORMAL; }
     | TOK_NONE      { $$ = BS_NONE; }
@@ -925,6 +1257,22 @@ force_focus_wrapping:
     }
     ;
 
+force_xinerama:
+    TOK_FORCE_XINERAMA bool
+    {
+        DLOG("force xinerama = %d\n", $2);
+        config.force_xinerama = $2;
+    }
+    ;
+
+workspace_back_and_forth:
+    TOK_WORKSPACE_AUTO_BAF bool
+    {
+        DLOG("automatic workspace back-and-forth = %d\n", $2);
+        config.workspace_auto_back_and_forth = $2;
+    }
+    ;
+
 workspace_bar:
     TOKWORKSPACEBAR bool
     {
@@ -1000,6 +1348,13 @@ workspace_name:
 assign:
     TOKASSIGN window_class STR
     {
+        /* This is the old, deprecated form of assignments. It’s provided for
+         * compatibility in version (4.1, 4.2, 4.3) and will be removed
+         * afterwards. It triggers an i3-nagbar warning starting from 4.1. */
+        ELOG("You are using the old assign syntax (without criteria). "
+             "Please see the User's Guide for the new syntax and fix "
+             "your config file.\n");
+        context->has_errors = true;
         printf("assignment of %s to *%s*\n", $2, $3);
         char *workspace = $3;
         char *criteria = $2;
@@ -1011,15 +1366,27 @@ assign:
         char *separator = NULL;
         if ((separator = strchr(criteria, '/')) != NULL) {
             *(separator++) = '\0';
-            match->title = sstrdup(separator);
+            char *pattern;
+            if (asprintf(&pattern, "(?i)%s", separator) == -1) {
+                ELOG("asprintf failed\n");
+                break;
+            }
+            match->title = regex_new(pattern);
+            free(pattern);
+            printf("  title = %s\n", separator);
+        }
+        if (*criteria != '\0') {
+            char *pattern;
+            if (asprintf(&pattern, "(?i)%s", criteria) == -1) {
+                ELOG("asprintf failed\n");
+                break;
+            }
+            match->class = regex_new(pattern);
+            free(pattern);
+            printf("  class = %s\n", criteria);
         }
-        if (*criteria != '\0')
-            match->class = sstrdup(criteria);
         free(criteria);
 
-        printf("  class = %s\n", match->class);
-        printf("  title = %s\n", match->title);
-
         /* Compatibility with older versions: If the assignment target starts
          * with ~, we create the equivalent of:
          *
@@ -1047,6 +1414,19 @@ assign:
         assignment->dest.workspace = workspace;
         TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
     }
+    | TOKASSIGN match STR
+    {
+        if (match_is_empty(&current_match)) {
+            ELOG("Match is empty, ignoring this assignment\n");
+            break;
+        }
+        printf("new assignment, using above criteria, to workspace %s\n", $3);
+        Assignment *assignment = scalloc(sizeof(Assignment));
+        assignment->match = current_match;
+        assignment->type = A_TO_WORKSPACE;
+        assignment->dest.workspace = $3;
+        TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
+    }
     ;
 
 window_class: