unl0kr: add a CLI option to show a custom message

Fixes: #33
This commit is contained in:
Vladimir Stoiakin
2025-03-12 11:11:22 +03:00
parent 18b576f259
commit 84d47d2824
9 changed files with 132 additions and 55 deletions

View File

@@ -23,6 +23,7 @@ If a change only affects particular applications, they are listed in parentheses
- feat(unl0kr): Enable software rotation (!32, thanks @xs5871 & @craftyguy)
- misc: do not hardcode endianess of a system (!41, thanks @vstoiakin)
- misc(unl0kr): Optimize the main loop (!38, thanks @vstoiakin)
- feat(unl0kr): Add the option to show a custom message to a user (!39, thanks @vstoiakin)
## 3.2.0 (2024-06-03)

View File

@@ -21,6 +21,8 @@ password is printed to STDOUT. All other output happens on STDERR.
## Optional
*-m, --message*
A message that will be displayed to a user.
*-C, --config-override*
Path to a config override file. Can be supplied multiple times. Config
files are merged in the following order:

View File

@@ -40,7 +40,7 @@ static struct {
lv_style_t msgbox_background;
lv_style_t bar;
lv_style_t bar_indicator;
} styles;
} styles;
static bool are_styles_initialised = false;
@@ -90,6 +90,8 @@ static void init_styles(const bbx_theme *theme) {
reset_style(&(styles.window));
lv_style_set_bg_opa(&(styles.window), LV_OPA_COVER);
lv_style_set_bg_color(&(styles.window), lv_color_hex(theme->window.bg_color));
lv_style_set_layout(&(styles.window), LV_LAYOUT_FLEX);
lv_style_set_flex_flow(&(styles.window), LV_FLEX_FLOW_COLUMN);
reset_style(&(styles.header));
lv_style_set_bg_opa(&(styles.header), LV_OPA_COVER);

View File

@@ -1085,7 +1085,7 @@ static const bbx_theme nord_dark = {
}
},
.label = {
.fg_color = 0x070c0d
.fg_color = NORD5
},
.msgbox = {
.fg_color = NORD6,

View File

@@ -13,6 +13,7 @@
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
@@ -39,6 +40,7 @@ static void print_usage();
static void init_opts(ul_cli_opts *opts) {
opts->num_config_files = 0;
opts->config_files = NULL;
opts->message = NULL;
opts->hor_res = -1;
opts->ver_res = -1;
opts->x_offset = 0;
@@ -58,6 +60,7 @@ static void print_usage() {
"password is printed to STDOUT. All other output happens on STDERR.\n"
"\n"
"Mandatory arguments to long options are mandatory for short options too.\n"
" -m, --message A message that will be displayed to a user\n"
" -C, --config-override Path to a config override file. Can be supplied\n"
" multiple times. Config files are merged in the\n"
" following order:\n"
@@ -93,6 +96,7 @@ void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *opts) {
init_opts(opts);
struct option long_opts[] = {
{ "message", required_argument, NULL, 'm' },
{ "config-override", required_argument, NULL, 'C' },
{ "geometry", required_argument, NULL, 'g' },
{ "dpi", required_argument, NULL, 'd' },
@@ -105,8 +109,11 @@ void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *opts) {
int opt, index = 0;
while ((opt = getopt_long(argc, argv, "C:g:d:r:hnvV", long_opts, &index)) != -1) {
while ((opt = getopt_long(argc, argv, "m:C:g:d:r:hnvV", long_opts, &index)) != -1) {
switch (opt) {
case 'm':
opts->message = strdup(optarg);
break;
case 'C':
opts->config_files = realloc(opts->config_files, (opts->num_config_files + 1) * sizeof(char *));
if (!opts->config_files) {

View File

@@ -18,6 +18,8 @@ typedef struct {
int num_config_files;
/* Paths of config file */
const char **config_files;
/* Message for a user */
const char *message;
/* Horizontal display resolution */
int hor_res;
/* Vertical display resolution */

View File

@@ -40,8 +40,13 @@ bool is_alternate_theme = false;
bool is_password_obscured = true;
bool is_keyboard_hidden = false;
lv_obj_t *container = NULL;
lv_obj_t *keyboard = NULL;
int32_t content_height_with_kb;
int32_t content_height_without_kb;
int32_t content_pad_bottom_with_kb;
int32_t content_pad_bottom_without_kb;
/**
* Static prototypes
@@ -117,12 +122,13 @@ static void toggle_keyboard_hidden(void);
static void set_keyboard_hidden(bool is_hidden);
/**
* Callback for the keyboard's vertical slide in / out animation.
* Callback for the pad animation.
*
* @param obj keyboard widget
* @param value y position
* @param obj container widget
* @param value the current value of the pad
*/
static void keyboard_anim_y_cb(void *obj, int32_t value);
static void pad_anim_cb(void *obj, int32_t value);
/**
* Handle LV_EVENT_VALUE_CHANGED events from the keyboard layout dropdown.
@@ -249,22 +255,43 @@ static void toggle_keyboard_hidden(void) {
static void set_keyboard_hidden(bool is_hidden) {
if (!conf_opts.general.animations) {
lv_obj_set_y(keyboard, is_hidden ? lv_obj_get_height(keyboard) : 0);
lv_obj_set_height(container, is_hidden? content_height_without_kb : content_height_with_kb);
lv_obj_set_style_pad_bottom(container,
is_hidden? content_pad_bottom_without_kb : content_pad_bottom_with_kb, LV_PART_MAIN);
return;
}
lv_anim_t keyboard_anim;
lv_anim_init(&keyboard_anim);
lv_anim_set_var(&keyboard_anim, keyboard);
lv_anim_set_values(&keyboard_anim, is_hidden ? 0 : lv_obj_get_height(keyboard), is_hidden ? lv_obj_get_height(keyboard) : 0);
lv_anim_set_var(&keyboard_anim, container);
lv_anim_set_exec_cb(&keyboard_anim, (lv_anim_exec_xcb_t) lv_obj_set_height);
lv_anim_set_path_cb(&keyboard_anim, lv_anim_path_ease_out);
lv_anim_set_time(&keyboard_anim, 500);
lv_anim_set_exec_cb(&keyboard_anim, keyboard_anim_y_cb);
lv_anim_set_values(&keyboard_anim,
is_hidden? content_height_with_kb : content_height_without_kb,
is_hidden? content_height_without_kb : content_height_with_kb);
lv_anim_start(&keyboard_anim);
if (content_pad_bottom_with_kb != content_pad_bottom_without_kb) {
lv_anim_t pad_anim;
lv_anim_init(&pad_anim);
lv_anim_set_var(&pad_anim, container);
lv_anim_set_exec_cb(&pad_anim, pad_anim_cb);
lv_anim_set_path_cb(&pad_anim, lv_anim_path_ease_out);
lv_anim_set_time(&pad_anim, 500);
lv_anim_set_values(&pad_anim,
is_hidden? content_pad_bottom_with_kb : content_pad_bottom_without_kb,
is_hidden? content_pad_bottom_without_kb : content_pad_bottom_with_kb);
lv_anim_start(&pad_anim);
}
}
static void keyboard_anim_y_cb(void *obj, int32_t value) {
lv_obj_set_y(obj, value);
static void pad_anim_cb(void *obj, int32_t value) {
lv_obj_set_style_pad_bottom(obj, value, LV_PART_MAIN);
}
static void layout_dropdown_value_changed_cb(lv_event_t *event) {
@@ -456,10 +483,6 @@ int main(int argc, char *argv[]) {
/* Set up display rotation */
lv_display_set_rotation(disp, cli_opts.rotation);
/* Store final display resolution for convenient later access */
const uint32_t hor_res = lv_disp_get_hor_res(disp);
const uint32_t ver_res = lv_disp_get_ver_res(disp);
/* Prepare for routing physical keyboard input into the textarea */
lv_group_t *keyboard_input_group = lv_group_create();
bbx_indev_set_keyboard_input_group(keyboard_input_group);
@@ -475,29 +498,20 @@ int main(int argc, char *argv[]) {
/* Initialise theme */
set_theme(is_alternate_theme);
/* Prevent scrolling when keyboard is off-screen */
lv_obj_clear_flag(lv_scr_act(), LV_OBJ_FLAG_SCROLLABLE);
/* Figure out a few numbers for sizing and positioning */
const int base_keyboard_height = ver_res > hor_res ? ver_res / 3 : ver_res / 2; /* Height for 4 rows */
const int keyboard_height = base_keyboard_height * 1.25; /* Add space for an extra top row */
const int padding = keyboard_height / 10;
const int textarea_container_max_width = LV_MIN(hor_res, ver_res);
const int32_t hor_res = lv_disp_get_hor_res(disp);
const int32_t ver_res = lv_disp_get_ver_res(disp);
const int32_t keyboard_height = ver_res > hor_res ? ver_res / 2.5 : ver_res / 1.8; /* Height for 5 rows */
/* Main flexbox */
lv_obj_t *container = lv_obj_create(lv_scr_act());
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(container, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_size(container, LV_PCT(100), ver_res - keyboard_height);
lv_obj_set_pos(container, 0, 0);
lv_obj_clear_flag(container, LV_OBJ_FLAG_SCROLLABLE);
/* Prevent scrolling when keyboard is off-screen */
lv_obj_t *screen = lv_screen_active();
lv_obj_clear_flag(screen, LV_OBJ_FLAG_SCROLLABLE);
/* Header flexbox */
lv_obj_t *header = lv_obj_create(container);
lv_obj_t *header = lv_obj_create(screen);
lv_obj_add_flag(header, BBX_WIDGET_HEADER);
lv_theme_apply(header); /* Force re-apply theme after setting flag so that the widget can be identified */
lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(header, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_size(header, LV_PCT(100), LV_SIZE_CONTENT);
/* Theme switcher button */
@@ -532,19 +546,45 @@ int main(int argc, char *argv[]) {
lv_label_set_text(shutdown_btn_label, LV_SYMBOL_POWER);
lv_obj_center(shutdown_btn_label);
/* Flexible spacer */
lv_obj_t *flexible_spacer = lv_obj_create(container);
lv_obj_set_size(flexible_spacer, LV_PCT(100), 0);
lv_obj_set_flex_grow(flexible_spacer, 1);
lv_obj_update_layout(layout_dropdown);
const int32_t dropwdown_height = lv_obj_get_height(layout_dropdown);
lv_obj_set_size(toggle_theme_btn, dropwdown_height, dropwdown_height);
lv_obj_set_size(toggle_kb_btn, dropwdown_height, dropwdown_height);
lv_obj_set_size(shutdown_btn, dropwdown_height, dropwdown_height);
lv_obj_update_layout(header);
content_height_without_kb = ver_res - lv_obj_get_height(header);
content_height_with_kb = content_height_without_kb - keyboard_height;
/* Container for a message and an input field */
container = lv_obj_create(screen);
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(container, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_set_size(container, LV_PCT(100), is_keyboard_hidden? content_height_without_kb : content_height_with_kb);
/* Message for a user */
lv_obj_t *message_label;
if (cli_opts.message) {
/* lv_label does not support wrapping and scrolling simultaneously,
so we place it in a scrollable container */
lv_obj_t *message_container = lv_obj_create(container);
lv_obj_set_width(message_container, LV_PCT(100));
lv_obj_set_flex_flow(message_container, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_grow(message_container, 1);
lv_obj_t *message_spacer = lv_obj_create(message_container);
lv_obj_set_width(message_spacer, 0);
lv_obj_set_flex_grow(message_spacer, 1);
message_label = lv_label_create(message_container);
lv_obj_set_size(message_label, LV_PCT(100), LV_SIZE_CONTENT);
lv_label_set_text(message_label, cli_opts.message);
}
/* Textarea flexbox */
lv_obj_t *textarea_container = lv_obj_create(container);
lv_obj_set_size(textarea_container, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_max_width(textarea_container, textarea_container_max_width, LV_PART_MAIN);
lv_obj_set_flex_flow(textarea_container, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(textarea_container, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_left(textarea_container, padding, LV_PART_MAIN);
lv_obj_set_style_pad_right(textarea_container, padding, LV_PART_MAIN);
/* Textarea */
lv_obj_t *textarea = lv_textarea_create(textarea_container);
@@ -559,27 +599,40 @@ int main(int argc, char *argv[]) {
/* Route physical keyboard input into textarea */
lv_group_add_obj(keyboard_input_group, textarea);
lv_obj_update_layout(textarea);
const int32_t textarea_height = lv_obj_get_height(textarea);
/* Reveal / obscure password button */
lv_obj_t *toggle_pw_btn = lv_btn_create(textarea_container);
const int textarea_height = lv_obj_get_height(textarea);
lv_obj_set_size(toggle_pw_btn, textarea_height, textarea_height);
lv_obj_t *toggle_pw_btn_label = lv_label_create(toggle_pw_btn);
lv_obj_center(toggle_pw_btn_label);
lv_label_set_text(toggle_pw_btn_label, LV_SYMBOL_EYE_OPEN);
lv_obj_add_event_cb(toggle_pw_btn, toggle_pw_btn_clicked_cb, LV_EVENT_CLICKED, NULL);
/* Set header button size to match dropdown (for some reason the height is only available here) */
const int dropwdown_height = lv_obj_get_height(layout_dropdown);
lv_obj_set_size(toggle_theme_btn, dropwdown_height, dropwdown_height);
lv_obj_set_size(toggle_kb_btn, dropwdown_height, dropwdown_height);
lv_obj_set_size(shutdown_btn, dropwdown_height, dropwdown_height);
/* The bottom pad is used to center content when the keyboard is hidden */
content_pad_bottom_with_kb = 20;
int32_t content_pad_row = 10;
/* Fixed spacer */
lv_obj_t *fixed_spacer = lv_obj_create(container);
lv_obj_set_size(fixed_spacer, LV_PCT(100), padding);
lv_obj_set_style_pad_top(container, 10, LV_PART_MAIN);
lv_obj_set_style_pad_left(container, 20, LV_PART_MAIN);
lv_obj_set_style_pad_right(container, 20, LV_PART_MAIN);
lv_obj_set_style_pad_row(container, content_pad_row, LV_PART_MAIN);
int32_t content_native_height = textarea_height;
if (cli_opts.message) {
lv_obj_update_layout(message_label);
content_native_height += content_pad_row + lv_obj_get_height(message_label);
}
content_pad_bottom_without_kb = (content_height_without_kb - content_native_height) / 2;
if (content_pad_bottom_without_kb < content_pad_bottom_with_kb)
content_pad_bottom_without_kb = content_pad_bottom_with_kb;
lv_obj_set_style_pad_bottom(container, is_keyboard_hidden? content_pad_bottom_without_kb : content_pad_bottom_with_kb, LV_PART_MAIN);
/* Keyboard (after textarea / label so that key popovers are not drawn over) */
keyboard = lv_keyboard_create(lv_scr_act());
keyboard = lv_keyboard_create(screen);
lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_TEXT_LOWER);
lv_keyboard_set_textarea(keyboard, textarea);
uint32_t num_keyboard_events = lv_obj_get_event_count(keyboard);
@@ -591,8 +644,7 @@ int main(int argc, char *argv[]) {
}
lv_obj_add_event_cb(keyboard, keyboard_value_changed_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(keyboard, keyboard_ready_cb, LV_EVENT_READY, NULL);
lv_obj_set_pos(keyboard, 0, is_keyboard_hidden ? keyboard_height : 0);
lv_obj_set_size(keyboard, hor_res, keyboard_height);
lv_obj_set_size(keyboard, LV_PCT(100), keyboard_height);
bbx_theme_prepare_keyboard(keyboard);
/* Apply textarea options */

View File

@@ -80,7 +80,7 @@ while read -r theme; do
readme="$readme"$'\n'"## $theme"$'\n\n'
for res in ${resolutions[@]}; do
$executable -g $res -C $config &
$executable -m "Please enter a password to unlock the root volume" -g $res -C $config &
pid=$!
sleep 3 # Wait for UI to render

View File

@@ -418,7 +418,18 @@ int exec_unl0kr(char** ret_password)
exit(EXIT_FAILURE);
}
execl(UNL0KR_BINARY, UNL0KR_BINARY, "-n", (char*) 0);
char* argv[5];
int argc = 2;
argv[0] = UNL0KR_BINARY;
argv[1] = "-n";
if (request.message) {
argv[2] = "-m";
argv[3] = request.message;
argc += 2;
}
argv[argc] = NULL;
execv(UNL0KR_BINARY, argv);
perror("exec() is failed");
exit(EXIT_FAILURE);