#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
-#include <limits.h>
#include "all.h"
}
/*
- * Calls i3-migrate-config-to-v4.pl to migrate a configuration file (input
+ * Calls i3-migrate-config-to-v4 to migrate a configuration file (input
* buffer).
*
* Returns the converted config file or NULL if there was an error (for
NULL, /* will be replaced by the executable path */
NULL
};
- exec_i3_utility("i3-migrate-config-to-v4.pl", argv);
+ exec_i3_utility("i3-migrate-config-to-v4", argv);
}
/* parent */
ret = read(readpipe[0], converted + read_bytes, conv_size - read_bytes);
if (ret == -1) {
warn("Cannot read from pipe");
+ FREE(converted);
return NULL;
}
read_bytes += ret;
configerror_pid = -1;
}
+/*
+ * 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);
+ }
+}
+
/*
* 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
ev_child *child = smalloc(sizeof(ev_child));
ev_child_init(child, &nagbar_exited, configerror_pid, 0);
ev_child_start(main_loop, child);
+
+ /* 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);
}
/*
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;
continue;
if (strcasecmp(key, "set") == 0) {
- if (value[0] != '$')
- die("Malformed variable assignment, name has to start with $\n");
+ if (value[0] != '$') {
+ ELOG("Malformed variable assignment, name has to start with $\n");
+ continue;
+ }
/* get key/value for this variable */
char *v_key = value, *v_value;
- if ((v_value = strstr(value, " ")) == NULL)
- die("Malformed variable assignment, need a value\n");
+ if ((v_value = strstr(value, " ")) == NULL &&
+ (v_value = strstr(value, "\t")) == NULL) {
+ ELOG("Malformed variable assignment, need a value\n");
+ continue;
+ }
*(v_value++) = '\0';
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.pl %s.O > %s\n", f, f);
+ printf(" i3-migrate-config-to-v4 %s.O > %s\n", f, f);
printf("****************************************************************\n");
printf("\n");
free(new);
} else {
printf("\n");
printf("**********************************************************************\n");
- printf("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4.pl\n");
+ printf("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4\n");
printf("was not correctly installed on your system?\n");
printf("**********************************************************************\n");
printf("\n");
exit(1);
}
+ check_for_duplicate_bindings(context);
+
if (context->has_errors) {
start_configerror_nagbar(f);
}
%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 TOK_MARK "mark"
%token TOK_CLASS "class"
+%token TOK_INSTANCE "instance"
%token TOK_ID "id"
%token TOK_CON_ID "con_id"
%token TOK_TITLE "title"
%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
| orientation
| workspace_layout
| new_window
+ | new_float
| focus_follows_mouse
| force_focus_wrapping
| workspace_bar
;
criteria:
+ criteria criterion
+ | criterion
+ ;
+
+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 = regex_new($3);
+ free($3);
}
| TOK_CON_ID '=' STR
{
| 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);
}
;
}
;
+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; }
}
DLOG("Should assign workspace %s to output %s\n", ws_name, $4);
- struct Workspace_Assignment *assignment = scalloc(sizeof(struct Workspace_Assignment));
- assignment->name = ws_name;
- assignment->output = $4;
- TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments);
+ /* Check for earlier assignments of the same workspace so that we
+ * don’t have assignments of a single workspace to different
+ * outputs */
+ struct Workspace_Assignment *assignment;
+ bool duplicate = false;
+ TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) {
+ if (strcasecmp(assignment->name, ws_name) == 0) {
+ ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n",
+ ws_name);
+ assignment->output = $4;
+ duplicate = true;
+ }
+ }
+ if (!duplicate) {
+ assignment = scalloc(sizeof(struct Workspace_Assignment));
+ assignment->name = ws_name;
+ assignment->output = $4;
+ TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments);
+ }
}
}
| TOKWORKSPACE NUMBER workspace_name
assign:
TOKASSIGN window_class STR
{
+ /* TODO: the assign command also needs some kind of new syntax where we
+ * just use criteria. Then deprecate the old form */
printf("assignment of %s to *%s*\n", $2, $3);
char *workspace = $3;
char *criteria = $2;
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:
*
- * for_window [class="foo"] mode floating
+ * for_window [class="foo"] floating enable
*/
if (*workspace == '~') {
workspace++;
assignment->dest.workspace = workspace;
TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
}
+ | TOKASSIGN match STR
+ {
+ 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:
char *hex;
if (asprintf(&hex, "#%s", $2) == -1)
die("asprintf()");
+ free($2);
$$ = get_colorpixel(hex);
free(hex);
}