From 04c58c7325202a2b0faf4ee47a4e311c6e915d65 Mon Sep 17 00:00:00 2001 From: Yaroslav Molochko Date: Mon, 24 Sep 2012 02:14:00 +0300 Subject: [PATCH] Implement variable border widths for pixel/normal fixes #325 --- docs/ipc | 2 + docs/userguide | 16 +++++- include/commands.h | 2 +- include/con.h | 2 +- include/config.h | 1 + include/data.h | 3 +- parser-specs/commands.spec | 16 +++++- src/cfgparse.l | 5 +- src/cfgparse.y | 31 +++++++++- src/commands.c | 30 +++++++--- src/con.c | 69 ++++++++++------------- src/config.c | 1 + src/ipc.c | 7 ++- src/load_layout.c | 10 +++- src/render.c | 6 +- src/x.c | 2 +- testcases/t/116-nestedcons.t | 1 + testcases/t/161-regress-borders-restart.t | 4 +- testcases/t/169-border-toggle.t | 4 +- testcases/t/174-border-config.t | 5 +- 20 files changed, 144 insertions(+), 73 deletions(-) diff --git a/docs/ipc b/docs/ipc index 2716f180..a6666ef3 100644 --- a/docs/ipc +++ b/docs/ipc @@ -274,6 +274,8 @@ name (string):: border (string):: Can be either "normal", "none" or "1pixel", dependending on the container’s border style. +current_border_width (integer):: + Number of pixels of the border width. layout (string):: Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or "output". diff --git a/docs/userguide b/docs/userguide index 6a602fa8..9ffb678e 100644 --- a/docs/userguide +++ b/docs/userguide @@ -483,14 +483,26 @@ This option determines which border style new windows will have. The default is *Syntax*: --------------------------------------------- -new_window +new_window --------------------------------------------- - *Example*: --------------------- new_window 1pixel --------------------- +The "normal" and "pixel" border styles support an optional border width in +pixels: + +*Example*: +--------------------- +# The same as new_window none +new_window pixel 0 + +# A 3 px border +new_window pixel 3 +--------------------- + + === Hiding vertical borders You can hide vertical borders adjacent to the screen edges using diff --git a/include/commands.h b/include/commands.h index 43bd0b0a..6d195a09 100644 --- a/include/commands.h +++ b/include/commands.h @@ -83,7 +83,7 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz * Implementation of 'border normal|none|1pixel|toggle'. * */ -void cmd_border(I3_CMD, char *border_style_str); +void cmd_border(I3_CMD, char *border_style_str, char *border_width); /** * Implementation of 'nop '. diff --git a/include/con.h b/include/con.h index b4624373..5bf82487 100644 --- a/include/con.h +++ b/include/con.h @@ -250,7 +250,7 @@ int con_border_style(Con *con); * floating window. * */ -void con_set_border_style(Con *con, int border_style); +void con_set_border_style(Con *con, int border_style, int border_width); /** * This function changes the layout of a given container. Use it to handle diff --git a/include/config.h b/include/config.h index 056aa5ae..76fee94d 100644 --- a/include/config.h +++ b/include/config.h @@ -98,6 +98,7 @@ struct Config { int default_layout; int container_stack_limit; int container_stack_limit_value; + int default_border_width; /** Default orientation for new containers */ int default_orientation; diff --git a/include/data.h b/include/data.h index e78354f4..3cf22f61 100644 --- a/include/data.h +++ b/include/data.h @@ -55,7 +55,7 @@ typedef struct Window i3Window; *****************************************************************************/ typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t; typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t; -typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_1PIXEL = 2 } border_style_t; +typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_PIXEL = 2 } border_style_t; /** parameter to specify whether tree_close() and x_window_kill() should kill * only this specific window or the whole X11 client */ @@ -485,6 +485,7 @@ struct Con { /* the x11 border pixel attribute */ int border_width; + int current_border_width; /* minimum increment size specified for the window (in pixels) */ int width_increment; diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index 2c46e66c..4224707c 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -61,10 +61,20 @@ state EXEC: command = string -> call cmd_exec($nosn, $command) -# border normal|none|1pixel|toggle +# border normal|none|1pixel|toggle|1pixel state BORDER: - border_style = 'normal', 'none', '1pixel', 'toggle' - -> call cmd_border($border_style) + border_style = 'normal', 'pixel' + -> BORDER_WIDTH + border_style = 'none', 'toggle' + -> call cmd_border($border_style, "0") + border_style = '1pixel' + -> call cmd_border($border_style, "1") + +state BORDER_WIDTH: + end + -> call cmd_border($border_style, "2") + border_width = word + -> call cmd_border($border_style, $border_width) # layout default|stacked|stacking|tabbed|splitv|splith # layout toggle [split|all] diff --git a/src/cfgparse.l b/src/cfgparse.l index b752851b..6eef8a5a 100644 --- a/src/cfgparse.l +++ b/src/cfgparse.l @@ -56,6 +56,7 @@ EOL (\r?\n) %s OUTPUT_COND %s FOR_WINDOW_COND %s EAT_WHITESPACE +%s BORDER_WIDTH %x BUFFER_LINE %x BAR @@ -171,6 +172,7 @@ EOL (\r?\n) } [ \t]*→[ \t]* { BEGIN(WANT_STRING); } [ \t]+ { BEGIN(WANT_STRING); } +[^\n][0-9]+ { printf("Border width set to: %s\n", yytext); yylval.number = atoi(yytext); return NUMBER;} --no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; } . { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); } --release { printf("--release\n"); yy_pop_state(); return TOK_RELEASE; } @@ -200,9 +202,10 @@ auto { return TOK_AUTO; } workspace_layout { return TOK_WORKSPACE_LAYOUT; } new_window { return TOKNEWWINDOW; } new_float { return TOKNEWFLOAT; } -normal { return TOK_NORMAL; } +normal { yy_push_state(BORDER_WIDTH); return TOK_NORMAL; } none { return TOK_NONE; } 1pixel { return TOK_1PIXEL; } +pixel { yy_push_state(BORDER_WIDTH); return TOK_PIXEL; } hide_edge_borders { return TOK_HIDE_EDGE_BORDERS; } both { return TOK_BOTH; } focus_follows_mouse { return TOKFOCUSFOLLOWSMOUSE; } diff --git a/src/cfgparse.y b/src/cfgparse.y index c3efb063..8bc7990e 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -108,6 +108,7 @@ static int detect_version(char *buf) { strncasecmp(bind, "focus down", strlen("focus down")) == 0 || strncasecmp(bind, "border normal", strlen("border normal")) == 0 || strncasecmp(bind, "border 1pixel", strlen("border 1pixel")) == 0 || + strncasecmp(bind, "border pixel", strlen("border pixel")) == 0 || strncasecmp(bind, "border borderless", strlen("border borderless")) == 0 || strncasecmp(bind, "--no-startup-id", strlen("--no-startup-id")) == 0 || strncasecmp(bind, "bar", strlen("bar")) == 0) { @@ -739,6 +740,7 @@ void parse_file(const char *f) { %token TOKNEWFLOAT "new_float" %token TOK_NORMAL "normal" %token TOK_NONE "none" +%token TOK_PIXEL "pixel" %token TOK_1PIXEL "1pixel" %token TOK_HIDE_EDGE_BORDERS "hide_edge_borders" %token TOK_BOTH "both" @@ -818,6 +820,7 @@ void parse_file(const char *f) { %type bar_mode_mode %type bar_modifier_modifier %type optional_no_startup_id +%type optional_border_width %type optional_release %type command %type word_or_number @@ -1480,9 +1483,26 @@ new_float: ; border_style: - TOK_NORMAL { $$ = BS_NORMAL; } - | TOK_NONE { $$ = BS_NONE; } - | TOK_1PIXEL { $$ = BS_1PIXEL; } + TOK_NORMAL optional_border_width + { + config.default_border_width = $2; + $$ = BS_NORMAL; + } + | TOK_1PIXEL + { + config.default_border_width = 1; + $$ = BS_PIXEL; + } + | TOK_NONE + { + config.default_border_width = 0; + $$ = BS_NONE; + } + | TOK_PIXEL optional_border_width + { + config.default_border_width = $2; + $$ = BS_PIXEL; + } ; bool: @@ -1753,6 +1773,11 @@ exec_always: } ; +optional_border_width: + /* empty */ { $$ = 2; } // 2 pixels is the default value for any type of border + | NUMBER { $$ = $1; } + ; + optional_no_startup_id: /* empty */ { $$ = false; } | TOK_NO_STARTUP_ID { $$ = true; } diff --git a/src/commands.c b/src/commands.c index 2bbde9bf..000dd208 100644 --- a/src/commands.c +++ b/src/commands.c @@ -776,11 +776,11 @@ void cmd_resize(I3_CMD, char *way, char *direction, char *resize_px, char *resiz } /* - * Implementation of 'border normal|none|1pixel|toggle'. + * Implementation of 'border normal|none|1pixel|toggle|pixel'. * */ -void cmd_border(I3_CMD, char *border_style_str) { - DLOG("border style should be changed to %s\n", border_style_str); +void cmd_border(I3_CMD, char *border_style_str, char *border_width ) { + DLOG("border style should be changed to %s with border width %s\n", border_style_str, border_width); owindow *current; HANDLE_EMPTY_MATCH; @@ -788,23 +788,39 @@ void cmd_border(I3_CMD, char *border_style_str) { TAILQ_FOREACH(current, &owindows, owindows) { DLOG("matching: %p / %s\n", current->con, current->con->name); int border_style = current->con->border_style; + char *end; + int tmp_border_width = -1; + tmp_border_width = strtol(border_width, &end, 10); + if (end == border_width) { + /* no valid digits found */ + tmp_border_width = -1; + } if (strcmp(border_style_str, "toggle") == 0) { border_style++; border_style %= 3; + if (border_style == BS_NORMAL) + current->con->current_border_width = 2; + else if (border_style == BS_NONE) + current->con->current_border_width = 0; + else if (border_style == BS_PIXEL) + current->con->current_border_width = 1; } else { if (strcmp(border_style_str, "normal") == 0) border_style = BS_NORMAL; - else if (strcmp(border_style_str, "none") == 0) + else if (strcmp(border_style_str, "pixel") == 0) + border_style = BS_PIXEL; + else if (strcmp(border_style_str, "1pixel") == 0){ + border_style = BS_PIXEL; + tmp_border_width = 1; + } else if (strcmp(border_style_str, "none") == 0) border_style = BS_NONE; - else if (strcmp(border_style_str, "1pixel") == 0) - border_style = BS_1PIXEL; else { ELOG("BUG: called with border_style=%s\n", border_style_str); ysuccess(false); return; } } - con_set_border_style(current->con, border_style); + con_set_border_style(current->con, border_style, tmp_border_width); } cmd_output->needs_tree_render = true; diff --git a/src/con.c b/src/con.c index acc5e8af..db9ad5ee 100644 --- a/src/con.c +++ b/src/con.c @@ -55,6 +55,7 @@ Con *con_new(Con *parent, i3Window *window) { new->type = CT_CON; new->window = window; new->border_style = config.default_border; + new->current_border_width = -1; static int cnt = 0; DLOG("opening window %d\n", cnt); @@ -968,52 +969,38 @@ Con *con_descend_direction(Con *con, direction_t direction) { */ Rect con_border_style_rect(Con *con) { adjacent_t borders_to_hide = ADJ_NONE; + int border_width = con->current_border_width; + DLOG("The border width for con is set to: %d\n", con->current_border_width); Rect result; + if (con->current_border_width < 0) + border_width = config.default_border_width; + DLOG("Effective border width is set to: %d\n", border_width); /* Shortcut to avoid calling con_adjacent_borders() on dock containers. */ int border_style = con_border_style(con); if (border_style == BS_NONE) return (Rect){ 0, 0, 0, 0 }; borders_to_hide = con_adjacent_borders(con) & config.hide_edge_borders; - switch (border_style) { - case BS_NORMAL: - result = (Rect){2, 0, -(2 * 2), -2}; - if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) { - result.x -= 2; - result.width += 2; - } - if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) { - result.width += 2; - } - /* With normal borders we never hide the upper border */ - if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) { - result.height += 2; - } - return result; - - case BS_1PIXEL: - result = (Rect){1, 1, -2, -2}; - if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) { - result.x -= 1; - result.width += 1; - } - if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) { - result.width += 1; - } - if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE) { - result.y -= 1; - result.height += 1; - } - if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) { - result.height += 1; - } - return result; - - case BS_NONE: - return (Rect){0, 0, 0, 0}; - - default: - assert(false); + if (border_style == BS_NORMAL) { + result = (Rect){border_width, 0 , -(2 * border_width), -(border_width)}; + } else { + result = (Rect){border_width, border_width, -(2 * border_width), -(2 * border_width)}; + } + if (borders_to_hide & ADJ_LEFT_SCREEN_EDGE) { + result.x -= border_width; + result.width += border_width; + } + if (borders_to_hide & ADJ_RIGHT_SCREEN_EDGE) { + result.width += border_width; } + if (borders_to_hide & ADJ_UPPER_SCREEN_EDGE && (border_style != BS_NORMAL)) { + result.y -= border_width; + result.height += border_width; + } + if (borders_to_hide & ADJ_LOWER_SCREEN_EDGE) { + result.height += border_width; + } + return result; + } /* @@ -1068,10 +1055,11 @@ int con_border_style(Con *con) { * floating window. * */ -void con_set_border_style(Con *con, int border_style) { +void con_set_border_style(Con *con, int border_style, int border_width) { /* Handle the simple case: non-floating containerns */ if (!con_is_floating(con)) { con->border_style = border_style; + con->current_border_width = border_width; return; } @@ -1090,6 +1078,7 @@ void con_set_border_style(Con *con, int border_style) { /* Change the border style, get new border/decoration values. */ con->border_style = border_style; + con->current_border_width = border_width; bsr = con_border_style_rect(con); int deco_height = (con->border_style == BS_NORMAL ? config.font.height + 5 : 0); diff --git a/src/config.c b/src/config.c index 0cfa8eb6..9e47d74b 100644 --- a/src/config.c +++ b/src/config.c @@ -417,6 +417,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, config.default_border = BS_NORMAL; config.default_floating_border = BS_NORMAL; + config.default_border_width = 2; /* Set default_orientation to NO_ORIENTATION for auto orientation. */ config.default_orientation = NO_ORIENTATION; diff --git a/src/ipc.c b/src/ipc.c index 6f8e962d..84ef2c36 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -266,11 +266,14 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { case BS_NONE: ystr("none"); break; - case BS_1PIXEL: - ystr("1pixel"); + case BS_PIXEL: + ystr("pixel"); break; } + ystr("current_border_width"); + y(integer, con->current_border_width); + dump_rect(gen, "rect", con->rect); dump_rect(gen, "window_rect", con->window_rect); dump_rect(gen, "geometry", con->geometry); diff --git a/src/load_layout.c b/src/load_layout.c index fc513f51..d5735885 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -183,8 +183,11 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { sasprintf(&buf, "%.*s", (int)len, val); if (strcasecmp(buf, "none") == 0) json_node->border_style = BS_NONE; - else if (strcasecmp(buf, "1pixel") == 0) - json_node->border_style = BS_1PIXEL; + else if (strcasecmp(buf, "1pixel") == 0) { + json_node->border_style = BS_PIXEL; + json_node->current_border_width = 1; + } else if (strcasecmp(buf, "pixel") == 0) + json_node->border_style = BS_PIXEL; else if (strcasecmp(buf, "normal") == 0) json_node->border_style = BS_NORMAL; else LOG("Unhandled \"border\": %s\n", buf); @@ -278,6 +281,9 @@ static int json_int(void *ctx, long val) { if (strcasecmp(last_key, "num") == 0) json_node->num = val; + if (strcasecmp(last_key, "current_border_width") == 0) + json_node->current_border_width = val; + if (!parsing_swallows && strcasecmp(last_key, "id") == 0) json_node->old_id = val; diff --git a/src/render.c b/src/render.c index 6f0880d8..369273c8 100644 --- a/src/render.c +++ b/src/render.c @@ -311,7 +311,7 @@ void render_con(Con *con, bool render_fullscreen) { child->deco_rect.width = child->rect.width; child->deco_rect.height = deco_height; - if (children > 1 || (child->border_style != BS_1PIXEL && child->border_style != BS_NONE)) { + if (children > 1 || (child->border_style != BS_PIXEL && child->border_style != BS_NONE)) { child->rect.y += (deco_height * children); child->rect.height -= (deco_height * children); } @@ -328,12 +328,12 @@ void render_con(Con *con, bool render_fullscreen) { child->deco_rect.x = x - con->rect.x + i * child->deco_rect.width; child->deco_rect.y = y - con->rect.y; - if (children > 1 || (child->border_style != BS_1PIXEL && child->border_style != BS_NONE)) { + if (children > 1 || (child->border_style != BS_PIXEL && child->border_style != BS_NONE)) { child->rect.y += deco_height; child->rect.height -= deco_height; child->deco_rect.height = deco_height; } else { - child->deco_rect.height = (child->border_style == BS_1PIXEL ? 1 : 0); + child->deco_rect.height = (child->border_style == BS_PIXEL ? 1 : 0); } } diff --git a/src/x.c b/src/x.c index f1bfeb71..2c785750 100644 --- a/src/x.c +++ b/src/x.c @@ -431,7 +431,7 @@ void x_draw_decoration(Con *con) { xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &bottomline); } /* 1pixel border needs an additional line at the top */ - if (p->border_style == BS_1PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { + if (p->border_style == BS_PIXEL && !(borders_to_hide & ADJ_UPPER_SCREEN_EDGE)) { xcb_rectangle_t topline = { br.x, 0, con->rect.width + br.width + br.x, br.y }; xcb_poly_fill_rectangle(conn, con->pixmap, con->pm_gc, 1, &topline); } diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index 70008801..fc0b742a 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -69,6 +69,7 @@ my $expected = { border => 'normal', 'floating_nodes' => $ignore, workspace_layout => 'default', + current_border_width => -1, }; # a shallow copy is sufficient, since we only ignore values at the root diff --git a/testcases/t/161-regress-borders-restart.t b/testcases/t/161-regress-borders-restart.t index 1db64575..de1b7c81 100644 --- a/testcases/t/161-regress-borders-restart.t +++ b/testcases/t/161-regress-borders-restart.t @@ -36,13 +36,13 @@ is(get_border_style(), 'normal', 'border style normal'); cmd 'border 1pixel'; -is(get_border_style(), '1pixel', 'border style 1pixel after changing'); +is(get_border_style(), 'pixel', 'border style 1pixel after changing'); # perform an inplace-restart cmd 'restart'; does_i3_live; -is(get_border_style(), '1pixel', 'border style still 1pixel after restart'); +is(get_border_style(), 'pixel', 'border style still 1pixel after restart'); done_testing; diff --git a/testcases/t/169-border-toggle.t b/testcases/t/169-border-toggle.t index 7377194d..33f3a8ec 100644 --- a/testcases/t/169-border-toggle.t +++ b/testcases/t/169-border-toggle.t @@ -28,7 +28,7 @@ is($nodes[0]->{border}, 'normal', 'border style normal'); cmd 'border 1pixel'; @nodes = @{get_ws_content($tmp)}; -is($nodes[0]->{border}, '1pixel', 'border style 1pixel'); +is($nodes[0]->{border}, 'pixel', 'border style 1pixel'); cmd 'border none'; @nodes = @{get_ws_content($tmp)}; @@ -44,7 +44,7 @@ is($nodes[0]->{border}, 'none', 'border style none'); cmd 'border toggle'; @nodes = @{get_ws_content($tmp)}; -is($nodes[0]->{border}, '1pixel', 'border style 1pixel'); +is($nodes[0]->{border}, 'pixel', 'border style 1pixel'); cmd 'border toggle'; @nodes = @{get_ws_content($tmp)}; diff --git a/testcases/t/174-border-config.t b/testcases/t/174-border-config.t index 6e837cf0..56ad865a 100644 --- a/testcases/t/174-border-config.t +++ b/testcases/t/174-border-config.t @@ -65,7 +65,8 @@ $first = open_window; @content = @{get_ws_content($tmp)}; ok(@content == 1, 'one container opened'); -is($content[0]->{border}, '1pixel', 'border normal by default'); +is($content[0]->{border}, 'pixel', 'border pixel by default'); +is($content[0]->{current_border_width}, -1, 'border width pixels -1 (default)'); exit_gracefully($pid); @@ -119,7 +120,7 @@ $wscontent = get_ws($tmp); @floating = @{$wscontent->{floating_nodes}}; ok(@floating == 1, 'one floating container opened'); $floatingcon = $floating[0]; -is($floatingcon->{nodes}->[0]->{border}, '1pixel', 'border normal by default'); +is($floatingcon->{nodes}->[0]->{border}, 'pixel', 'border pixel by default'); exit_gracefully($pid); -- 2.39.2