commit 1e284b9488e46266b66d1c5031f7e815e889f7c0
parent fbf109178c1c098a8dbe3888e27ad46b436c5c8e
Author: SuperDJY <cmsxbc@users.noreply.github.com>
Date: Tue, 11 Jan 2022 01:34:12 +0800
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 <hi@raymond.li>
Diffstat:
3 files changed, 158 insertions(+), 17 deletions(-)
diff --git 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
@@ -241,6 +241,23 @@ Sets the color of the status text while verifying and when password is wrong.
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.
See strftime(3) for a full list of format specifiers.
@@ -258,19 +275,6 @@ Sets the string to be shown while verifying the password/input/key/etc.
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 <michael+i3lock at stapelberg dot de>
Jan-Erik Rediger <badboy at archlinux.us>
diff --git 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];
@@ -315,6 +323,94 @@ static cairo_font_face_t *get_font_face(int which) {
}
/*
+ * 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
*/
static void draw_text(cairo_t *ctx, text_t text) {
@@ -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
@@ -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);