From: Michael Stapelberg Date: Mon, 8 Oct 2012 11:23:48 +0000 (+0200) Subject: port the entire old config parser to the new one X-Git-Tag: 4.4~72 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=40c624e1c4d62b62b4930e09b24558eaa8874fc9;p=i3%2Fi3 port the entire old config parser to the new one --- diff --git a/include/config_directives.h b/include/config_directives.h index 5922144f..1faaa973 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -4,26 +4,75 @@ * i3 - an improved dynamic tiling window manager * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * - * commands.c: all command functions (see commands_parser.c) + * config_directives.h: all config storing functions (see config_parser.c) * */ #ifndef I3_CONFIG_DIRECTIVES_H #define I3_CONFIG_DIRECTIVES_H -//#include "config_parser.h" +#include "config_parser.h" -/** The beginning of the prototype for every cmd_ function. */ -#define I3_CFG Match *current_match, struct CommandResult *cmd_output +/** The beginning of the prototype for every cfg_ function. */ +#define I3_CFG Match *current_match, struct ConfigResult *result -/** +/* Defines a configuration function, that is, anything that can be called by + * 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__ ) + +/* The following functions are called by the config parser, see + * parser-specs/config.spec. They get the parsed parameters and store them in + * our data structures, e.g. cfg_font gets a font name and stores it in + * config.font. * - */ -void cfg_font(I3_CFG, const char *font); + * Since they are so similar, individual comments were omitted. */ + +CFGFUN(criteria_init, int _state); +CFGFUN(criteria_add, const char *ctype, const char *cvalue); +CFGFUN(criteria_pop_state); -void cfg_mode_binding(I3_CFG, const char *bindtype, const char *modifiers, const char *key, const char *command); +CFGFUN(font, const char *font); +CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command); +CFGFUN(for_window, const char *command); +CFGFUN(floating_minimum_size, const long width, const long height); +CFGFUN(floating_maximum_size, const long width, const long height); +CFGFUN(default_orientation, const char *orientation); +CFGFUN(workspace_layout, const char *layout); +CFGFUN(workspace_back_and_forth, const char *value); +CFGFUN(focus_follows_mouse, const char *value); +CFGFUN(force_focus_wrapping, const char *value); +CFGFUN(force_xinerama, const char *value); +CFGFUN(fake_outputs, const char *outputs); +CFGFUN(force_display_urgency_hint, const long duration_ms); +CFGFUN(hide_edge_borders, const char *borders); +CFGFUN(assign, const char *workspace); +CFGFUN(ipc_socket, const char *path); +CFGFUN(restart_state, const char *path); +CFGFUN(popup_during_fullscreen, const char *value); +CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator); +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); -void cfg_enter_mode(I3_CFG, const char *mode); +CFGFUN(enter_mode, const char *mode); +CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command); -void cfg_exec(I3_CFG, const char *exectype, const char *no_startup_id, const char *command); +CFGFUN(bar_font, const char *font); +CFGFUN(bar_mode, const char *mode); +CFGFUN(bar_output, const char *output); +CFGFUN(bar_verbose, const char *verbose); +CFGFUN(bar_modifier, const char *modifier); +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); +CFGFUN(bar_socket_path, const char *socket_path); +CFGFUN(bar_tray_output, const char *output); +CFGFUN(bar_color_single, const char *colorclass, const char *color); +CFGFUN(bar_status_command, const char *command); +CFGFUN(bar_workspace_buttons, const char *value); +CFGFUN(bar_finish); #endif diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 75c07232..a5b3b6b6 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -9,18 +9,251 @@ # Use :source highlighting.vim in vim to get syntax highlighting # for this file. -# TODO: get it to parse the default config :) -# TODO: comment handling (on their own line, at the end of a line) +# TODO: should we implement an include statement for the criteria part so we DRY? state INITIAL: # We have an end token here for all the commands which just call some # function without using an explicit 'end' token. end -> - #'[' -> call cmd_criteria_init(); CRITERIA - 'font' -> FONT - 'mode' -> MODENAME - exectype = 'exec_always', 'exec' - -> EXEC + '#' -> IGNORE_LINE + 'set' -> IGNORE_LINE + bindtype = 'bindsym', 'bindcode' -> BINDING + 'bar' -> BARBRACE + 'font' -> FONT + 'mode' -> MODENAME + 'floating_minimum_size' -> FLOATING_MINIMUM_SIZE_WIDTH + 'floating_maximum_size' -> FLOATING_MAXIMUM_SIZE_WIDTH + 'floating_modifier' -> FLOATING_MODIFIER + 'default_orientation' -> DEFAULT_ORIENTATION + 'workspace_layout' -> WORKSPACE_LAYOUT + windowtype = 'new_window', 'new_float' -> NEW_WINDOW + 'hide_edge_borders' -> HIDE_EDGE_BORDERS + 'for_window' -> FOR_WINDOW + 'assign' -> ASSIGN + 'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE + 'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING + 'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA + 'workspace_auto_back_and_forth' -> WORKSPACE_BACK_AND_FORTH + 'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS + 'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT + 'workspace' -> WORKSPACE + 'ipc_socket', 'ipc-socket' -> IPC_SOCKET + 'restart_state' -> RESTART_STATE + 'popup_during_fullscreen' -> POPUP_DURING_FULLSCREEN + exectype = 'exec_always', 'exec' -> EXEC + colorclass = 'client.background' + -> COLOR_SINGLE + colorclass = 'client.focused_inactive', 'client.focused', 'client.unfocused', 'client.urgent' + -> COLOR_BORDER + +# We ignore comments and 'set' lines (variables). +state IGNORE_LINE: + end, string + -> INITIAL + +# floating_minimum_size x +state FLOATING_MINIMUM_SIZE_WIDTH: + width = number + -> FLOATING_MINIMUM_SIZE_X + +state FLOATING_MINIMUM_SIZE_X: + 'x' + -> FLOATING_MINIMUM_SIZE_HEIGHT + +state FLOATING_MINIMUM_SIZE_HEIGHT: + height = number + -> call cfg_floating_minimum_size(&width, &height) + +# floating_maximum_size x +state FLOATING_MAXIMUM_SIZE_WIDTH: + width = number + -> FLOATING_MAXIMUM_SIZE_X + +state FLOATING_MAXIMUM_SIZE_X: + 'x' + -> FLOATING_MAXIMUM_SIZE_HEIGHT + +state FLOATING_MAXIMUM_SIZE_HEIGHT: + height = number + -> call cfg_floating_maximum_size(&width, &height) + +# floating_modifier +state FLOATING_MODIFIER: + modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control' + -> + '+' + -> + end + -> call cfg_floating_modifier($modifiers) + +# default_orientation +state DEFAULT_ORIENTATION: + orientation = 'horizontal', 'vertical', 'auto' + -> call cfg_default_orientation($orientation) + +# workspace_layout +state WORKSPACE_LAYOUT: + layout = 'default', 'stacking', 'stacked', 'tabbed' + -> call cfg_workspace_layout($layout) + +# new_window +# new_float +# TODO: new_float is not in the userguide yet +# TODO: pixel is not in the userguide yet +state NEW_WINDOW: + border = 'normal', 'pixel' + -> NEW_WINDOW_PIXELS + border = '1pixel', 'none' + -> call cfg_new_window($windowtype, $border, -1) + +state NEW_WINDOW_PIXELS: + end + -> call cfg_new_window($windowtype, $border, 2) + width = number + -> NEW_WINDOW_PIXELS_PX + +state NEW_WINDOW_PIXELS_PX: + 'px' + -> + end + -> call cfg_new_window($windowtype, $border, &width) + +# hide_edge_borders +# also hide_edge_borders for compatibility +state HIDE_EDGE_BORDERS: + hide_borders = 'none', 'vertical', 'horizontal', 'both' + -> call cfg_hide_edge_borders($hide_borders) + hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active' + -> call cfg_hide_edge_borders($hide_borders) + +# for_window command +state FOR_WINDOW: + '[' + -> call cfg_criteria_init(FOR_WINDOW_COMMAND); CRITERIA + +state FOR_WINDOW_COMMAND: + command = string + -> call cfg_for_window($command) + +# assign [→] workspace +state ASSIGN: + '[' + -> call cfg_criteria_init(ASSIGN_WORKSPACE); CRITERIA + +state ASSIGN_WORKSPACE: + '→' + -> + workspace = string + -> call cfg_assign($workspace) + +# Criteria: Used by for_window and assign. +state CRITERIA: + ctype = 'class' -> CRITERION + ctype = 'instance' -> CRITERION + ctype = 'window_role' -> CRITERION + ctype = 'con_id' -> CRITERION + ctype = 'id' -> CRITERION + ctype = 'con_mark' -> CRITERION + ctype = 'title' -> CRITERION + ctype = 'urgent' -> CRITERION + ']' + -> call cfg_criteria_pop_state() + +state CRITERION: + '=' -> CRITERION_STR + +state CRITERION_STR: + cvalue = word + -> call cfg_criteria_add($ctype, $cvalue); CRITERIA + +# focus_follows_mouse bool +state FOCUS_FOLLOWS_MOUSE: + value = word + -> call cfg_focus_follows_mouse($value) + +# force_focus_wrapping +state FORCE_FOCUS_WRAPPING: + value = word + -> call cfg_force_focus_wrapping($value) + +# force_xinerama +state FORCE_XINERAMA: + value = word + -> call cfg_force_xinerama($value) + +# workspace_back_and_forth +state WORKSPACE_BACK_AND_FORTH: + value = word + -> call cfg_workspace_back_and_forth($value) + + +# fake_outputs (for testcases) +state FAKE_OUTPUTS: + outputs = string + -> call cfg_fake_outputs($outputs) + +# force_display_urgency_hint ms +state FORCE_DISPLAY_URGENCY_HINT: + duration_ms = number + -> FORCE_DISPLAY_URGENCY_HINT_MS + +state FORCE_DISPLAY_URGENCY_HINT_MS: + 'ms' + -> + end + -> call cfg_force_display_urgency_hint(&duration_ms) + +# workspace output +state WORKSPACE: + workspace = word + -> WORKSPACE_OUTPUT + +state WORKSPACE_OUTPUT: + 'output' + -> WORKSPACE_OUTPUT_STR + +state WORKSPACE_OUTPUT_STR: + output = string + -> call cfg_workspace($workspace, $output) + +# ipc-socket +state IPC_SOCKET: + path = string + -> call cfg_ipc_socket($path) + +# restart_state (for testcases) +state RESTART_STATE: + path = string + -> call cfg_restart_state($path) + +# popup_during_fullscreen +state POPUP_DURING_FULLSCREEN: + value = 'ignore', 'leave_fullscreen' + -> call cfg_popup_during_fullscreen($value) + +# client.background +state COLOR_SINGLE: + color = word + -> call cfg_color_single($colorclass, $color) + +# colorclass border background text indicator +state COLOR_BORDER: + border = word + -> COLOR_BACKGROUND + +state COLOR_BACKGROUND: + background = word + -> COLOR_TEXT + +state COLOR_TEXT: + text = word + -> COLOR_INDICATOR + +state COLOR_INDICATOR: + indicator = word + -> call cfg_color($colorclass, $border, $background, $text, $indicator) + end + -> call cfg_color($colorclass, $border, $background, $text, NULL) # [--no-startup-id] command state EXEC: @@ -29,6 +262,30 @@ state EXEC: command = string -> call cfg_exec($exectype, $no_startup_id, $command) +# font font +state FONT: + font = string + -> call cfg_font($font) + +# bindsym/bindcode +state BINDING: + modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Mode_switch' + -> + '+' + -> + key = word + -> BINDCOMMAND + +state BINDCOMMAND: + release = '--release' + -> + command = string + -> call cfg_binding($bindtype, $modifiers, $key, $release, $command) + +################################################################################ +# Mode configuration +################################################################################ + state MODENAME: modename = word -> call cfg_enter_mode($modename); MODEBRACE @@ -38,13 +295,21 @@ state MODEBRACE: -> MODE state MODE: + end -> + '#' -> MODE_IGNORE_LINE + 'set' -> MODE_IGNORE_LINE bindtype = 'bindsym', 'bindcode' -> MODE_BINDING '}' -> INITIAL +# We ignore comments and 'set' lines (variables). +state MODE_IGNORE_LINE: + end, string + -> MODE + state MODE_BINDING: - modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control' + modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Mode_switch' -> '+' -> @@ -52,9 +317,121 @@ state MODE_BINDING: -> MODE_BINDCOMMAND state MODE_BINDCOMMAND: + release = '--release' + -> command = string - -> call cfg_mode_binding($bindtype, $modifiers, $key, $command); MODE + -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $command); MODE -state FONT: +################################################################################ +# Bar configuration (i3bar) +################################################################################ + +state BARBRACE: + '{' + -> BAR + +state BAR: + end -> + '#' -> BAR_IGNORE_LINE + 'set' -> BAR_IGNORE_LINE + 'i3bar_command' -> BAR_BAR_COMMAND + 'status_command' -> BAR_STATUS_COMMAND + 'socket_path' -> BAR_SOCKET_PATH + 'mode' -> BAR_MODE + 'modifier' -> BAR_MODIFIER + 'position' -> BAR_POSITION + 'output' -> BAR_OUTPUT + 'tray_output' -> BAR_TRAY_OUTPUT + 'font' -> BAR_FONT + 'workspace_buttons' -> BAR_WORKSPACE_BUTTONS + 'verbose' -> BAR_VERBOSE + 'colors' -> BAR_COLORS_BRACE + '}' + -> call cfg_bar_finish(); INITIAL + +# We ignore comments and 'set' lines (variables). +state BAR_IGNORE_LINE: + end, string + -> BAR + +state BAR_BAR_COMMAND: + command = string + -> call cfg_bar_i3bar_command($command); BAR + +state BAR_STATUS_COMMAND: + command = string + -> call cfg_bar_status_command($command); BAR + +state BAR_SOCKET_PATH: + path = string + -> call cfg_bar_socket_path($path); BAR + +state BAR_MODE: + mode = 'dock', 'hide' + -> call cfg_bar_mode($mode); BAR + +state BAR_MODIFIER: + modifier = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Control', 'Shift' + -> call cfg_bar_modifier($modifier); BAR + +state BAR_POSITION: + position = 'top', 'bottom' + -> call cfg_bar_position($position); BAR + +state BAR_OUTPUT: + output = string + -> call cfg_bar_output($output); BAR + +state BAR_TRAY_OUTPUT: + output = string + -> call cfg_bar_tray_output($output); BAR + +state BAR_FONT: font = string - -> call cfg_font($font) + -> call cfg_bar_font($font); BAR + +state BAR_WORKSPACE_BUTTONS: + value = word + -> call cfg_bar_workspace_buttons($value); BAR + +state BAR_VERBOSE: + value = word + -> call cfg_bar_verbose($value); BAR + +state BAR_COLORS_BRACE: + '{' + -> BAR_COLORS + +state BAR_COLORS: + end -> + '#' -> BAR_COLORS_IGNORE_LINE + 'set' -> BAR_COLORS_IGNORE_LINE + colorclass = 'background', 'statusline' + -> BAR_COLORS_SINGLE + colorclass = 'focused_workspace', 'active_workspace', 'inactive_workspace', 'urgent_workspace' + -> BAR_COLORS_BORDER + '}' + -> BAR + +# We ignore comments and 'set' lines (variables). +state BAR_COLORS_IGNORE_LINE: + end, string + -> BAR_COLORS + +state BAR_COLORS_SINGLE: + color = word + -> call cfg_bar_color_single($colorclass, $color); BAR_COLORS + +state BAR_COLORS_BORDER: + border = word + -> BAR_COLORS_BACKGROUND + +state BAR_COLORS_BACKGROUND: + background = word + -> BAR_COLORS_TEXT + +state BAR_COLORS_TEXT: + end + -> call cfg_bar_color($colorclass, $border, $background, NULL); BAR_COLORS + text = word + -> call cfg_bar_color($colorclass, $border, $background, $text); BAR_COLORS diff --git a/parser-specs/highlighting.vim b/parser-specs/highlighting.vim index f3d1aaba..e0966d57 100644 --- a/parser-specs/highlighting.vim +++ b/parser-specs/highlighting.vim @@ -9,7 +9,7 @@ syntax match i3specComment /#.*/ highlight link i3specComment Comment syntax region i3specLiteral start=/'/ end=/'/ -syntax keyword i3specToken string word end +syntax keyword i3specToken string word number end highlight link i3specLiteral String highlight link i3specToken String diff --git a/src/config_directives.c b/src/config_directives.c index 9660866e..70e5792a 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -6,7 +6,7 @@ * i3 - an improved dynamic tiling window manager * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE) * - * config_directives.c: all command functions (see config_parser.c) + * config_directives.c: all config storing functions (see config_parser.c) * */ #include @@ -24,9 +24,150 @@ y(map_close); \ } while (0) +/******************************************************************************* + * Criteria functions. + ******************************************************************************/ + +static int criteria_next_state; + +/* + * Initializes the specified 'Match' data structure and the initial state of + * commands.c for matching target windows of a command. + * + */ +CFGFUN(criteria_init, int _state) { + criteria_next_state = _state; + + DLOG("Initializing criteria, current_match = %p, state = %d\n", current_match, _state); + match_init(current_match); +} + +CFGFUN(criteria_pop_state) { + result->next_state = criteria_next_state; +} + +/* + * Interprets a ctype=cvalue pair and adds it to the current match + * specification. + * + */ +CFGFUN(criteria_add, const char *ctype, const char *cvalue) { + DLOG("ctype=*%s*, cvalue=*%s*\n", ctype, cvalue); + + if (strcmp(ctype, "class") == 0) { + current_match->class = regex_new(cvalue); + return; + } + + if (strcmp(ctype, "instance") == 0) { + current_match->instance = regex_new(cvalue); + return; + } + + if (strcmp(ctype, "window_role") == 0) { + current_match->role = regex_new(cvalue); + return; + } + + if (strcmp(ctype, "con_id") == 0) { + char *end; + long parsed = strtol(cvalue, &end, 10); + if (parsed == LONG_MIN || + parsed == LONG_MAX || + parsed < 0 || + (end && *end != '\0')) { + ELOG("Could not parse con id \"%s\"\n", cvalue); + } else { + current_match->con_id = (Con*)parsed; + printf("id as int = %p\n", current_match->con_id); + } + return; + } + + if (strcmp(ctype, "id") == 0) { + char *end; + long parsed = strtol(cvalue, &end, 10); + if (parsed == LONG_MIN || + parsed == LONG_MAX || + parsed < 0 || + (end && *end != '\0')) { + ELOG("Could not parse window id \"%s\"\n", cvalue); + } else { + current_match->id = parsed; + printf("window id as int = %d\n", current_match->id); + } + return; + } + + if (strcmp(ctype, "con_mark") == 0) { + current_match->mark = regex_new(cvalue); + return; + } + + if (strcmp(ctype, "title") == 0) { + current_match->title = regex_new(cvalue); + return; + } + + if (strcmp(ctype, "urgent") == 0) { + if (strcasecmp(cvalue, "latest") == 0 || + strcasecmp(cvalue, "newest") == 0 || + strcasecmp(cvalue, "recent") == 0 || + strcasecmp(cvalue, "last") == 0) { + current_match->urgent = U_LATEST; + } else if (strcasecmp(cvalue, "oldest") == 0 || + strcasecmp(cvalue, "first") == 0) { + current_match->urgent = U_OLDEST; + } + return; + } + + ELOG("Unknown criterion: %s\n", ctype); +} + +/* TODO: refactor the above criteria code into a single file (with src/commands.c). */ + +/******************************************************************************* + * Utility functions + ******************************************************************************/ + +static bool eval_boolstr(const char *str) { + return (strcasecmp(str, "1") == 0 || + strcasecmp(str, "yes") == 0 || + strcasecmp(str, "true") == 0 || + strcasecmp(str, "on") == 0 || + strcasecmp(str, "enable") == 0 || + strcasecmp(str, "active") == 0); +} + +static uint32_t modifiers_from_str(const char *str) { + /* It might be better to use strtok() here, but the simpler strstr() should + * do for now. */ + uint32_t result = 0; + if (str == NULL) + return result; + if (strstr(str, "Mod1") != NULL) + result |= BIND_MOD1; + if (strstr(str, "Mod2") != NULL) + result |= BIND_MOD2; + if (strstr(str, "Mod3") != NULL) + result |= BIND_MOD3; + if (strstr(str, "Mod4") != NULL) + result |= BIND_MOD4; + if (strstr(str, "Mod5") != NULL) + result |= BIND_MOD5; + if (strstr(str, "Control") != NULL) + result |= BIND_CONTROL; + if (strstr(str, "Shift") != NULL) + result |= BIND_SHIFT; + if (strstr(str, "Mode_switch") != NULL) + result |= BIND_MODE_SWITCH; + return result; +} + static char *font_pattern; -void cfg_font(I3_CFG, const char *font) { +CFGFUN(font, const char *font) { config.font = load_font(font, true); set_font(&config.font); @@ -35,16 +176,57 @@ void cfg_font(I3_CFG, const char *font) { font_pattern = sstrdup(font); } -void cfg_mode_binding(I3_CFG, const char *bindtype, const char *modifiers, const char *key, const char *command) { - printf("cfg_mode_binding: got bindtype\n"); +// TODO: refactor with mode_binding +CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) { + Binding *new_binding = scalloc(sizeof(Binding)); + new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS); + if (strcmp(bindtype, "bindsym") == 0) { + new_binding->symbol = sstrdup(key); + } else { + // TODO: strtol with proper error handling + new_binding->keycode = atoi(key); + } + new_binding->mods = modifiers_from_str(modifiers); + new_binding->command = sstrdup(command); + TAILQ_INSERT_TAIL(bindings, new_binding, bindings); } -void cfg_enter_mode(I3_CFG, const char *mode) { - // TODO: error handling: if mode == '{', the mode name is missing - printf("mode name: %s\n", mode); + +/******************************************************************************* + * Mode handling + ******************************************************************************/ + +static struct bindings_head *current_bindings; + +CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *command) { + Binding *new_binding = scalloc(sizeof(Binding)); + new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS); + if (strcmp(bindtype, "bindsym") == 0) { + new_binding->symbol = sstrdup(key); + } else { + // TODO: strtol with proper error handling + new_binding->keycode = atoi(key); + } + new_binding->mods = modifiers_from_str(modifiers); + new_binding->command = sstrdup(command); + TAILQ_INSERT_TAIL(current_bindings, new_binding, bindings); +} + +CFGFUN(enter_mode, const char *modename) { + if (strcasecmp(modename, "default") == 0) { + ELOG("You cannot use the name \"default\" for your mode\n"); + exit(1); + } + DLOG("\t now in mode %s\n", modename); + struct Mode *mode = scalloc(sizeof(struct Mode)); + mode->name = sstrdup(modename); + mode->bindings = scalloc(sizeof(struct bindings_head)); + TAILQ_INIT(mode->bindings); + current_bindings = mode->bindings; + SLIST_INSERT_HEAD(&modes, mode, modes); } -void cfg_exec(I3_CFG, const char *exectype, const char *no_startup_id, const char *command) { +CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command) { struct Autostart *new = smalloc(sizeof(struct Autostart)); new->command = sstrdup(command); new->no_startup_id = (no_startup_id != NULL); @@ -54,3 +236,315 @@ void cfg_exec(I3_CFG, const char *exectype, const char *no_startup_id, const cha TAILQ_INSERT_TAIL(&autostarts_always, new, autostarts_always); } } + +CFGFUN(for_window, const char *command) { + if (match_is_empty(current_match)) { + ELOG("Match is empty, ignoring this for_window statement\n"); + return; + } + DLOG("\t should execute command %s for the criteria mentioned above\n", command); + Assignment *assignment = scalloc(sizeof(Assignment)); + assignment->type = A_COMMAND; + match_copy(&(assignment->match), current_match); + assignment->dest.command = sstrdup(command); + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); +} + +CFGFUN(floating_minimum_size, const long width, const long height) { + config.floating_minimum_width = width; + config.floating_minimum_height = height; +} + +CFGFUN(floating_maximum_size, const long width, const long height) { + config.floating_maximum_width = width; + config.floating_maximum_height = height; +} + +CFGFUN(floating_modifier, const char *modifiers) { + config.floating_modifier = modifiers_from_str(modifiers); +} + +CFGFUN(default_orientation, const char *orientation) { + if (strcmp(orientation, "horizontal") == 0) + config.default_orientation = HORIZ; + else if (strcmp(orientation, "vertical") == 0) + config.default_orientation = VERT; + else config.default_orientation = NO_ORIENTATION; +} + +CFGFUN(workspace_layout, const char *layout) { + if (strcmp(layout, "default") == 0) + config.default_layout = L_DEFAULT; + else if (strcmp(layout, "stacking") == 0 || + strcmp(layout, "stacked") == 0) + config.default_layout = L_STACKED; + else config.default_layout = L_TABBED; +} + +CFGFUN(new_window, const char *windowtype, const char *border, const long width) { + // FIXME: when using new_float *and* new_window with different border + // types, this breaks because default_border_width gets overwritten. + + int border_style; + int border_width; + + if (strcmp(border, "1pixel") == 0) { + border_style = BS_PIXEL; + border_width = 1; + } else if (strcmp(border, "none") == 0) { + border_style = BS_NONE; + border_width = 0; + } else if (strcmp(border, "pixel") == 0) { + border_style = BS_PIXEL; + border_width = width; + } else { + border_style = BS_NORMAL; + border_width = width; + } + + if (strcmp(windowtype, "new_window") == 0) { + config.default_border = border_style; + } else { + config.default_floating_border = border_style; + } +} + +CFGFUN(hide_edge_borders, const char *borders) { + if (strcmp(borders, "vertical") == 0) + config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; + else if (strcmp(borders, "horizontal") == 0) + config.hide_edge_borders = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; + else if (strcmp(borders, "both") == 0) + config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; + else if (strcmp(borders, "none") == 0) + config.hide_edge_borders = ADJ_NONE; + else if (eval_boolstr(borders)) + config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; + else config.hide_edge_borders = ADJ_NONE; +} + +CFGFUN(focus_follows_mouse, const char *value) { + config.disable_focus_follows_mouse = !eval_boolstr(value); +} + +CFGFUN(force_xinerama, const char *value) { + config.force_xinerama = eval_boolstr(value); +} + +CFGFUN(force_focus_wrapping, const char *value) { + config.force_focus_wrapping = eval_boolstr(value); +} + +CFGFUN(workspace_back_and_forth, const char *value) { + config.workspace_auto_back_and_forth = eval_boolstr(value); +} + +CFGFUN(fake_outputs, const char *outputs) { + config.fake_outputs = sstrdup(outputs); +} + +CFGFUN(force_display_urgency_hint, const long duration_ms) { + config.workspace_urgency_timer = duration_ms / 1000.0; +} + +CFGFUN(workspace, const char *workspace, const char *output) { + DLOG("Assigning workspace \"%s\" to output \"%s\"\n", workspace, output); + /* 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, workspace) == 0) { + ELOG("You have a duplicate workspace assignment for workspace \"%s\"\n", + workspace); + assignment->output = sstrdup(output); + duplicate = true; + } + } + if (!duplicate) { + assignment = scalloc(sizeof(struct Workspace_Assignment)); + assignment->name = sstrdup(workspace); + assignment->output = sstrdup(output); + TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments); + } +} + +CFGFUN(ipc_socket, const char *path) { + config.ipc_socket_path = sstrdup(path); +} + +CFGFUN(restart_state, const char *path) { + config.restart_state_path = sstrdup(path); +} + +CFGFUN(popup_during_fullscreen, const char *value) { + config.popup_during_fullscreen = + (strcmp(value, "ignore") == 0 ? PDF_IGNORE : PDF_LEAVE_FULLSCREEN); +} + +CFGFUN(color_single, const char *colorclass, const char *color) { + /* used for client.background only currently */ + config.client.background = get_colorpixel(color); +} + +CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator) { +#define APPLY_COLORS(classname) \ + do { \ + if (strcmp(colorclass, "client." #classname) == 0) { \ + config.client.classname.border = get_colorpixel(border); \ + config.client.classname.background = get_colorpixel(background); \ + config.client.classname.text = get_colorpixel(text); \ + if (indicator != NULL) { \ + config.client. classname .indicator = get_colorpixel(indicator); \ + } \ + } \ + } while (0) + + APPLY_COLORS(focused_inactive); + APPLY_COLORS(focused); + APPLY_COLORS(unfocused); + APPLY_COLORS(urgent); + +#undef APPLY_COLORS +} + +CFGFUN(assign, const char *workspace) { + if (match_is_empty(current_match)) { + ELOG("Match is empty, ignoring this assignment\n"); + return; + } + DLOG("new assignment, using above criteria, to workspace %s\n", workspace); + Assignment *assignment = scalloc(sizeof(Assignment)); + match_copy(&(assignment->match), current_match); + assignment->type = A_TO_WORKSPACE; + assignment->dest.workspace = sstrdup(workspace); + TAILQ_INSERT_TAIL(&assignments, assignment, assignments); +} + +/******************************************************************************* + * Bar configuration (i3bar) + ******************************************************************************/ + +static Barconfig current_bar; + +CFGFUN(bar_font, const char *font) { + FREE(current_bar.font); + current_bar.font = sstrdup(font); +} + +CFGFUN(bar_mode, const char *mode) { + current_bar.mode = (strcmp(mode, "hide") == 0 ? M_HIDE : M_DOCK); +} + +CFGFUN(bar_output, const char *output) { + 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] = sstrdup(output); + current_bar.num_outputs = new_outputs; +} + +CFGFUN(bar_verbose, const char *verbose) { + current_bar.verbose = eval_boolstr(verbose); +} + +CFGFUN(bar_modifier, const char *modifier) { + if (strcmp(modifier, "Mod1") == 0) + current_bar.modifier = M_MOD1; + else if (strcmp(modifier, "Mod2") == 0) + current_bar.modifier = M_MOD2; + else if (strcmp(modifier, "Mod3") == 0) + current_bar.modifier = M_MOD3; + else if (strcmp(modifier, "Mod4") == 0) + current_bar.modifier = M_MOD4; + else if (strcmp(modifier, "Mod5") == 0) + current_bar.modifier = M_MOD5; + else if (strcmp(modifier, "Control") == 0) + current_bar.modifier = M_CONTROL; + else if (strcmp(modifier, "Shift") == 0) + current_bar.modifier = M_SHIFT; +} + +CFGFUN(bar_position, const char *position) { + current_bar.position = (strcmp(position, "top") == 0 ? P_TOP : P_BOTTOM); +} + +CFGFUN(bar_i3bar_command, const char *i3bar_command) { + FREE(current_bar.i3bar_command); + current_bar.i3bar_command = sstrdup(i3bar_command); +} + +CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text) { +#define APPLY_COLORS(classname) \ + do { \ + if (strcmp(colorclass, #classname) == 0) { \ + if (text != NULL) { \ + /* New syntax: border, background, text */ \ + current_bar.colors. classname ## _border = sstrdup(border); \ + current_bar.colors. classname ## _bg = sstrdup(background); \ + current_bar.colors. classname ## _text = sstrdup(text); \ + } else { \ + /* Old syntax: text, background */ \ + current_bar.colors. classname ## _bg = sstrdup(background); \ + current_bar.colors. classname ## _text = sstrdup(border); \ + } \ + } \ + } while (0) + + APPLY_COLORS(focused_workspace); + APPLY_COLORS(active_workspace); + APPLY_COLORS(inactive_workspace); + APPLY_COLORS(urgent_workspace); + +#undef APPLY_COLORS +} + +CFGFUN(bar_socket_path, const char *socket_path) { + FREE(current_bar.socket_path); + current_bar.socket_path = sstrdup(socket_path); +} + +CFGFUN(bar_tray_output, const char *output) { + FREE(current_bar.tray_output); + current_bar.tray_output = sstrdup(output); +} + +CFGFUN(bar_color_single, const char *colorclass, const char *color) { + if (strcmp(colorclass, "background") == 0) + current_bar.colors.background = sstrdup(color); + else current_bar.colors.statusline = sstrdup(color); +} + +CFGFUN(bar_status_command, const char *command) { + FREE(current_bar.status_command); + current_bar.status_command = sstrdup(command); +} + +CFGFUN(bar_workspace_buttons, const char *value) { + current_bar.hide_workspace_buttons = !eval_boolstr(value); +} + +CFGFUN(bar_finish) { + DLOG("\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'; + } + + /* If no font was explicitly set, we use the i3 font as default */ + if (!current_bar.font && font_pattern) + current_bar.font = sstrdup(font_pattern); + + /* Copy the current (static) structure into a dynamically allocated + * one, then cleanup our static one. */ + Barconfig *bar_config = scalloc(sizeof(Barconfig)); + memcpy(bar_config, ¤t_bar, sizeof(Barconfig)); + TAILQ_INSERT_TAIL(&barconfigs, bar_config, configs); + + memset(¤t_bar, '\0', sizeof(Barconfig)); +}