diff --git a/docs/userguide b/docs/userguide index 5eb48554..2e42493c 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1006,6 +1006,24 @@ focus:: none:: The window will neither be focused, nor be marked urgent. +=== Drawing marks on window decoration + +If activated, marks on windows are drawn in their window decoration. However, +any mark starting with an underscore in its name (+_+) will not be drawn even if +this option is activated. + +The default for this option is +yes+. + +*Syntax*: +------------------- +show_marks [yes|no] +------------------- + +*Example*: +-------------- +show_marks yes +-------------- + == Configuring i3bar The bar at the bottom of your monitor is drawn by a separate process called @@ -1886,6 +1904,8 @@ The additional +--toggle+ option will remove the mark if the window already has this mark, add it if the window has none or replace the current mark if it has another mark. +Refer to +show_marks+ if you don't want marks to be shown in the window decoration. + *Syntax*: ------------------------------ mark [--toggle] identifier diff --git a/include/config.h b/include/config.h index 5b534fb7..fb11cbe3 100644 --- a/include/config.h +++ b/include/config.h @@ -179,6 +179,10 @@ struct Config { FOWA_NONE } focus_on_window_activation; + /** Specifies whether or not marks should be displayed in the window + * decoration. Marks starting with a "_" will be ignored either way. */ + bool show_marks; + /** The default border style for new windows. */ border_style_t default_border; diff --git a/include/config_directives.h b/include/config_directives.h index 8f99e648..b9189b2c 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -52,6 +52,7 @@ CFGFUN(force_xinerama, const char *value); CFGFUN(fake_outputs, const char *outputs); CFGFUN(force_display_urgency_hint, 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(assign, const char *workspace); CFGFUN(ipc_socket, const char *path); diff --git a/include/data.h b/include/data.h index cec571e9..d40ed292 100644 --- a/include/data.h +++ b/include/data.h @@ -543,6 +543,8 @@ struct Con { /* user-definable mark to jump to this container later */ char *mark; + /* cached to decide whether a redraw is needed */ + bool mark_changed; double percent; diff --git a/parser-specs/config.spec b/parser-specs/config.spec index e23c37c8..81357206 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -39,6 +39,7 @@ state INITIAL: 'fake_outputs', 'fake-outputs' -> FAKE_OUTPUTS 'force_display_urgency_hint' -> FORCE_DISPLAY_URGENCY_HINT 'focus_on_window_activation' -> FOCUS_ON_WINDOW_ACTIVATION + 'show_marks' -> SHOW_MARKS 'workspace' -> WORKSPACE 'ipc_socket', 'ipc-socket' -> IPC_SOCKET 'restart_state' -> RESTART_STATE @@ -205,6 +206,11 @@ state FORCE_DISPLAY_URGENCY_HINT: duration_ms = number -> FORCE_DISPLAY_URGENCY_HINT_MS +# show_marks +state SHOW_MARKS: + value = word + -> call cfg_show_marks($value) + state FORCE_DISPLAY_URGENCY_HINT_MS: 'ms' -> diff --git a/src/commands.c b/src/commands.c index 2aa1f537..dc60e974 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1046,6 +1046,7 @@ void cmd_mark(I3_CMD, char *mark, char *toggle) { owindow *current; TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); + current->con->mark_changed = true; if (toggle != NULL && current->con->mark && strcmp(current->con->mark, mark) == 0) { DLOG("removing window mark %s\n", mark); FREE(current->con->mark); @@ -1070,8 +1071,10 @@ void cmd_mark(I3_CMD, char *mark, char *toggle) { if (matched) continue; - if (con->mark && strcmp(con->mark, mark) == 0) + if (con->mark && strcmp(con->mark, mark) == 0) { FREE(con->mark); + con->mark_changed = true; + } } cmd_output->needs_tree_render = true; @@ -1087,14 +1090,20 @@ void cmd_unmark(I3_CMD, char *mark) { if (mark == NULL) { Con *con; TAILQ_FOREACH(con, &all_cons, all_cons) { + if (con->mark == NULL) + continue; + FREE(con->mark); + con->mark_changed = true; } DLOG("removed all window marks"); } else { Con *con; TAILQ_FOREACH(con, &all_cons, all_cons) { - if (con->mark && strcmp(con->mark, mark) == 0) + if (con->mark && strcmp(con->mark, mark) == 0) { FREE(con->mark); + con->mark_changed = true; + } } DLOG("removed window mark %s\n", mark); } diff --git a/src/config.c b/src/config.c index 36b7a163..8a1cb99c 100644 --- a/src/config.c +++ b/src/config.c @@ -190,6 +190,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000"); INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000"); + config.show_marks = true; + config.default_border = BS_NORMAL; config.default_floating_border = BS_NORMAL; config.default_border_width = logical_px(2); diff --git a/src/config_directives.c b/src/config_directives.c index 039cb29e..eddfaa3d 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -346,6 +346,10 @@ CFGFUN(focus_on_window_activation, const char *mode) { DLOG("Set new focus_on_window_activation mode = %i", config.focus_on_window_activation); } +CFGFUN(show_marks, const char *value) { + config.show_marks = eval_boolstr(value); +} + 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 diff --git a/src/x.c b/src/x.c index 9dd09117..d29d4bef 100644 --- a/src/x.c +++ b/src/x.c @@ -363,6 +363,7 @@ void x_draw_decoration(Con *con) { (con->window == NULL || !con->window->name_x_changed) && !parent->pixmap_recreated && !con->pixmap_recreated && + !con->mark_changed && memcmp(p, con->deco_render_params, sizeof(struct deco_render_params)) == 0) { free(p); goto copy_pixmaps; @@ -381,6 +382,7 @@ void x_draw_decoration(Con *con) { parent->pixmap_recreated = false; con->pixmap_recreated = false; + con->mark_changed = false; /* 2: draw the client.background, but only for the parts around the client_rect */ if (con->window != NULL) { @@ -531,10 +533,25 @@ void x_draw_decoration(Con *con) { //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult); int indent_px = (indent_level * 5) * indent_mult; + int mark_width = 0; + if (config.show_marks && con->mark != NULL && (con->mark)[0] != '_') { + char *formatted_mark; + sasprintf(&formatted_mark, "[%s]", con->mark); + i3String *mark = i3string_from_utf8(formatted_mark); + FREE(formatted_mark); + mark_width = predict_text_width(mark); + + draw_text(mark, parent->pixmap, parent->pm_gc, + con->deco_rect.x + con->deco_rect.width - mark_width - logical_px(2), + con->deco_rect.y + text_offset_y, mark_width); + + I3STRING_FREE(mark); + } + draw_text(win->name, parent->pixmap, parent->pm_gc, - con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y, - con->deco_rect.width - 2 - indent_px); + con->deco_rect.x + logical_px(2) + indent_px, con->deco_rect.y + text_offset_y, + con->deco_rect.width - logical_px(2) - indent_px - mark_width - logical_px(2)); after_title: /* Since we don’t clip the text at all, it might in some cases be painted diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index a6b1fe0c..3314aaf7 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -433,7 +433,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4 EOT my $expected_all_tokens = <<'EOT'; -ERROR: CONFIG: Expected one of these tokens: , '#', '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', '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', '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: , '#', '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', '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' EOT my $expected_end = <<'EOT';