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>
This commit is contained in:
SuperDJY 2022-01-11 01:34:12 +08:00 committed by GitHub
parent fbf109178c
commit 1e284b9488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 158 additions and 17 deletions

View File

@ -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 <michael+i3lock at stapelberg dot de>
Jan-Erik Rediger <badboy at archlinux.us>

View File

@ -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);

View File

@ -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);