[client] added configuration file loading support

the client now will look for a configuration file in the following
locations by default.

* /etc/looking-glass.conf
* ~/.looking-glass.conf

All configuration files are loaded and may override values specified by
any prior configuration files loaded.

Sample Config:

    global:
    {
      fullScreen=false;
      showFPS=true;
      x=0;
      y=0;
      w=800;
      h=600;
    }

    OpenGL:
    {
      mipmap="false";
    }
This commit is contained in:
Geoffrey McRae 2017-12-28 19:55:13 +11:00
parent 59fa025292
commit 40bfdcdf8c
3 changed files with 182 additions and 11 deletions

View File

@ -6,7 +6,7 @@ CFLAGS += -ffast-math
CFLAGS += -fdata-sections -ffunction-sections CFLAGS += -fdata-sections -ffunction-sections
LDFLAGS += -Wl,--gc-sections LDFLAGS += -Wl,--gc-sections
LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11 LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11 libconfig
CFLAGS += $(shell pkg-config --cflags $(LIBS)) CFLAGS += $(shell pkg-config --cflags $(LIBS))
LDFLAGS += $(shell pkg-config --libs $(LIBS)) LDFLAGS += $(shell pkg-config --libs $(LIBS))
BUILD ?= .build BUILD ?= .build

View File

@ -50,8 +50,8 @@ LG_RendererOpt;
typedef struct LG_RendererOptValue typedef struct LG_RendererOptValue
{ {
const LG_RendererOpt * opt; const LG_RendererOpt * opt;
const char * value; char * value;
} LG_RendererOptValue; } LG_RendererOptValue;
typedef LG_RendererOpt * LG_RendererOptions; typedef LG_RendererOpt * LG_RendererOptions;

View File

@ -24,6 +24,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <SDL2/SDL_ttf.h> #include <SDL2/SDL_ttf.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <pwd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
@ -33,6 +34,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <assert.h> #include <assert.h>
#include <libconfig.h>
#include <fontconfig/fontconfig.h> #include <fontconfig/fontconfig.h>
#include "debug.h" #include "debug.h"
@ -75,6 +77,7 @@ RendererOpts;
struct AppParams struct AppParams
{ {
const char * configFile;
bool autoResize; bool autoResize;
bool allowResize; bool allowResize;
bool keepAspect; bool keepAspect;
@ -83,10 +86,10 @@ struct AppParams
bool center; bool center;
int x, y; int x, y;
unsigned int w, h; unsigned int w, h;
const char * shmFile; char * shmFile;
bool showFPS; bool showFPS;
bool useSpice; bool useSpice;
const char * spiceHost; char * spiceHost;
unsigned int spicePort; unsigned int spicePort;
bool scaleMouseInput; bool scaleMouseInput;
bool hideMouse; bool hideMouse;
@ -100,6 +103,7 @@ struct AppParams
struct AppState state; struct AppState state;
struct AppParams params = struct AppParams params =
{ {
.configFile = "/etc/looking-glass.conf",
.autoResize = false, .autoResize = false,
.allowResize = true, .allowResize = true,
.keepAspect = true, .keepAspect = true,
@ -950,6 +954,7 @@ void doHelp(char * app)
"\n" "\n"
" -h Print out this help\n" " -h Print out this help\n"
"\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" " -f PATH Specify the path to the shared memory file [current: %s]\n"
"\n" "\n"
" -s Disable spice client\n" " -s Disable spice client\n"
@ -1012,11 +1017,156 @@ void doLicense()
); );
} }
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_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_int(global, "x", &params.x)) params.center = false;
if (config_setting_lookup_int(global, "y", &params.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;
}
}
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
{
const LG_Renderer * r = LG_Renderers[i];
RendererOpts * opts = &params.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[]) int main(int argc, char * argv[])
{ {
params.shmFile = strdup(params.shmFile );
params.spiceHost = strdup(params.spiceHost);
{
// 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(;;) for(;;)
{ {
switch(getopt(argc, argv, "hf:sc:p:jMvkg:o:anrdFx:y:w:b:Ql")) switch(getopt(argc, argv, "hC:f:sc:p:jMvkg:o:anrdFx:y:w:b:Ql"))
{ {
case '?': case '?':
case 'h': case 'h':
@ -1027,8 +1177,15 @@ int main(int argc, char * argv[])
case -1: case -1:
break; break;
case 'C':
params.configFile = optarg;
if (!load_config(optarg))
return -1;
continue;
case 'f': case 'f':
params.shmFile = optarg; free(params.shmFile);
params.shmFile = strdup(optarg);
continue; continue;
case 's': case 's':
@ -1036,7 +1193,8 @@ int main(int argc, char * argv[])
continue; continue;
case 'c': case 'c':
params.spiceHost = optarg; free(params.spiceHost);
params.spiceHost = strdup(optarg);
continue; continue;
case 'p': case 'p':
@ -1162,7 +1320,7 @@ int main(int argc, char * argv[])
if (opt->validator && !opt->validator(value)) if (opt->validator && !opt->validator(value))
{ {
fprintf(stderr, "Renderer \"%s\" reported Invalid value for option \"%s\"\n", renderer->get_name(), option); fprintf(stderr, "Renderer \"%s\" reported invalid value for option \"%s\"\n", renderer->get_name(), option);
doHelp(argv[0]); doHelp(argv[0]);
return -1; return -1;
} }
@ -1174,7 +1332,7 @@ int main(int argc, char * argv[])
} }
opts->argv[opts->argc].opt = opt; opts->argv[opts->argc].opt = opt;
opts->argv[opts->argc].value = value; opts->argv[opts->argc].value = strdup(value);
++opts->argc; ++opts->argc;
continue; continue;
} }
@ -1235,5 +1393,18 @@ int main(int argc, char * argv[])
return -1; return -1;
} }
return run(); const int ret = run();
free(params.shmFile);
free(params.spiceHost);
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
{
RendererOpts * opts = &params.rendererOpts[i];
for(unsigned int j = 0; j < opts->argc; ++j)
free(opts->argv[j].value);
free(opts->argv);
}
return ret;
} }