From 4dee965fdfc0281d79dd0fe7d1cedd04ea3cd74d Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 30 Mar 2019 15:52:00 +1100 Subject: [PATCH] [client] main: move config and option parsing into a seperate unit --- VERSION | 2 +- client/CMakeLists.txt | 1 + client/src/config.c | 621 ++++++++++++++++++++++++++++++++++++++++++ client/src/config.h | 22 ++ client/src/main.c | 590 +-------------------------------------- 5 files changed, 649 insertions(+), 587 deletions(-) create mode 100644 client/src/config.c create mode 100644 client/src/config.h diff --git a/VERSION b/VERSION index 9351c6a3..abafaf95 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -a12-132-g5d254c7751+1 \ No newline at end of file +a12-133-g35094a57cb+1 \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 2b50c4f6..ee6b69ad 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -73,6 +73,7 @@ link_libraries( set(SOURCES src/main.c src/app.c + src/config.c src/lg-renderer.c src/ll.c src/utils.c diff --git a/client/src/config.c b/client/src/config.c new file mode 100644 index 00000000..621581ae --- /dev/null +++ b/client/src/config.c @@ -0,0 +1,621 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2019 Geoffrey McRae +https://looking-glass.hostfission.com + +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 2 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, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "main.h" +#include "config.h" +#include "debug.h" + +#include +#include +#include +#include + +static bool load(const char * configFile); +static void doLicense(); +static void doHelp(char * app); + +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 + struct stat st; + if (stat("/etc/looking-glass.conf", &st) >= 0) + { + DEBUG_INFO("Loading config from: /etc/looking-glass.conf"); + if (!load("/etc/looking-glass.conf")) + return false; + } + + 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) + { + DEBUG_INFO("Loading config from: %s", buffer); + if (!load(buffer)) + return false; + } + + for(;;) + { + 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; + } + + if (optind != argc) + { + fprintf(stderr, "A non option was supplied\n"); + doHelp(argv[0]); + return false; + } + + return true; +} + +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" + ); +} + +static void doLicense() +{ + fprintf(stderr, + "\n" + "Looking Glass - KVM FrameRelay (KVMFR) Client\n" + "Copyright(C) 2017-2019 Geoffrey McRae \n" + "https://looking-glass.hostfission.com\n" + "\n" + "This program is free software; you can redistribute it and / or modify it under\n" + "the terms of the GNU General Public License as published by the Free Software\n" + "Foundation; either version 2 of the License, or (at your option) any later\n" + "version.\n" + "\n" + "This program is distributed in the hope that it will be useful, but WITHOUT ANY\n" + "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n" + "PARTICULAR PURPOSE.See the GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License along with\n" + "this program; if not, write to the Free Software Foundation, Inc., 59 Temple\n" + "Place, Suite 330, Boston, MA 02111 - 1307 USA\n" + "\n" + ); +} \ No newline at end of file diff --git a/client/src/config.h b/client/src/config.h new file mode 100644 index 00000000..242a7920 --- /dev/null +++ b/client/src/config.h @@ -0,0 +1,22 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017-2019 Geoffrey McRae +https://looking-glass.hostfission.com + +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 2 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, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +bool config_load(int argc, char * argv[]); \ No newline at end of file diff --git a/client/src/main.c b/client/src/main.c index fea94273..8bc4b87e 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -18,15 +18,15 @@ Place, Suite 330, Boston, MA 02111-1307 USA */ #include "main.h" +#include "config.h" #include #include #include #include #include -#include -#include #include +#include #include #include #include @@ -34,7 +34,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include -#include #include "debug.h" #include "utils.h" @@ -1290,592 +1289,11 @@ int run() return 0; } -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" - ); -} - -void doLicense() -{ - fprintf(stderr, - "\n" - "Looking Glass - KVM FrameRelay (KVMFR) Client\n" - "Copyright(C) 2017-2019 Geoffrey McRae \n" - "https://looking-glass.hostfission.com\n" - "\n" - "This program is free software; you can redistribute it and / or modify it under\n" - "the terms of the GNU General Public License as published by the Free Software\n" - "Foundation; either version 2 of the License, or (at your option) any later\n" - "version.\n" - "\n" - "This program is distributed in the hope that it will be useful, but WITHOUT ANY\n" - "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n" - "PARTICULAR PURPOSE.See the GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License along with\n" - "this program; if not, write to the Free Software Foundation, Inc., 59 Temple\n" - "Place, Suite 330, Boston, MA 02111 - 1307 USA\n" - "\n" - ); -} - -static bool load_config(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; -} - int main(int argc, char * argv[]) { - params.shmFile = strdup(params.shmFile ); - params.spiceHost = strdup(params.spiceHost ); - params.windowTitle = strdup(params.windowTitle); - - { - // load any global then local config options first - struct stat st; - if (stat("/etc/looking-glass.conf", &st) >= 0) - { - DEBUG_INFO("Loading config from: /etc/looking-glass.conf"); - if (!load_config("/etc/looking-glass.conf")) - return -1; - } - - 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) - { - DEBUG_INFO("Loading config from: %s", buffer); - if (!load_config(buffer)) - return -1; - } - } - - for(;;) - { - 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 -1; - - case -1: - break; - - case 'C': - params.configFile = optarg; - if (!load_config(optarg)) - return -1; - 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 -1; - } - 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 -1; - } - - 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 -1; - } - - 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 -1; - } - - const char * option = strtok(NULL , "="); - if (!option) - { - fprintf(stderr, "Renderer option name not specified\n"); - doHelp(argv[0]); - return -1; - } - - 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 -1; - } - - 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 -1; - } - - 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 0; - } - break; - } - - if (optind != argc) - { - fprintf(stderr, "A non option was supplied\n"); - doHelp(argv[0]); + if (!config_load(argc, argv)) return -1; - } + if (params.grabKeyboard) {