*Syntax*:
----------------------------------
-bindsym [--release] [--whole-window] [Modifiers+]button[n] command
+bindsym [--release] [--border] [--whole-window] [Modifiers+]button[n] command
----------------------------------
By default, the binding will only run when you click on the titlebar of the
-window. If the +--whole-window+ flag is given, it will run when any part of the
-window is clicked. If the +--release+ flag is given, it will run when the mouse
-button is released.
+window. If the +--release+ flag is given, it will run when the mouse button
+is released.
+
+If the +--whole-window+ flag is given, the binding will also run when any part
+of the window is clicked, with the exception of the border. To have a bind run
+when the border is clicked, specify the +--border+ flag.
*Examples*:
--------------------------------
also when restarting i3 you should use the +exec_always+
keyword. These commands will be run in order.
+See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
+and +,+ (comma): they chain commands together in i3, so you need to use quoted
+strings if they appear in your command.
+
*Syntax*:
-------------------
exec [--no-startup-id] command
force_display_urgency_hint 500 ms
---------------------------------
+ === Delaying exiting on zero displays
+
+ Outputs may disappear momentarily and come back later. For example,
+ using a docking station that does not announce the undock (e.g. ACPI Undock
+ event triggered through manually pushing a button before actually ejecting
+ the notebook). During the removal of the notebook from the docking station,
+ all outputs disappear momentarily.
+
+ To prevent i3 from exiting when no output is available momentarily, you can
+ tell i3 to delay a certain time first and check available outputs again using
+ the +delay_exit_on_zero_displays+ directive. Setting the value to 0 disables
+ this feature.
+
+ The default is 500ms.
+
+ *Syntax*:
+ ----------------------------------------
+ delay_exit_on_zero_displays <timeout> ms
+ ----------------------------------------
+
+ *Example*:
+ ----------------------------------
+ delay_exit_on_zero_displays 500 ms
+ ----------------------------------
+
=== Focus on window activation
[[focus_on_window_activation]]
i3-msg border none
--------------------------
+[[command_chaining]]
+
Commands can be chained by using +;+ (a semicolon). So, to move a window to a
specific workspace and immediately switch to that workspace, you can configure
the following keybinding:
Compares the window instance (the first part of WM_CLASS)
window_role::
Compares the window role (WM_WINDOW_ROLE).
+window_type::
+ Compare the window type (_NET_WM_WINDOW_TYPE). Possible values are
+ +normal+, +dialog+, +utility+, +toolbar+, +splash+, +menu+, +dropdown_menu+,
+ +popup_menu+ and +toolti+.
id::
Compares the X11 window ID, which you can get via +xwininfo+ for example.
title::
What good is a window manager if you can’t actually start any applications?
The exec command starts an application by passing the command you specify to a
shell. This implies that you can use globbing (wildcards) and programs will be
-searched in your $PATH.
+searched in your +$PATH+.
+
+See <<command_chaining>> for details on the special meaning of +;+ (semicolon)
+and +,+ (comma): they chain commands together in i3, so you need to use quoted
+strings if they appear in your command.
*Syntax*:
------------------------------
bindsym $mod+x move container to output VGA1
--------------------------------------------------------
+=== Moving containers/workspaces to marks
+
+To move a container to another container with a specific mark (see <<vim_like_marks>>),
+you can use the following command.
+
+The window will be moved right after the marked container in the tree, i.e., it ends up
+in the same position as if you had opened a new window when the marked container was
+focused. If the mark is on a split container, the window will appear as a new child
+after the currently focused child within that container.
+
+*Syntax*:
+------------------------------------
+move window|container to mark <mark>
+------------------------------------
+
+*Example*:
+--------------------------------------------------------
+for_window [instance="tabme"] move window to mark target
+--------------------------------------------------------
+
[[resizingconfig]]
=== Resizing containers/windows
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* include/config.h: Contains all structs/variables for the configurable
* part of i3 as well as functions handling the configuration file (calling
* flag can be delayed using an urgency timer. */
float workspace_urgency_timer;
+ /** Use a timer to delay exiting when no output is available.
+ * This can prevent i3 from exiting when all outputs disappear momentarily. */
+ float zero_disp_exit_timer_ms;
+
/** Behavior when a window sends a NET_ACTIVE_WINDOW message. */
enum {
/* Focus if the target workspace is visible, set urgency hint otherwise. */
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config_directives.h: all config storing functions (see config_parser.c)
*
CFGFUN(force_xinerama, const char *value);
CFGFUN(fake_outputs, const char *outputs);
CFGFUN(force_display_urgency_hint, const long duration_ms);
+ CFGFUN(delay_exit_on_zero_displays, const long duration_ms);
CFGFUN(focus_on_window_activation, const char *mode);
CFGFUN(show_marks, const char *value);
CFGFUN(hide_edge_borders, const char *borders);
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 *whole_window, const char *command);
+CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command);
CFGFUN(enter_mode, const char *mode);
-CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
+CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command);
CFGFUN(bar_font, const char *font);
CFGFUN(bar_separator_symbol, const char *separator);
# vim:ts=2:sw=2:expandtab
#
# i3 - an improved dynamic tiling window manager
-# © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+# © 2009 Michael Stapelberg and contributors (see also: LICENSE)
#
# parser-specs/config.spec: Specification file for generate-command-parser.pl
# which will generate the appropriate header files for our C parser.
'workspace_auto_back_and_forth' -> WORKSPACE_BACK_AND_FORTH
'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS
'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT
+ 'delay_exit_on_zero_displays' -> DELAY_EXIT_ON_ZERO_DISPLAYS
'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION
'show_marks' -> SHOW_MARKS
'workspace' -> WORKSPACE
ctype = 'window_role' -> CRITERION
ctype = 'con_id' -> CRITERION
ctype = 'id' -> CRITERION
+ ctype = 'window_type' -> CRITERION
ctype = 'con_mark' -> CRITERION
ctype = 'title' -> CRITERION
ctype = 'urgent' -> CRITERION
end
-> call cfg_force_display_urgency_hint(&duration_ms)
+ # delay_exit_on_zero_displays <delay> ms
+ state DELAY_EXIT_ON_ZERO_DISPLAYS:
+ duration_ms = number
+ -> DELAY_EXIT_ON_ZERO_DISPLAYS_MS
+
+ state DELAY_EXIT_ON_ZERO_DISPLAYS_MS:
+ 'ms'
+ ->
+ end
+ -> call cfg_delay_exit_on_zero_displays(&duration_ms)
+
# focus_on_window_activation <smart|urgent|focus|none>
state FOCUS_ON_WINDOW_ACTIVATION:
mode = word
state BINDING:
release = '--release'
->
+ border = '--border'
+ ->
whole_window = '--whole-window'
->
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
state BINDCOMMAND:
release = '--release'
->
+ border = '--border'
+ ->
whole_window = '--whole-window'
->
command = string
- -> call cfg_binding($bindtype, $modifiers, $key, $release, $whole_window, $command)
+ -> call cfg_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $command)
################################################################################
# Mode configuration
state MODE_BINDING:
release = '--release'
->
+ border = '--border'
+ ->
whole_window = '--whole-window'
->
modifiers = 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', 'Shift', 'Control', 'Ctrl', 'Mode_switch', '$mod'
state MODE_BINDCOMMAND:
release = '--release'
->
+ border = '--border'
+ ->
whole_window = '--whole-window'
->
command = string
- -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $whole_window, $command); MODE
+ -> call cfg_mode_binding($bindtype, $modifiers, $key, $release, $border, $whole_window, $command); MODE
################################################################################
# Bar configuration (i3bar)
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config.c: Configuration file (calling the parser (src/config_parser.c) with
* the correct path, switching key bindings mode).
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
+ /* Set default zero displays exit delay to 500ms */
+ if (config.zero_disp_exit_timer_ms == 0)
+ config.zero_disp_exit_timer_ms = 500;
+
parse_configuration(override_configpath, true);
if (reload) {
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* config_directives.c: all config storing functions (see config_parser.c)
*
return;
}
+ if (strcmp(ctype, "window_type") == 0) {
+ if (strcasecmp(cvalue, "normal") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_NORMAL;
+ else if (strcasecmp(cvalue, "dialog") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_DIALOG;
+ else if (strcasecmp(cvalue, "utility") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_UTILITY;
+ else if (strcasecmp(cvalue, "toolbar") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLBAR;
+ else if (strcasecmp(cvalue, "splash") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_SPLASH;
+ else if (strcasecmp(cvalue, "menu") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_MENU;
+ else if (strcasecmp(cvalue, "dropdown_menu") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
+ else if (strcasecmp(cvalue, "popup_menu") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_POPUP_MENU;
+ else if (strcasecmp(cvalue, "tooltip") == 0)
+ current_match->window_type = A__NET_WM_WINDOW_TYPE_TOOLTIP;
+ else
+ ELOG("unknown window_type value \"%s\"\n", cvalue);
+
+ return;
+ }
+
if (strcmp(ctype, "con_mark") == 0) {
current_match->mark = regex_new(cvalue);
return;
font_pattern = sstrdup(font);
}
-CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
- configure_binding(bindtype, modifiers, key, release, whole_window, command, DEFAULT_BINDING_MODE);
+CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) {
+ configure_binding(bindtype, modifiers, key, release, border, whole_window, command, DEFAULT_BINDING_MODE);
}
/*******************************************************************************
static char *current_mode;
-CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command) {
- configure_binding(bindtype, modifiers, key, release, whole_window, command, current_mode);
+CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) {
+ configure_binding(bindtype, modifiers, key, release, border, whole_window, command, current_mode);
}
CFGFUN(enter_mode, const char *modename) {
config.workspace_urgency_timer = duration_ms / 1000.0;
}
+ CFGFUN(delay_exit_on_zero_displays, const long duration_ms) {
+ config.zero_disp_exit_timer_ms = duration_ms;
+ }
+
CFGFUN(focus_on_window_activation, const char *mode) {
if (strcmp(mode, "smart") == 0)
config.focus_on_window_activation = FOWA_SMART;
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* main.c: Initialization, main loop
*
int main(int argc, char *argv[]) {
/* Keep a symbol pointing to the I3_VERSION string constant so that we have
* it in gdb backtraces. */
- const char *i3_version __attribute__((unused)) = I3_VERSION;
+ const char *_i3_version __attribute__((unused)) = i3_version;
char *override_configpath = NULL;
bool autostart = true;
char *layout_path = NULL;
only_check_config = true;
break;
case 'v':
- printf("i3 version " I3_VERSION " © 2009-2014 Michael Stapelberg and contributors\n");
+ printf("i3 version %s © 2009 Michael Stapelberg and contributors\n", i3_version);
exit(EXIT_SUCCESS);
break;
case 'm':
- printf("Binary i3 version: " I3_VERSION " © 2009-2014 Michael Stapelberg and contributors\n");
+ printf("Binary i3 version: %s © 2009 Michael Stapelberg and contributors\n", i3_version);
display_running_version();
exit(EXIT_SUCCESS);
break;
free(cwd);
}
- LOG("i3 " I3_VERSION " starting\n");
+ LOG("i3 %s starting\n", i3_version);
conn = xcb_connect(NULL, &conn_screen);
if (xcb_connection_has_error(conn))
root_screen = xcb_aux_get_screen(conn, conn_screen);
root = root_screen->root;
+/* Place requests for the atoms we need as soon as possible */
+#define xmacro(atom) \
+ xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
+#include "atoms.xmacro"
+#undef xmacro
+
/* By default, we use the same depth and visual as the root window, which
* usually is TrueColor (24 bit depth) and the corresponding visual.
* However, we also check if a 32 bit depth and visual are available (for
xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(conn, root);
xcb_query_pointer_cookie_t pointercookie = xcb_query_pointer(conn, root);
+/* Setup NetWM atoms */
+#define xmacro(name) \
+ do { \
+ xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
+ if (!reply) { \
+ ELOG("Could not get atom " #name "\n"); \
+ exit(-1); \
+ } \
+ A_##name = reply->atom; \
+ free(reply); \
+ } while (0);
+#include "atoms.xmacro"
+#undef xmacro
+
load_configuration(conn, override_configpath, false);
if (config.ipc_socket_path == NULL) {
}
DLOG("root geometry reply: (%d, %d) %d x %d\n", greply->x, greply->y, greply->width, greply->height);
-/* Place requests for the atoms we need as soon as possible */
-#define xmacro(atom) \
- xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
-#include "atoms.xmacro"
-#undef xmacro
-
xcursor_load_cursors();
/* Set a cursor for the root window (otherwise the root window will show no
restore_connect();
-/* Setup NetWM atoms */
-#define xmacro(name) \
- do { \
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \
- if (!reply) { \
- ELOG("Could not get atom " #name "\n"); \
- exit(-1); \
- } \
- A_##name = reply->atom; \
- free(reply); \
- } while (0);
-#include "atoms.xmacro"
-#undef xmacro
-
property_handlers_init();
ewmh_setup_hints();
ELOG("ERROR: No screen at (%d, %d), starting on the first screen\n",
pointerreply->root_x, pointerreply->root_y);
output = get_first_output();
+ if (!output)
+ die("No usable outputs available.\n");
}
con_focus(con_descend_focused(output_get_content(output->con)));
* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* For more information on RandR, please see the X.org RandR specification at
* http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt
if (output->active)
return output;
- die("No usable outputs available.\n");
+ return NULL;
}
/*
return;
}
+ if (output->connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
+ DLOG("Disabling output %s: it is disconnected\n", new->name);
+ new->to_be_disabled = true;
+ return;
+ }
+
bool updated = update_if_necessary(&(new->rect.x), crtc->x) |
update_if_necessary(&(new->rect.y), crtc->y) |
update_if_necessary(&(new->rect.width), crtc->width) |
if (!new->active) {
DLOG("width/height 0/0, disabling output\n");
return;
+ } else {
+ new->to_be_disabled = false;
}
DLOG("mode: %dx%d+%d+%d\n", new->rect.width, new->rect.height,
new->changed = true;
}
- /*
- * (Re-)queries the outputs via RandR and stores them in the list of outputs.
- *
- */
- void randr_query_outputs(void) {
+ static bool __randr_query_outputs(void) {
Output *output, *other, *first;
xcb_randr_get_output_primary_cookie_t pcookie;
xcb_randr_get_screen_resources_current_cookie_t rcookie;
xcb_randr_output_t *randr_outputs;
if (randr_disabled)
- return;
+ return true;
/* Get screen resources (primary output, crtcs, outputs, modes) */
rcookie = xcb_randr_get_screen_resources_current(conn, root);
DLOG("primary output is %08x\n", primary->output);
if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) {
disable_randr(conn);
- return;
+ return true;
}
cts = res->config_timestamp;
DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name);
first = get_first_output();
+ if (!first) {
+ FREE(res);
+ FREE(primary);
+ return false;
+ }
/* TODO: refactor the following code into a nice function. maybe
* use an on_destroy callback which is implement differently for
FREE(res);
FREE(primary);
+
+ return true;
+ }
+
+ /*
+ * (Re-)queries the outputs via RandR and stores them in the list of outputs.
+ *
+ */
+ void randr_query_outputs(void) {
+ static bool first_query = true;
+
+ if (first_query) {
+ /* find monitors at least once via RandR */
+ if (!__randr_query_outputs())
+ die("No usable outputs available.\n");
+ first_query = false;
+ } else {
+ /* requery */
+ if (!__randr_query_outputs()) {
+ DLOG("sleep %f ms due to zero displays\n", config.zero_disp_exit_timer_ms);
+ usleep(config.zero_disp_exit_timer_ms * 1000);
+
+ if (!__randr_query_outputs())
+ die("No usable outputs available.\n");
+ }
+ }
}
/*
bindsym --release Mod1+x exec foo
bindsym --whole-window button3 nop
bindsym --release --whole-window button3 nop
+ bindsym --border button3 nop
+ bindsym --release --border button3 nop
}
EOT
my $expected = <<'EOT';
cfg_enter_mode(meh)
-cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), resize grow)
-cfg_mode_binding(bindcode, Mod1, 44, (null), (null), resize shrink)
-cfg_mode_binding(bindsym, Mod1, x, --release, (null), exec foo)
-cfg_mode_binding(bindsym, (null), button3, (null), --whole-window, nop)
-cfg_mode_binding(bindsym, (null), button3, --release, --whole-window, nop)
+cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), (null), resize grow)
+cfg_mode_binding(bindcode, Mod1, 44, (null), (null), (null), resize shrink)
+cfg_mode_binding(bindsym, Mod1, x, --release, (null), (null), exec foo)
+cfg_mode_binding(bindsym, (null), button3, (null), (null), --whole-window, nop)
+cfg_mode_binding(bindsym, (null), button3, --release, (null), --whole-window, nop)
+cfg_mode_binding(bindsym, (null), button3, (null), --border, (null), nop)
+cfg_mode_binding(bindsym, (null), button3, --release, --border, (null), nop)
EOT
is(parser_calls($config),
$expected,
'force_display_urgency_hint ok');
+ ################################################################################
+ # delay_exit_on_zero_displays
+ ################################################################################
+
+ is(parser_calls('delay_exit_on_zero_displays 300'),
+ "cfg_delay_exit_on_zero_displays(300)\n",
+ 'delay_exit_on_zero_displays ok');
+
+ is(parser_calls('delay_exit_on_zero_displays 500 ms'),
+ "cfg_delay_exit_on_zero_displays(500)\n",
+ 'delay_exit_on_zero_displays ok');
+
+ is(parser_calls('delay_exit_on_zero_displays 700ms'),
+ "cfg_delay_exit_on_zero_displays(700)\n",
+ 'delay_exit_on_zero_displays ok');
+
+ $config = <<'EOT';
+ delay_exit_on_zero_displays 300
+ delay_exit_on_zero_displays 500 ms
+ delay_exit_on_zero_displays 700ms
+ delay_exit_on_zero_displays 700
+ EOT
+
+ $expected = <<'EOT';
+ cfg_delay_exit_on_zero_displays(300)
+ cfg_delay_exit_on_zero_displays(500)
+ cfg_delay_exit_on_zero_displays(700)
+ cfg_delay_exit_on_zero_displays(700)
+ EOT
+
+ is(parser_calls($config),
+ $expected,
+ 'delay_exit_on_zero_displays ok');
+
################################################################################
# workspace
################################################################################
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', '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', 'delay_exit_on_zero_displays', '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';
$expected = <<'EOT';
cfg_enter_mode(yo)
-cfg_mode_binding(bindsym, (null), x, (null), (null), resize shrink left)
+cfg_mode_binding(bindsym, (null), x, (null), (null), (null), resize shrink left)
ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'bindsym', 'bindcode', 'bind', '}'
ERROR: CONFIG: (in file <stdin>)
ERROR: CONFIG: Line 1: mode "yo" {