2019-07-16 11:52:22 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdbool.h>
|
2019-07-17 10:12:14 +02:00
|
|
|
#include <ctype.h>
|
2019-07-16 11:52:22 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <pwd.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
2020-03-12 10:20:05 +01:00
|
|
|
#include <linux/input-event-codes.h>
|
2020-03-09 20:03:04 +01:00
|
|
|
#include <xkbcommon/xkbcommon.h>
|
2020-07-07 10:44:55 +02:00
|
|
|
#include <fontconfig/fontconfig.h>
|
2020-03-09 20:03:04 +01:00
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
#define LOG_MODULE "config"
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
#include "log.h"
|
2020-03-09 20:03:04 +01:00
|
|
|
#include "input.h"
|
2020-08-07 20:42:34 +01:00
|
|
|
#include "macros.h"
|
2020-07-30 18:53:51 +02:00
|
|
|
#include "tokenize.h"
|
2020-05-01 11:46:24 +02:00
|
|
|
#include "util.h"
|
2020-03-09 20:03:04 +01:00
|
|
|
#include "wayland.h"
|
2020-08-04 23:28:16 +01:00
|
|
|
#include "xmalloc.h"
|
2020-03-09 20:03:04 +01:00
|
|
|
|
2019-07-21 11:06:28 +02:00
|
|
|
static const uint32_t default_foreground = 0xdcdccc;
|
|
|
|
|
static const uint32_t default_background = 0x111111;
|
|
|
|
|
|
|
|
|
|
static const uint32_t default_regular[] = {
|
2019-09-21 12:01:29 +02:00
|
|
|
0x222222,
|
2019-07-21 11:06:28 +02:00
|
|
|
0xcc9393,
|
|
|
|
|
0x7f9f7f,
|
|
|
|
|
0xd0bf8f,
|
|
|
|
|
0x6ca0a3,
|
|
|
|
|
0xdc8cc3,
|
|
|
|
|
0x93e0e3,
|
|
|
|
|
0xdcdccc,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const uint32_t default_bright[] = {
|
2019-08-23 19:42:14 +02:00
|
|
|
0x666666,
|
2019-07-21 11:06:28 +02:00
|
|
|
0xdca3a3,
|
|
|
|
|
0xbfebbf,
|
|
|
|
|
0xf0dfaf,
|
|
|
|
|
0x8cd0d3,
|
2019-09-21 12:09:50 +02:00
|
|
|
0xfcace3,
|
|
|
|
|
0xb3ffff,
|
2019-07-21 11:06:28 +02:00
|
|
|
0xffffff,
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-29 17:27:01 +02:00
|
|
|
static const char *const binding_action_map[] = {
|
2020-03-12 10:46:27 +01:00
|
|
|
[BIND_ACTION_NONE] = NULL,
|
2020-03-09 20:03:04 +01:00
|
|
|
[BIND_ACTION_SCROLLBACK_UP] = "scrollback-up",
|
|
|
|
|
[BIND_ACTION_SCROLLBACK_DOWN] = "scrollback-down",
|
|
|
|
|
[BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy",
|
|
|
|
|
[BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste",
|
|
|
|
|
[BIND_ACTION_PRIMARY_PASTE] = "primary-paste",
|
|
|
|
|
[BIND_ACTION_SEARCH_START] = "search-start",
|
|
|
|
|
[BIND_ACTION_FONT_SIZE_UP] = "font-increase",
|
|
|
|
|
[BIND_ACTION_FONT_SIZE_DOWN] = "font-decrease",
|
|
|
|
|
[BIND_ACTION_FONT_SIZE_RESET] = "font-reset",
|
|
|
|
|
[BIND_ACTION_SPAWN_TERMINAL] = "spawn-terminal",
|
2020-03-12 09:34:09 +01:00
|
|
|
[BIND_ACTION_MINIMIZE] = "minimize",
|
|
|
|
|
[BIND_ACTION_MAXIMIZE] = "maximize",
|
|
|
|
|
[BIND_ACTION_FULLSCREEN] = "fullscreen",
|
2020-07-15 09:46:13 +02:00
|
|
|
[BIND_ACTION_PIPE_SCROLLBACK] = "pipe-scrollback",
|
|
|
|
|
[BIND_ACTION_PIPE_VIEW] = "pipe-visible",
|
2020-07-31 17:02:53 +02:00
|
|
|
[BIND_ACTION_PIPE_SELECTED] = "pipe-selected",
|
2020-03-09 20:03:04 +01:00
|
|
|
};
|
|
|
|
|
|
2020-03-12 09:34:09 +01:00
|
|
|
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
|
|
|
|
|
"binding action map size mismatch");
|
|
|
|
|
|
2020-07-29 17:27:01 +02:00
|
|
|
static const char *const search_binding_action_map[] = {
|
|
|
|
|
[BIND_ACTION_SEARCH_NONE] = NULL,
|
|
|
|
|
[BIND_ACTION_SEARCH_CANCEL] = "cancel",
|
|
|
|
|
[BIND_ACTION_SEARCH_COMMIT] = "commit",
|
|
|
|
|
[BIND_ACTION_SEARCH_FIND_PREV] = "find-prev",
|
|
|
|
|
[BIND_ACTION_SEARCH_FIND_NEXT] = "find-next",
|
|
|
|
|
[BIND_ACTION_SEARCH_EDIT_LEFT] = "cursor-left",
|
|
|
|
|
[BIND_ACTION_SEARCH_EDIT_LEFT_WORD] = "cursor-left-word",
|
|
|
|
|
[BIND_ACTION_SEARCH_EDIT_RIGHT] = "cursor-right",
|
|
|
|
|
[BIND_ACTION_SEARCH_EDIT_RIGHT_WORD] = "cursor-right-word",
|
|
|
|
|
[BIND_ACTION_SEARCH_EDIT_HOME] = "cursor-home",
|
|
|
|
|
[BIND_ACTION_SEARCH_EDIT_END] = "cursor-end",
|
|
|
|
|
[BIND_ACTION_SEARCH_DELETE_PREV] = "delete-prev",
|
|
|
|
|
[BIND_ACTION_SEARCH_DELETE_PREV_WORD] = "delete-prev-word",
|
|
|
|
|
[BIND_ACTION_SEARCH_DELETE_NEXT] = "delete-next",
|
|
|
|
|
[BIND_ACTION_SEARCH_DELETE_NEXT_WORD] = "delete-next-word",
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_WORD] = "extend-to-word-boundary",
|
|
|
|
|
[BIND_ACTION_SEARCH_EXTEND_WORD_WS] = "extend-to-next-whitespace",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
|
|
|
|
|
"search binding action map size mismatch");
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
#define LOG_AND_NOTIFY_ERR(fmt, ...) \
|
|
|
|
|
LOG_ERR(fmt, ## __VA_ARGS__); \
|
|
|
|
|
{ \
|
2020-08-04 23:28:16 +01:00
|
|
|
char *text = xasprintf(fmt, ## __VA_ARGS__); \
|
2020-07-31 17:07:14 +02:00
|
|
|
struct user_notification notif = { \
|
|
|
|
|
.kind = USER_NOTIFICATION_ERROR, \
|
|
|
|
|
.text = text, \
|
|
|
|
|
}; \
|
|
|
|
|
tll_push_back(conf->notifications, notif); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_WARN(fmt, ...) \
|
|
|
|
|
LOG_WARN(fmt, ## __VA_ARGS__); \
|
|
|
|
|
{ \
|
2020-08-04 23:28:16 +01:00
|
|
|
char *text = xasprintf(fmt, ## __VA_ARGS__); \
|
2020-07-31 17:07:14 +02:00
|
|
|
struct user_notification notif = { \
|
|
|
|
|
.kind = USER_NOTIFICATION_WARNING, \
|
|
|
|
|
.text = text, \
|
|
|
|
|
}; \
|
|
|
|
|
tll_push_back(conf->notifications, notif); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define LOG_AND_NOTIFY_ERRNO(fmt, ...) \
|
|
|
|
|
{ \
|
|
|
|
|
int _errno = errno; \
|
|
|
|
|
LOG_ERRNO(fmt, ## __VA_ARGS__); \
|
|
|
|
|
{ \
|
|
|
|
|
int len = snprintf(NULL, 0, fmt, ## __VA_ARGS__); \
|
|
|
|
|
int errno_len = snprintf(NULL, 0, ": %s", strerror(_errno)); \
|
2020-08-04 23:28:16 +01:00
|
|
|
char *text = xmalloc(len + errno_len + 1); \
|
2020-07-31 17:07:14 +02:00
|
|
|
snprintf(text, len + errno_len + 1, fmt, ## __VA_ARGS__); \
|
|
|
|
|
snprintf(&text[len], errno_len + 1, ": %s", strerror(_errno)); \
|
|
|
|
|
struct user_notification notif = { \
|
|
|
|
|
.kind = USER_NOTIFICATION_ERROR, \
|
|
|
|
|
.text = text, \
|
|
|
|
|
}; \
|
|
|
|
|
tll_push_back(conf->notifications, notif); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-17 09:29:56 +02:00
|
|
|
static char *
|
|
|
|
|
get_shell(void)
|
|
|
|
|
{
|
2020-03-02 18:45:38 +01:00
|
|
|
const char *shell = getenv("SHELL");
|
|
|
|
|
|
|
|
|
|
if (shell == NULL) {
|
|
|
|
|
struct passwd *passwd = getpwuid(getuid());
|
|
|
|
|
if (passwd == NULL) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_ERRNO("failed to lookup user: falling back to 'sh'");
|
|
|
|
|
shell = "sh";
|
|
|
|
|
} else
|
|
|
|
|
shell = passwd->pw_shell;
|
2019-07-17 09:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("user's shell: %s", shell);
|
2020-08-04 23:28:16 +01:00
|
|
|
return xstrdup(shell);
|
2019-07-17 09:29:56 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
static char *
|
|
|
|
|
get_config_path_user_config(void)
|
|
|
|
|
{
|
|
|
|
|
struct passwd *passwd = getpwuid(getuid());
|
|
|
|
|
if (passwd == NULL) {
|
|
|
|
|
LOG_ERRNO("failed to lookup user");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *home_dir = passwd->pw_dir;
|
|
|
|
|
LOG_DBG("user's home directory: %s", home_dir);
|
|
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
char *path = xasprintf("%s/.config/footrc", home_dir);
|
2019-07-16 11:52:22 +02:00
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
get_config_path_xdg(void)
|
|
|
|
|
{
|
|
|
|
|
const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
|
|
|
|
|
if (xdg_config_home == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
char *path = xasprintf("%s/footrc", xdg_config_home);
|
2019-07-16 11:52:22 +02:00
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
get_config_path(void)
|
|
|
|
|
{
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
|
|
char *config = get_config_path_xdg();
|
|
|
|
|
if (config != NULL && stat(config, &st) == 0 && S_ISREG(st.st_mode))
|
|
|
|
|
return config;
|
|
|
|
|
free(config);
|
|
|
|
|
|
|
|
|
|
/* 'Default' XDG_CONFIG_HOME */
|
|
|
|
|
config = get_config_path_user_config();
|
|
|
|
|
if (config != NULL && stat(config, &st) == 0 && S_ISREG(st.st_mode))
|
|
|
|
|
return config;
|
|
|
|
|
free(config);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-11 16:10:14 +01:00
|
|
|
static bool
|
|
|
|
|
str_to_bool(const char *s)
|
|
|
|
|
{
|
|
|
|
|
return strcasecmp(s, "on") == 0 ||
|
|
|
|
|
strcasecmp(s, "true") == 0 ||
|
|
|
|
|
strcasecmp(s, "yes") == 0 ||
|
|
|
|
|
strtoul(s, NULL, 0) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
static bool
|
2019-07-29 20:13:26 +02:00
|
|
|
str_to_ulong(const char *s, int base, unsigned long *res)
|
2019-07-23 18:54:58 +02:00
|
|
|
{
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
2019-07-29 20:13:26 +02:00
|
|
|
*res = strtoul(s, &end, base);
|
|
|
|
|
return errno == 0 && *end == '\0';
|
|
|
|
|
}
|
2019-07-23 18:54:58 +02:00
|
|
|
|
2019-08-15 18:15:43 +02:00
|
|
|
static bool
|
|
|
|
|
str_to_double(const char *s, double *res)
|
|
|
|
|
{
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
|
|
|
|
|
*res = strtod(s, &end);
|
|
|
|
|
return errno == 0 && *end == '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 20:13:26 +02:00
|
|
|
static bool
|
2020-07-31 17:07:14 +02:00
|
|
|
str_to_color(const char *s, uint32_t *color, bool allow_alpha, const char *path, int lineno,
|
|
|
|
|
const char *section, const char *key)
|
2019-07-29 20:13:26 +02:00
|
|
|
{
|
|
|
|
|
unsigned long value;
|
|
|
|
|
if (!str_to_ulong(s, 16, &value)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_ERRNO("%s:%d: [%s]: %s: invalid color: %s", path, lineno, section, key, s);
|
2019-07-23 18:54:58 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 18:42:49 +01:00
|
|
|
if (!allow_alpha && (value & 0xff000000) != 0) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_ERR("%s:%d: [%s]: %s: color value must not have an alpha component: %s",
|
|
|
|
|
path, lineno, section, key, s);
|
2020-03-02 18:42:49 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*color = value;
|
2019-07-23 18:54:58 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
static bool
|
2019-07-17 10:12:14 +02:00
|
|
|
parse_section_main(const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
2019-07-16 11:52:22 +02:00
|
|
|
{
|
2019-07-18 14:29:40 +02:00
|
|
|
if (strcmp(key, "term") == 0) {
|
|
|
|
|
free(conf->term);
|
2020-08-04 23:28:16 +01:00
|
|
|
conf->term = xstrdup(value);
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-17 09:40:58 +02:00
|
|
|
else if (strcmp(key, "shell") == 0) {
|
|
|
|
|
free(conf->shell);
|
2020-08-04 23:28:16 +01:00
|
|
|
conf->shell = xstrdup(value);
|
2019-07-17 09:40:58 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-20 18:35:10 +01:00
|
|
|
else if (strcmp(key, "login-shell") == 0) {
|
2020-03-11 16:10:14 +01:00
|
|
|
conf->login_shell = str_to_bool(value);
|
2020-02-20 18:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-01 19:59:47 +02:00
|
|
|
else if (strcmp(key, "title") == 0) {
|
|
|
|
|
free(conf->title);
|
2020-08-04 23:28:16 +01:00
|
|
|
conf->title = xstrdup(value);
|
2020-04-01 19:59:47 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-01 18:40:51 +02:00
|
|
|
else if (strcmp(key, "app-id") == 0) {
|
|
|
|
|
free(conf->app_id);
|
2020-08-04 23:28:16 +01:00
|
|
|
conf->app_id = xstrdup(value);
|
2020-04-01 18:40:51 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-23 17:26:41 +02:00
|
|
|
else if (strcmp(key, "geometry") == 0) {
|
|
|
|
|
unsigned width, height;
|
|
|
|
|
if (sscanf(value, "%ux%u", &width, &height) != 2 || width == 0 || height == 0) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
"%s: %d: [default]: geometry: expected WIDTHxHEIGHT, "
|
|
|
|
|
"where both are positive integers, got '%s'",
|
2019-08-23 17:26:41 +02:00
|
|
|
path, lineno, value);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->width = width;
|
|
|
|
|
conf->height = height;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 19:00:56 +01:00
|
|
|
else if (strcmp(key, "pad") == 0) {
|
|
|
|
|
unsigned x, y;
|
|
|
|
|
if (sscanf(value, "%ux%u", &x, &y) != 2) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
"%s:%d: [default]: pad: expected PAD_XxPAD_Y, "
|
|
|
|
|
"where both are positive integers, got '%s'",
|
2020-02-15 19:00:56 +01:00
|
|
|
path, lineno, value);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->pad_x = x;
|
|
|
|
|
conf->pad_y = y;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 12:04:00 +01:00
|
|
|
else if (strcmp(key, "initial-window-mode") == 0) {
|
2020-03-26 19:39:12 +01:00
|
|
|
if (strcmp(value, "windowed") == 0)
|
|
|
|
|
conf->startup_mode = STARTUP_WINDOWED;
|
|
|
|
|
else if (strcmp(value, "maximized") == 0)
|
|
|
|
|
conf->startup_mode = STARTUP_MAXIMIZED;
|
|
|
|
|
else if (strcmp(value, "fullscreen") == 0)
|
|
|
|
|
conf->startup_mode = STARTUP_FULLSCREEN;
|
|
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
"%s:%d: [default]: initial-window-mode: expected either "
|
|
|
|
|
"'windowed', 'maximized' or 'fullscreen'",
|
2020-03-26 19:39:12 +01:00
|
|
|
path, lineno);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-18 14:29:40 +02:00
|
|
|
else if (strcmp(key, "font") == 0) {
|
2020-08-04 23:28:16 +01:00
|
|
|
char *copy = xstrdup(value);
|
2019-12-05 19:33:54 +01:00
|
|
|
for (const char *font = strtok(copy, ","); font != NULL; font = strtok(NULL, ",")) {
|
|
|
|
|
/* Trim spaces, strictly speaking not necessary, but looks nice :) */
|
|
|
|
|
while (*font != '\0' && isspace(*font))
|
|
|
|
|
font++;
|
|
|
|
|
if (*font != '\0')
|
2020-07-07 10:44:55 +02:00
|
|
|
tll_push_back(conf->fonts, config_font_parse(font));
|
2019-12-05 19:33:54 +01:00
|
|
|
}
|
2019-07-30 18:04:28 +02:00
|
|
|
free(copy);
|
2019-07-18 14:29:40 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-29 20:13:26 +02:00
|
|
|
else if (strcmp(key, "workers") == 0) {
|
|
|
|
|
unsigned long count;
|
|
|
|
|
if (!str_to_ulong(value, 10, &count)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
"%s:%d: [default]: workers: expected an integer, got '%s'",
|
|
|
|
|
path, lineno, value);
|
2019-07-29 20:13:26 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
conf->render_worker_count = count;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-01 19:28:14 +02:00
|
|
|
else if (strcmp(key, "scrollback") == 0) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_WARN("deprecated: [default]: scrollback: use 'scrollback.lines' instead'");
|
2020-07-29 17:41:24 +02:00
|
|
|
|
2020-08-02 23:54:04 +01:00
|
|
|
const char *fmt = "%s:%d: \033[1mdefault.scrollback\033[21m, use \033[1mscrollback.lines\033[21m instead";
|
2020-08-04 23:28:16 +01:00
|
|
|
char *text = xasprintf(fmt, path, lineno);
|
2020-07-29 19:43:11 +02:00
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
struct user_notification deprecation = {
|
|
|
|
|
.kind = USER_NOTIFICATION_DEPRECATED,
|
2020-07-29 19:43:11 +02:00
|
|
|
.text = text,
|
|
|
|
|
};
|
2020-07-30 18:57:21 +02:00
|
|
|
tll_push_back(conf->notifications, deprecation);
|
2020-07-29 19:43:11 +02:00
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
unsigned long lines;
|
|
|
|
|
if (!str_to_ulong(value, 10, &lines)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR(
|
|
|
|
|
"%s:%d: [default]: scrollback: expected an integer, got '%s'",
|
|
|
|
|
path, lineno, value);
|
2020-07-29 17:41:24 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
conf->scrollback.lines = lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [default]: %s: invalid key", path, lineno, key);
|
2020-07-29 17:41:24 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
parse_section_scrollback(const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(key, "lines") == 0) {
|
2019-08-01 19:28:14 +02:00
|
|
|
unsigned long lines;
|
|
|
|
|
if (!str_to_ulong(value, 10, &lines)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [scrollback]: lines: expected an integer, got '%s'", path, lineno, value);
|
2019-08-01 19:28:14 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-25 14:31:45 +02:00
|
|
|
conf->scrollback.lines = lines;
|
2019-08-01 19:28:14 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
else if (strcmp(key, "indicator-position") == 0) {
|
2020-07-25 14:31:45 +02:00
|
|
|
if (strcmp(value, "none") == 0)
|
2020-07-27 20:02:51 +02:00
|
|
|
conf->scrollback.indicator.position = SCROLLBACK_INDICATOR_POSITION_NONE;
|
2020-07-25 14:40:46 +02:00
|
|
|
else if (strcmp(value, "fixed") == 0)
|
2020-07-27 20:02:51 +02:00
|
|
|
conf->scrollback.indicator.position = SCROLLBACK_INDICATOR_POSITION_FIXED;
|
2020-07-25 14:40:46 +02:00
|
|
|
else if (strcmp(value, "relative") == 0)
|
2020-07-27 20:02:51 +02:00
|
|
|
conf->scrollback.indicator.position = SCROLLBACK_INDICATOR_POSITION_RELATIVE;
|
2020-07-25 14:31:45 +02:00
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [scrollback]: indicator-position must be one of "
|
2020-07-26 09:44:16 +02:00
|
|
|
"'none', 'fixed' or 'moving'",
|
2020-07-25 14:31:45 +02:00
|
|
|
path, lineno);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-24 18:20:26 +02:00
|
|
|
|
2020-07-29 17:41:24 +02:00
|
|
|
else if (strcmp(key, "indicator-format") == 0) {
|
2020-07-28 19:57:26 +02:00
|
|
|
if (strcmp(value, "percentage") == 0) {
|
2020-07-26 11:39:02 +02:00
|
|
|
conf->scrollback.indicator.format
|
|
|
|
|
= SCROLLBACK_INDICATOR_FORMAT_PERCENTAGE;
|
|
|
|
|
} else if (strcmp(value, "line") == 0) {
|
|
|
|
|
conf->scrollback.indicator.format
|
|
|
|
|
= SCROLLBACK_INDICATOR_FORMAT_LINENO;
|
|
|
|
|
} else {
|
2020-07-28 19:56:53 +02:00
|
|
|
free(conf->scrollback.indicator.text);
|
|
|
|
|
conf->scrollback.indicator.text = NULL;
|
|
|
|
|
|
|
|
|
|
size_t len = mbstowcs(NULL, value, -1);
|
|
|
|
|
if (len < 0) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERRNO("%s:%d: [scrollback]: indicator-format: invalid value: %s", path, lineno, value);
|
2020-07-28 19:56:53 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
conf->scrollback.indicator.text = xcalloc(len + 1, sizeof(wchar_t));
|
2020-07-28 19:56:53 +02:00
|
|
|
mbstowcs(conf->scrollback.indicator.text, value, len);
|
2020-07-24 18:34:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-03 19:43:06 +02:00
|
|
|
else if (strcmp(key, "multiplier") == 0) {
|
|
|
|
|
double multiplier;
|
|
|
|
|
if (!str_to_double(value, &multiplier)) {
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [scrollback]: multiplier: "
|
|
|
|
|
"invalid value: %s", path, lineno, value);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->scrollback.multiplier = multiplier;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [scrollback]: %s: invalid key", path, lineno, key);
|
2019-07-16 11:52:22 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-21 11:31:16 +02:00
|
|
|
static bool
|
|
|
|
|
parse_section_colors(const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *color = NULL;
|
|
|
|
|
|
|
|
|
|
if (strcmp(key, "foreground") == 0) color = &conf->colors.fg;
|
|
|
|
|
else if (strcmp(key, "background") == 0) color = &conf->colors.bg;
|
|
|
|
|
else if (strcmp(key, "regular0") == 0) color = &conf->colors.regular[0];
|
|
|
|
|
else if (strcmp(key, "regular1") == 0) color = &conf->colors.regular[1];
|
|
|
|
|
else if (strcmp(key, "regular2") == 0) color = &conf->colors.regular[2];
|
|
|
|
|
else if (strcmp(key, "regular3") == 0) color = &conf->colors.regular[3];
|
|
|
|
|
else if (strcmp(key, "regular4") == 0) color = &conf->colors.regular[4];
|
|
|
|
|
else if (strcmp(key, "regular5") == 0) color = &conf->colors.regular[5];
|
|
|
|
|
else if (strcmp(key, "regular6") == 0) color = &conf->colors.regular[6];
|
|
|
|
|
else if (strcmp(key, "regular7") == 0) color = &conf->colors.regular[7];
|
|
|
|
|
else if (strcmp(key, "bright0") == 0) color = &conf->colors.bright[0];
|
|
|
|
|
else if (strcmp(key, "bright1") == 0) color = &conf->colors.bright[1];
|
|
|
|
|
else if (strcmp(key, "bright2") == 0) color = &conf->colors.bright[2];
|
|
|
|
|
else if (strcmp(key, "bright3") == 0) color = &conf->colors.bright[3];
|
|
|
|
|
else if (strcmp(key, "bright4") == 0) color = &conf->colors.bright[4];
|
|
|
|
|
else if (strcmp(key, "bright5") == 0) color = &conf->colors.bright[5];
|
|
|
|
|
else if (strcmp(key, "bright6") == 0) color = &conf->colors.bright[6];
|
|
|
|
|
else if (strcmp(key, "bright7") == 0) color = &conf->colors.bright[7];
|
2020-08-12 18:53:32 +02:00
|
|
|
else if (strcmp(key, "selection-foreground") == 0) color = &conf->colors.selection_fg;
|
|
|
|
|
else if (strcmp(key, "selection-background") == 0) color = &conf->colors.selection_bg;
|
2019-08-15 18:15:43 +02:00
|
|
|
else if (strcmp(key, "alpha") == 0) {
|
|
|
|
|
double alpha;
|
|
|
|
|
if (!str_to_double(value, &alpha) || alpha < 0. || alpha > 1.) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s: %d: [colors]: alpha: expected a value in the range 0.0-1.0",
|
2019-08-15 18:15:43 +02:00
|
|
|
path, lineno);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-16 22:06:06 +02:00
|
|
|
conf->colors.alpha = alpha * 65535.;
|
2019-08-15 18:15:43 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2019-07-21 11:31:16 +02:00
|
|
|
|
|
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [colors]: %s: invalid key", path, lineno, key);
|
2019-07-21 11:31:16 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
uint32_t color_value;
|
2020-07-31 17:07:14 +02:00
|
|
|
if (!str_to_color(value, &color_value, false, path, lineno, "colors", key))
|
2019-07-21 11:31:16 +02:00
|
|
|
return false;
|
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
*color = color_value;
|
2019-07-21 11:31:16 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-22 20:15:14 +02:00
|
|
|
static bool
|
|
|
|
|
parse_section_cursor(const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(key, "style") == 0) {
|
|
|
|
|
if (strcmp(value, "block") == 0)
|
|
|
|
|
conf->cursor.style = CURSOR_BLOCK;
|
|
|
|
|
else if (strcmp(value, "bar") == 0)
|
|
|
|
|
conf->cursor.style = CURSOR_BAR;
|
|
|
|
|
else if (strcmp(value, "underline") == 0)
|
|
|
|
|
conf->cursor.style = CURSOR_UNDERLINE;
|
|
|
|
|
|
|
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid 'style': %s", path, lineno, value);
|
2019-07-22 20:15:14 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-30 17:45:34 +02:00
|
|
|
else if (strcmp(key, "blink") == 0)
|
|
|
|
|
conf->cursor.blink = str_to_bool(value);
|
|
|
|
|
|
2019-07-23 18:54:58 +02:00
|
|
|
else if (strcmp(key, "color") == 0) {
|
2020-08-04 23:28:16 +01:00
|
|
|
char *value_copy = xstrdup(value);
|
2019-07-23 18:54:58 +02:00
|
|
|
const char *text = strtok(value_copy, " ");
|
|
|
|
|
const char *cursor = strtok(NULL, " ");
|
|
|
|
|
|
|
|
|
|
uint32_t text_color, cursor_color;
|
|
|
|
|
if (text == NULL || cursor == NULL ||
|
2020-07-31 17:07:14 +02:00
|
|
|
!str_to_color(text, &text_color, false, path, lineno, "cursor", "color") ||
|
|
|
|
|
!str_to_color(cursor, &cursor_color, false, path, lineno, "cursor", "color"))
|
2019-07-23 18:54:58 +02:00
|
|
|
{
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid cursor colors: %s", path, lineno, value);
|
2019-07-23 18:54:58 +02:00
|
|
|
free(value_copy);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-09 13:35:02 +02:00
|
|
|
conf->cursor.color.text = 1u << 31 | text_color;
|
|
|
|
|
conf->cursor.color.cursor = 1u << 31 | cursor_color;
|
2019-07-23 18:54:58 +02:00
|
|
|
free(value_copy);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-22 20:15:14 +02:00
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [cursor]: %s: invalid key", path, lineno, key);
|
2019-07-22 20:15:14 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-04 07:33:15 +02:00
|
|
|
static bool
|
|
|
|
|
parse_section_mouse(const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(key, "hide-when-typing") == 0)
|
|
|
|
|
conf->mouse.hide_when_typing = str_to_bool(value);
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: [mouse]: %s: invalid key", path, lineno, key);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 18:42:49 +01:00
|
|
|
static bool
|
|
|
|
|
parse_section_csd(const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(key, "preferred") == 0) {
|
|
|
|
|
if (strcmp(value, "server") == 0)
|
|
|
|
|
conf->csd.preferred = CONF_CSD_PREFER_SERVER;
|
|
|
|
|
else if (strcmp(value, "client") == 0)
|
|
|
|
|
conf->csd.preferred = CONF_CSD_PREFER_CLIENT;
|
|
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: csd.preferred: expected either 'server' or 'client'", path, lineno);
|
2020-03-02 18:42:49 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-03 18:18:59 +01:00
|
|
|
else if (strcmp(key, "color") == 0) {
|
2020-03-02 18:42:49 +01:00
|
|
|
uint32_t color;
|
2020-07-31 17:07:14 +02:00
|
|
|
if (!str_to_color(value, &color, true, path, lineno, "csd", "color")) {
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid titlebar-color: %s", path, lineno, value);
|
2020-03-02 18:42:49 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->csd.color.title_set = true;
|
|
|
|
|
conf->csd.color.title = color;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-03 18:18:59 +01:00
|
|
|
else if (strcmp(key, "size") == 0) {
|
2020-03-02 18:42:49 +01:00
|
|
|
unsigned long pixels;
|
|
|
|
|
if (!str_to_ulong(value, 10, &pixels)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value);
|
2020-03-02 18:42:49 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->csd.title_height = pixels;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
else if (strcmp(key, "button-width") == 0) {
|
|
|
|
|
unsigned long pixels;
|
|
|
|
|
if (!str_to_ulong(value, 10, &pixels)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value);
|
2020-03-02 20:29:28 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->csd.button_width = pixels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "button-minimize-color") == 0) {
|
|
|
|
|
uint32_t color;
|
2020-07-31 17:07:14 +02:00
|
|
|
if (!str_to_color(value, &color, true, path, lineno, "csd", "button-minimize-color")) {
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid button-minimize-color: %s", path, lineno, value);
|
2020-03-02 20:29:28 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->csd.color.minimize_set = true;
|
|
|
|
|
conf->csd.color.minimize = color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "button-maximize-color") == 0) {
|
|
|
|
|
uint32_t color;
|
2020-07-31 17:07:14 +02:00
|
|
|
if (!str_to_color(value, &color, true, path, lineno, "csd", "button-maximize-color")) {
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid button-maximize-color: %s", path, lineno, value);
|
2020-03-02 20:29:28 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->csd.color.maximize_set = true;
|
|
|
|
|
conf->csd.color.maximize = color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "button-close-color") == 0) {
|
|
|
|
|
uint32_t color;
|
2020-07-31 17:07:14 +02:00
|
|
|
if (!str_to_color(value, &color, true, path, lineno, "csd", "button-close-color")) {
|
|
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid button-close-color: %s", path, lineno, value);
|
2020-03-02 20:29:28 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->csd.color.close_set = true;
|
|
|
|
|
conf->csd.color.close = color;
|
|
|
|
|
}
|
2020-03-08 14:08:48 +01:00
|
|
|
|
|
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [csd]: %s: invalid key", path, lineno, key);
|
2020-03-08 14:08:48 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 18:42:49 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-12 17:16:35 +01:00
|
|
|
static bool
|
2020-07-31 17:07:14 +02:00
|
|
|
verify_key_combo(struct config *conf, const char *combo, const char *path, unsigned lineno)
|
2020-03-12 17:16:35 +01:00
|
|
|
{
|
2020-07-30 18:57:21 +02:00
|
|
|
/* Check regular key bindings */
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_foreach(conf->bindings.key, it) {
|
2020-08-04 23:28:16 +01:00
|
|
|
char *copy = xstrdup(it->item.key);
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
|
|
|
for (char *save = NULL, *collision = strtok_r(copy, " ", &save);
|
|
|
|
|
collision != NULL;
|
|
|
|
|
collision = strtok_r(NULL, " ", &save))
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(combo, collision) == 0) {
|
2020-07-30 18:57:21 +02:00
|
|
|
bool has_pipe = it->item.pipe.cmd != NULL;
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s%s%s%s'", path, lineno, combo,
|
2020-07-30 18:57:21 +02:00
|
|
|
binding_action_map[it->item.action],
|
|
|
|
|
has_pipe ? " [" : "",
|
|
|
|
|
has_pipe ? it->item.pipe.cmd : "",
|
|
|
|
|
has_pipe ? "]" : "");
|
|
|
|
|
free(copy);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(copy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check scrollback search bindings */
|
|
|
|
|
tll_foreach(conf->bindings.search, it) {
|
2020-08-04 23:28:16 +01:00
|
|
|
char *copy = xstrdup(it->item.key);
|
2020-07-30 18:57:21 +02:00
|
|
|
|
|
|
|
|
for (char *save = NULL, *collision = strtok_r(copy, " ", &save);
|
|
|
|
|
collision != NULL;
|
|
|
|
|
collision = strtok_r(NULL, " ", &save))
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(combo, collision) == 0) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to '%s'", path, lineno, combo,
|
2020-07-30 18:57:21 +02:00
|
|
|
search_binding_action_map[it->item.action]);
|
2020-03-12 17:16:35 +01:00
|
|
|
free(copy);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(copy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct xkb_context *ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
|
|
|
struct xkb_keymap *keymap = xkb_keymap_new_from_names(
|
2020-04-13 12:03:11 +02:00
|
|
|
ctx, &(struct xkb_rule_names){}, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
2020-03-12 17:16:35 +01:00
|
|
|
|
2020-03-18 15:30:14 +01:00
|
|
|
bool valid_combo = input_parse_key_binding(keymap, combo, NULL);
|
2020-03-12 17:16:35 +01:00
|
|
|
|
|
|
|
|
xkb_keymap_unref(keymap);
|
|
|
|
|
xkb_context_unref(ctx);
|
|
|
|
|
|
|
|
|
|
if (!valid_combo) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid key combination: %s", path, lineno, combo);
|
2020-03-12 17:16:35 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
static int
|
|
|
|
|
argv_compare(char *const *argv1, char *const *argv2)
|
|
|
|
|
{
|
|
|
|
|
assert(argv1 != NULL);
|
|
|
|
|
assert(argv2 != NULL);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; ; i++) {
|
|
|
|
|
if (argv1[i] == NULL && argv2[i] == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
if (argv1[i] == NULL)
|
|
|
|
|
return -1;
|
|
|
|
|
if (argv2[i] == NULL)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
int ret = strcmp(argv1[i], argv2[i]);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(false);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
static bool
|
2020-03-12 10:20:05 +01:00
|
|
|
parse_section_key_bindings(
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
2020-03-09 20:03:04 +01:00
|
|
|
{
|
2020-07-30 18:53:51 +02:00
|
|
|
char *pipe_cmd = NULL;
|
|
|
|
|
char **pipe_argv = NULL;
|
2020-07-15 17:28:09 +02:00
|
|
|
size_t pipe_len = 0;
|
2020-07-15 13:32:31 +02:00
|
|
|
|
2020-07-15 17:28:09 +02:00
|
|
|
if (value[0] == '[') {
|
|
|
|
|
const char *pipe_cmd_end = strrchr(value, ']');
|
|
|
|
|
if (pipe_cmd_end == NULL) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: unclosed '['", path, lineno);
|
2020-07-15 17:28:09 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 17:10:12 +02:00
|
|
|
pipe_len = pipe_cmd_end - value - 1;
|
2020-08-08 20:34:30 +01:00
|
|
|
pipe_cmd = xstrndup(&value[1], pipe_len);
|
2020-07-30 18:53:51 +02:00
|
|
|
|
|
|
|
|
if (!tokenize_cmdline(pipe_cmd, &pipe_argv)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: syntax error in command line", path, lineno);
|
2020-07-30 18:53:51 +02:00
|
|
|
free(pipe_cmd);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-07-15 17:28:09 +02:00
|
|
|
|
|
|
|
|
value = pipe_cmd_end + 1;
|
2020-07-30 18:53:51 +02:00
|
|
|
while (isspace(*value))
|
|
|
|
|
value++;
|
2020-07-15 17:28:09 +02:00
|
|
|
}
|
2020-07-15 13:32:31 +02:00
|
|
|
|
|
|
|
|
for (enum bind_action_normal action = 0;
|
|
|
|
|
action < BIND_ACTION_COUNT;
|
|
|
|
|
action++)
|
|
|
|
|
{
|
2020-03-12 10:46:27 +01:00
|
|
|
if (binding_action_map[action] == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-07-15 17:28:09 +02:00
|
|
|
if (strcmp(key, binding_action_map[action]) != 0)
|
2020-03-09 20:03:04 +01:00
|
|
|
continue;
|
|
|
|
|
|
2020-03-18 14:52:04 +01:00
|
|
|
if (strcasecmp(value, "none") == 0) {
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_foreach(conf->bindings.key, it) {
|
|
|
|
|
if (it->item.action == action) {
|
|
|
|
|
free(it->item.key);
|
2020-07-30 18:53:51 +02:00
|
|
|
free(it->item.pipe.cmd);
|
|
|
|
|
free(it->item.pipe.argv);
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_remove(conf->bindings.key, it);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-30 18:53:51 +02:00
|
|
|
free(pipe_argv);
|
|
|
|
|
free(pipe_cmd);
|
2020-03-12 17:16:35 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-03-09 20:03:04 +01:00
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
if (!verify_key_combo(conf, value, path, lineno)) {
|
2020-07-30 18:53:51 +02:00
|
|
|
free(pipe_argv);
|
|
|
|
|
free(pipe_cmd);
|
2020-03-09 20:03:04 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 16:39:07 +02:00
|
|
|
bool already_added = false;
|
|
|
|
|
tll_foreach(conf->bindings.key, it) {
|
|
|
|
|
if (it->item.action == action &&
|
2020-07-30 18:53:51 +02:00
|
|
|
((it->item.pipe.argv == NULL && pipe_argv == NULL) ||
|
|
|
|
|
(it->item.pipe.argv != NULL && pipe_argv != NULL &&
|
|
|
|
|
argv_compare(it->item.pipe.argv, pipe_argv) == 0)))
|
2020-07-15 16:39:07 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
free(it->item.key);
|
2020-07-30 18:53:51 +02:00
|
|
|
free(it->item.pipe.cmd);
|
|
|
|
|
free(it->item.pipe.argv);
|
2020-07-15 16:39:07 +02:00
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
it->item.key = xstrdup(value);
|
2020-07-30 18:53:51 +02:00
|
|
|
it->item.pipe.cmd = pipe_cmd;
|
|
|
|
|
it->item.pipe.argv = pipe_argv;
|
2020-07-15 16:39:07 +02:00
|
|
|
already_added = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!already_added) {
|
|
|
|
|
struct config_key_binding_normal binding = {
|
|
|
|
|
.action = action,
|
2020-08-04 23:28:16 +01:00
|
|
|
.key = xstrdup(value),
|
2020-07-30 18:53:51 +02:00
|
|
|
.pipe = {
|
|
|
|
|
.cmd = pipe_cmd,
|
|
|
|
|
.argv = pipe_argv,
|
|
|
|
|
},
|
2020-07-15 16:39:07 +02:00
|
|
|
};
|
|
|
|
|
tll_push_back(conf->bindings.key, binding);
|
|
|
|
|
}
|
2020-03-09 20:03:04 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [key-bindings]: %s: invalid key", path, lineno, key);
|
2020-03-09 20:03:04 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 17:27:01 +02:00
|
|
|
static bool
|
|
|
|
|
parse_section_search_bindings(
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
for (enum bind_action_search action = 0;
|
|
|
|
|
action < BIND_ACTION_SEARCH_COUNT;
|
|
|
|
|
action++)
|
|
|
|
|
{
|
|
|
|
|
if (search_binding_action_map[action] == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (strcmp(key, search_binding_action_map[action]) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (strcasecmp(value, "none") == 0) {
|
|
|
|
|
tll_foreach(conf->bindings.search, it) {
|
|
|
|
|
if (it->item.action == action) {
|
|
|
|
|
free(it->item.key);
|
|
|
|
|
tll_remove(conf->bindings.search, it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
if (!verify_key_combo(conf, value, path, lineno)) {
|
2020-07-29 17:27:01 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool already_added = false;
|
|
|
|
|
tll_foreach(conf->bindings.search, it) {
|
2020-07-30 18:53:51 +02:00
|
|
|
if (it->item.action == action) {
|
2020-07-29 17:27:01 +02:00
|
|
|
|
|
|
|
|
free(it->item.key);
|
|
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
it->item.key = xstrdup(value);
|
2020-07-29 17:27:01 +02:00
|
|
|
already_added = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!already_added) {
|
|
|
|
|
struct config_key_binding_search binding = {
|
|
|
|
|
.action = action,
|
2020-08-04 23:28:16 +01:00
|
|
|
.key = xstrdup(value),
|
2020-07-29 17:27:01 +02:00
|
|
|
};
|
|
|
|
|
tll_push_back(conf->bindings.search, binding);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [search-bindings]: %s: invalid key", path, lineno, key);
|
2020-07-29 17:27:01 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-12 10:20:05 +01:00
|
|
|
static bool
|
|
|
|
|
parse_section_mouse_bindings(
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
2020-03-18 15:30:14 +01:00
|
|
|
for (enum bind_action_normal action = 0; action < BIND_ACTION_COUNT; action++) {
|
2020-03-12 10:46:27 +01:00
|
|
|
if (binding_action_map[action] == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-03-12 10:20:05 +01:00
|
|
|
if (strcmp(key, binding_action_map[action]) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-03-12 10:46:27 +01:00
|
|
|
if (strcmp(value, "NONE") == 0) {
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_foreach(conf->bindings.mouse, it) {
|
|
|
|
|
if (it->item.action == action) {
|
|
|
|
|
tll_remove(conf->bindings.mouse, it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-12 10:46:27 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-12 10:20:05 +01:00
|
|
|
const char *map[] = {
|
|
|
|
|
[BTN_LEFT] = "BTN_LEFT",
|
|
|
|
|
[BTN_RIGHT] = "BTN_RIGHT",
|
|
|
|
|
[BTN_MIDDLE] = "BTN_MIDDLE",
|
|
|
|
|
[BTN_SIDE] = "BTN_SIDE",
|
|
|
|
|
[BTN_EXTRA] = "BTN_EXTRA",
|
|
|
|
|
[BTN_FORWARD] = "BTN_FORWARD",
|
|
|
|
|
[BTN_BACK] = "BTN_BACK",
|
|
|
|
|
[BTN_TASK] = "BTN_TASK",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ALEN(map); i++) {
|
|
|
|
|
if (map[i] == NULL || strcmp(map[i], value) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-03-12 10:46:27 +01:00
|
|
|
const int count = 1;
|
|
|
|
|
|
|
|
|
|
/* Make sure button isn't already mapped to another action */
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_foreach(conf->bindings.mouse, it) {
|
|
|
|
|
if (it->item.button == i && it->item.count == count) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: %s already mapped to %s", path, lineno,
|
2020-07-15 16:39:07 +02:00
|
|
|
value, binding_action_map[it->item.action]);
|
2020-03-12 10:46:27 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 16:39:07 +02:00
|
|
|
bool already_added = false;
|
|
|
|
|
tll_foreach(conf->bindings.mouse, it) {
|
|
|
|
|
if (it->item.action == action) {
|
|
|
|
|
it->item.button = i;
|
|
|
|
|
it->item.count = count;
|
|
|
|
|
already_added = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!already_added) {
|
|
|
|
|
struct mouse_binding binding = {
|
|
|
|
|
.action = action,
|
|
|
|
|
.button = i,
|
|
|
|
|
.count = count,
|
|
|
|
|
};
|
|
|
|
|
tll_push_back(conf->bindings.mouse, binding);
|
|
|
|
|
}
|
2020-03-12 10:20:05 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid mouse button: %s", path, lineno, value);
|
2020-03-12 10:20:05 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [mouse-bindings]: %s: invalid key", path, lineno, key);
|
2020-03-12 10:20:05 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 16:46:54 +01:00
|
|
|
static bool
|
|
|
|
|
parse_section_tweak(
|
|
|
|
|
const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(key, "delayed-render-lower") == 0) {
|
|
|
|
|
unsigned long ns;
|
|
|
|
|
if (!str_to_ulong(value, 10, &ns)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value);
|
2020-03-17 16:46:54 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ns > 16666666) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: timeout must not exceed 16ms", path, lineno);
|
2020-03-17 16:46:54 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->tweak.delayed_render_lower_ns = ns;
|
|
|
|
|
LOG_WARN("tweak: delayed-render-lower=%lu", ns);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (strcmp(key, "delayed-render-upper") == 0) {
|
|
|
|
|
unsigned long ns;
|
|
|
|
|
if (!str_to_ulong(value, 10, &ns)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value);
|
2020-03-17 16:46:54 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ns > 16666666) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: timeout must not exceed 16ms", path, lineno);
|
2020-03-17 16:46:54 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->tweak.delayed_render_upper_ns = ns;
|
|
|
|
|
LOG_WARN("tweak: delayed-render-upper=%lu", ns);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-25 20:48:02 +01:00
|
|
|
else if (strcmp(key, "max-shm-pool-size-mb") == 0) {
|
|
|
|
|
unsigned long mb;
|
|
|
|
|
if (!str_to_ulong(value, 10, &mb)) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: expected an integer, got '%s'", path, lineno, value);
|
2020-03-25 20:48:02 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf->tweak.max_shm_pool_size = min(mb * 1024 * 1024, INT32_MAX);
|
2020-08-06 23:20:46 +02:00
|
|
|
LOG_WARN("tweak: max-shm-pool-size=%lld bytes",
|
|
|
|
|
(long long)conf->tweak.max_shm_pool_size);
|
2020-03-25 20:48:02 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 16:46:54 +01:00
|
|
|
else {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%u: [tweak]: %s: invalid key", path, lineno, key);
|
2020-03-17 16:46:54 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
static bool
|
2020-07-31 17:07:14 +02:00
|
|
|
parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_are_fatal)
|
2019-07-16 11:52:22 +02:00
|
|
|
{
|
|
|
|
|
enum section {
|
|
|
|
|
SECTION_MAIN,
|
2020-07-29 17:41:24 +02:00
|
|
|
SECTION_SCROLLBACK,
|
2019-07-21 11:31:16 +02:00
|
|
|
SECTION_COLORS,
|
2019-07-22 20:15:14 +02:00
|
|
|
SECTION_CURSOR,
|
2020-08-04 07:33:15 +02:00
|
|
|
SECTION_MOUSE,
|
2020-03-02 18:42:49 +01:00
|
|
|
SECTION_CSD,
|
2020-03-09 20:03:04 +01:00
|
|
|
SECTION_KEY_BINDINGS,
|
2020-07-29 17:27:01 +02:00
|
|
|
SECTION_SEARCH_BINDINGS,
|
2020-03-12 10:20:05 +01:00
|
|
|
SECTION_MOUSE_BINDINGS,
|
2020-03-17 16:46:54 +01:00
|
|
|
SECTION_TWEAK,
|
2020-03-09 20:03:04 +01:00
|
|
|
SECTION_COUNT,
|
2019-07-16 11:52:22 +02:00
|
|
|
} section = SECTION_MAIN;
|
|
|
|
|
|
|
|
|
|
/* Function pointer, called for each key/value line */
|
|
|
|
|
typedef bool (*parser_fun_t)(
|
2019-07-17 10:12:14 +02:00
|
|
|
const char *key, const char *value, struct config *conf,
|
|
|
|
|
const char *path, unsigned lineno);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
static const struct {
|
|
|
|
|
parser_fun_t fun;
|
|
|
|
|
const char *name;
|
|
|
|
|
} section_info[] = {
|
2020-07-29 17:27:01 +02:00
|
|
|
[SECTION_MAIN] = {&parse_section_main, "main"},
|
2020-07-29 17:41:24 +02:00
|
|
|
[SECTION_SCROLLBACK] = {&parse_section_scrollback, "scrollback"},
|
2020-07-29 17:27:01 +02:00
|
|
|
[SECTION_COLORS] = {&parse_section_colors, "colors"},
|
|
|
|
|
[SECTION_CURSOR] = {&parse_section_cursor, "cursor"},
|
2020-08-04 07:33:15 +02:00
|
|
|
[SECTION_MOUSE] = {&parse_section_mouse, "mouse"},
|
2020-07-29 17:27:01 +02:00
|
|
|
[SECTION_CSD] = {&parse_section_csd, "csd"},
|
|
|
|
|
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
|
|
|
|
|
[SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"},
|
|
|
|
|
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
|
|
|
|
|
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
2019-07-16 11:52:22 +02:00
|
|
|
};
|
|
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
|
2019-07-17 10:12:14 +02:00
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
unsigned lineno = 0;
|
2020-03-02 18:48:29 +01:00
|
|
|
|
2019-07-21 11:46:46 +02:00
|
|
|
char *_line = NULL;
|
2020-03-02 18:48:29 +01:00
|
|
|
size_t count = 0;
|
2019-07-16 11:52:22 +02:00
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
#define error_or_continue() \
|
|
|
|
|
{ \
|
|
|
|
|
if (errors_are_fatal) \
|
|
|
|
|
goto err; \
|
|
|
|
|
else \
|
|
|
|
|
continue; \
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-16 11:52:22 +02:00
|
|
|
while (true) {
|
|
|
|
|
errno = 0;
|
|
|
|
|
lineno++;
|
|
|
|
|
|
2019-07-21 11:46:46 +02:00
|
|
|
ssize_t ret = getline(&_line, &count, f);
|
2019-07-16 11:52:22 +02:00
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
if (errno != 0) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERRNO("failed to read from configuration");
|
|
|
|
|
if (errors_are_fatal)
|
|
|
|
|
goto err;
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
/* Strip leading whitespace */
|
2019-07-21 11:46:46 +02:00
|
|
|
char *line = _line;
|
|
|
|
|
{
|
|
|
|
|
while (isspace(*line))
|
|
|
|
|
line++;
|
|
|
|
|
if (line[0] != '\0') {
|
|
|
|
|
char *end = line + strlen(line) - 1;
|
|
|
|
|
while (isspace(*end))
|
|
|
|
|
end--;
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Empty line, or comment */
|
2020-03-02 18:48:29 +01:00
|
|
|
if (line[0] == '\0' || line[0] == '#')
|
2019-07-21 11:46:46 +02:00
|
|
|
continue;
|
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
/* Split up into key/value pair + trailing comment */
|
|
|
|
|
char *key_value = strtok(line, "#");
|
2020-08-07 20:42:34 +01:00
|
|
|
char UNUSED *comment = strtok(NULL, "\n");
|
2020-07-28 19:55:09 +02:00
|
|
|
|
2019-07-21 11:46:46 +02:00
|
|
|
/* Check for new section */
|
2020-07-28 19:55:09 +02:00
|
|
|
if (key_value[0] == '[') {
|
|
|
|
|
char *end = strchr(key_value, ']');
|
2019-07-21 11:31:16 +02:00
|
|
|
if (end == NULL) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: syntax error: %s", path, lineno, key_value);
|
|
|
|
|
error_or_continue();
|
2019-07-21 11:31:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*end = '\0';
|
|
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
section = SECTION_COUNT;
|
|
|
|
|
for (enum section i = 0; i < SECTION_COUNT; i++) {
|
2020-07-28 19:55:09 +02:00
|
|
|
if (strcmp(&key_value[1], section_info[i].name) == 0) {
|
2020-03-09 20:03:04 +01:00
|
|
|
section = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (section == SECTION_COUNT) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: invalid section name: %s", path, lineno, &key_value[1]);
|
|
|
|
|
error_or_continue();
|
2019-07-21 11:31:16 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
/* Process next line */
|
2019-07-21 11:31:16 +02:00
|
|
|
continue;
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
char *key = strtok(key_value, "=");
|
|
|
|
|
if (key == NULL) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s:%d: syntax error: no key specified", path, lineno);
|
|
|
|
|
error_or_continue();
|
2020-07-28 19:55:09 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-17 10:12:14 +02:00
|
|
|
char *value = strtok(NULL, "\n");
|
2020-07-28 19:55:09 +02:00
|
|
|
if (value == NULL) {
|
|
|
|
|
/* Empty value, i.e. "key=" */
|
|
|
|
|
value = key + strlen(key);
|
|
|
|
|
}
|
2019-07-17 10:12:14 +02:00
|
|
|
|
2019-07-21 11:46:46 +02:00
|
|
|
/* Strip trailing whitespace from key (leading stripped earlier) */
|
2020-08-02 20:01:07 +02:00
|
|
|
{
|
2019-07-21 11:46:46 +02:00
|
|
|
assert(!isspace(*key));
|
|
|
|
|
|
2019-07-17 10:12:14 +02:00
|
|
|
char *end = key + strlen(key) - 1;
|
|
|
|
|
while (isspace(*end))
|
|
|
|
|
end--;
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
/* Strip leading+trailing whitespace from value */
|
2019-07-17 10:12:14 +02:00
|
|
|
{
|
|
|
|
|
while (isspace(*value))
|
|
|
|
|
value++;
|
|
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
if (value[0] != '\0') {
|
|
|
|
|
char *end = value + strlen(value) - 1;
|
|
|
|
|
while (isspace(*end))
|
|
|
|
|
end--;
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-17 10:12:14 +02:00
|
|
|
|
2020-07-28 19:55:09 +02:00
|
|
|
LOG_DBG("section=%s, key='%s', value='%s', comment='%s'",
|
|
|
|
|
section_info[section].name, key, value, comment);
|
2019-07-17 10:12:14 +02:00
|
|
|
|
2020-03-09 20:03:04 +01:00
|
|
|
parser_fun_t section_parser = section_info[section].fun;
|
2019-07-16 11:52:22 +02:00
|
|
|
assert(section_parser != NULL);
|
|
|
|
|
|
2019-07-21 11:46:46 +02:00
|
|
|
if (!section_parser(key, value, conf, path, lineno))
|
2020-07-31 17:07:14 +02:00
|
|
|
error_or_continue();
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-02 18:48:29 +01:00
|
|
|
free(_line);
|
2019-07-16 11:52:22 +02:00
|
|
|
return true;
|
2019-07-21 11:46:46 +02:00
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
free(_line);
|
|
|
|
|
return false;
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-14 12:59:06 +01:00
|
|
|
static char *
|
|
|
|
|
get_server_socket_path(void)
|
|
|
|
|
{
|
|
|
|
|
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
|
|
|
|
|
if (xdg_runtime == NULL)
|
2020-08-04 23:28:16 +01:00
|
|
|
return xstrdup("/tmp/foot.sock");
|
2019-12-14 12:59:06 +01:00
|
|
|
|
2020-08-02 13:10:31 +02:00
|
|
|
const char *wayland_display = getenv("WAYLAND_DISPLAY");
|
|
|
|
|
if (wayland_display == NULL) {
|
2020-08-04 23:28:16 +01:00
|
|
|
return xasprintf("%s/foot.sock", xdg_runtime);
|
2020-08-02 13:10:31 +02:00
|
|
|
}
|
2020-03-09 18:47:10 +01:00
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
return xasprintf("%s/foot-%s.sock", xdg_runtime, wayland_display);
|
2019-12-14 12:59:06 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-17 10:12:14 +02:00
|
|
|
bool
|
2020-07-31 17:07:14 +02:00
|
|
|
config_load(struct config *conf, const char *conf_path, bool errors_are_fatal)
|
2019-07-16 11:52:22 +02:00
|
|
|
{
|
2019-07-17 10:12:14 +02:00
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
|
|
*conf = (struct config) {
|
2020-08-04 23:28:16 +01:00
|
|
|
.term = xstrdup("foot"),
|
2019-07-17 09:29:56 +02:00
|
|
|
.shell = get_shell(),
|
2020-08-04 23:28:16 +01:00
|
|
|
.title = xstrdup("foot"),
|
|
|
|
|
.app_id = xstrdup("foot"),
|
2020-02-29 11:01:23 +01:00
|
|
|
.width = 700,
|
|
|
|
|
.height = 500,
|
2020-02-15 19:00:56 +01:00
|
|
|
.pad_x = 2,
|
|
|
|
|
.pad_y = 2,
|
2020-03-26 19:39:12 +01:00
|
|
|
.startup_mode = STARTUP_WINDOWED,
|
2019-07-30 18:04:28 +02:00
|
|
|
.fonts = tll_init(),
|
2020-07-25 14:31:45 +02:00
|
|
|
.scrollback = {
|
|
|
|
|
.lines = 1000,
|
|
|
|
|
.indicator = {
|
2020-07-27 20:02:51 +02:00
|
|
|
.position = SCROLLBACK_INDICATOR_POSITION_RELATIVE,
|
2020-07-28 19:56:53 +02:00
|
|
|
.format = SCROLLBACK_INDICATOR_FORMAT_TEXT,
|
|
|
|
|
.text = wcsdup(L""),
|
2020-07-25 14:31:45 +02:00
|
|
|
},
|
2020-08-03 19:43:06 +02:00
|
|
|
.multiplier = 1.,
|
2020-07-25 14:31:45 +02:00
|
|
|
},
|
2019-07-21 11:06:28 +02:00
|
|
|
.colors = {
|
|
|
|
|
.fg = default_foreground,
|
|
|
|
|
.bg = default_background,
|
|
|
|
|
.regular = {
|
|
|
|
|
default_regular[0],
|
|
|
|
|
default_regular[1],
|
|
|
|
|
default_regular[2],
|
|
|
|
|
default_regular[3],
|
|
|
|
|
default_regular[4],
|
|
|
|
|
default_regular[5],
|
|
|
|
|
default_regular[6],
|
|
|
|
|
default_regular[7],
|
|
|
|
|
},
|
|
|
|
|
.bright = {
|
|
|
|
|
default_bright[0],
|
|
|
|
|
default_bright[1],
|
|
|
|
|
default_bright[2],
|
|
|
|
|
default_bright[3],
|
|
|
|
|
default_bright[4],
|
|
|
|
|
default_bright[5],
|
|
|
|
|
default_bright[6],
|
|
|
|
|
default_bright[7],
|
|
|
|
|
},
|
2019-08-16 22:06:06 +02:00
|
|
|
.alpha = 0xffff,
|
2020-08-12 18:53:32 +02:00
|
|
|
.selection_fg = 0x80000000, /* Use default bg */
|
|
|
|
|
.selection_bg = 0x80000000, /* Use default fg */
|
|
|
|
|
.selection_uses_custom_colors = false,
|
2019-07-21 11:06:28 +02:00
|
|
|
},
|
2019-07-22 20:15:14 +02:00
|
|
|
|
|
|
|
|
.cursor = {
|
|
|
|
|
.style = CURSOR_BLOCK,
|
2020-06-30 17:45:34 +02:00
|
|
|
.blink = false,
|
2019-07-23 18:54:58 +02:00
|
|
|
.color = {
|
|
|
|
|
.text = 0,
|
|
|
|
|
.cursor = 0,
|
|
|
|
|
},
|
2019-07-22 20:15:14 +02:00
|
|
|
},
|
2020-08-04 07:33:15 +02:00
|
|
|
.mouse = {
|
|
|
|
|
.hide_when_typing = false,
|
|
|
|
|
},
|
2020-03-02 18:42:49 +01:00
|
|
|
.csd = {
|
|
|
|
|
.preferred = CONF_CSD_PREFER_SERVER,
|
|
|
|
|
.title_height = 26,
|
|
|
|
|
.border_width = 5,
|
2020-03-06 19:11:31 +01:00
|
|
|
.button_width = 26,
|
2020-03-02 18:42:49 +01:00
|
|
|
},
|
|
|
|
|
|
2019-07-29 20:13:26 +02:00
|
|
|
.render_worker_count = sysconf(_SC_NPROCESSORS_ONLN),
|
2019-12-14 12:59:06 +01:00
|
|
|
.server_socket_path = get_server_socket_path(),
|
2020-02-03 19:58:32 +01:00
|
|
|
.presentation_timings = false,
|
|
|
|
|
.hold_at_exit = false,
|
2020-03-17 16:46:54 +01:00
|
|
|
|
|
|
|
|
.tweak = {
|
|
|
|
|
.delayed_render_lower_ns = 500000, /* 0.5ms */
|
|
|
|
|
.delayed_render_upper_ns = 16666666 / 2, /* half a frame period (60Hz) */
|
2020-03-25 20:48:02 +01:00
|
|
|
.max_shm_pool_size = 512 * 1024 * 1024,
|
2020-03-17 16:46:54 +01:00
|
|
|
},
|
2020-07-29 19:42:12 +02:00
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
.notifications = tll_init(),
|
2019-07-16 11:52:22 +02:00
|
|
|
};
|
|
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
struct config_key_binding_normal scrollback_up = {BIND_ACTION_SCROLLBACK_UP, xstrdup("Shift+Page_Up")};
|
|
|
|
|
struct config_key_binding_normal scrollback_down = {BIND_ACTION_SCROLLBACK_DOWN, xstrdup("Shift+Page_Down")};
|
|
|
|
|
struct config_key_binding_normal clipboard_copy = {BIND_ACTION_CLIPBOARD_COPY, xstrdup("Control+Shift+C")};
|
|
|
|
|
struct config_key_binding_normal clipboard_paste = {BIND_ACTION_CLIPBOARD_PASTE, xstrdup("Control+Shift+V")};
|
|
|
|
|
struct config_key_binding_normal search_start = {BIND_ACTION_SEARCH_START, xstrdup("Control+Shift+R")};
|
|
|
|
|
struct config_key_binding_normal font_size_up = {BIND_ACTION_FONT_SIZE_UP, xstrdup("Control+plus Control+equal Control+KP_Add")};
|
|
|
|
|
struct config_key_binding_normal font_size_down = {BIND_ACTION_FONT_SIZE_DOWN, xstrdup("Control+minus Control+KP_Subtract")};
|
|
|
|
|
struct config_key_binding_normal font_size_reset = {BIND_ACTION_FONT_SIZE_RESET, xstrdup("Control+0 Control+KP_0")};
|
|
|
|
|
struct config_key_binding_normal spawn_terminal = {BIND_ACTION_SPAWN_TERMINAL, xstrdup("Control+Shift+N")};
|
2020-07-15 16:39:07 +02:00
|
|
|
|
|
|
|
|
tll_push_back(conf->bindings.key, scrollback_up);
|
|
|
|
|
tll_push_back(conf->bindings.key, scrollback_down);
|
|
|
|
|
tll_push_back(conf->bindings.key, clipboard_copy);
|
|
|
|
|
tll_push_back(conf->bindings.key, clipboard_paste);
|
|
|
|
|
tll_push_back(conf->bindings.key, search_start);
|
|
|
|
|
tll_push_back(conf->bindings.key, font_size_up);
|
|
|
|
|
tll_push_back(conf->bindings.key, font_size_down);
|
|
|
|
|
tll_push_back(conf->bindings.key, font_size_reset);
|
|
|
|
|
tll_push_back(conf->bindings.key, spawn_terminal);
|
|
|
|
|
|
|
|
|
|
struct mouse_binding primary_paste = {BIND_ACTION_PRIMARY_PASTE, BTN_MIDDLE, 1};
|
|
|
|
|
tll_push_back(conf->bindings.mouse, primary_paste);
|
|
|
|
|
|
2020-08-04 23:28:16 +01:00
|
|
|
struct config_key_binding_search search_cancel = {BIND_ACTION_SEARCH_CANCEL, xstrdup("Control+g Escape")};
|
|
|
|
|
struct config_key_binding_search search_commit = {BIND_ACTION_SEARCH_COMMIT, xstrdup("Return")};
|
|
|
|
|
struct config_key_binding_search search_find_prev = {BIND_ACTION_SEARCH_FIND_PREV, xstrdup("Control+r")};
|
|
|
|
|
struct config_key_binding_search search_find_next = {BIND_ACTION_SEARCH_FIND_NEXT, xstrdup("Control+s")};
|
|
|
|
|
struct config_key_binding_search search_edit_left = {BIND_ACTION_SEARCH_EDIT_LEFT, xstrdup("Left Control+b")};
|
|
|
|
|
struct config_key_binding_search search_edit_left_word = {BIND_ACTION_SEARCH_EDIT_LEFT_WORD, xstrdup("Control+Left Mod1+b")};
|
|
|
|
|
struct config_key_binding_search search_edit_right = {BIND_ACTION_SEARCH_EDIT_RIGHT, xstrdup("Right Control+f")};
|
|
|
|
|
struct config_key_binding_search search_edit_right_word = {BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, xstrdup("Control+Right Mod1+f")};
|
|
|
|
|
struct config_key_binding_search search_edit_home = {BIND_ACTION_SEARCH_EDIT_HOME, xstrdup("Home Control+a")};
|
|
|
|
|
struct config_key_binding_search search_edit_end = {BIND_ACTION_SEARCH_EDIT_END, xstrdup("End Control+e")};
|
|
|
|
|
struct config_key_binding_search search_del_prev = {BIND_ACTION_SEARCH_DELETE_PREV, xstrdup("BackSpace")};
|
|
|
|
|
struct config_key_binding_search search_del_prev_word = {BIND_ACTION_SEARCH_DELETE_PREV_WORD, xstrdup("Mod1+BackSpace Control+BackSpace")};
|
|
|
|
|
struct config_key_binding_search search_del_next = {BIND_ACTION_SEARCH_DELETE_NEXT, xstrdup("Delete")};
|
|
|
|
|
struct config_key_binding_search search_del_next_word = {BIND_ACTION_SEARCH_DELETE_NEXT_WORD, xstrdup("Mod1+d Control+Delete")};
|
|
|
|
|
struct config_key_binding_search search_ext_word = {BIND_ACTION_SEARCH_EXTEND_WORD, xstrdup("Control+w")};
|
|
|
|
|
struct config_key_binding_search search_ext_word_ws = {BIND_ACTION_SEARCH_EXTEND_WORD_WS, xstrdup("Control+Shift+W")};
|
2020-07-15 16:39:07 +02:00
|
|
|
|
|
|
|
|
tll_push_back(conf->bindings.search, search_cancel);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_commit);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_find_prev);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_find_next);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_edit_left);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_edit_left_word);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_edit_right);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_edit_right_word);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_edit_home);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_edit_end);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_del_prev);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_del_prev_word);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_del_next);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_del_next_word);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_ext_word);
|
|
|
|
|
tll_push_back(conf->bindings.search, search_ext_word_ws);
|
|
|
|
|
|
2019-12-17 19:08:43 +01:00
|
|
|
char *default_path = NULL;
|
|
|
|
|
if (conf_path == NULL) {
|
|
|
|
|
if ((default_path = get_config_path()) == NULL) {
|
|
|
|
|
/* Default conf */
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_WARN("no configuration found, using defaults");
|
|
|
|
|
ret = !errors_are_fatal;
|
2019-12-17 19:08:43 +01:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conf_path = default_path;
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-17 19:08:43 +01:00
|
|
|
assert(conf_path != NULL);
|
|
|
|
|
LOG_INFO("loading configuration from %s", conf_path);
|
2019-08-12 19:31:21 +02:00
|
|
|
|
2019-12-17 19:08:43 +01:00
|
|
|
FILE *f = fopen(conf_path, "r");
|
2019-07-16 11:52:22 +02:00
|
|
|
if (f == NULL) {
|
2020-07-31 17:07:14 +02:00
|
|
|
LOG_AND_NOTIFY_ERR("%s: failed to open", conf_path);
|
|
|
|
|
ret = !errors_are_fatal;
|
2019-07-16 11:52:22 +02:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-31 17:07:14 +02:00
|
|
|
ret = parse_config_file(f, conf, conf_path, errors_are_fatal);
|
2019-07-16 11:52:22 +02:00
|
|
|
fclose(f);
|
|
|
|
|
|
2020-08-12 18:53:32 +02:00
|
|
|
conf->colors.selection_uses_custom_colors =
|
|
|
|
|
conf->colors.selection_fg >> 24 == 0 &&
|
|
|
|
|
conf->colors.selection_bg >> 24 == 0;
|
|
|
|
|
|
2020-02-28 18:33:30 +01:00
|
|
|
out:
|
2019-12-17 19:08:43 +01:00
|
|
|
if (ret && tll_length(conf->fonts) == 0)
|
2020-07-07 10:44:55 +02:00
|
|
|
tll_push_back(conf->fonts, config_font_parse("monospace"));
|
2019-12-17 19:08:43 +01:00
|
|
|
|
|
|
|
|
free(default_path);
|
2019-07-17 10:12:14 +02:00
|
|
|
return ret;
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
config_free(struct config conf)
|
|
|
|
|
{
|
2019-07-21 11:46:46 +02:00
|
|
|
free(conf.term);
|
2019-07-17 09:29:56 +02:00
|
|
|
free(conf.shell);
|
2020-04-01 19:59:47 +02:00
|
|
|
free(conf.title);
|
2020-04-01 18:40:51 +02:00
|
|
|
free(conf.app_id);
|
2020-07-28 19:56:53 +02:00
|
|
|
free(conf.scrollback.indicator.text);
|
2020-07-07 10:44:55 +02:00
|
|
|
tll_foreach(conf.fonts, it)
|
|
|
|
|
config_font_destroy(&it->item);
|
|
|
|
|
tll_free(conf.fonts);
|
2019-12-14 12:59:06 +01:00
|
|
|
free(conf.server_socket_path);
|
2020-03-08 12:08:46 +01:00
|
|
|
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_foreach(conf.bindings.key, it) {
|
|
|
|
|
free(it->item.key);
|
2020-07-30 18:53:51 +02:00
|
|
|
free(it->item.pipe.cmd);
|
|
|
|
|
free(it->item.pipe.argv);
|
2020-07-15 13:32:31 +02:00
|
|
|
}
|
2020-07-15 16:39:07 +02:00
|
|
|
tll_foreach(conf.bindings.search, it)
|
|
|
|
|
free(it->item.key);
|
|
|
|
|
|
|
|
|
|
tll_free(conf.bindings.key);
|
|
|
|
|
tll_free(conf.bindings.mouse);
|
|
|
|
|
tll_free(conf.bindings.search);
|
2020-07-29 19:42:12 +02:00
|
|
|
|
2020-07-30 18:57:21 +02:00
|
|
|
tll_foreach(conf.notifications, it)
|
2020-07-29 19:42:12 +02:00
|
|
|
free(it->item.text);
|
2020-07-30 18:57:21 +02:00
|
|
|
tll_free(conf.notifications);
|
2019-07-16 11:52:22 +02:00
|
|
|
}
|
2020-07-07 10:44:55 +02:00
|
|
|
|
|
|
|
|
struct config_font
|
|
|
|
|
config_font_parse(const char *pattern)
|
|
|
|
|
{
|
|
|
|
|
FcPattern *pat = FcNameParse((const FcChar8 *)pattern);
|
|
|
|
|
|
|
|
|
|
double pt_size = -1.0;
|
|
|
|
|
FcPatternGetDouble(pat, FC_SIZE, 0, &pt_size);
|
|
|
|
|
FcPatternRemove(pat, FC_SIZE, 0);
|
|
|
|
|
|
|
|
|
|
int px_size = -1;
|
|
|
|
|
FcPatternGetInteger(pat, FC_PIXEL_SIZE, 0, &px_size);
|
|
|
|
|
FcPatternRemove(pat, FC_PIXEL_SIZE, 0);
|
|
|
|
|
|
|
|
|
|
if (pt_size == -1. && px_size == -1)
|
|
|
|
|
pt_size = 8.0;
|
|
|
|
|
|
|
|
|
|
char *stripped_pattern = (char *)FcNameUnparse(pat);
|
|
|
|
|
FcPatternDestroy(pat);
|
|
|
|
|
|
|
|
|
|
return (struct config_font){
|
|
|
|
|
.pattern = stripped_pattern,
|
|
|
|
|
.pt_size = pt_size,
|
|
|
|
|
.px_size = px_size};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
config_font_destroy(struct config_font *font)
|
|
|
|
|
{
|
|
|
|
|
if (font == NULL)
|
|
|
|
|
return;
|
|
|
|
|
free(font->pattern);
|
|
|
|
|
}
|