Add screenshots

This commit is contained in:
Johannes Marbach
2024-03-30 12:46:09 +00:00
parent 3ea3f50cfd
commit 471c1fe0e3
49 changed files with 285 additions and 44 deletions

View File

@@ -10,9 +10,11 @@ If a change only affects particular applications, they are listed in parentheses
## Unreleased ## Unreleased
- feat(buffyboard): Add fbdev force-refresh quirk via config
- feat(buffyboard): Allow choosing theme via config and add all themes from unl0kr
- feat(buffyboard): Handle input device connection/disconnection at runtime; adds new dependency libudev - feat(buffyboard): Handle input device connection/disconnection at runtime; adds new dependency libudev
- feat(buffyboard): Allow choosing theme via config and add all themes from unl0kr
- feat(buffyboard): Add fbdev force-refresh quirk via config
- feat(buffyboard): Allow disabling input devices via config
- feat(buffyboard): Add CLI flags for overriding geometry & DPI
## 3.0.0 (2024-03-22) ## 3.0.0 (2024-03-22)

View File

@@ -24,6 +24,14 @@ Here are a few highlights of what already works:
- Automatic resizing (and later reset) of active VT to prevent overlap with keyboard - Automatic resizing (and later reset) of active VT to prevent overlap with keyboard
- Theming support - Theming support
Screenshots of the currently available themes may be found in the [screenshots] folder.
<img src="screenshots/breezy-light-540x960.png" alt="540x960" height="300"/>
<img src="screenshots/breezy-dark-540x960.png" alt="540x960" height="300"/>
<br>
<img src="screenshots/pmos-light-800x480.png" alt="800x480" height="300"/>
<img src="screenshots/pmos-dark-800x480.png" alt="800x480" height="300"/>
# Usage # Usage
You can get an overview of available command line options by running it with the `-h` or `--help` argument. You can get an overview of available command line options by running it with the `-h` or `--help` argument.
@@ -33,21 +41,25 @@ $ buffyboard --help
Usage: buffyboard [OPTION] Usage: buffyboard [OPTION]
Mandatory arguments to long options are mandatory for short options too. Mandatory arguments to long options are mandatory for short options too.
-C, --config-override Path to a config override file. Can be supplied -C, --config-override Path to a config override file. Can be supplied
multiple times. Config files are merged in the multiple times. Config files are merged in the
following order: following order:
* /etc/buffyboard.conf * /etc/buffyboard.conf
* /etc/buffyboard.conf.d/* (alphabetically) * /etc/buffyboard.conf.d/* (alphabetically)
* Override files (in supplied order) * Override files (in supplied order)
-r, --rotate=[0-3] Rotate the UI to the given orientation. The -g, --geometry=NxM[@X,Y] Force a display size of N horizontal times M
values match the ones provided by the kernel in vertical pixels, offset horizontally by X
/sys/class/graphics/fbcon/rotate. pixels and vertically by Y pixels
* 0 - normal orientation (0 degree) -d --dpi=N Override the display's DPI value
* 1 - clockwise orientation (90 degrees) -r, --rotate=[0-3] Rotate the UI to the given orientation. The
* 2 - upside down orientation (180 degrees) values match the ones provided by the kernel in
* 3 - counterclockwise orientation (270 degrees) /sys/class/graphics/fbcon/rotate.
-h, --help Print this message and exit * 0 - normal orientation (0 degree)
-V, --version Print the buffyboard version and exit * 1 - clockwise orientation (90 degrees)
* 2 - upside down orientation (180 degrees)
* 3 - counterclockwise orientation (270 degrees)
-h, --help Print this message and exit
-V, --version Print the buffyboard version and exit
``` ```
For an example configuration file, see [buffyboard.conf]. For an example configuration file, see [buffyboard.conf].
@@ -155,6 +167,7 @@ The [FontAwesome] font is licensed under the Open Font License version 1.1.
[lv_sim_emscripten]: https://github.com/lvgl/lv_sim_emscripten/blob/master/mouse_cursor_icon.c [lv_sim_emscripten]: https://github.com/lvgl/lv_sim_emscripten/blob/master/mouse_cursor_icon.c
[lvgl]: https://github.com/lvgl/lvgl [lvgl]: https://github.com/lvgl/lvgl
[open issues]: https://gitlab.com/cherrypicker/buffyboard/-/issues [open issues]: https://gitlab.com/cherrypicker/buffyboard/-/issues
[screenshots]: ./screenshots
[squeek2lvgl]: https://gitlab.com/cherrypicker/squeek2lvgl] [squeek2lvgl]: https://gitlab.com/cherrypicker/squeek2lvgl]
[squeekboard layouts]: https://gitlab.gnome.org/World/Phosh/squeekboard/-/blob/master/data/keyboards [squeekboard layouts]: https://gitlab.gnome.org/World/Phosh/squeekboard/-/blob/master/data/keyboards
[squeekboard's US terminal layout]: https://gitlab.gnome.org/World/Phosh/squeekboard/-/blob/master/data/keyboards/terminal/us.yaml [squeekboard's US terminal layout]: https://gitlab.gnome.org/World/Phosh/squeekboard/-/blob/master/data/keyboards/terminal/us.yaml

View File

@@ -1,5 +1,9 @@
[theme] [theme]
default=breezy-light default=breezy-light
#[input]
#pointer=false
#touchscreen=false
#[quirks] #[quirks]
#fbdev_force_refresh=true #fbdev_force_refresh=true

View File

@@ -37,6 +37,13 @@ static void print_usage();
*/ */
static void init_opts(bb_cli_opts *opts) { static void init_opts(bb_cli_opts *opts) {
opts->num_config_files = 0;
opts->config_files = NULL;
opts->hor_res = -1;
opts->ver_res = -1;
opts->x_offset = 0;
opts->y_offset = 0;
opts->dpi = 0;
opts->rotation = LV_DISPLAY_ROTATION_0; opts->rotation = LV_DISPLAY_ROTATION_0;
} }
@@ -46,21 +53,25 @@ static void print_usage() {
"Usage: buffyboard [OPTION]\n" "Usage: buffyboard [OPTION]\n"
"\n" "\n"
"Mandatory arguments to long options are mandatory for short options too.\n" "Mandatory arguments to long options are mandatory for short options too.\n"
" -C, --config-override Path to a config override file. Can be supplied\n" " -C, --config-override Path to a config override file. Can be supplied\n"
" multiple times. Config files are merged in the\n" " multiple times. Config files are merged in the\n"
" following order:\n" " following order:\n"
" * /etc/buffyboard.conf\n" " * /etc/buffyboard.conf\n"
" * /etc/buffyboard.conf.d/* (alphabetically)\n" " * /etc/buffyboard.conf.d/* (alphabetically)\n"
" * Override files (in supplied order)\n" " * Override files (in supplied order)\n"
" -r, --rotate=[0-3] Rotate the UI to the given orientation. The\n" " -g, --geometry=NxM[@X,Y] Force a display size of N horizontal times M\n"
" values match the ones provided by the kernel in\n" " vertical pixels, offset horizontally by X\n"
" /sys/class/graphics/fbcon/rotate.\n" " pixels and vertically by Y pixels\n"
" * 0 - normal orientation (0 degree)\n" " -d --dpi=N Override the display's DPI value\n"
" * 1 - clockwise orientation (90 degrees)\n" " -r, --rotate=[0-3] Rotate the UI to the given orientation. The\n"
" * 2 - upside down orientation (180 degrees)\n" " values match the ones provided by the kernel in\n"
" * 3 - counterclockwise orientation (270 degrees)\n" " /sys/class/graphics/fbcon/rotate.\n"
" -h, --help Print this message and exit\n" " * 0 - normal orientation (0 degree)\n"
" -V, --version Print the buffyboard version and exit\n"); " * 1 - clockwise orientation (90 degrees)\n"
" * 2 - upside down orientation (180 degrees)\n"
" * 3 - counterclockwise orientation (270 degrees)\n"
" -h, --help Print this message and exit\n"
" -V, --version Print the buffyboard version and exit\n");
/*-------------------------------- 78 CHARS --------------------------------*/ /*-------------------------------- 78 CHARS --------------------------------*/
} }
@@ -74,15 +85,17 @@ void bb_cli_parse_opts(int argc, char *argv[], bb_cli_opts *opts) {
struct option long_opts[] = { struct option long_opts[] = {
{ "config-override", required_argument, NULL, 'C' }, { "config-override", required_argument, NULL, 'C' },
{ "rotate", required_argument, NULL, 'r' }, { "geometry", required_argument, NULL, 'g' },
{ "help", no_argument, NULL, 'h' }, { "dpi", required_argument, NULL, 'd' },
{ "version", no_argument, NULL, 'V' }, { "rotate", required_argument, NULL, 'r' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
int opt, index = 0; int opt, index = 0;
while ((opt = getopt_long(argc, argv, "C:r:hV", long_opts, &index)) != -1) { while ((opt = getopt_long(argc, argv, "C:g:d:r:hV", long_opts, &index)) != -1) {
switch (opt) { switch (opt) {
case 'C': case 'C':
opts->config_files = realloc(opts->config_files, (opts->num_config_files + 1) * sizeof(char *)); opts->config_files = realloc(opts->config_files, (opts->num_config_files + 1) * sizeof(char *));
@@ -93,6 +106,20 @@ void bb_cli_parse_opts(int argc, char *argv[], bb_cli_opts *opts) {
opts->config_files[opts->num_config_files] = optarg; opts->config_files[opts->num_config_files] = optarg;
opts->num_config_files++; opts->num_config_files++;
break; break;
case 'g':
if (sscanf(optarg, "%ix%i@%i,%i", &(opts->hor_res), &(opts->ver_res), &(opts->x_offset), &(opts->y_offset)) != 4) {
if (sscanf(optarg, "%ix%i", &(opts->hor_res), &(opts->ver_res)) != 2) {
bb_log(BB_LOG_LEVEL_ERROR, "Invalid geometry argument \"%s\"\n", optarg);
exit(EXIT_FAILURE);
}
}
break;
case 'd':
if (sscanf(optarg, "%i", &(opts->dpi)) != 1) {
bb_log(BB_LOG_LEVEL_ERROR, "Invalid dpi argument \"%s\"\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'r': { case 'r': {
int orientation; int orientation;
if (sscanf(optarg, "%i", &orientation) != 1 || orientation < 0 || orientation > 3) { if (sscanf(optarg, "%i", &orientation) != 1 || orientation < 0 || orientation > 3) {

View File

@@ -17,6 +17,16 @@ typedef struct {
int num_config_files; int num_config_files;
/* Paths of config file */ /* Paths of config file */
const char **config_files; const char **config_files;
/* Horizontal display resolution */
int hor_res;
/* Vertical display resolution */
int ver_res;
/* Horizontal display offset */
int x_offset;
/* Vertical display offset */
int y_offset;
/* DPI */
int dpi;
/* Display rotation */ /* Display rotation */
lv_display_rotation_t rotation; lv_display_rotation_t rotation;
} bb_cli_opts; } bb_cli_opts;

View File

@@ -151,6 +151,16 @@ static int parsing_handler(void* user_data, const char* section, const char* key
return 1; return 1;
} }
} }
} else if (strcmp(section, "input") == 0) {
if (strcmp(key, "pointer") == 0) {
if (parse_bool(value, &(opts->input.pointer))) {
return 1;
}
} else if (strcmp(key, "touchscreen") == 0) {
if (parse_bool(value, &(opts->input.touchscreen))) {
return 1;
}
}
} else if (strcmp(section, "quirks") == 0) { } else if (strcmp(section, "quirks") == 0) {
if (strcmp(key, "fbdev_force_refresh") == 0) { if (strcmp(key, "fbdev_force_refresh") == 0) {
if (parse_bool(value, &(opts->quirks.fbdev_force_refresh))) { if (parse_bool(value, &(opts->quirks.fbdev_force_refresh))) {
@@ -184,6 +194,8 @@ static bool parse_bool(const char *value, bool *result) {
void bb_config_init_opts(bb_config_opts *opts) { void bb_config_init_opts(bb_config_opts *opts) {
opts->theme.default_id = BB_THEMES_THEME_BREEZY_DARK; opts->theme.default_id = BB_THEMES_THEME_BREEZY_DARK;
opts->input.pointer = true;
opts->input.touchscreen = true;
opts->quirks.fbdev_force_refresh = false; opts->quirks.fbdev_force_refresh = false;
} }

View File

@@ -17,7 +17,17 @@
typedef struct { typedef struct {
/* Default theme */ /* Default theme */
bb_themes_theme_id_t default_id; bb_themes_theme_id_t default_id;
} ul_config_opts_theme; } bb_config_opts_theme;
/**
* Options related to input devices
*/
typedef struct {
/* If true and a pointer device is connected, use it for input */
bool pointer;
/* If true and a touchscreen device is connected, use it for input */
bool touchscreen;
} bb_config_opts_input;
/** /**
* (Normally unneeded) quirky options * (Normally unneeded) quirky options
@@ -32,7 +42,9 @@ typedef struct {
*/ */
typedef struct { typedef struct {
/* Options related to the theme */ /* Options related to the theme */
ul_config_opts_theme theme; bb_config_opts_theme theme;
/* Options related to input devices */
bb_config_opts_input input;
/* Options related to (normally unneeded) quirks */ /* Options related to (normally unneeded) quirks */
bb_config_opts_quirks quirks; bb_config_opts_quirks quirks;
} bb_config_opts; } bb_config_opts;

View File

@@ -214,6 +214,18 @@ int main(int argc, char *argv[]) {
if (conf_opts.quirks.fbdev_force_refresh) { if (conf_opts.quirks.fbdev_force_refresh) {
lv_linux_fbdev_set_force_refresh(disp, true); lv_linux_fbdev_set_force_refresh(disp, true);
} }
/* Override display properties with command line options if necessary */
lv_display_set_offset(disp, cli_opts.x_offset, cli_opts.y_offset);
if (cli_opts.hor_res > 0 || cli_opts.ver_res > 0) {
lv_display_set_physical_resolution(disp, lv_disp_get_hor_res(disp), lv_disp_get_ver_res(disp));
lv_display_set_resolution(disp, cli_opts.hor_res, cli_opts.ver_res);
}
if (cli_opts.dpi > 0) {
lv_display_set_dpi(disp, cli_opts.dpi);
}
/* Set up display rotation */
int32_t hor_res_phys = lv_display_get_horizontal_resolution(disp); int32_t hor_res_phys = lv_display_get_horizontal_resolution(disp);
int32_t ver_res_phys = lv_display_get_vertical_resolution(disp); int32_t ver_res_phys = lv_display_get_vertical_resolution(disp);
lv_display_set_physical_resolution(disp, hor_res_phys, ver_res_phys); lv_display_set_physical_resolution(disp, hor_res_phys, ver_res_phys);
@@ -236,7 +248,7 @@ int main(int argc, char *argv[]) {
} }
/* Start input device monitor and auto-connect available devices */ /* Start input device monitor and auto-connect available devices */
bb_indev_start_monitor_and_autoconnect(false, true, true); bb_indev_start_monitor_and_autoconnect(false, conf_opts.input.pointer, conf_opts.input.touchscreen);
/* Initialise theme */ /* Initialise theme */
bb_theme_apply(bb_themes_themes[conf_opts.theme.default_id]); bb_theme_apply(bb_themes_themes[conf_opts.theme.default_id]);

View File

@@ -0,0 +1,100 @@
#!/bin/bash
# Change this depending on what device you're generating the screenshots on
fb_res=1920x1080
fb_depth=8
fb_format=rgba
executable=$1
outdir=screenshots
config=buffyboard-screenshots.conf
themes=(
breezy-light
breezy-dark
pmos-light
pmos-dark
)
resolutions=(
# Nokia N900
480x800
800x480
# Samsung Galaxy A3 2015
540x960
960x540
# Samsung Galaxy Tab A 8.0 2015
768x1024
1024x768
# Pine64 PineTab (landscape)
1280x800
# Pine64 PinePhone (landscape)
1440x720
# BQ Aquaris X Pro (landscape)
1920x1080
)
if [[ ! -f $executable || ! -x $executable ]]; then
echo "Error: Could not find executable at $executable" 1>&2
exit 1
fi
function write_config() {
cat << EOF > $config
[theme]
default=$1
[input]
pointer=false
touchscreen=false
EOF
}
# Hide cursor
echo -e '\033[?25l'
function clean_up() {
rm -f $config
# Show cursor
echo -e '\033[?25h'
}
trap clean_up EXIT
rm -rf "$outdir"
mkdir "$outdir"
readme="# Buffyboard themes"$'\n'
for theme in ${themes[@]}; do
write_config $theme
readme="$readme"$'\n'"## $theme"$'\n\n'
for res in ${resolutions[@]}; do
$executable -g $res -C $config > /dev/null 2>&1 &
pid=$!
sleep 3 # Wait for UI to render
cat /dev/fb0 > "$outdir/$theme-$res"
convert -size $fb_res \
-depth $fb_depth \
screenshot-background.png \
$fb_format:"$outdir/$theme-$res" \
-crop $res+0+0 \
-gravity NorthWest \
-composite \
"$outdir/$theme-$res.png"
rm "$outdir/$theme-$res"
kill -15 $pid
readme="$readme<img src=\"$theme-$res.png\" alt=\"$res\" height=\"300\"/>"$'\n'
sleep 1 # Delay to prevent terminal mode set / reset interference
done
done
echo -n "$readme" > "$outdir/README.md"

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,49 @@
# Buffyboard themes
## breezy-light
<img src="breezy-light-480x800.png" alt="480x800" height="300"/>
<img src="breezy-light-800x480.png" alt="800x480" height="300"/>
<img src="breezy-light-540x960.png" alt="540x960" height="300"/>
<img src="breezy-light-960x540.png" alt="960x540" height="300"/>
<img src="breezy-light-768x1024.png" alt="768x1024" height="300"/>
<img src="breezy-light-1024x768.png" alt="1024x768" height="300"/>
<img src="breezy-light-1280x800.png" alt="1280x800" height="300"/>
<img src="breezy-light-1440x720.png" alt="1440x720" height="300"/>
<img src="breezy-light-1920x1080.png" alt="1920x1080" height="300"/>
## breezy-dark
<img src="breezy-dark-480x800.png" alt="480x800" height="300"/>
<img src="breezy-dark-800x480.png" alt="800x480" height="300"/>
<img src="breezy-dark-540x960.png" alt="540x960" height="300"/>
<img src="breezy-dark-960x540.png" alt="960x540" height="300"/>
<img src="breezy-dark-768x1024.png" alt="768x1024" height="300"/>
<img src="breezy-dark-1024x768.png" alt="1024x768" height="300"/>
<img src="breezy-dark-1280x800.png" alt="1280x800" height="300"/>
<img src="breezy-dark-1440x720.png" alt="1440x720" height="300"/>
<img src="breezy-dark-1920x1080.png" alt="1920x1080" height="300"/>
## pmos-light
<img src="pmos-light-480x800.png" alt="480x800" height="300"/>
<img src="pmos-light-800x480.png" alt="800x480" height="300"/>
<img src="pmos-light-540x960.png" alt="540x960" height="300"/>
<img src="pmos-light-960x540.png" alt="960x540" height="300"/>
<img src="pmos-light-768x1024.png" alt="768x1024" height="300"/>
<img src="pmos-light-1024x768.png" alt="1024x768" height="300"/>
<img src="pmos-light-1280x800.png" alt="1280x800" height="300"/>
<img src="pmos-light-1440x720.png" alt="1440x720" height="300"/>
<img src="pmos-light-1920x1080.png" alt="1920x1080" height="300"/>
## pmos-dark
<img src="pmos-dark-480x800.png" alt="480x800" height="300"/>
<img src="pmos-dark-800x480.png" alt="800x480" height="300"/>
<img src="pmos-dark-540x960.png" alt="540x960" height="300"/>
<img src="pmos-dark-960x540.png" alt="960x540" height="300"/>
<img src="pmos-dark-768x1024.png" alt="768x1024" height="300"/>
<img src="pmos-dark-1024x768.png" alt="1024x768" height="300"/>
<img src="pmos-dark-1280x800.png" alt="1280x800" height="300"/>
<img src="pmos-dark-1440x720.png" alt="1440x720" height="300"/>
<img src="pmos-dark-1920x1080.png" alt="1920x1080" height="300"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -39,11 +39,11 @@ static void print_usage();
static void init_opts(ul_cli_opts *opts) { static void init_opts(ul_cli_opts *opts) {
opts->num_config_files = 0; opts->num_config_files = 0;
opts->config_files = NULL; opts->config_files = NULL;
opts->hor_res = -1; opts->hor_res = -1;
opts->ver_res = -1; opts->ver_res = -1;
opts->x_offset = 0; opts->x_offset = 0;
opts->y_offset = 0; opts->y_offset = 0;
opts->dpi = 0;
opts->verbose = false; opts->verbose = false;
} }

View File

@@ -5,7 +5,7 @@ fb_res=1920x1080
fb_depth=8 fb_depth=8
fb_format=rgba fb_format=rgba
unl0kr=$1 executable=$1
outdir=screenshots outdir=screenshots
config=unl0kr-screenshots.conf config=unl0kr-screenshots.conf
@@ -34,8 +34,8 @@ resolutions=(
1920x1080 1920x1080
) )
if [[ ! -f $unl0kr || ! -x $unl0kr ]]; then if [[ ! -f $executable || ! -x $executable ]]; then
echo "Error: Could not find unl0kr executable at $unl0kr" 1>&2 echo "Error: Could not find executable at $executable" 1>&2
exit 1 exit 1
fi fi
@@ -79,7 +79,7 @@ for theme in ${themes[@]}; do
readme="$readme"$'\n'"## $theme"$'\n\n' readme="$readme"$'\n'"## $theme"$'\n\n'
for res in ${resolutions[@]}; do for res in ${resolutions[@]}; do
CRYPTTAB_SOURCE=/dev/sda1 $unl0kr -g $res -C unl0kr-screenshots.conf & CRYPTTAB_SOURCE=/dev/sda1 $executable -g $res -C $config &
pid=$! pid=$!
sleep 3 # Wait for UI to render sleep 3 # Wait for UI to render