From e3c98ddc353c6dc78733794b1685addfffcccb32 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 21 May 2019 15:03:59 +1000 Subject: [PATCH] [client] port all configuration parsing to use the new option helper --- VERSION | 2 +- client/CMakeLists.txt | 1 - client/include/interface/renderer.h | 41 +- client/renderers/EGL/egl.c | 52 +- client/renderers/OpenGL/opengl.c | 130 ++-- client/src/app.c | 2 +- client/src/config.c | 1011 ++++++++++++--------------- client/src/config.h | 1 + client/src/main.c | 54 +- client/src/main.h | 20 +- 10 files changed, 555 insertions(+), 759 deletions(-) diff --git a/VERSION b/VERSION index 08826912..5a606dc0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -a12-199-ga29639fceb+1 \ No newline at end of file +a12-200-gdb0d966102+1 \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index be6adc6b..e2fbe10d 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -41,7 +41,6 @@ find_package(PkgConfig) pkg_check_modules(PKGCONFIG REQUIRED sdl2 x11 - libconfig ) execute_process( diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index 7d3640ae..d4ab9293 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -40,28 +40,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA (x)->render && \ (x)->update_fps) -#define LGR_OPTION_COUNT(x) (sizeof(x) / sizeof(LG_RendererOpt)) - -typedef bool(* LG_RendererOptValidator)(const char * value); -typedef void(* LG_RendererOptHandler )(void * opaque, const char * value); - -typedef struct LG_RendererOpt -{ - const char * name; - const char * desc; - LG_RendererOptValidator validator; - LG_RendererOptHandler handler; -} -LG_RendererOpt; - -typedef struct LG_RendererOptValue -{ - const LG_RendererOpt * opt; - char * value; -} LG_RendererOptValue; - -typedef LG_RendererOpt * LG_RendererOptions; - typedef struct LG_RendererParams { // TTF_Font * font; @@ -99,7 +77,12 @@ typedef enum LG_RendererCursor } LG_RendererCursor; -typedef const char * (* LG_RendererGetName )(); +// returns the friendly name of the renderer +typedef const char * (* LG_RendererGetName)(); + +// called pre-creation to allow the renderer to register any options it might have +typedef void (* LG_RendererSetup)(); + typedef bool (* LG_RendererCreate )(void ** opaque, const LG_RendererParams params); typedef bool (* LG_RendererInitialize )(void * opaque, Uint32 * sdlFlags); typedef void (* LG_RendererDeInitialize)(void * opaque); @@ -113,10 +96,10 @@ typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgU typedef struct LG_Renderer { - LG_RendererCreate create; LG_RendererGetName get_name; - LG_RendererOptions options; - unsigned int option_count; + LG_RendererSetup setup; + + LG_RendererCreate create; LG_RendererInitialize initialize; LG_RendererDeInitialize deinitialize; LG_RendererOnResize on_resize; @@ -128,8 +111,4 @@ typedef struct LG_Renderer LG_RendererRender render; LG_RendererUpdateFPS update_fps; } -LG_Renderer; - -// generic option helpers -bool LG_RendererValidatorBool(const char * value); -bool LG_RendererValueToBool (const char * value); \ No newline at end of file +LG_Renderer; \ No newline at end of file diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 4929140a..0c89dd1f 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "interface/renderer.h" #include "common/debug.h" +#include "common/option.h" #include "utils.h" #include "dynamic/fonts.h" @@ -46,11 +47,6 @@ struct Options bool vsync; }; -static struct Options defaultOptions = -{ - .vsync = false -}; - struct Inst { LG_RendererParams params; @@ -96,6 +92,18 @@ struct Inst }; +static struct Option egl_options[] = +{ + { + .module = "egl", + .name = "vsync", + .description = "Enable vsync", + .type = OPTION_TYPE_BOOL, + .value.x_bool = false + }, + {0} +}; + void update_mouse_shape(struct Inst * this); const char * egl_get_name() @@ -103,6 +111,11 @@ const char * egl_get_name() return "EGL"; } +void egl_setup() +{ + option_register(egl_options); +} + bool egl_create(void ** opaque, const LG_RendererParams params) { // create our local storage @@ -116,8 +129,9 @@ bool egl_create(void ** opaque, const LG_RendererParams params) // safe off parameteres and init our default option values struct Inst * this = (struct Inst *)*opaque; - memcpy(&this->params, ¶ms , sizeof(LG_RendererParams)); - memcpy(&this->opt , &defaultOptions, sizeof(struct Options )); + memcpy(&this->params, ¶ms, sizeof(LG_RendererParams)); + + this->opt.vsync = option_get_bool("egl", "vsync"); this->translateX = 0; this->translateY = 0; @@ -484,31 +498,11 @@ void egl_update_fps(void * opaque, const float avgUPS, const float avgFPS) egl_fps_update(this->fps, avgUPS, avgFPS); } -static void handle_opt_vsync(void * opaque, const char *value) -{ - struct Inst * this = (struct Inst *)opaque; - if (!this) - return; - - this->opt.vsync = LG_RendererValueToBool(value); -} - -static LG_RendererOpt egl_options[] = -{ - { - .name = "vsync", - .desc ="Enable or disable vsync [default: enabled]", - .validator = LG_RendererValidatorBool, - .handler = handle_opt_vsync - } -}; - struct LG_Renderer LGR_EGL = { - .create = egl_create, .get_name = egl_get_name, - .options = egl_options, - .option_count = LGR_OPTION_COUNT(egl_options), + .setup = egl_setup, + .create = egl_create, .initialize = egl_initialize, .deinitialize = egl_deinitialize, .on_resize = egl_on_resize, diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index 5a7a7b67..a1f1bde4 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -31,6 +31,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include "common/debug.h" +#include "common/option.h" #include "utils.h" #include "lg-decoders.h" #include "dynamic/fonts.h" @@ -47,7 +48,39 @@ Place, Suite 330, Boston, MA 02111-1307 USA #define FADE_TIME 1000000 -struct Options +static struct Option opengl_options[] = +{ + { + .module = "opengl", + .name = "mipmap", + .description = "Enable mipmapping", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "opengl", + .name = "vsync", + .description = "Enable vsync", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "opengl", + .name = "preventBuffer", + .description = "Prevent the driver from buffering frames", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "opengl", + .name = "amdPinnedMem", + .description = "Use GL_AMD_pinned_memory if it is available", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + } +}; + +struct OpenGL_Options { bool mipmap; bool vsync; @@ -55,14 +88,6 @@ struct Options bool amdPinnedMem; }; -static struct Options defaultOptions = -{ - .mipmap = true, - .vsync = true, - .preventBuffer = true, - .amdPinnedMem = true, -}; - struct Alert { bool ready; @@ -76,8 +101,8 @@ struct Alert struct Inst { - LG_RendererParams params; - struct Options opt; + LG_RendererParams params; + struct OpenGL_Options opt; bool amdPinnedMemSupport; bool renderStarted; @@ -157,6 +182,11 @@ const char * opengl_get_name() return "OpenGL"; } +static void opengl_setup() +{ + option_register(opengl_options); +} + bool opengl_create(void ** opaque, const LG_RendererParams params) { // create our local storage @@ -169,8 +199,13 @@ bool opengl_create(void ** opaque, const LG_RendererParams params) memset(*opaque, 0, sizeof(struct Inst)); struct Inst * this = (struct Inst *)*opaque; - memcpy(&this->params, ¶ms , sizeof(LG_RendererParams)); - memcpy(&this->opt , &defaultOptions, sizeof(struct Options )); + memcpy(&this->params, ¶ms, sizeof(LG_RendererParams)); + + this->opt.mipmap = option_get_bool("opengl", "mipmap" ); + this->opt.vsync = option_get_bool("opengl", "vsync" ); + this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer"); + this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" ); + LG_LOCK_INIT(this->formatLock); LG_LOCK_INIT(this->syncLock ); @@ -770,76 +805,11 @@ static void render_wait(struct Inst * this) glDisable(GL_BLEND); } -static void handle_opt_mipmap(void * opaque, const char *value) -{ - struct Inst * this = (struct Inst *)opaque; - if (!this) - return; - - this->opt.mipmap = LG_RendererValueToBool(value); -} - -static void handle_opt_vsync(void * opaque, const char *value) -{ - struct Inst * this = (struct Inst *)opaque; - if (!this) - return; - - this->opt.vsync = LG_RendererValueToBool(value); -} - -static void handle_opt_prevent_buffer(void * opaque, const char *value) -{ - struct Inst * this = (struct Inst *)opaque; - if (!this) - return; - - this->opt.preventBuffer = LG_RendererValueToBool(value); -} - -static void handle_opt_amd_pinned_mem(void * opaque, const char *value) -{ - struct Inst * this = (struct Inst *)opaque; - if (!this) - return; - - this->opt.amdPinnedMem = LG_RendererValueToBool(value); -} - - -static LG_RendererOpt opengl_options[] = -{ - { - .name = "mipmap", - .desc = "Enable or disable mipmapping [default: enabled]", - .validator = LG_RendererValidatorBool, - .handler = handle_opt_mipmap - }, - { - .name = "vsync", - .desc ="Enable or disable vsync [default: enabled]", - .validator = LG_RendererValidatorBool, - .handler = handle_opt_vsync - }, - { - .name = "preventBuffer", - .desc = "Prevent the driver from buffering frames [default: disabled]", - .validator = LG_RendererValidatorBool, - .handler = handle_opt_prevent_buffer - }, - { - .name = "amdPinnedMem", - .desc = "Use GL_AMD_pinned_memory if it is available [default: enabled]", - .validator = LG_RendererValidatorBool, - .handler = handle_opt_amd_pinned_mem - } -}; - const LG_Renderer LGR_OpenGL = { .get_name = opengl_get_name, - .options = opengl_options, - .option_count = LGR_OPTION_COUNT(opengl_options), + .setup = opengl_setup, + .create = opengl_create, .initialize = opengl_initialize, .deinitialize = opengl_deinitialize, diff --git a/client/src/app.c b/client/src/app.c index 0e2909aa..b2684ee4 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -23,7 +23,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA void app_alert(LG_MsgAlert type, const char * fmt, ...) { - if (!state.lgr || params.disableAlerts) + if (!state.lgr || !params.showAlerts) return; va_list args; diff --git a/client/src/config.c b/client/src/config.c index 5ec69a44..83fe34d7 100644 --- a/client/src/config.c +++ b/client/src/config.c @@ -19,319 +19,368 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "main.h" #include "config.h" +#include "common/option.h" #include "common/debug.h" +#include "common/stringutils.h" #include -#include #include #include -static bool load(const char * configFile); +// forwards +static bool optRendererParse (struct Option * opt, const char * str); +static StringList optRendererValues (struct Option * opt); +static char * optRendererToString (struct Option * opt); +static bool optPosParse (struct Option * opt, const char * str); +static StringList optPosValues (struct Option * opt); +static char * optPosToString (struct Option * opt); +static bool optSizeParse (struct Option * opt, const char * str); +static StringList optSizeValues (struct Option * opt); +static char * optSizeToString (struct Option * opt); +static char * optScancodeToString (struct Option * opt); + static void doLicense(); -static void doHelp(char * app); + +static struct Option options[] = +{ + // app options + { + .module = "app", + .name = "configFile", + .description = "A file to read additional configuration from", + .shortopt = 'C', + .type = OPTION_TYPE_STRING, + .value.x_string = NULL, + }, + { + .module = "app", + .name = "shmFile", + .description = "The path to the shared memory file", + .shortopt = 'f', + .type = OPTION_TYPE_STRING, + .value.x_string = "/dev/shm/looking-glass", + }, + { + .module = "app", + .name = "shmSize", + .description = "Specify the size in MB of the shared memory file (0 = detect)", + .shortopt = 'L', + .type = OPTION_TYPE_INT, + .value.x_int = 0, + }, + { + .module = "app", + .name = "renderer", + .description = "Specify the renderer to use", + .shortopt = 'g', + .type = OPTION_TYPE_CUSTOM, + .parser = optRendererParse, + .getValues = optRendererValues, + .toString = optRendererToString + }, + { + .module = "app", + .name = "license", + .description = "Show the licence for this application and then terminate", + .shortopt = 'l', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + + // window options + { + .module = "win", + .name = "title", + .description = "The window title", + .type = OPTION_TYPE_STRING, + .value.x_string = "Looking Glass (client)" + }, + { + .module = "win", + .name = "position", + .description = "Initial window position at startup", + .type = OPTION_TYPE_CUSTOM, + .parser = optPosParse, + .getValues = optPosValues, + .toString = optPosToString + }, + { + .module = "win", + .name = "size", + .description = "Initial window size at startup", + .type = OPTION_TYPE_CUSTOM, + .parser = optSizeParse, + .getValues = optSizeValues, + .toString = optSizeToString + }, + { + .module = "win", + .name = "autoResize", + .description = "Auto resize the window to the guest", + .shortopt = 'a', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + { + .module = "win", + .name = "allowResize", + .description = "Aallow the window to be manually resized", + .shortopt = 'n', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true, + }, + { + .module = "win", + .name = "keepAspect", + .description = "Maintain the correct aspect ratio", + .shortopt = 'r', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true, + }, + { + .module = "win", + .name = "borderless", + .description = "Borderless mode", + .shortopt = 'd', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + { + .module = "win", + .name = "fullScreen", + .description = "Launch in fullscreen borderless mode", + .shortopt = 'F', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + { + .module = "win", + .name = "fpsLimit", + .description = "Frame rate limit (0 = disable - not recommended)", + .shortopt = 'K', + .type = OPTION_TYPE_INT, + .value.x_int = 200, + }, + { + .module = "win", + .name = "showFPS", + .description = "Enable the FPS & UPS display", + .shortopt = 'k', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + { + .module = "win", + .name = "ignoreQuit", + .description = "Ignore requests to quit (ie: Alt+F4)", + .shortopt = 'Q', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + { + .module = "win", + .name = "noScreensaver", + .description = "Prevent the screensaver from starting", + .shortopt = 'S', + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, + { + .module = "win", + .name = "alerts", + .description = "Show on screen alert messages", + .shortopt = 'q', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true, + }, + + // input options + { + .module = "input", + .name = "grabKeyboard", + .description = "Grab the keyboard in capture mode", + .shortopt = 'G', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true, + }, + { + .module = "input", + .name = "escapeKey", + .description = "Specify the escape key, see https://wiki.libsdl.org/SDLScancodeLookup for valid values", + .shortopt = 'm', + .type = OPTION_TYPE_INT, + .value.x_int = SDL_SCANCODE_SCROLLLOCK, + .toString = optScancodeToString + }, + { + .module = "input", + .name = "hideCursor", + .description = "Hide the local mouse cursor", + .shortopt = 'M', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true, + }, + + // spice options + { + .module = "spice", + .name = "enable", + .description = "Enable the built in SPICE client for input and/or clipboard support", + .shortopt = 's', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "spice", + .name = "host", + .description = "The SPICE server host or UNIX socket", + .shortopt = 'c', + .type = OPTION_TYPE_STRING, + .value.x_string = "127.0.0.1" + }, + { + .module = "spice", + .name = "port", + .description = "The SPICE server port (0 = unix socket)", + .shortopt = 'p', + .type = OPTION_TYPE_INT, + .value.x_int = 5900 + }, + { + .module = "spice", + .name = "input", + .description = "Use SPICE to send keyboard and mouse input events to the guest", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "spice", + .name = "clipboard", + .description = "Use SPICE to syncronize the clipboard contents with the guest", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "spice", + .name = "clipboardToVM", + .description = "Allow the clipboard to be syncronized TO the VM", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "spice", + .name = "clipboardToLocal", + .description = "Allow the clipboard to be syncronized FROM the VM", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + { + .module = "spice", + .name = "scaleCursor", + .description = "Scale cursor input position to screen size when up/down scaled", + .shortopt = 'j', + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, + {0} +}; + +void config_init() +{ + params.center = true; + params.w = 1024; + params.h = 768; + + option_register(options); +} bool config_load(int argc, char * argv[]) { - // duplicate the constants to avoid crashing out when trying to free - // these values. This is to allow the defaults to be overridden. - params.shmFile = strdup(params.shmFile ); - params.spiceHost = strdup(params.spiceHost ); - params.windowTitle = strdup(params.windowTitle); - - // load any global then local config options first + // load any global options first struct stat st; - if (stat("/etc/looking-glass.conf", &st) >= 0) + if (stat("/etc/looking-glass-client.ini", &st) >= 0) { - DEBUG_INFO("Loading config from: /etc/looking-glass.conf"); - if (!load("/etc/looking-glass.conf")) + DEBUG_INFO("Loading config from: /etc/looking-glass-client.ini"); + if (!option_load("/etc/looking-glass-client.ini")) return false; } + // load user's local options struct passwd * pw = getpwuid(getuid()); - const char pattern[] = "%s/.looking-glass.conf"; - const size_t len = strlen(pw->pw_dir) + sizeof(pattern); - char buffer[len]; - snprintf(buffer, len, pattern, pw->pw_dir); - if (stat(buffer, &st) >= 0) + char * localFile; + alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir); + if (stat(localFile, &st) >= 0) { - DEBUG_INFO("Loading config from: %s", buffer); - if (!load(buffer)) + DEBUG_INFO("Loading config from: %s", localFile); + if (!option_load(localFile)) + { + free(localFile); + return false; + } + } + free(localFile); + + // parse the command line arguments + if (!option_parse(argc, argv)) + return false; + + // if a file was specified to also load, do it + const char * configFile = option_get_string("app", "configFile"); + if (configFile) + { + DEBUG_INFO("Loading config from: %s", configFile); + if (!option_load(configFile)) return false; } - for(;;) + // validate the values are sane + if (!option_validate()) + return false; + + if (option_get_bool("app", "license")) { - switch(getopt(argc, argv, "hC:f:L:s:c:p:jMvK:kg:o:anrdFx:y:w:b:QSGm:lqt:")) - { - case '?': - case 'h': - default : - doHelp(argv[0]); - return false; - - case -1: - break; - - case 'C': - params.configFile = optarg; - if (!load(optarg)) - return false; - continue; - - case 'f': - free(params.shmFile); - params.shmFile = strdup(optarg); - continue; - - case 'L': - params.shmSize = atoi(optarg) * 1024 * 1024; - continue; - - case 's': - { - if (strcasecmp("ALL", optarg) == 0) - { - params.useSpiceInput = false; - params.useSpiceClipboard = false; - } - else if (strcasecmp("INPUT" , optarg) == 0) params.useSpiceInput = false; - else if (strcasecmp("CLIPBOARD" , optarg) == 0) params.useSpiceClipboard = false; - else if (strcasecmp("CLIPBOARD_TO_VM" , optarg) == 0) params.clipboardToVM = false; - else if (strcasecmp("CLIPBOARD_TO_LOCAL", optarg) == 0) params.clipboardToLocal = false; - else - { - fprintf(stderr, "Invalid spice feature: %s\n", optarg); - fprintf(stderr, "Must be one of ALL, INPUT, CLIPBOARD, CLIPBOARD_TO_VM, CLIPBOARD_TO_LOCAL\n"); - doHelp(argv[0]); - return false; - } - continue; - } - - case 'c': - free(params.spiceHost); - params.spiceHost = strdup(optarg); - continue; - - case 'p': - params.spicePort = atoi(optarg); - continue; - - case 'j': - params.scaleMouseInput = false; - continue; - - case 'M': - params.hideMouse = false; - continue; - - case 'K': - params.fpsLimit = atoi(optarg); - continue; - - case 'k': - params.showFPS = true; - continue; - - case 'g': - { - bool ok = false; - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - if (strcasecmp(LG_Renderers[i]->get_name(), optarg) == 0) - { - params.forceRenderer = true; - params.forceRendererIndex = i; - ok = true; - break; - } - - if (!ok) - { - fprintf(stderr, "No such renderer: %s\n", optarg); - fprintf(stderr, "Use '-o list' obtain a list of options\n"); - doHelp(argv[0]); - return false; - } - - continue; - } - - case 'o': - { - if (strcasecmp(optarg, "list") == 0) - { - size_t maxLen = 0; - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - { - const LG_Renderer * r = LG_Renderers[i]; - for(unsigned int j = 0; j < r->option_count; ++j) - { - const size_t len = strlen(r->options[j].name); - if (len > maxLen) - maxLen = len; - } - } - - fprintf(stderr, "\nRenderer Option List\n"); - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - { - const LG_Renderer * r = LG_Renderers[i]; - fprintf(stderr, "\n%s\n", r->get_name()); - for(unsigned int j = 0; j < r->option_count; ++j) - { - const size_t pad = maxLen - strlen(r->options[j].name); - for(int i = 0; i < pad; ++i) - fputc(' ', stderr); - - fprintf(stderr, " %s - %s\n", r->options[j].name, r->options[j].desc); - } - } - fprintf(stderr, "\n"); - return false; - } - - const LG_Renderer * renderer = NULL; - RendererOpts * opts = NULL; - - const size_t len = strlen(optarg); - const char * name = strtok(optarg, ":"); - - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - if (strcasecmp(LG_Renderers[i]->get_name(), name) == 0) - { - renderer = LG_Renderers[i]; - opts = ¶ms.rendererOpts[i]; - break; - } - - if (!renderer) - { - fprintf(stderr, "No such renderer: %s\n", name); - doHelp(argv[0]); - return false; - } - - const char * option = strtok(NULL , "="); - if (!option) - { - fprintf(stderr, "Renderer option name not specified\n"); - doHelp(argv[0]); - return false; - } - - const LG_RendererOpt * opt = NULL; - for(unsigned int i = 0; i < renderer->option_count; ++i) - if (strcasecmp(option, renderer->options[i].name) == 0) - { - opt = &renderer->options[i]; - break; - } - - if (!opt) - { - fprintf(stderr, "Renderer \"%s\" doesn't have the option: %s\n", renderer->get_name(), option); - doHelp(argv[0]); - return false; - } - - const char * value = NULL; - if (len > strlen(name) + strlen(option) + 2) - value = option + strlen(option) + 1; - - if (opt->validator && !opt->validator(value)) - { - fprintf(stderr, "Renderer \"%s\" reported invalid value for option \"%s\"\n", renderer->get_name(), option); - doHelp(argv[0]); - return false; - } - - if (opts->argc == opts->size) - { - opts->size += 5; - opts->argv = realloc(opts->argv, sizeof(LG_RendererOptValue) * opts->size); - } - - opts->argv[opts->argc].opt = opt; - opts->argv[opts->argc].value = strdup(value); - ++opts->argc; - continue; - } - - case 'a': - params.autoResize = true; - continue; - - case 'n': - params.allowResize = false; - continue; - - case 'r': - params.keepAspect = false; - continue; - - case 'd': - params.borderless = true; - continue; - - case 'F': - params.fullscreen = true; - continue; - - case 'x': - params.center = false; - params.x = atoi(optarg); - continue; - - case 'y': - params.center = false; - params.y = atoi(optarg); - continue; - - case 'w': - params.w = atoi(optarg); - continue; - - case 'b': - params.h = atoi(optarg); - continue; - - case 'Q': - params.ignoreQuit = true; - continue; - - case 'S': - params.allowScreensaver = false; - continue; - - case 'G': - params.grabKeyboard = false; - continue; - - case 'm': - params.escapeKey = atoi(optarg); - continue; - - case 'q': - params.disableAlerts = true; - continue; - - case 't': - free(params.windowTitle); - params.windowTitle = strdup(optarg); - continue; - - case 'l': - doLicense(); - return false; - } - break; + doLicense(); + return false; } - if (optind != argc) + // setup the application params for the basic types + params.shmFile = option_get_string("app", "shmFile"); + params.shmSize = option_get_int ("app", "shmSize") * 1048576; + + params.windowTitle = option_get_string("win", "title" ); + params.autoResize = option_get_bool ("win", "autoResize" ); + params.allowResize = option_get_bool ("win", "allowResize" ); + params.keepAspect = option_get_bool ("win", "keepAspect" ); + params.borderless = option_get_bool ("win", "borderless" ); + params.fullscreen = option_get_bool ("win", "fullScreen" ); + params.fpsLimit = option_get_int ("win", "fpsLimit" ); + params.showFPS = option_get_bool ("win", "showFPS" ); + params.ignoreQuit = option_get_bool ("win", "ignoreQuit" ); + params.noScreensaver = option_get_bool ("win", "noScreensaver"); + params.showAlerts = option_get_bool ("win", "alerts" ); + + params.grabKeyboard = option_get_bool ("input", "grabKeyboard"); + params.escapeKey = option_get_int ("input", "escapeKey" ); + params.hideMouse = option_get_bool ("input", "hideCursor" ); + + if (option_get_bool("spice", "enable")) { - fprintf(stderr, "A non option was supplied\n"); - doHelp(argv[0]); - return false; + params.spiceHost = option_get_string("spice", "host"); + params.spicePort = option_get_int ("spice", "port"); + + params.useSpiceInput = option_get_bool("spice", "input" ); + params.useSpiceClipboard = option_get_bool("spice", "clipboard"); + + if (params.useSpiceClipboard) + { + params.clipboardToVM = option_get_bool("spice", "clipboardToVM" ); + params.clipboardToLocal = option_get_bool("spice", "clipboardToLocal"); + + if (!params.clipboardToVM && !params.clipboardToLocal) + params.useSpiceClipboard = false; + } + + params.scaleMouseInput = option_get_bool("spice", "scaleCursor"); } return true; @@ -339,276 +388,7 @@ bool config_load(int argc, char * argv[]) void config_free() { - free(params.shmFile ); - free(params.spiceHost ); - free(params.windowTitle); - - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - { - RendererOpts * opts = ¶ms.rendererOpts[i]; - for(unsigned int j = 0; j < opts->argc; ++j) - free(opts->argv[j].value); - free(opts->argv); - } -} - -static bool load(const char * configFile) -{ - config_t cfg; - int itmp; - const char *stmp; - - config_init(&cfg); - if (!config_read_file(&cfg, configFile)) - { - DEBUG_ERROR("Config file error %s:%d - %s", - config_error_file(&cfg), - config_error_line(&cfg), - config_error_text(&cfg) - ); - return false; - } - - config_setting_t * global = config_lookup(&cfg, "global"); - if (global) - { - if (config_setting_lookup_string(global, "shmFile", &stmp)) - { - free(params.shmFile); - params.shmFile = strdup(stmp); - } - - if (config_setting_lookup_int(global, "shmSize", &itmp)) - params.shmSize = itmp * 1024 * 1024; - - if (config_setting_lookup_string(global, "forceRenderer", &stmp)) - { - bool ok = false; - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - if (strcasecmp(LG_Renderers[i]->get_name(), stmp) == 0) - { - params.forceRenderer = true; - params.forceRendererIndex = i; - ok = true; - break; - } - - if (!ok) - { - DEBUG_ERROR("No such renderer: %s", stmp); - config_destroy(&cfg); - return false; - } - } - - if (config_setting_lookup_bool(global, "scaleMouseInput" , &itmp)) params.scaleMouseInput = (itmp != 0); - if (config_setting_lookup_bool(global, "hideMouse" , &itmp)) params.hideMouse = (itmp != 0); - if (config_setting_lookup_bool(global, "showFPS" , &itmp)) params.showFPS = (itmp != 0); - if (config_setting_lookup_bool(global, "autoResize" , &itmp)) params.autoResize = (itmp != 0); - if (config_setting_lookup_bool(global, "allowResize" , &itmp)) params.allowResize = (itmp != 0); - if (config_setting_lookup_bool(global, "keepAspect" , &itmp)) params.keepAspect = (itmp != 0); - if (config_setting_lookup_bool(global, "borderless" , &itmp)) params.borderless = (itmp != 0); - if (config_setting_lookup_bool(global, "fullScreen" , &itmp)) params.fullscreen = (itmp != 0); - if (config_setting_lookup_bool(global, "ignoreQuit" , &itmp)) params.ignoreQuit = (itmp != 0); - if (config_setting_lookup_bool(global, "allowScreensaver", &itmp)) params.allowScreensaver = (itmp != 0); - if (config_setting_lookup_bool(global, "disableAlerts" , &itmp)) params.disableAlerts = (itmp != 0); - - if (config_setting_lookup_int(global, "x", ¶ms.x)) params.center = false; - if (config_setting_lookup_int(global, "y", ¶ms.y)) params.center = false; - - if (config_setting_lookup_int(global, "w", &itmp)) - { - if (itmp < 1) - { - DEBUG_ERROR("Invalid window width, must be greater then 1px"); - config_destroy(&cfg); - return false; - } - params.w = (unsigned int)itmp; - } - - if (config_setting_lookup_int(global, "h", &itmp)) - { - if (itmp < 1) - { - DEBUG_ERROR("Invalid window height, must be greater then 1px"); - config_destroy(&cfg); - return false; - } - params.h = (unsigned int)itmp; - } - - if (config_setting_lookup_int(global, "fpsLimit", &itmp)) - { - if (itmp < 1) - { - DEBUG_ERROR("Invalid FPS limit, must be greater then 0"); - config_destroy(&cfg); - return false; - } - params.fpsLimit = (unsigned int)itmp; - } - - if (config_setting_lookup_int(global, "escapeKey", &itmp)) - { - if (itmp <= SDL_SCANCODE_UNKNOWN || itmp > SDL_SCANCODE_APP2) - { - DEBUG_ERROR("Invalid capture key value, see https://wiki.libsdl.org/SDLScancodeLookup"); - config_destroy(&cfg); - return false; - } - params.escapeKey = (SDL_Scancode)itmp; - } - - if (config_setting_lookup_string(global, "windowTitle", &stmp)) - { - free(params.windowTitle); - params.windowTitle = strdup(stmp); - } - - } - - config_setting_t * spice = config_lookup(&cfg, "spice"); - if (spice) - { - if (config_setting_lookup_bool(spice, "useInput", &itmp)) - params.useSpiceInput = (itmp != 0); - - if (config_setting_lookup_bool(spice, "useClipboard", &itmp)) - params.useSpiceClipboard = (itmp != 0); - - if (config_setting_lookup_bool(spice, "clipboardToVM", &itmp)) - params.clipboardToVM = (itmp != 0); - - if (config_setting_lookup_bool(spice, "clipboardToLocal", &itmp)) - params.clipboardToLocal = (itmp != 0); - - if (config_setting_lookup_string(spice, "host", &stmp)) - { - free(params.spiceHost); - params.spiceHost = strdup(stmp); - } - - if (config_setting_lookup_int(spice, "port", &itmp)) - { - if (itmp < 0 || itmp > 65535) - { - DEBUG_ERROR("Invalid spice port"); - config_destroy(&cfg); - return false; - } - params.spicePort = itmp; - } - } - - for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) - { - const LG_Renderer * r = LG_Renderers[i]; - RendererOpts * opts = ¶ms.rendererOpts[i]; - config_setting_t * group = config_lookup(&cfg, r->get_name()); - if (!group) - continue; - - for(unsigned int j = 0; j < r->option_count; ++j) - { - const char * name = r->options[j].name; - if (!config_setting_lookup_string(group, name, &stmp)) - continue; - - if (r->options[j].validator && !r->options[j].validator(stmp)) - { - DEBUG_ERROR("Renderer \"%s\" reported invalid value for option \"%s\"", r->get_name(), name); - config_destroy(&cfg); - return false; - } - - if (opts->argc == opts->size) - { - opts->size += 5; - opts->argv = realloc(opts->argv, sizeof(LG_RendererOptValue) * opts->size); - } - - opts->argv[opts->argc].opt = &r->options[j]; - opts->argv[opts->argc].value = strdup(stmp); - ++opts->argc; - } - } - - config_destroy(&cfg); - return true; -} - -static void doHelp(char * app) -{ - char x[8], y[8]; - snprintf(x, sizeof(x), "%d", params.x); - snprintf(y, sizeof(y), "%d", params.y); - - fprintf(stderr, - "\n" - "Looking Glass Client\n" - "Usage: %s [OPTION]...\n" - "Example: %s -h\n" - "\n" - " -h Print out this help\n" - "\n" - " -C PATH Specify an additional configuration file to load\n" - " -f PATH Specify the path to the shared memory file [current: %s]\n" - " -L SIZE Specify the size in MB of the shared memory file (0 = detect) [current: %d]\n" - "\n" - " -s FEATURE Disable spice feature (specify multiple times for each feature)\n" - "\n" - " ALL Disable the spice client entirely\n" - " INPUT Disable spice keyboard & mouse input\n" - " CLIPBOARD Disable spice clipboard support\n" - " CLIPBOARD_TO_VM Disable local clipboard to VM sync\n" - " CLIPBOARD_TO_LOCAL Disable VM clipboard to local sync\n" - "\n" - " -c HOST Specify the spice host or UNIX socket [current: %s]\n" - " -p PORT Specify the spice port or 0 for UNIX socket [current: %d]\n" - " -j Disable cursor position scaling\n" - " -M Don't hide the host cursor\n" - "\n" - " -K Set the FPS limit [current: %d]\n" - " -k Enable FPS display\n" - " -g NAME Force the use of a specific renderer\n" - " -o OPTION Specify a renderer option (ie: opengl:vsync=0)\n" - " Alternatively specify \"list\" to list all renderers and their options\n" - "\n" - " -a Auto resize the window to the guest\n" - " -n Don't allow the window to be manually resized\n" - " -r Don't maintain the aspect ratio\n" - " -d Borderless mode\n" - " -F Borderless fullscreen mode\n" - " -x XPOS Initial window X position [current: %s]\n" - " -y YPOS Initial window Y position [current: %s]\n" - " -w WIDTH Initial window width [current: %u]\n" - " -b HEIGHT Initial window height [current: %u]\n" - " -Q Ignore requests to quit (ie: Alt+F4)\n" - " -S Disable the screensaver\n" - " -G Don't capture the keyboard in capture mode\n" - " -m CODE Specify the escape key [current: %u (%s)]\n" - " See https://wiki.libsdl.org/SDLScancodeLookup for valid values\n" - " -q Disable alert messages [current: %s]\n" - " -t TITLE Use a custom title for the main window\n" - "\n" - " -l License information\n" - "\n", - app, - app, - params.shmFile, - params.shmSize, - params.spiceHost, - params.spicePort, - params.fpsLimit, - params.center ? "center" : x, - params.center ? "center" : y, - params.w, - params.h, - params.escapeKey, - SDL_GetScancodeName(params.escapeKey), - params.disableAlerts ? "disabled" : "enabled" - ); + option_free(); } static void doLicense() @@ -633,4 +413,117 @@ static void doLicense() "Place, Suite 330, Boston, MA 02111 - 1307 USA\n" "\n" ); +} + +static bool optRendererParse(struct Option * opt, const char * str) +{ + if (strcasecmp(str, "auto") == 0) + { + params.forceRenderer = false; + return true; + } + + for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) + if (strcasecmp(str, LG_Renderers[i]->get_name()) == 0) + { + params.forceRenderer = true; + params.forceRendererIndex = i; + return true; + } + + return false; +} + +static StringList optRendererValues(struct Option * opt) +{ + StringList sl = stringlist_new(false); + + // this typecast is safe as the stringlist doesn't own the values + for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) + stringlist_push(sl, (char *)LG_Renderers[i]->get_name()); + + return sl; +} + +static char * optRendererToString(struct Option * opt) +{ + if (!params.forceRenderer) + return strdup("auto"); + + if (params.forceRendererIndex >= LG_RENDERER_COUNT) + return NULL; + + return strdup(LG_Renderers[params.forceRendererIndex]->get_name()); +} + +static bool optPosParse(struct Option * opt, const char * str) +{ + if (strcmp(str, "center") == 0) + { + params.center = true; + return true; + } + + if (sscanf(str, "%dx%d", ¶ms.x, ¶ms.y) == 2) + { + params.center = false; + return true; + } + + return false; +} + +static StringList optPosValues(struct Option * opt) +{ + StringList sl = stringlist_new(false); + stringlist_push(sl, "center"); + stringlist_push(sl, "x, ie: 100x100"); + return sl; +} + +static char * optPosToString(struct Option * opt) +{ + if (params.center) + return strdup("center"); + + int len = snprintf(NULL, 0, "%dx%d", params.x, params.y); + char * str = malloc(len + 1); + sprintf(str, "%dx%d", params.x, params.y); + + return str; +} + +static bool optSizeParse(struct Option * opt, const char * str) +{ + if (sscanf(str, "%dx%d", ¶ms.w, ¶ms.h) == 2) + { + if (params.w < 1 || params.h < 1) + return false; + return true; + } + + return false; +} + +static StringList optSizeValues(struct Option * opt) +{ + StringList sl = stringlist_new(false); + stringlist_push(sl, "x, ie: 100x100"); + return sl; +} + +static char * optSizeToString(struct Option * opt) +{ + int len = snprintf(NULL, 0, "%dx%d", params.w, params.h); + char * str = malloc(len + 1); + sprintf(str, "%dx%d", params.w, params.h); + + return str; +} + +static char * optScancodeToString(struct Option * opt) +{ + char * str; + alloc_sprintf(&str, "%d = %s", opt->value.x_int, SDL_GetScancodeName(opt->value.x_int)); + return str; } \ No newline at end of file diff --git a/client/src/config.h b/client/src/config.h index 45457a71..cd1af77b 100644 --- a/client/src/config.h +++ b/client/src/config.h @@ -19,5 +19,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include +void config_init(); bool config_load(int argc, char * argv[]); void config_free(); \ No newline at end of file diff --git a/client/src/main.c b/client/src/main.c index d01e8aee..8d02dd0f 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -48,39 +48,9 @@ static int renderThread(void * unused); static int frameThread (void * unused); struct AppState state; -struct AppParams params = -{ - .configFile = "/etc/looking-glass.conf", - .autoResize = false, - .allowResize = true, - .keepAspect = true, - .borderless = false, - .fullscreen = false, - .center = true, - .x = 0, - .y = 0, - .w = 1024, - .h = 768, - .shmFile = "/dev/shm/looking-glass", - .shmSize = 0, - .fpsLimit = 200, - .showFPS = false, - .useSpiceInput = true, - .useSpiceClipboard = true, - .spiceHost = "127.0.0.1", - .spicePort = 5900, - .clipboardToVM = true, - .clipboardToLocal = true, - .scaleMouseInput = true, - .hideMouse = true, - .ignoreQuit = false, - .allowScreensaver = true, - .grabKeyboard = true, - .escapeKey = SDL_SCANCODE_SCROLLLOCK, - .disableAlerts = false, - .forceRenderer = false, - .windowTitle = "Looking Glass (Client)" -}; + +// this structure is initialized in config.c +struct AppParams params = { 0 }; static void updatePositionInfo() { @@ -888,8 +858,7 @@ static void * map_memory() static bool try_renderer(const int index, const LG_RendererParams lgrParams, Uint32 * sdlFlags) { - const LG_Renderer *r = LG_Renderers[index]; - RendererOpts *opts = ¶ms.rendererOpts[index]; + const LG_Renderer *r = LG_Renderers[index]; if (!IS_LG_RENDERER_VALID(r)) { @@ -902,10 +871,6 @@ static bool try_renderer(const int index, const LG_RendererParams lgrParams, Uin if (!r->create(&state.lgrData, lgrParams)) return false; - // set it's options - for(unsigned int i = 0; i < opts->argc; ++i) - opts->argv[i].opt->handler(state.lgrData, opts->argv[i].value); - // initialize the renderer if (!r->initialize(state.lgrData, sdlFlags)) { @@ -1050,7 +1015,7 @@ int run() if (params.fullscreen) SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0"); - if (params.allowScreensaver) + if (!params.noScreensaver) SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); if (!params.center) @@ -1219,7 +1184,7 @@ int run() { if (state.shm->flags & KVMFR_HEADER_FLAG_PAUSED) { - if (state.lgr && !params.disableAlerts) + if (state.lgr && params.showAlerts) state.lgr->on_alert( state.lgrData, LG_ALERT_WARNING, @@ -1302,10 +1267,15 @@ int main(int argc, char * argv[]) if (!installCrashHandler(argv[0])) DEBUG_WARN("Failed to install the crash handler"); + config_init(); + + // early renderer setup for option registration + for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i) + LG_Renderers[i]->setup(); + if (!config_load(argc, argv)) return -1; - if (params.grabKeyboard) { SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); diff --git a/client/src/main.h b/client/src/main.h index 50712a8d..11319e82 100644 --- a/client/src/main.h +++ b/client/src/main.h @@ -68,17 +68,8 @@ struct AppState KeybindHandle kbInput; }; -typedef struct RenderOpts -{ - unsigned int size; - unsigned int argc; - LG_RendererOptValue * argv; -} -RendererOpts; - struct AppParams { - const char * configFile; bool autoResize; bool allowResize; bool keepAspect; @@ -87,29 +78,28 @@ struct AppParams bool center; int x, y; unsigned int w, h; - char * shmFile; + const char * shmFile; unsigned int shmSize; unsigned int fpsLimit; bool showFPS; bool useSpiceInput; bool useSpiceClipboard; - char * spiceHost; + const char * spiceHost; unsigned int spicePort; bool clipboardToVM; bool clipboardToLocal; bool scaleMouseInput; bool hideMouse; bool ignoreQuit; - bool allowScreensaver; + bool noScreensaver; bool grabKeyboard; SDL_Scancode escapeKey; - bool disableAlerts; + bool showAlerts; bool forceRenderer; unsigned int forceRendererIndex; - RendererOpts rendererOpts[LG_RENDERER_COUNT]; - char * windowTitle; + const char * windowTitle; }; struct CBRequest