From 1e284b9488e46266b66d1c5031f7e815e889f7c0 Mon Sep 17 00:00:00 2001 From: SuperDJY Date: Tue, 11 Jan 2022 01:34:12 +0800 Subject: [PATCH] feat(control char): add basic control char support (#238) * feat(control char): add basic control char support * feat(control char): fix wrong x when \n after \b * feat(control char): add `\t` support, same behavior as `\t` in c printf * Gonna go with 4 spaces per tab to be safer * fix(control chars): leading control chars run into 'out of bounds memory acessing', and render at wrong position * doc(control chars): describe the control chars behavior and declare the influenced options and bump date to SEP 2021. * update to NOV * Bump years * Redo manpage Co-authored-by: Raymond Li --- i3lock.1 | 60 +++++++++++++++++++++------- unlock_indicator.c | 99 +++++++++++++++++++++++++++++++++++++++++++++- unlock_indicator.h | 16 ++++++++ 3 files changed, 158 insertions(+), 17 deletions(-) diff --git a/i3lock.1 b/i3lock.1 index 88aa16c..1ce7606 100644 --- a/i3lock.1 +++ b/i3lock.1 @@ -8,7 +8,7 @@ .fi .. -.TH i3lock-color 1 "JUN 2021" Linux "User Manuals" +.TH i3lock-color 1 "JAN 2022" Linux "User Manuals" .SH NAME i3lock-color \- improved screen locker @@ -240,6 +240,23 @@ Sets the color of the status text while verifying and when password is wrong. .B \-\-{layout, time, date, greeter}\-color=rrggbbaa Sets text colors. +.TP +.B \-\-keylayout mode +Displays the keylayout. Positionable similar to date, time, and indicator. +Modes are as follows: +.RS +.IP \[bu] 2 +0 - Displays the full string returned by the query, i.e. "English (US)" +.IP \[bu] +1 - Displays up until the first parenthesis, i.e. "English" +.IP \[bu] +2 - Displays just the contents of the parenthesis, i.e. "US" +.RE + +.B For all following -str or -text options, some control characters +.B (i.e. \\\\n, \\\\t) are supported. See \fBCONTROL CHARACTERS\fR +.B for more details. + .TP .B \-\-time\-str="%H:%M:%S" Sets the format used for generating the time string. @@ -257,19 +274,6 @@ Sets the string to be shown while verifying the password/input/key/etc. .B \-\-wrong\-text="wrong!" Sets the string to be shown upon entering an incorrect password. -.TP -.B \-\-keylayout mode -Displays the keylayout. Positionable similar to date, time, and indicator. -Modes are as follows: -.RS -.IP \[bu] 2 -0 - Displays the full string returned by the query, i.e. "English (US)" -.IP \[bu] -1 - Displays up until the first parenthesis, i.e. "English" -.IP \[bu] -2 - Displays just the contents of the parenthesis, i.e. "US" -.RE - .TP .B \-\-noinput\-text="no input" Sets the string to be shown upon pressing backspace without anything to delete. @@ -484,6 +488,25 @@ The interval to wait until switching to the next image. .B \-\-slideshow\-random\-selection Randomize the order of the images. +.SH CONTROL CHARACTERS +Control characters (\\r \\n \\b \\t) are supported in text OPTIONS. Their behavior +are almost as same as anywhere else. +.TP +.B Carriage Return(\\\\r) +Move to the start of line (left edge). +Notes: The rendered characters would still live there. +.TP +.B Line Feed(\\\\n) +Move to start of next line (left edge). +.TP +.B Backspace(\\\\b) +Overwrite last one char if exists. +Notes: The rendered character would still live there. +.TP +.B Tab(\\\\t) +Move to next tab stop position.The width of one character for moving is as same as character 'a'. +Note: The width may be strange if the font is not mono-spaced. + .SH SEE ALSO .IR xautolock(1) \- use i3lock as your screen saver @@ -491,7 +514,14 @@ Randomize the order of the images. .IR convert(1) \- feed a wide variety of image formats to i3lock -.SH AUTHOR +.SH HOMEPAGE +https://github.com/Raymo111/i3lock-color + +Please report bugs and submit pull-requests as follows: +For i3lock (upstream): https://github.com/i3/i3lock +For i3lock-color (enhancements on top of i3lock): https://github.com/Raymo111/i3lock-color + +.SH AUTHORS Michael Stapelberg Jan-Erik Rediger diff --git a/unlock_indicator.c b/unlock_indicator.c index b254c0f..e10984a 100644 --- a/unlock_indicator.c +++ b/unlock_indicator.c @@ -256,6 +256,14 @@ static cairo_font_face_t *font_faces[6] = { NULL, }; +static control_char_config_t control_characters[] = { + {'\n', CC_POS_RESET, 0, CC_POS_CHANGE, 1}, + {'\b', CC_POS_CHANGE, -1, CC_POS_KEEP, 0}, + {'\r', CC_POS_RESET, 0, CC_POS_KEEP, 0}, + {'\t', CC_POS_TAB, 4, CC_POS_KEEP, 0}, +}; +size_t control_char_count = sizeof control_characters / sizeof(control_char_config_t); + static cairo_font_face_t *get_font_face(int which) { if (font_faces[which]) { return font_faces[which]; @@ -314,6 +322,94 @@ static cairo_font_face_t *get_font_face(int which) { return face; } +/* + * Splits the given text by "control chars", + * And then draws the given text onto the cairo context. + */ +static void draw_text_with_cc(cairo_t *ctx, text_t text, double start_x) { + // get scaled_font + cairo_scaled_font_t *sft; + cairo_matrix_t fm, ctm; + cairo_matrix_init_scale(&fm, text.size, text.size); + cairo_get_matrix(ctx, &ctm); + cairo_font_options_t *opts; + opts = cairo_font_options_create(); + sft = cairo_scaled_font_create(text.font, &fm, &ctm, opts); + cairo_font_options_destroy(opts); + /* use `a` to represent common character width, using in `\t` */ + cairo_text_extents_t te; + cairo_text_extents(ctx, "a", &te); + + // convert text to glyphs. + cairo_status_t status; + cairo_glyph_t* glyphs; + int nglyphs = 0, + len = 0, + start = 0, + lineno = 0, + x = start_x, + y = text.y; + size_t cur_cc; + + while (text.str[start + len] != '\0') { + char is_cc = 0; + do { + for (cur_cc = 0; cur_cc < control_char_count; cur_cc++) { + if (text.str[start+len] == control_characters[cur_cc].character) { + is_cc = 1; + break; + } + } + } while (text.str[start+(len++)] != '\0' && !is_cc); + if (len > is_cc) { + status = cairo_scaled_font_text_to_glyphs( + sft, x, y, text.str + start, is_cc ? len - 1: len, + &glyphs, &nglyphs, + NULL, NULL, NULL + ); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_glyph_path(ctx, glyphs, nglyphs); + } else { + DEBUG("draw %c failed\n", text.str[start]); + } + } + if (is_cc && (cur_cc < control_char_count)) { + if (control_characters[cur_cc].x_behavior == CC_POS_CHANGE) { + char x_offset = control_characters[cur_cc].x_behavior_arg; + if (x_offset < 0 && x_offset > -nglyphs) { + x = glyphs[nglyphs+x_offset].x; + } else if (x_offset > 0) { + if (nglyphs >= 1) { // the case is some leading control chars.(although there is none now) + x = glyphs[nglyphs - 1].x + x_offset * te.x_advance; + } else { // deal the leading control chars. + x += x_offset * te.x_advance; + } + } + } else if (control_characters[cur_cc].x_behavior == CC_POS_RESET) { + x = start_x; + } else if (control_characters[cur_cc].x_behavior == CC_POS_TAB) { + if (nglyphs > 0) { // there may be leading tab, such as '\t\t' or '\n\t' + int advance = control_characters[cur_cc].x_behavior_arg - ((nglyphs - 1) % control_characters[cur_cc].x_behavior_arg); + x = glyphs[nglyphs - 1].x + advance * te.x_advance; + } else { // deal the leading tab. + x += control_characters[cur_cc].x_behavior_arg * te.x_advance; + } + } + if (control_characters[cur_cc].y_behavior == CC_POS_CHANGE) { + lineno += control_characters[cur_cc].y_behavior_arg; + } // CC_POS_KEEP is default for y + } + y = text.y + text.size * lineno; + if (len > is_cc) { + cairo_glyph_free(glyphs); + } + nglyphs = 0; + start += len; + len = 0; + } + cairo_scaled_font_destroy(sft); +} + /* * Draws the given text onto the cairo context */ @@ -342,9 +438,8 @@ static void draw_text(cairo_t *ctx, text_t text) { } cairo_set_source_rgba(ctx, text.color.red, text.color.green, text.color.blue, text.color.alpha); - cairo_move_to(ctx, x, text.y); - cairo_text_path(ctx, text.str); + draw_text_with_cc(ctx, text, x); cairo_fill_preserve(ctx); cairo_set_source_rgba(ctx, text.outline_color.red, text.outline_color.green, text.outline_color.blue, text.outline_color.alpha); diff --git a/unlock_indicator.h b/unlock_indicator.h index 3c07243..8a16ec1 100644 --- a/unlock_indicator.h +++ b/unlock_indicator.h @@ -47,6 +47,22 @@ typedef enum { MAX, } background_type_t; + +typedef enum { + CC_POS_RESET, + CC_POS_CHANGE, + CC_POS_KEEP, + CC_POS_TAB +} control_char_pos_t; + +typedef struct { + char character; + control_char_pos_t x_behavior; + int x_behavior_arg; + control_char_pos_t y_behavior; + int y_behavior_arg; +} control_char_config_t; + void render_lock(uint32_t* resolution, xcb_drawable_t drawable); void draw_image(uint32_t* resolution, cairo_surface_t* img, cairo_t* xcb_ctx); void init_colors_once(void);