From 51b1945744826200a030a082061766bd750ac301 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Thu, 23 Sep 2021 21:05:36 +0200 Subject: [PATCH] Add CLI interface Fixes #5 --- README.md | 15 +++++++ command_line.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ command_line.h | 47 +++++++++++++++++++++ log.c | 54 +++++++++++++++++++++++++ log.h | 59 +++++++++++++++++++++++++++ main.c | 42 ++++++++++++++----- meson.build | 6 ++- unl0kr.h | 28 +++++++++++++ 8 files changed, 348 insertions(+), 11 deletions(-) create mode 100644 command_line.c create mode 100644 command_line.h create mode 100644 log.c create mode 100644 log.h create mode 100644 unl0kr.h diff --git a/README.md b/README.md index 9e5010c..26dc996 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,21 @@ Below is a summary of contributions upstreamed thus far. - [Automatic device discovery via libinput] (✅ merged) - [Make it possible to use multiple devices with the libinput and XKB drivers] (⏳ in review) +# Usage + +For an overview of available command line options, run unl0kr with the `-h` or `--help` argument. + +``` +$ unl0kr --help +Usage: unl0kr [OPTION] + +Mandatory arguments to long options are mandatory for short options too. + -g, --geometry=NxM Force a display size of N horizontal times M vertical pixels + -h, --help Print this message and exit + -v, --verbose Enable more detailed logging output on STDERR + -V, --version Print the unl0kr version and exit +``` + # Development ## Dependencies diff --git a/command_line.c b/command_line.c new file mode 100644 index 0000000..8b9bbd9 --- /dev/null +++ b/command_line.c @@ -0,0 +1,108 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "command_line.h" + +#include "unl0kr.h" + +#include +#include +#include + + +/** + * Static prototypes + */ + +/** + * Initialise a command line options struct with default values. + * + * @param opts pointer to the options struct + */ +static void init_opts(ul_cli_opts *cli_opts); + +/** + * Output usage instructions. + */ +static void print_usage(); + + +/** + * Static functions + */ + +static void init_opts(ul_cli_opts *cli_opts) { + cli_opts->hor_res = -1; + cli_opts->ver_res = -1; + cli_opts->verbose = false; +} + +static void print_usage() { + fprintf(stderr, + "Usage: unl0kr [OPTION]\n" + "\n" + "Mandatory arguments to long options are mandatory for short options too.\n" + " -g, --geometry=NxM Force a display size of N horizontal times M vertical pixels\n" + " -h, --help Print this message and exit\n" + " -v, --verbose Enable more detailed logging output on STDERR\n" + " -V, --version Print the unl0kr version and exit\n"); +} + + +/** + * Public functions + */ + +void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *cli_opts) { + init_opts(cli_opts); + + struct option opts[] = { + { "geometry", required_argument, NULL, 'g' }, + { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + int opt, index = 0; + + while ((opt = getopt_long(argc, argv, "g:hvV", opts, &index)) != -1) { + switch (opt) { + case 'g': + if (sscanf(optarg, "%ix%i", &(cli_opts->hor_res), &(cli_opts->ver_res)) != 2) { + fprintf(stderr, "Error: invalid geometry argument \"%s\"\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'h': + print_usage(); + exit(EXIT_SUCCESS); + case 'v': + cli_opts->verbose = true; + break; + case 'V': + fprintf(stderr, "unl0kr %s\n", UL_VERSION); + exit(0); + default: + print_usage(); + exit(EXIT_FAILURE); + } + } +} diff --git a/command_line.h b/command_line.h new file mode 100644 index 0000000..121dc29 --- /dev/null +++ b/command_line.h @@ -0,0 +1,47 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef UL_COMMAND_LINE_H +#define UL_COMMAND_LINE_H + +#include + +/** + * Options parsed from command line arguments + */ +typedef struct { + /* Horizontal display resolution */ + int hor_res; + /* Vertical display resolution */ + int ver_res; + /* Verbose mode. If true, provide more detailed logging output on STDERR. */ + bool verbose; +} ul_cli_opts; + +/** + * Parse command line arguments and exit on failure. + * + * @param argc number of provided command line arguments + * @param argv arguments as an array of strings + * @param cli_opts pointer for writing the parsed options into + */ +void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *cli_opts); + +#endif /* UL_COMMAND_LINE_H */ diff --git a/log.c b/log.c new file mode 100644 index 0000000..7449d0e --- /dev/null +++ b/log.c @@ -0,0 +1,54 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "log.h" + +#include + + +/** + * Static variables + */ + +static ul_log_level log_level = UL_LOG_LEVEL_ERROR; + + +/** + * Public functions + */ + +void ul_set_log_level(ul_log_level level) { + log_level = level; +} + +void ul_log(ul_log_level level, const char *format, ...) { + if (level > log_level) { + return; + } + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +void ul_print_cb(const char *msg) { + ul_log(UL_LOG_LEVEL_VERBOSE, "%s\n", msg); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..a194336 --- /dev/null +++ b/log.h @@ -0,0 +1,59 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef UL_LOG_H +#define UL_LOG_H + +#include + +/** + * Log levels + */ +typedef enum { + /* Errors only */ + UL_LOG_LEVEL_ERROR = 0, + /* Include non-errors in log */ + UL_LOG_LEVEL_VERBOSE = 1 +} ul_log_level; + +/** + * Set the log level. + * + * @param level new log level value + */ +void ul_set_log_level(ul_log_level level); + +/** + * Log a message. + * + * @param level log level of the message + * @param format message format string + * @param ... parameters to fill into the format string + */ +void ul_log(ul_log_level level, const char *format, ...); + +/** + * Handle an LVGL log message. + * + * @param msg message to print + */ +void ul_print_cb(const char *msg); + +#endif /* UL_LOG_H */ diff --git a/main.c b/main.c index d02553c..830ddcf 100644 --- a/main.c +++ b/main.c @@ -18,9 +18,12 @@ */ +#include "command_line.h" #include "cursor.h" #include "libinput_multi.h" #include "libinput_xkb.h" +#include "log.h" +#include "unl0kr.h" #include "lv_drivers/display/fbdev.h" #include "lv_drivers/indev/libinput_drv.h" @@ -329,17 +332,36 @@ static void keyboard_ready_cb(lv_event_t *event) { * Main */ -int main(void) { - /* Initialise lvgl and framebuffer driver */ - lv_init(); - fbdev_init(); +int main(int argc, char *argv[]) { + /* Parse command line options */ + ul_cli_opts opts; + ul_cli_parse_opts(argc, argv, &opts); - /* Query display size */ + /* Set up log level */ + if (opts.verbose) { + ul_set_log_level(UL_LOG_LEVEL_VERBOSE); + } + + /* Announce ourselves */ + ul_log(UL_LOG_LEVEL_VERBOSE, "unl0kr %s", UL_VERSION); + + /* Initialise LVGL and set up logging callback */ + lv_init(); + lv_log_register_print_cb(ul_print_cb); + + /* Initialise framebuffer driver and query display size */ + fbdev_init(); uint32_t hor_res; uint32_t ver_res; - fbdev_get_sizes(&hor_res, &ver_res); + fbdev_get_sizes(&hor_res, &ver_res); - // hor_res = ver_res * 0.6; /* Simulate mobile screen */ + /* Override display size with command line options if necessary */ + if (opts.hor_res > 0) { + hor_res = LV_MIN(hor_res, opts.hor_res); + } + if (opts.ver_res > 0) { + ver_res = LV_MIN(ver_res, opts.ver_res); + } /* Prepare display buffer */ const size_t buf_size = hor_res * ver_res / 10; /* At least 1/10 of the display size is recommended */ @@ -364,7 +386,7 @@ int main(void) { lv_indev_t *keyboard_indevs[MAX_KEYBOARDS] = { NULL, NULL, NULL }; size_t num_keyboards = libinput_find_devs(LIBINPUT_CAPABILITY_KEYBOARD, keyboard_devices, MAX_KEYBOARDS, false); for (int i = 0; i < num_keyboards; ++i) { - printf("found keyboard device %s\n", keyboard_devices[i]); + ul_log(UL_LOG_LEVEL_VERBOSE, "Connecting keyboard device %s\n", keyboard_devices[i]); lv_indev_drv_init(&keyboard_indev_drvs[i]); keyboard_indev_drvs[i].type = LV_INDEV_TYPE_KEYPAD; keyboard_indev_drvs[i].read_cb = libinput_multi_read; @@ -385,7 +407,7 @@ int main(void) { lv_indev_t *pointer_indevs[MAX_POINTER_DEVICES] = { NULL, NULL, NULL, NULL }; size_t num_pointer_devices = libinput_find_devs(LIBINPUT_CAPABILITY_POINTER, pointer_devices, MAX_POINTER_DEVICES, false); for (int i = 0; i < num_pointer_devices; ++i) { - printf("found pointer device %s\n", pointer_devices[i]); + ul_log(UL_LOG_LEVEL_VERBOSE, "Connecting pointer device %s\n", pointer_devices[i]); lv_indev_drv_init(&pointer_indev_drvs[i]); pointer_indev_drvs[i].type = LV_INDEV_TYPE_POINTER; pointer_indev_drvs[i].read_cb = libinput_multi_read; @@ -410,7 +432,7 @@ int main(void) { lv_indev_drv_t touchscreen_indev_drvs[MAX_TOUCHSCREENS]; size_t num_touchscreens = libinput_find_devs(LIBINPUT_CAPABILITY_TOUCH, touchscreens, MAX_TOUCHSCREENS, false); for (int i = 0; i < num_touchscreens; ++i) { - printf("found touchscreen %s\n", touchscreens[i]); + ul_log(UL_LOG_LEVEL_VERBOSE, "Connecting touchscreen device %s\n", touchscreens[i]); lv_indev_drv_init(&touchscreen_indev_drvs[i]); touchscreen_indev_drvs[i].type = LV_INDEV_TYPE_POINTER; touchscreen_indev_drvs[i].read_cb = libinput_multi_read; diff --git a/meson.build b/meson.build index 89284ea..42328a9 100644 --- a/meson.build +++ b/meson.build @@ -24,11 +24,15 @@ project( meson_version: '>=0.53.0' ) +add_project_arguments('-DUL_VERSION="@0@"'.format(meson.project_version()), language: ['c']) + unl0kr_sources = [ + 'command_line.c', 'cursor.c', - 'main.c', + 'log.c', 'libinput_multi.c', 'libinput_xkb.c', + 'main.c', 'montserrat_extended_32.c', 'sq2lv_layouts.c', ] diff --git a/unl0kr.h b/unl0kr.h new file mode 100644 index 0000000..c6096d8 --- /dev/null +++ b/unl0kr.h @@ -0,0 +1,28 @@ +/** + * Copyright 2021 Johannes Marbach + * + * This file is part of unl0kr, hereafter referred to as the program. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef UL_UNL0KR_H +#define UL_UNL0KR_H + +#ifndef UL_VERSION +#define UL_VERSION "?" /* Just to silence IDE warning. Real version injected by meson during build. */ +#endif + +#endif /* UL_UNL0KR_H */