mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-28 08:17:18 +00:00
1025 lines
30 KiB
C
1025 lines
30 KiB
C
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2024 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* 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 "kb.h"
|
|
|
|
#include "common/option.h"
|
|
#include "common/debug.h"
|
|
#include "common/paths.h"
|
|
#include "common/stringutils.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <pwd.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
// 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 bool optScancodeParse (struct Option * opt, const char * str);
|
|
static StringList optScancodeValues (struct Option * opt);
|
|
static bool optScancodeValidate (struct Option * opt, const char ** error);
|
|
static char * optScancodeToString (struct Option * opt);
|
|
static bool optRotateValidate (struct Option * opt, const char ** error);
|
|
static bool optMicDefaultParse (struct Option * opt, const char * str);
|
|
static StringList optMicDefaultValues (struct Option * opt);
|
|
static char * optMicDefaultToString(struct Option * opt);
|
|
|
|
static void doLicense(void);
|
|
|
|
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 = "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 license for this application and then terminate",
|
|
.shortopt = 'l',
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "app",
|
|
.name = "cursorPollInterval",
|
|
.description = "How often to check for a cursor update in microseconds",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 1000
|
|
},
|
|
{
|
|
.module = "app",
|
|
.name = "framePollInterval",
|
|
.description = "How often to check for a frame update in microseconds",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 1000
|
|
},
|
|
{
|
|
.module = "app",
|
|
.name = "allowDMA",
|
|
.description = "Allow direct DMA transfers if supported (see `README.md` in the `module` dir)",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
|
|
// 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 = "Allow 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 = "forceAspect",
|
|
.description = "Force the window to maintain the aspect ratio",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "dontUpscale",
|
|
.description = "Never try to upscale the window",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "intUpscale",
|
|
.description = "Allow only integer upscaling",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "shrinkOnUpscale",
|
|
.description = "Limit the window dimensions when dontUpscale is enabled",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.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 = "maximize",
|
|
.description = "Launch window maximized",
|
|
.shortopt = 'T',
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "minimizeOnFocusLoss",
|
|
.description = "Minimize window on focus loss",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "fpsMin",
|
|
.description = "Frame rate minimum (0 = disable - not recommended, -1 = auto detect)",
|
|
.shortopt = 'K',
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = -1,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "ignoreQuit",
|
|
.description = "Ignore requests to quit (i.e. 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 = "autoScreensaver",
|
|
.description = "Prevent the screensaver from starting when guest requests it",
|
|
.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,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "quickSplash",
|
|
.description = "Skip fading out the splash screen when a connection is established",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "overlayDimsDesktop",
|
|
.description = "Dim the desktop when in interactive overlay mode",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "rotate",
|
|
.description = "Rotate the displayed image (0, 90, 180, 270)",
|
|
.type = OPTION_TYPE_INT,
|
|
.validator = optRotateValidate,
|
|
.value.x_int = 0,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "uiFont",
|
|
.description = "The font to use when rendering on-screen UI",
|
|
.type = OPTION_TYPE_STRING,
|
|
.value.x_string = "DejaVu Sans Mono",
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "uiSize",
|
|
.description = "The font size to use when rendering on-screen UI",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 14
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "jitRender",
|
|
.description = "Enable just-in-time rendering",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "win",
|
|
.name = "requestActivation",
|
|
.description = "Request activation when attention is needed",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
|
|
// input options
|
|
{
|
|
.module = "input",
|
|
.name = "captureOnFocus",
|
|
.description = "Enable capture mode when the window becomes focused",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "grabKeyboard",
|
|
.description = "Grab the keyboard in capture mode",
|
|
.shortopt = 'G',
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "grabKeyboardOnFocus",
|
|
.description = "Grab the keyboard when focused",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "releaseKeysOnFocusLoss",
|
|
.description = "On focus loss, send key up events to guest for all held keys",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "escapeKey",
|
|
.description = "Specify the escape/menu key to use (use \"help\" to see valid values)",
|
|
.shortopt = 'm',
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = KEY_SCROLLLOCK,
|
|
.parser = optScancodeParse,
|
|
.getValues = optScancodeValues,
|
|
.validator = optScancodeValidate,
|
|
.toString = optScancodeToString,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "ignoreWindowsKeys",
|
|
.description = "Do not pass events for the windows keys to the guest",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "hideCursor",
|
|
.description = "Hide the local mouse cursor",
|
|
.shortopt = 'M',
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "mouseSens",
|
|
.description = "Initial mouse sensitivity when in capture mode (-9 to 9)",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 0,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "mouseSmoothing",
|
|
.description = "Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing)",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "rawMouse",
|
|
.description = "Use RAW mouse input when in capture mode (good for gaming)",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "mouseRedraw",
|
|
.description = "Mouse movements trigger redraws (ignores FPS minimum)",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "autoCapture",
|
|
.description = "Try to keep the mouse captured when needed",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "captureOnly",
|
|
.description = "Only enable input via SPICE if in capture mode",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
{
|
|
.module = "input",
|
|
.name = "helpMenuDelay",
|
|
.description = "Show help menu after holding down the escape key for this many milliseconds",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 200
|
|
},
|
|
|
|
// 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 synchronize the clipboard contents with the guest",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "clipboardToVM",
|
|
.description = "Allow the clipboard to be synchronized TO the VM",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "clipboardToLocal",
|
|
.description = "Allow the clipboard to be synchronized FROM the VM",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "audio",
|
|
.description = "Enable SPICE audio support",
|
|
.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
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "captureOnStart",
|
|
.description = "Capture mouse and keyboard on start",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "alwaysShowCursor",
|
|
.description = "Always show host cursor",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "showCursorDot",
|
|
.description = "Use a \"dot\" cursor when the window does not have focus",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{
|
|
.module = "spice",
|
|
.name = "largeCursorDot",
|
|
.description = "Use a larger version of the \"dot\" cursor",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = false
|
|
},
|
|
|
|
// audio options
|
|
{
|
|
.module = "audio",
|
|
.name = "periodSize",
|
|
.description = "Requested audio device period size in samples",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 2048
|
|
},
|
|
{
|
|
.module = "audio",
|
|
.name = "bufferLatency",
|
|
.description = "Additional buffer latency in milliseconds",
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = 13
|
|
},
|
|
{
|
|
.module = "audio",
|
|
.name = "micDefault",
|
|
.description = "Default action when an application opens the microphone (prompt, allow, deny)",
|
|
.type = OPTION_TYPE_CUSTOM,
|
|
.parser = optMicDefaultParse,
|
|
.getValues = optMicDefaultValues,
|
|
.toString = optMicDefaultToString
|
|
},
|
|
{
|
|
.module = "audio",
|
|
.name = "micShowIndicator",
|
|
.description = "Display microphone usage indicator",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{
|
|
.module = "audio",
|
|
.name = "syncVolume",
|
|
.description = "Synchronize the volume level with the guest",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true
|
|
},
|
|
{0}
|
|
};
|
|
|
|
void config_init(void)
|
|
{
|
|
g_params.center = true;
|
|
g_params.w = 1024;
|
|
g_params.h = 768;
|
|
|
|
option_register(options);
|
|
}
|
|
|
|
bool config_load(int argc, char * argv[])
|
|
{
|
|
// load any global options first
|
|
struct stat st;
|
|
if (stat("/etc/looking-glass-client.ini", &st) >= 0 && S_ISREG(st.st_mode))
|
|
{
|
|
DEBUG_INFO("Loading config from: /etc/looking-glass-client.ini");
|
|
if (!option_load("/etc/looking-glass-client.ini"))
|
|
return false;
|
|
}
|
|
|
|
// load config from user's home directory
|
|
struct passwd * pw = getpwuid(getuid());
|
|
if (!pw)
|
|
DEBUG_WARN("getpwuid failed, unable to load user configuration");
|
|
else
|
|
{
|
|
char * localFile;
|
|
alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir);
|
|
if (!localFile)
|
|
{
|
|
DEBUG_ERROR("out of memory");
|
|
return false;
|
|
}
|
|
|
|
if (stat(localFile, &st) >= 0 && S_ISREG(st.st_mode))
|
|
{
|
|
DEBUG_INFO("Loading config from: %s", localFile);
|
|
if (!option_load(localFile))
|
|
{
|
|
free(localFile);
|
|
return false;
|
|
}
|
|
}
|
|
free(localFile);
|
|
}
|
|
|
|
// load config from XDG_CONFIG_HOME
|
|
char * xdgFile;
|
|
alloc_sprintf(&xdgFile, "%s/client.ini", lgConfigDir());
|
|
if (!xdgFile)
|
|
{
|
|
DEBUG_ERROR("out of memory");
|
|
return false;
|
|
}
|
|
|
|
if (xdgFile && stat(xdgFile, &st) >= 0 && S_ISREG(st.st_mode))
|
|
{
|
|
DEBUG_INFO("Loading config from: %s", xdgFile);
|
|
if (!option_load(xdgFile))
|
|
{
|
|
free(xdgFile);
|
|
return false;
|
|
}
|
|
}
|
|
free(xdgFile);
|
|
|
|
// 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)
|
|
{
|
|
if (stat(configFile, &st) < 0 || !S_ISREG(st.st_mode))
|
|
{
|
|
DEBUG_ERROR("app:configFile set to invalid file: %s", configFile);
|
|
return false;
|
|
}
|
|
|
|
DEBUG_INFO("Loading config from: %s", configFile);
|
|
if (!option_load(configFile))
|
|
return false;
|
|
}
|
|
|
|
// validate the values are sane
|
|
if (!option_validate())
|
|
return false;
|
|
|
|
if (option_get_bool("app", "license"))
|
|
{
|
|
doLicense();
|
|
return false;
|
|
}
|
|
|
|
// setup the application params for the basic types
|
|
g_params.cursorPollInterval = option_get_int ("app" , "cursorPollInterval");
|
|
g_params.framePollInterval = option_get_int ("app" , "framePollInterval" );
|
|
g_params.allowDMA = option_get_bool ("app" , "allowDMA" );
|
|
|
|
g_params.windowTitle = option_get_string("win", "title" );
|
|
g_params.autoResize = option_get_bool ("win", "autoResize" );
|
|
g_params.allowResize = option_get_bool ("win", "allowResize" );
|
|
g_params.keepAspect = option_get_bool ("win", "keepAspect" );
|
|
g_params.forceAspect = option_get_bool ("win", "forceAspect" );
|
|
g_params.dontUpscale = option_get_bool ("win", "dontUpscale" );
|
|
g_params.intUpscale = option_get_bool ("win", "intUpscale" );
|
|
g_params.shrinkOnUpscale = option_get_bool ("win", "shrinkOnUpscale" );
|
|
g_params.borderless = option_get_bool ("win", "borderless" );
|
|
g_params.fullscreen = option_get_bool ("win", "fullScreen" );
|
|
g_params.maximize = option_get_bool ("win", "maximize" );
|
|
g_params.fpsMin = option_get_int ("win", "fpsMin" );
|
|
g_params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
|
|
g_params.noScreensaver = option_get_bool ("win", "noScreensaver" );
|
|
g_params.autoScreensaver = option_get_bool ("win", "autoScreensaver" );
|
|
g_params.showAlerts = option_get_bool ("win", "alerts" );
|
|
g_params.quickSplash = option_get_bool ("win", "quickSplash" );
|
|
g_params.overlayDim = option_get_bool ("win", "overlayDimsDesktop");
|
|
g_params.uiFont = option_get_string("win", "uiFont" );
|
|
g_params.uiSize = option_get_int ("win", "uiSize" );
|
|
g_params.jitRender = option_get_bool ("win", "jitRender" );
|
|
g_params.requestActivation = option_get_bool ("win", "requestActivation" );
|
|
|
|
if (g_params.noScreensaver && g_params.autoScreensaver)
|
|
{
|
|
DEBUG_WARN("win:noScreensaver (-S) and win:autoScreensaver "
|
|
"can't be used simultaneously");
|
|
return false;
|
|
}
|
|
|
|
switch(option_get_int("win", "rotate"))
|
|
{
|
|
case 0 : g_params.winRotate = LG_ROTATE_0 ; break;
|
|
case 90 : g_params.winRotate = LG_ROTATE_90 ; break;
|
|
case 180: g_params.winRotate = LG_ROTATE_180; break;
|
|
case 270: g_params.winRotate = LG_ROTATE_270; break;
|
|
}
|
|
|
|
g_params.captureOnFocus = option_get_bool("input", "captureOnFocus" );
|
|
g_params.grabKeyboard = option_get_bool("input", "grabKeyboard" );
|
|
g_params.grabKeyboardOnFocus = option_get_bool("input", "grabKeyboardOnFocus" );
|
|
g_params.releaseKeysOnFocusLoss = option_get_bool("input", "releaseKeysOnFocusLoss");
|
|
g_params.escapeKey = option_get_int ("input", "escapeKey" );
|
|
g_params.ignoreWindowsKeys = option_get_bool("input", "ignoreWindowsKeys" );
|
|
g_params.hideMouse = option_get_bool("input", "hideCursor" );
|
|
g_params.mouseSens = option_get_int ("input", "mouseSens" );
|
|
g_params.mouseSmoothing = option_get_bool("input", "mouseSmoothing" );
|
|
g_params.rawMouse = option_get_bool("input", "rawMouse" );
|
|
g_params.mouseRedraw = option_get_bool("input", "mouseRedraw" );
|
|
g_params.autoCapture = option_get_bool("input", "autoCapture" );
|
|
g_params.captureInputOnly = option_get_bool("input", "captureOnly" );
|
|
|
|
if (g_params.jitRender && !g_params.mouseRedraw)
|
|
{
|
|
DEBUG_WARN("win:jitRender is enabled, forcing input:mouseRedraw");
|
|
g_params.mouseRedraw = true;
|
|
}
|
|
|
|
g_params.helpMenuDelayUs = option_get_int("input", "helpMenuDelay") * (uint64_t) 1000;
|
|
|
|
g_params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
|
|
|
|
if ((g_params.useSpice = option_get_bool("spice", "enable")))
|
|
{
|
|
g_params.spiceHost = option_get_string("spice", "host");
|
|
g_params.spicePort = option_get_int ("spice", "port");
|
|
|
|
g_params.useSpiceInput = option_get_bool("spice", "input" );
|
|
g_params.useSpiceClipboard = option_get_bool("spice", "clipboard");
|
|
g_params.useSpiceAudio = option_get_bool("spice", "audio" );
|
|
|
|
if (g_params.useSpiceClipboard)
|
|
{
|
|
g_params.clipboardToVM = option_get_bool("spice", "clipboardToVM" );
|
|
g_params.clipboardToLocal = option_get_bool("spice", "clipboardToLocal");
|
|
g_params.useSpiceClipboard = g_params.clipboardToVM || g_params.clipboardToLocal;
|
|
}
|
|
else
|
|
{
|
|
g_params.clipboardToVM = false;
|
|
g_params.clipboardToLocal = false;
|
|
}
|
|
|
|
g_params.scaleMouseInput = option_get_bool("spice", "scaleCursor");
|
|
g_params.captureOnStart = option_get_bool("spice", "captureOnStart");
|
|
g_params.alwaysShowCursor = option_get_bool("spice", "alwaysShowCursor");
|
|
g_params.showCursorDot = option_get_bool("spice", "showCursorDot");
|
|
g_params.largeCursorDot = option_get_bool("spice", "largeCursorDot");
|
|
}
|
|
|
|
g_params.audioPeriodSize = option_get_int("audio", "periodSize");
|
|
g_params.audioBufferLatency = option_get_int("audio", "bufferLatency");
|
|
g_params.micShowIndicator = option_get_bool("audio", "micShowIndicator");
|
|
g_params.audioSyncVolume = option_get_bool("audio", "syncVolume");
|
|
|
|
return true;
|
|
}
|
|
|
|
void config_free(void)
|
|
{
|
|
option_free();
|
|
}
|
|
|
|
static void doLicense(void)
|
|
{
|
|
fprintf(stderr,
|
|
// BEGIN LICENSE BLOCK
|
|
"\n"
|
|
"Looking Glass\n"
|
|
"Copyright © 2017-2024 The Looking Glass Authors\n"
|
|
"https://looking-glass.io\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"
|
|
// END LICENSE BLOCK
|
|
);
|
|
}
|
|
|
|
static bool optRendererParse(struct Option * opt, const char * str)
|
|
{
|
|
if (!str)
|
|
return false;
|
|
|
|
if (strcasecmp(str, "auto") == 0)
|
|
{
|
|
g_params.forceRenderer = false;
|
|
return true;
|
|
}
|
|
|
|
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
|
if (strcasecmp(str, LG_Renderers[i]->getName()) == 0)
|
|
{
|
|
g_params.forceRenderer = true;
|
|
g_params.forceRendererIndex = i;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static StringList optRendererValues(struct Option * opt)
|
|
{
|
|
StringList sl = stringlist_new(false);
|
|
if (!sl)
|
|
return NULL;
|
|
|
|
// 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]->getName());
|
|
|
|
return sl;
|
|
}
|
|
|
|
static char * optRendererToString(struct Option * opt)
|
|
{
|
|
if (!g_params.forceRenderer)
|
|
return strdup("auto");
|
|
|
|
if (g_params.forceRendererIndex >= LG_RENDERER_COUNT)
|
|
return NULL;
|
|
|
|
return strdup(LG_Renderers[g_params.forceRendererIndex]->getName());
|
|
}
|
|
|
|
static bool optPosParse(struct Option * opt, const char * str)
|
|
{
|
|
if (!str)
|
|
return false;
|
|
|
|
if (strcmp(str, "center") == 0)
|
|
{
|
|
g_params.center = true;
|
|
return true;
|
|
}
|
|
|
|
if (sscanf(str, "%dx%d", &g_params.x, &g_params.y) == 2)
|
|
{
|
|
g_params.center = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static StringList optPosValues(struct Option * opt)
|
|
{
|
|
StringList sl = stringlist_new(false);
|
|
if (!sl)
|
|
return NULL;
|
|
|
|
stringlist_push(sl, "center");
|
|
stringlist_push(sl, "<left>x<top>, e.g. 100x100");
|
|
return sl;
|
|
}
|
|
|
|
static char * optPosToString(struct Option * opt)
|
|
{
|
|
if (g_params.center)
|
|
return strdup("center");
|
|
|
|
int len = snprintf(NULL, 0, "%dx%d", g_params.x, g_params.y);
|
|
char * str = malloc(len + 1);
|
|
if (!str)
|
|
{
|
|
DEBUG_ERROR("out of memory");
|
|
return NULL;
|
|
}
|
|
sprintf(str, "%dx%d", g_params.x, g_params.y);
|
|
|
|
return str;
|
|
}
|
|
|
|
static bool optSizeParse(struct Option * opt, const char * str)
|
|
{
|
|
if (!str)
|
|
return false;
|
|
|
|
if (sscanf(str, "%ux%u", &g_params.w, &g_params.h) == 2)
|
|
{
|
|
if (g_params.w < 1 || g_params.h < 1)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static StringList optSizeValues(struct Option * opt)
|
|
{
|
|
StringList sl = stringlist_new(false);
|
|
if (!sl)
|
|
return NULL;
|
|
|
|
stringlist_push(sl, "<left>x<top>, e.g. 100x100");
|
|
return sl;
|
|
}
|
|
|
|
static char * optSizeToString(struct Option * opt)
|
|
{
|
|
int len = snprintf(NULL, 0, "%ux%u", g_params.w, g_params.h);
|
|
char * str = malloc(len + 1);
|
|
if (!str)
|
|
{
|
|
DEBUG_ERROR("out of memory");
|
|
return NULL;
|
|
}
|
|
sprintf(str, "%ux%u", g_params.w, g_params.h);
|
|
|
|
return str;
|
|
}
|
|
|
|
static StringList optScancodeValues(struct Option * opt)
|
|
{
|
|
StringList sl = stringlist_new(false);
|
|
if (!sl)
|
|
return NULL;
|
|
|
|
// this typecast is safe as the stringlist doesn't own the values
|
|
for(unsigned int i = 0; i < KEY_MAX; ++i)
|
|
if (linux_to_str[i])
|
|
stringlist_push(sl, (char *)linux_to_str[i]);
|
|
|
|
return sl;
|
|
}
|
|
|
|
static bool optScancodeValidate(struct Option * opt, const char ** error)
|
|
{
|
|
if (opt->value.x_int >= 0 && opt->value.x_int < KEY_MAX)
|
|
return true;
|
|
|
|
*error = "Out of range";
|
|
return false;
|
|
}
|
|
|
|
static bool optScancodeParse(struct Option * opt, const char * str)
|
|
{
|
|
for(unsigned int i = 0; i < KEY_MAX; ++i)
|
|
{
|
|
if (!linux_to_str[i])
|
|
continue;
|
|
|
|
if (strcasecmp(linux_to_str[i], str) == 0)
|
|
{
|
|
opt->value.x_int = i;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// fallback for pre-keycode name support
|
|
char * end;
|
|
opt->value.x_int = strtol(str, &end, 10);
|
|
if (*end)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static char * optScancodeToString(struct Option * opt)
|
|
{
|
|
char * str;
|
|
alloc_sprintf(&str, "%d = %s", opt->value.x_int,
|
|
linux_to_str[opt->value.x_int]);
|
|
return str;
|
|
}
|
|
|
|
static bool optRotateValidate(struct Option * opt, const char ** error)
|
|
{
|
|
switch(opt->value.x_int)
|
|
{
|
|
case 0:
|
|
case 90:
|
|
case 180:
|
|
case 270:
|
|
return true;
|
|
}
|
|
|
|
*error = "Rotation angle must be one of 0, 90, 180 or 270";
|
|
return false;
|
|
}
|
|
|
|
static bool optMicDefaultParse(struct Option * opt, const char * str)
|
|
{
|
|
if (!str)
|
|
return false;
|
|
|
|
if (strcasecmp(str, "prompt") == 0)
|
|
g_params.micDefaultState = MIC_DEFAULT_PROMPT;
|
|
else if (strcasecmp(str, "allow") == 0)
|
|
g_params.micDefaultState = MIC_DEFAULT_ALLOW;
|
|
else if (strcasecmp(str, "deny") == 0)
|
|
g_params.micDefaultState = MIC_DEFAULT_DENY;
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static StringList optMicDefaultValues(struct Option * opt)
|
|
{
|
|
StringList sl = stringlist_new(false);
|
|
if (!sl)
|
|
return NULL;
|
|
|
|
stringlist_push(sl, (char *)"prompt");
|
|
stringlist_push(sl, (char *)"allow");
|
|
stringlist_push(sl, (char *)"deny");
|
|
return sl;
|
|
}
|
|
|
|
static char * optMicDefaultToString(struct Option * opt)
|
|
{
|
|
switch (g_params.micDefaultState)
|
|
{
|
|
case MIC_DEFAULT_PROMPT:
|
|
return strdup("prompt");
|
|
case MIC_DEFAULT_ALLOW:
|
|
return strdup("allow");
|
|
case MIC_DEFAULT_DENY:
|
|
return strdup("deny");
|
|
}
|
|
|
|
return NULL;
|
|
}
|