mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-23 22:13:40 +00:00
1a407a67b1
This makes dealing with window manager shortcuts that overlap with guest keys more pleasant, while retaining the previous functionality for users who prefer it. For instance, previously, using Alt+Tab (or $mod as Alt in i3/sway movement commands) would result in the guest retaining Alt as pressed. When the guest regained focus, it would continue thinking Alt is pressed, leading to accidentally triggering obscure shortcuts. One had to remember to press Alt again to "unstick" things, which was suboptimal.
737 lines
22 KiB
C
737 lines
22 KiB
C
/*
|
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
|
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
|
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 "kb.h"
|
|
|
|
#include "common/option.h"
|
|
#include "common/debug.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 optScancodeValidate(struct Option * opt, const char ** error);
|
|
static char * optScancodeToString(struct Option * opt);
|
|
static bool optRotateValidate (struct Option * opt, const char ** error);
|
|
|
|
static void doLicense();
|
|
|
|
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 possible (VM-VM only for now)",
|
|
.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 = "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 = true,
|
|
},
|
|
{
|
|
.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 = "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 = "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 = "rotate",
|
|
.description = "Rotate the displayed image (0, 90, 180, 270)",
|
|
.type = OPTION_TYPE_INT,
|
|
.validator = optRotateValidate,
|
|
.value.x_int = 0,
|
|
},
|
|
|
|
// 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 = "grabKeyboardOnFocus",
|
|
.description = "Grab the keyboard when focused",
|
|
.type = OPTION_TYPE_BOOL,
|
|
.value.x_bool = true,
|
|
},
|
|
{
|
|
.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 key, see <linux/input-event-codes.h> for valid values",
|
|
.shortopt = 'm',
|
|
.type = OPTION_TYPE_INT,
|
|
.value.x_int = KEY_SCROLLLOCK,
|
|
.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
|
|
},
|
|
|
|
// 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
|
|
},
|
|
{
|
|
.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
|
|
},
|
|
{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)
|
|
{
|
|
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());
|
|
char * localFile;
|
|
alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir);
|
|
if (stat(localFile, &st) >= 0)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// 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.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.showFPS = option_get_bool ("win", "showFPS" );
|
|
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" );
|
|
|
|
if (g_params.noScreensaver && g_params.autoScreensaver)
|
|
{
|
|
fprintf(stderr, "win:noScreensaver (-S) and win:autoScreensaver "
|
|
"can't be used simultaneously\n");
|
|
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.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" );
|
|
|
|
g_params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
|
|
|
|
if (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");
|
|
|
|
if (g_params.useSpiceClipboard)
|
|
{
|
|
g_params.clipboardToVM = option_get_bool("spice", "clipboardToVM" );
|
|
g_params.clipboardToLocal = option_get_bool("spice", "clipboardToLocal");
|
|
|
|
if (!g_params.clipboardToVM && !g_params.clipboardToLocal)
|
|
g_params.useSpiceClipboard = 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");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void config_free(void)
|
|
{
|
|
option_free();
|
|
}
|
|
|
|
static void doLicense(void)
|
|
{
|
|
fprintf(stderr,
|
|
"\n"
|
|
"Looking Glass - KVM FrameRelay (KVMFR) Client\n"
|
|
"Copyright(C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>\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 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]->get_name()) == 0)
|
|
{
|
|
g_params.forceRenderer = true;
|
|
g_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 (!g_params.forceRenderer)
|
|
return strdup("auto");
|
|
|
|
if (g_params.forceRendererIndex >= LG_RENDERER_COUNT)
|
|
return NULL;
|
|
|
|
return strdup(LG_Renderers[g_params.forceRendererIndex]->get_name());
|
|
}
|
|
|
|
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);
|
|
stringlist_push(sl, "center");
|
|
stringlist_push(sl, "<left>x<top>, ie: 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);
|
|
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, "%dx%d", &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);
|
|
stringlist_push(sl, "<left>x<top>, ie: 100x100");
|
|
return sl;
|
|
}
|
|
|
|
static char * optSizeToString(struct Option * opt)
|
|
{
|
|
int len = snprintf(NULL, 0, "%dx%d", g_params.w, g_params.h);
|
|
char * str = malloc(len + 1);
|
|
sprintf(str, "%dx%d", g_params.w, g_params.h);
|
|
|
|
return str;
|
|
}
|
|
|
|
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 char * optScancodeToString(struct Option * opt)
|
|
{
|
|
char * str;
|
|
alloc_sprintf(&str, "%d = %s", opt->value.x_int,
|
|
xfree86_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;
|
|
}
|