Return in child parsing as soon as the match was made.
bindsym mod4+s [title="^Sup ::"] scratchpad show
------------------------------------------------
+=== Nop
+
+There is a no operation command +nop+ which allows you to override default
+behavior. This can be useful for, e.g., disabling a focus change on clicks with
+the middle mouse button.
+
+The optional +comment+ argument is ignored, but will be printed to the log file
+for debugging purposes.
+
+*Syntax*:
+---------------
+nop [<comment>]
+---------------
+
+*Example*:
+----------------------------------------------
+# Disable focus change for clicks on titlebars
+# with the middle mouse button
+bindsym button2 nop
+----------------------------------------------
+
=== i3bar control
There are two options in the configuration of each i3bar instance that can be
size_t namelen = 0;
const char *utf8_name = cur_ws->canonical_name;
for (const char *walk = utf8_name; *walk != '\0'; walk++) {
- if (*walk == '"')
+ if (*walk == '"' || *walk == '\\')
num_quotes++;
/* While we’re looping through the name anyway, we can save one
* strlen(). */
for (inpos = 0, outpos = strlen("workspace \"");
inpos < namelen;
inpos++, outpos++) {
- if (utf8_name[inpos] == '"') {
+ if (utf8_name[inpos] == '"' || utf8_name[inpos] == '\\') {
buffer[outpos] = '\\';
outpos++;
}
}
}
+/* Strut partial tells i3 where to reserve space for i3bar. This is determined
+ * by the `position` bar config directive. */
+xcb_void_cookie_t config_strut_partial(i3_output *output) {
+ /* A local struct to save the strut_partial property */
+ struct {
+ uint32_t left;
+ uint32_t right;
+ uint32_t top;
+ uint32_t bottom;
+ uint32_t left_start_y;
+ uint32_t left_end_y;
+ uint32_t right_start_y;
+ uint32_t right_end_y;
+ uint32_t top_start_x;
+ uint32_t top_end_x;
+ uint32_t bottom_start_x;
+ uint32_t bottom_end_x;
+ } __attribute__((__packed__)) strut_partial;
+ memset(&strut_partial, 0, sizeof(strut_partial));
+
+ switch (config.position) {
+ case POS_NONE:
+ break;
+ case POS_TOP:
+ strut_partial.top = bar_height;
+ strut_partial.top_start_x = output->rect.x;
+ strut_partial.top_end_x = output->rect.x + output->rect.w;
+ break;
+ case POS_BOT:
+ strut_partial.bottom = bar_height;
+ strut_partial.bottom_start_x = output->rect.x;
+ strut_partial.bottom_end_x = output->rect.x + output->rect.w;
+ break;
+ }
+ return xcb_change_property(xcb_connection,
+ XCB_PROP_MODE_REPLACE,
+ output->bar,
+ atoms[_NET_WM_STRUT_PARTIAL],
+ XCB_ATOM_CARDINAL,
+ 32,
+ 12,
+ &strut_partial);
+}
+
/*
* Reconfigure all bars and create new bars for recently activated outputs
*
1,
(unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
- /* We need to tell i3, where to reserve space for i3bar */
- /* left, right, top, bottom, left_start_y, left_end_y,
- * right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
- * bottom_end_x */
- /* A local struct to save the strut_partial property */
- struct {
- uint32_t left;
- uint32_t right;
- uint32_t top;
- uint32_t bottom;
- uint32_t left_start_y;
- uint32_t left_end_y;
- uint32_t right_start_y;
- uint32_t right_end_y;
- uint32_t top_start_x;
- uint32_t top_end_x;
- uint32_t bottom_start_x;
- uint32_t bottom_end_x;
- } __attribute__((__packed__)) strut_partial;
- memset(&strut_partial, 0, sizeof(strut_partial));
-
- switch (config.position) {
- case POS_NONE:
- break;
- case POS_TOP:
- strut_partial.top = bar_height;
- strut_partial.top_start_x = walk->rect.x;
- strut_partial.top_end_x = walk->rect.x + walk->rect.w;
- break;
- case POS_BOT:
- strut_partial.bottom = bar_height;
- strut_partial.bottom_start_x = walk->rect.x;
- strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
- break;
- }
- xcb_void_cookie_t strut_cookie = xcb_change_property(xcb_connection,
- XCB_PROP_MODE_REPLACE,
- walk->bar,
- atoms[_NET_WM_STRUT_PARTIAL],
- XCB_ATOM_CARDINAL,
- 32,
- 12,
- &strut_partial);
+ xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
/* We also want a graphics context for the bars (it defines the properties
* with which we draw to them) */
values[3] = bar_height;
values[4] = XCB_STACK_MODE_ABOVE;
+ DLOG("Reconfiguring strut partial property for output %s\n", walk->name);
+ xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
+
DLOG("Destroying buffer for output %s\n", walk->name);
xcb_free_pixmap(xcb_connection, walk->buffer);
if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
xcb_request_failed(chg_cookie, "Could not change window") ||
xcb_request_failed(pm_cookie, "Could not create pixmap") ||
+ xcb_request_failed(strut_cookie, "Could not set strut") ||
(redraw_bars && (xcb_request_failed(umap_cookie, "Could not unmap window") ||
(config.hide_on_modifier == M_DOCK && xcb_request_failed(map_cookie, "Could not map window"))))) {
exit(EXIT_FAILURE);
state NOP:
comment = string
-> call cmd_nop($comment)
+ end
+ -> call cmd_nop(NULL)
state SCRATCHPAD:
'show'
if (**walk == '"') {
beginning++;
(*walk)++;
- while (**walk != '\0' && (**walk != '"' || *(*walk - 1) == '\\'))
- (*walk)++;
+ for (; **walk != '\0' && **walk != '"'; (*walk)++)
+ if (**walk == '\\' && *(*walk + 1) != '\0')
+ (*walk)++;
} else {
if (!as_word) {
/* For a string (starting with 's'), the delimiters are
for (inpos = 0, outpos = 0;
inpos < (*walk - beginning);
inpos++, outpos++) {
- /* We only handle escaped double quotes to not break
- * backwards compatibility with people using \w in
- * regular expressions etc. */
- if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
+ /* We only handle escaped double quotes and backslashes to not break
+ * backwards compatibility with people using \w in regular expressions
+ * etc. */
+ if (beginning[inpos] == '\\' && (beginning[inpos + 1] == '"' || beginning[inpos + 1] == '\\'))
inpos++;
str[outpos] = beginning[inpos];
}
return true;
}
+/*
+ * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients.
+ *
+ */
+static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+ xcb_atom_t name, xcb_get_property_reply_t *prop) {
+ DLOG("strut partial change for window 0x%08x\n", window);
+
+ Con *con;
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
+ return false;
+ }
+
+ if (prop == NULL) {
+ xcb_generic_error_t *err = NULL;
+ xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL,
+ XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX);
+ prop = xcb_get_property_reply(conn, strut_cookie, &err);
+
+ if (err != NULL) {
+ DLOG("got error when getting strut partial property: %d\n", err->error_code);
+ free(err);
+ return false;
+ }
+
+ if (prop == NULL) {
+ return false;
+ }
+ }
+
+ DLOG("That is con %p / %s\n", con, con->name);
+
+ window_update_strut_partial(con->window, prop);
+
+ /* we only handle this change for dock clients */
+ if (con->parent == NULL || con->parent->type != CT_DOCKAREA) {
+ return true;
+ }
+
+ Con *search_at = croot;
+ Con *output = con_get_output(con);
+ if (output != NULL) {
+ DLOG("Starting search at output %s\n", output->name);
+ search_at = output;
+ }
+
+ /* find out the desired position of this dock window */
+ if (con->window->reserved.top > 0 && con->window->reserved.bottom == 0) {
+ DLOG("Top dock client\n");
+ con->window->dock = W_DOCK_TOP;
+ } else if (con->window->reserved.top == 0 && con->window->reserved.bottom > 0) {
+ DLOG("Bottom dock client\n");
+ con->window->dock = W_DOCK_BOTTOM;
+ } else {
+ DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
+ if (con->geometry.y < (int16_t)(search_at->rect.height / 2)) {
+ DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
+ con->geometry.y, (search_at->rect.height / 2));
+ con->window->dock = W_DOCK_TOP;
+ } else {
+ DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
+ con->geometry.y, (search_at->rect.height / 2));
+ con->window->dock = W_DOCK_BOTTOM;
+ }
+ }
+
+ /* find the dockarea */
+ Con *dockarea = con_for_window(search_at, con->window, NULL);
+ assert(dockarea != NULL);
+
+ /* attach the dock to the dock area */
+ con_detach(con);
+ con->parent = dockarea;
+ TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused);
+ TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes);
+
+ tree_render();
+
+ return true;
+}
+
/* Returns false if the event could not be processed (e.g. the window could not
* be found), true otherwise */
typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
{0, UINT_MAX, handle_clientleader_change},
{0, UINT_MAX, handle_transient_for},
{0, 128, handle_windowrole_change},
- {0, 128, handle_class_change}};
+ {0, 128, handle_class_change},
+ {0, UINT_MAX, handle_strut_partial_change}};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
/*
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
property_handlers[6].atom = A_WM_WINDOW_ROLE;
property_handlers[7].atom = XCB_ATOM_WM_CLASS;
+ property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
@docked = get_dock_clients('top');
is(@docked, 1, 'dock client on top');
+# now change strut_partial to reserve space on the bottom and the dock should
+# be moved to the bottom dock area
+$x->change_property(
+ PROP_MODE_REPLACE,
+ $window->id,
+ $atomname->id,
+ $atomtype->id,
+ 32, # 32 bit integer
+ 12,
+ pack('L12', 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 1280, 0)
+);
+
+sync_with_i3;
+@docked = get_dock_clients('bottom');
+is(@docked, 1, 'dock client on bottom');
+
$window->destroy;
wait_for_unmap $window;
'error for unknown literal ok');
################################################################################
-# 3: Verify that escaping of double quotes works correctly
+# 3: Verify that escaping works correctly
################################################################################
is(parser_calls('workspace "foo"'),
'cmd_workspace_name(foo "bar)',
'Command with escaped double quotes ok');
+is(parser_calls('workspace "foo \\'),
+ 'cmd_workspace_name(foo \\)',
+ 'Command with single backslash in the end ok');
+
+is(parser_calls('workspace "foo\\\\bar"'),
+ 'cmd_workspace_name(foo\\bar)',
+ 'Command with escaped backslashes ok');
+
+is(parser_calls('workspace "foo\\\\\\"bar"'),
+ 'cmd_workspace_name(foo\\"bar)',
+ 'Command with escaped double quotes after escaped backslashes ok');
+
################################################################################
# 4: Verify that resize commands with a "px or ppt"-construction are parsed
# correctly